├── func
├── scss_to_css
│ ├── BUILD.bazel
│ └── index.js
├── css_to_json
│ ├── index.js
│ └── BUILD.bazel
├── hello_world
│ ├── main.go
│ └── BUILD.bazel
├── watttime_demo
│ ├── BUILD.bazel
│ └── main.go
├── readable_profiles
│ ├── BUILD.bazel
│ └── main.go
├── encrypt_secret_value
│ ├── BUILD.bazel
│ └── index.js
├── enforce_contract
│ ├── BUILD.bazel
│ └── index.js
├── azure_hedge
│ └── BUILD.bazel
├── grant_access
│ ├── BUILD.bazel
│ └── AmazonRootCA1.pem
├── renewable_record
│ └── BUILD.bazel
├── validate_entity
│ └── BUILD.bazel
├── convert_media
│ ├── BUILD.bazel
│ └── main.go
└── index_entity
│ ├── BUILD.bazel
│ └── main.go
├── app
├── image_feature
│ └── BUILD.bazel
└── knn_spherical
│ └── BUILD.bazel
├── iac
├── precheck-cognito
│ ├── .gitignore
│ ├── message
│ │ ├── README.md
│ │ ├── package.json
│ │ └── index.js
│ ├── tsconfig.json
│ ├── Pulumi.yaml
│ ├── Pulumi.dev.yaml
│ ├── index.ts
│ ├── post-confirmation
│ │ ├── package.json
│ │ └── index.js
│ ├── project.json
│ ├── stack-reference.ts
│ └── eslintrc.json
└── precheck-oidc-auth
│ ├── .gitignore
│ ├── tsconfig.json
│ ├── Pulumi.dev.yaml
│ ├── user
│ ├── package.json
│ ├── index.js
│ └── package-lock.json
│ ├── Pulumi.yaml
│ ├── login
│ ├── package.json
│ └── index.js
│ ├── token
│ ├── package.json
│ └── index.js
│ ├── index.ts
│ ├── callback
│ └── package.json
│ ├── project.json
│ ├── .eslintrc.json
│ ├── stack-reference.ts
│ ├── shared
│ └── logger.js
│ └── README.md
├── architecture
├── plugins.md
├── commands.md
└── sites.md
├── .vscode
└── settings.json
├── lib
├── user
│ ├── user.go
│ └── BUILD.bazel
├── utils
│ ├── uuid.go
│ ├── jwt.go
│ ├── BUILD.bazel
│ ├── adub.go
│ └── compact.go
├── hedge
│ ├── package.json
│ └── BUILD.bazel
├── attr
│ ├── BUILD.bazel
│ └── attr.go
├── chat
│ ├── BUILD.bazel
│ └── chat.go
├── vocab
│ ├── BUILD.bazel
│ └── vocab.go
├── profiles
│ └── BUILD.bazel
├── watttime
│ ├── BUILD.bazel
│ └── watttime.go
├── cc
│ └── BUILD.bazel
├── ads
│ ├── BUILD.bazel
│ └── ads.go
├── es
│ ├── BUILD.bazel
│ └── es.go
├── os
│ ├── BUILD.bazel
│ └── os.go
├── search
│ ├── BUILD.bazel
│ └── stats.go
├── gov
│ ├── BUILD.bazel
│ └── gov.go
├── sign
│ └── BUILD.bazel
├── repo
│ └── BUILD.bazel
├── entity
│ └── BUILD.bazel
└── shapeshift
│ └── BUILD.bazel
├── BUILD.bazel
├── mappings
└── renewable-record.json
├── email_templates
├── temp_password.json
└── mytemplate.json
├── docker-compose.yml
├── .gitignore
├── api
├── model
│ ├── main.go
│ └── BUILD.bazel
├── entity
│ ├── main.go
│ ├── BUILD.bazel
│ ├── test_data
│ │ └── post_shapeshift.json
│ └── types.json.tmpl
├── versait
│ └── BUILD.bazel
├── proxy
│ └── BUILD.bazel
├── index
│ └── BUILD.bazel
├── gov
│ ├── BUILD.bazel
│ └── main.go
├── authorizer
│ ├── BUILD.bazel
│ └── main.go
├── authorizer2
│ ├── BUILD.bazel
│ └── main.go
├── authorizer3
│ ├── BUILD.bazel
│ └── main.go
├── stream
│ ├── BUILD.bazel
│ └── AmazonRootCA1.pem
├── chat
│ ├── BUILD.bazel
│ ├── AmazonRootCA1.pem
│ └── queries.tmpl
├── user
│ └── BUILD.bazel
└── media
│ └── BUILD.bazel
├── node_deps.bzl
├── edge
└── renewable_redirect
│ └── BUILD.bazel
├── package.json
├── hooks
├── site_complete
│ ├── BUILD.bazel
│ └── main.go
├── site_start
│ ├── main.go
│ └── BUILD.bazel
├── site_staticwebapp
│ └── BUILD.bazel
├── site_init
│ └── BUILD.bazel
├── site_modify
│ └── BUILD.bazel
├── entity_index
│ └── BUILD.bazel
├── index_create
│ ├── BUILD.bazel
│ └── main.go
├── site_write
│ ├── BUILD.bazel
│ └── main.go
├── site_deploy
│ └── BUILD.bazel
└── site_environment
│ └── BUILD.bazel
├── manifests
└── entity-api.yaml
├── cloudform
├── bucket.yml
├── search2.sh
├── search_minimal.sh
├── search_minimal.yaml
├── main.yaml
├── search_distinct.sh
├── cognito_role.sh
├── cognito_role.yaml
├── search.sh
├── users.yaml
├── search.yaml
└── search_distinct.yaml
├── job
└── renewable_report
│ └── BUILD.bazel
├── HelloWorld.yml
├── serverless-watttime-demo.yml
├── go.mod
├── serverless-versait.yml
├── Dockerfile
└── serverless-splits.yml
/func/scss_to_css/BUILD.bazel:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/func/scss_to_css/index.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/image_feature/BUILD.bazel:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/iac/precheck-cognito/.gitignore:
--------------------------------------------------------------------------------
1 | Pulumi.local*.yaml
--------------------------------------------------------------------------------
/iac/precheck-oidc-auth/.gitignore:
--------------------------------------------------------------------------------
1 | Pulumi.local*.yaml
--------------------------------------------------------------------------------
/architecture/plugins.md:
--------------------------------------------------------------------------------
1 | Call functions if they exists at certain points to alter
--------------------------------------------------------------------------------
/iac/precheck-cognito/message/README.md:
--------------------------------------------------------------------------------
1 | npm install fails with node 20 best bet use 18.
--------------------------------------------------------------------------------
/iac/precheck-oidc-auth/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json"
3 | }
4 |
--------------------------------------------------------------------------------
/iac/precheck-cognito/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json"
3 | }
4 |
--------------------------------------------------------------------------------
/architecture/commands.md:
--------------------------------------------------------------------------------
1 | dev: serverless deploy
2 | prod: serverless deploy --aws-profile prod --stage prod
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.codeActionsOnSave": null,
3 | "git.ignoreLimitWarning": true
4 | }
--------------------------------------------------------------------------------
/lib/user/user.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | type PublicUserProfile struct {
4 | Id string
5 | UserName string
6 | }
7 |
--------------------------------------------------------------------------------
/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@bazel_gazelle//:def.bzl", "gazelle")
2 |
3 | gazelle(
4 | name = "gazelle",
5 | prefix = "goclassifieds",
6 | )
7 |
--------------------------------------------------------------------------------
/lib/utils/uuid.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import "github.com/google/uuid"
4 |
5 | func GenerateId() string {
6 | uuid, _ := uuid.NewUUID()
7 | return uuid.String()
8 | }
9 |
--------------------------------------------------------------------------------
/mappings/renewable-record.json:
--------------------------------------------------------------------------------
1 | {
2 | "properties": {
3 | "carbon": {
4 | "type": "float"
5 | },
6 | "electricity": {
7 | "type": "float"
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/lib/hedge/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@rollthecloudinc/hedge",
3 | "version": "0.0.23",
4 | "main": "hedge_lib.js",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/rollthecloudinc/hedge"
8 | }
9 | }
--------------------------------------------------------------------------------
/lib/attr/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_library")
2 |
3 | go_library(
4 | name = "attr",
5 | srcs = ["attr.go"],
6 | importpath = "goclassifieds/lib/attr",
7 | visibility = ["//visibility:public"],
8 | )
9 |
--------------------------------------------------------------------------------
/lib/chat/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_library")
2 |
3 | go_library(
4 | name = "chat",
5 | srcs = ["chat.go"],
6 | importpath = "goclassifieds/lib/chat",
7 | visibility = ["//visibility:public"],
8 | )
9 |
--------------------------------------------------------------------------------
/lib/user/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_library")
2 |
3 | go_library(
4 | name = "user",
5 | srcs = ["user.go"],
6 | importpath = "goclassifieds/lib/user",
7 | visibility = ["//visibility:public"],
8 | )
9 |
--------------------------------------------------------------------------------
/lib/vocab/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_library")
2 |
3 | go_library(
4 | name = "vocab",
5 | srcs = ["vocab.go"],
6 | importpath = "goclassifieds/lib/vocab",
7 | visibility = ["//visibility:public"],
8 | )
9 |
--------------------------------------------------------------------------------
/lib/profiles/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_library")
2 |
3 | go_library(
4 | name = "profiles",
5 | srcs = ["profiles.go"],
6 | importpath = "goclassifieds/lib/profiles",
7 | visibility = ["//visibility:public"],
8 | )
9 |
--------------------------------------------------------------------------------
/iac/precheck-oidc-auth/Pulumi.dev.yaml:
--------------------------------------------------------------------------------
1 | config:
2 | precheck-oidc-auth:ApiDeploymentStage: dev
3 | precheck-oidc-auth:LoginGovRedirectUri: "https://5bh4unsxta.execute-api.us-east-2.amazonaws.com/dev/callback"
4 | aws:profile: precheck-dev
5 | aws:region: us-east-1
6 |
--------------------------------------------------------------------------------
/lib/watttime/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_library")
2 |
3 | go_library(
4 | name = "watttime",
5 | srcs = ["watttime.go"],
6 | importpath = "goclassifieds/lib/watttime",
7 | visibility = ["//visibility:public"],
8 | deps = [
9 | ],
10 | )
11 |
--------------------------------------------------------------------------------
/iac/precheck-cognito/Pulumi.yaml:
--------------------------------------------------------------------------------
1 | name: precheck-cognito
2 | description: Authenication system using cognito and identity pools
3 | runtime:
4 | name: nodejs
5 | options:
6 | packagemanager: npm
7 | config:
8 | pulumi:tags:
9 | value:
10 | pulumi:template: aws-typescript
11 |
--------------------------------------------------------------------------------
/lib/cc/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_library")
2 |
3 | go_library(
4 | name = "cc",
5 | srcs = ["cc.go"],
6 | importpath = "goclassifieds/lib/cc",
7 | visibility = ["//visibility:public"],
8 | deps = [
9 | "//lib/attr",
10 | ],
11 | )
12 |
--------------------------------------------------------------------------------
/email_templates/temp_password.json:
--------------------------------------------------------------------------------
1 | {
2 | "Template": {
3 | "TemplateName": "TempPassword",
4 | "SubjectPart": "Greetings, {{name}}!",
5 | "HtmlPart": "
Hello {{name}},
Your temp password is {{tempPassword}}
",
6 | "TextPart": "Dear {{name}},\r\nYour temp password is {{tempPassword}}"
7 | }
8 | }
--------------------------------------------------------------------------------
/email_templates/mytemplate.json:
--------------------------------------------------------------------------------
1 | {
2 | "Template": {
3 | "TemplateName": "MyTemplate",
4 | "SubjectPart": "Greetings, {{name}}!",
5 | "HtmlPart": "Hello {{name}},
Your favorite animal is {{favoriteanimal}}.
",
6 | "TextPart": "Dear {{name}},\r\nYour favorite animal is {{favoriteanimal}}."
7 | }
8 | }
--------------------------------------------------------------------------------
/lib/ads/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_library")
2 |
3 | go_library(
4 | name = "ads",
5 | srcs = ["ads.go"],
6 | importpath = "goclassifieds/lib/ads",
7 | visibility = ["//visibility:public"],
8 | deps = [
9 | "//lib/attr",
10 | "//lib/vocab",
11 | ],
12 | )
13 |
--------------------------------------------------------------------------------
/func/css_to_json/index.js:
--------------------------------------------------------------------------------
1 | var cssjson = require("cssjson");
2 |
3 | module.exports.handler = function(event, context, callback) {
4 | console.log('hello world');
5 | console.log(event);
6 | console.log(context);
7 | var json = cssjson.toJSON(event.Content, { comments: true });
8 | console.log(json);
9 | callback(null, json);
10 | }
--------------------------------------------------------------------------------
/lib/es/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_library")
2 |
3 | go_library(
4 | name = "es",
5 | srcs = ["es.go"],
6 | importpath = "goclassifieds/lib/es",
7 | visibility = ["//visibility:public"],
8 | deps = [
9 | "@com_github_elastic_go_elasticsearch_v7//:go-elasticsearch",
10 | ],
11 | )
12 |
--------------------------------------------------------------------------------
/lib/os/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_library")
2 |
3 | go_library(
4 | name = "os",
5 | srcs = ["os.go"],
6 | importpath = "goclassifieds/lib/os",
7 | visibility = ["//visibility:public"],
8 | deps = [
9 | "@com_github_opensearch_project_opensearch_go//:opensearch-go",
10 | ],
11 | )
12 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.8'
2 |
3 | services:
4 | bazel-environment:
5 | build: .
6 | image: bazel6-go17-nodejs
7 | volumes:
8 | - /var/run/docker.sock:/var/run/docker.sock
9 | - ~/projects/hedge:/workspace
10 | - ~/.aws:/root/.aws:ro
11 | working_dir: /workspace
12 | stdin_open: true
13 | tty: true
14 | privileged: true
--------------------------------------------------------------------------------
/lib/search/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_library")
2 |
3 | go_library(
4 | name = "search",
5 | srcs = ["dialect.go", "analyzers.go","engine.go","loader.go"],
6 | importpath = "goclassifieds/lib/search",
7 | visibility = ["//visibility:public"],
8 | deps = [
9 | "@com_github_google_go_github_v46//github",
10 | ],
11 | )
12 |
13 |
14 |
--------------------------------------------------------------------------------
/iac/precheck-oidc-auth/user/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "user",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "node-fetch": "^2.7.0"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/gov/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_library")
2 |
3 | go_library(
4 | name = "gov",
5 | srcs = ["gov.go"],
6 | importpath = "goclassifieds/lib/gov",
7 | visibility = ["//visibility:public"],
8 | deps = [
9 | "//lib/utils",
10 | "@com_github_google_go_github_v46//github",
11 | "@com_github_gocql_gocql//:gocql",
12 | ],
13 | )
14 |
--------------------------------------------------------------------------------
/iac/precheck-oidc-auth/Pulumi.yaml:
--------------------------------------------------------------------------------
1 | name: precheck-oidc-auth
2 | description: Centralized OIDC providing infinite integration capabilities unrestricted by vendor-to-vendor incompatibilities since this can be used as a middle man to satisfy gaps.
3 | runtime:
4 | name: nodejs
5 | options:
6 | packagemanager: npm
7 | config:
8 | pulumi:tags:
9 | value:
10 | pulumi:template: aws-typescript
11 |
--------------------------------------------------------------------------------
/iac/precheck-cognito/Pulumi.dev.yaml:
--------------------------------------------------------------------------------
1 | config:
2 | precheck-cognito:SesVerifiedEmail: "VERIFIED email address to dispatch transactional emails"
3 | precheck-cognito:OidcDomain: "ykpt3qxd62.execute-api.us-east-1.amazonaws.com"
4 | precheck-cognito:OidcStage: "dev"
5 | precheck-cognito:LoginGovClientId: "urn:gov:gsa:openidconnect.profiles:sp:sso:dhs:precheck-dev-pkce"
6 | aws:region: "us-east-1"
7 | aws:profile: "precheck-dev"
--------------------------------------------------------------------------------
/iac/precheck-oidc-auth/login/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "login",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "aws-sdk": "^2.1692.0",
14 | "crypto": "^1.0.1"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/iac/precheck-oidc-auth/token/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "token",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "aws-sdk": "^2.1692.0",
14 | "querystring": "^0.2.1"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/iac/precheck-cognito/index.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | import { loadConfig, register } from 'tsconfig-paths'
4 |
5 | const config = loadConfig('.')
6 | if (config.resultType === 'failed') {
7 | console.log('Could not load tsconfig to map paths, aborting.')
8 | process.exit(1)
9 | }
10 |
11 | register({
12 | baseUrl: config.absoluteBaseUrl,
13 | paths: config.paths
14 | })
15 |
16 | import './pulumi'
17 | export * from './pulumi'
18 |
--------------------------------------------------------------------------------
/iac/precheck-oidc-auth/index.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | import { loadConfig, register } from 'tsconfig-paths'
4 |
5 | const config = loadConfig('.')
6 | if (config.resultType === 'failed') {
7 | console.log('Could not load tsconfig to map paths, aborting.')
8 | process.exit(1)
9 | }
10 |
11 | register({
12 | baseUrl: config.absoluteBaseUrl,
13 | paths: config.paths
14 | })
15 |
16 | import './pulumi'
17 | export * from './pulumi'
18 |
--------------------------------------------------------------------------------
/lib/utils/jwt.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "strings"
5 |
6 | jwt "github.com/dgrijalva/jwt-go"
7 | "github.com/gin-gonic/gin"
8 | )
9 |
10 | func GetSubject(context *gin.Context) string {
11 | authPieces := strings.Split(context.GetHeader("Authorization"), " ")
12 | if len(authPieces) == 2 {
13 | claims := jwt.StandardClaims{}
14 | jwt.ParseWithClaims(authPieces[1], &claims, nil)
15 | return claims.Subject
16 | }
17 | return ""
18 | }
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .serverless
2 | bin
3 | node_modules
4 |
5 | bazel-bin
6 | bazel-goclassifieds
7 | bazel-out
8 | bazel-testlogs
9 | bazel-vertigo
10 | bazel-hedge
11 |
12 | private.dev.json
13 | private.prod.json
14 |
15 | api/entity/rtc-vertigo-dev.private-key.pem
16 | api/entity/rtc-vertigo-prod.private-key.pem
17 |
18 | api/media/rtc-vertigo-dev.private-key.pem
19 | api/media/rtc-vertigo-prod.private-key.pem
20 |
21 | .npmrc
22 | azure_credentials.json
23 |
24 | go/*
--------------------------------------------------------------------------------
/func/hello_world/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/aws/aws-lambda-go/events"
5 | "github.com/aws/aws-lambda-go/lambda"
6 | )
7 |
8 | func HandleRequest(req *events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
9 | body := "{\"works\": true}"
10 | return events.APIGatewayProxyResponse{StatusCode: 200, Body: body, Headers: map[string]string{"Content-Type": "application/json"}}, nil
11 | }
12 |
13 | func main() {
14 | lambda.Start(HandleRequest)
15 | }
16 |
--------------------------------------------------------------------------------
/iac/precheck-oidc-auth/callback/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "callback",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "aws-sdk": "^2.1692.0",
14 | "jsonwebtoken": "^9.0.2",
15 | "jwks-rsa": "^3.1.0",
16 | "node-fetch": "^2.7.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/api/model/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "context"
6 | "net/http"
7 |
8 | "github.com/aws/aws-lambda-go/events"
9 | "github.com/aws/aws-lambda-go/lambda"
10 | )
11 |
12 | func handler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
13 | return events.APIGatewayProxyResponse{
14 | StatusCode: http.StatusOK,
15 | Body: "Hello World",
16 | }, nil
17 | }
18 |
19 | func main() {
20 | log.SetFlags(0)
21 | lambda.Start(handler)
22 | }
--------------------------------------------------------------------------------
/iac/precheck-cognito/post-confirmation/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "precheck-cognito-post-confirmation",
3 | "version": "1.0.0",
4 | "description": "A Lambda function to do things after user confirmation of account.",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node index.js",
8 | "test": "echo \"No test specified\" && exit 0"
9 | },
10 | "author": "Telos",
11 | "license": "MIT",
12 | "dependencies": {
13 | "aws-sdk": "^2.1390.0"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/node_deps.bzl:
--------------------------------------------------------------------------------
1 | def setup_node_dependencies():
2 | # Load Node.js rules
3 | load("@build_bazel_rules_nodejs//:index.bzl", "node_repositories", "npm_install")
4 |
5 | # Declare Node.js repositories
6 | node_repositories(
7 | node_version = "16.6.2",
8 | package_json = ["//:package.json"],
9 | )
10 |
11 | # Install NPM packages
12 | npm_install(
13 | name = "npm",
14 | package_json = "//:package.json",
15 | package_lock_json = "//:package-lock.json",
16 | )
--------------------------------------------------------------------------------
/api/entity/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 |
6 | "goclassifieds/lib/shapeshift"
7 |
8 | "github.com/aws/aws-lambda-go/events"
9 | "github.com/aws/aws-lambda-go/lambda"
10 | )
11 |
12 | var handler shapeshift.Handler
13 |
14 | type Handler func(req *events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error)
15 |
16 | func init() {
17 | ac := shapeshift.ShapeshiftActionContext()
18 | handler = shapeshift.InitializeHandler(ac)
19 | }
20 |
21 | func main() {
22 | log.SetFlags(0)
23 | lambda.Start(handler)
24 | }
25 |
--------------------------------------------------------------------------------
/func/css_to_json/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@npm//@bazel/esbuild:index.bzl", "esbuild")
2 |
3 | esbuild(
4 | name = "css_to_json",
5 | entry_point = "index.js",
6 | platform = "node",
7 | target = "node10",
8 | tool = select({
9 | "@bazel_tools//src/conditions:darwin": "@esbuild_darwin//:bin/esbuild",
10 | "@bazel_tools//src/conditions:windows": "@esbuild_windows//:esbuild.exe",
11 | "@bazel_tools//src/conditions:linux_x86_64": "@esbuild_linux//:bin/esbuild",
12 | }),
13 | deps = [
14 | "@npm//cssjson",
15 | ],
16 | )
17 |
--------------------------------------------------------------------------------
/edge/renewable_redirect/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@npm//@bazel/esbuild:index.bzl", "esbuild")
2 |
3 | esbuild(
4 | name = "renewable_redirect",
5 | entry_point = "index.js",
6 | platform = "node",
7 | target = "node10",
8 | tool = select({
9 | "@bazel_tools//src/conditions:darwin": "@esbuild_darwin//:bin/esbuild",
10 | "@bazel_tools//src/conditions:windows": "@esbuild_windows//:esbuild.exe",
11 | "@bazel_tools//src/conditions:linux_x86_64": "@esbuild_linux//:bin/esbuild",
12 | }),
13 | deps = [
14 | "@npm//matcher",
15 | ],
16 | )
17 |
--------------------------------------------------------------------------------
/app/knn_spherical/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "knn_spherical",
5 | embed = [":knn_spherical_app"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/app/knn_spherical",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 |
13 | go_library(
14 | name = "knn_spherical_app",
15 | srcs = ["main.go"],
16 | importpath = "goclassifieds/app/knn_spherical",
17 | visibility = ["//visibility:private"],
18 | deps = [
19 | ],
20 | )
21 |
--------------------------------------------------------------------------------
/iac/precheck-cognito/message/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "precheck-cognito-message",
3 | "version": "1.0.0",
4 | "description": "A Lambda function to handle custom Cognito messages with SMS and email fallback.",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node index.js",
8 | "test": "echo \"No test specified\" && exit 0"
9 | },
10 | "author": "Telos",
11 | "license": "MIT",
12 | "dependencies": {
13 | "@aws-crypto/client-node": "^4.2.0",
14 | "aws-sdk": "^2.1390.0",
15 | "base64-js": "^1.5.1"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/lib/utils/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_library")
2 |
3 | go_library(
4 | name = "utils",
5 | srcs = [
6 | "jwt.go",
7 | "uuid.go",
8 | "log.go",
9 | "compact.go",
10 | "adub.go"
11 | ],
12 | importpath = "goclassifieds/lib/utils",
13 | visibility = ["//visibility:public"],
14 | deps = [
15 | "@com_github_dgrijalva_jwt_go//:jwt-go",
16 | "@com_github_gin_gonic_gin//:gin",
17 | "@com_github_google_uuid//:uuid",
18 | "@com_github_aws_aws_lambda_go//events",
19 | ],
20 | )
21 |
--------------------------------------------------------------------------------
/lib/sign/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_library")
2 |
3 | go_library(
4 | name = "sign",
5 | srcs = ["sign.go"],
6 | importpath = "goclassifieds/lib/sign",
7 | visibility = ["//visibility:public"],
8 | deps = [
9 | "@com_github_aws_aws_sdk_go//aws/credentials",
10 | "@com_github_aws_aws_sdk_go//aws/signer/v4:go_default_library",
11 | "@com_github_aws_aws_sdk_go//aws/session",
12 | "@com_github_aws_aws_sdk_go//service/cognitoidentity",
13 | "@com_github_aws_aws_sdk_go//service/cognitoidentityprovider",
14 | "@com_github_aws_aws_sdk_go//aws",
15 | ],
16 | )
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "goclassifieds",
3 | "devDependencies": {
4 | "@bazel/bazelisk": "^1.9.0",
5 | "@bazel/esbuild": "^3.6.0",
6 | "@bazel/ibazel": "^0.15.10",
7 | "@bazel/typescript": "3.8.0",
8 | "ajv": "^8.17.1",
9 | "ajv-formats": "^3.0.1",
10 | "cssjson": "^2.1.3",
11 | "libsodium-wrappers": "^0.7.10",
12 | "matcher": "^5.0.0",
13 | "serverless-custom-packaging-plugin": "^0.1.2",
14 | "serverless-knative": "^0.6.0",
15 | "serverless-prune-plugin": "^2.0.1"
16 | },
17 | "dependencies": {
18 | "serverless": "^3.40.0",
19 | "serverless-step-functions": "^3.23.3"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/api/model/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "model",
5 | embed = [":model_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/api/model",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 |
13 | go_library(
14 | name = "model_lib",
15 | srcs = ["main.go"],
16 | importpath = "goclassifieds/api/model",
17 | visibility = ["//visibility:private"],
18 | deps = [
19 | "@com_github_aws_aws_lambda_go//events",
20 | "@com_github_aws_aws_lambda_go//lambda",
21 | ],
22 | )
23 |
--------------------------------------------------------------------------------
/func/watttime_demo/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "watttime_demo",
5 | embed = [":watttime_demo_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/func/watttime_demo",
9 | visibility = ["//visibility:public"],
10 | )
11 |
12 | go_library(
13 | name = "watttime_demo_lib",
14 | srcs = ["main.go"],
15 | importpath = "goclassifieds/func/watttime_demo",
16 | visibility = ["//visibility:private"],
17 | deps = [
18 | "//lib/watttime",
19 | "@com_github_aws_aws_lambda_go//lambda",
20 | ],
21 | )
22 |
--------------------------------------------------------------------------------
/api/versait/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "versait",
5 | embed = [":versait_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/api/versait",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 |
13 | go_library(
14 | name = "versait_lib",
15 | srcs = ["main.go"],
16 | importpath = "goclassifieds/api/versait",
17 | visibility = ["//visibility:private"],
18 | deps = [
19 | "@com_github_aws_aws_lambda_go//events",
20 | "@com_github_aws_aws_lambda_go//lambda",
21 | ],
22 | )
23 |
--------------------------------------------------------------------------------
/func/hello_world/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "hello_world",
5 | embed = [":hello_world_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/func/hello_world",
9 | visibility = ["//visibility:public"],
10 | )
11 |
12 | go_library(
13 | name = "hello_world_lib",
14 | srcs = ["main.go"],
15 | importpath = "goclassifieds/func/hello_world",
16 | visibility = ["//visibility:private"],
17 | deps = [
18 | "@com_github_aws_aws_lambda_go//events",
19 | "@com_github_aws_aws_lambda_go//lambda",
20 | ],
21 | )
22 |
--------------------------------------------------------------------------------
/func/readable_profiles/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "readable_profiles",
5 | embed = [":readable_profiles_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/func/readable_profiles",
9 | visibility = ["//visibility:public"],
10 | )
11 |
12 | go_library(
13 | name = "readable_profiles_lib",
14 | srcs = ["main.go"],
15 | importpath = "goclassifieds/func/readable_profiles",
16 | visibility = ["//visibility:private"],
17 | deps = [
18 | "//lib/entity",
19 | "@com_github_aws_aws_lambda_go//lambda",
20 | ],
21 | )
22 |
--------------------------------------------------------------------------------
/hooks/site_complete/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "site_complete",
5 | embed = [":site_complete_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/hooks/site_complete",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 |
13 | go_library(
14 | name = "site_complete_lib",
15 | srcs = ["main.go"],
16 | importpath = "goclassifieds/hooks/site_complete",
17 | visibility = ["//visibility:private"],
18 | deps = [
19 | "//lib/entity",
20 | "@com_github_aws_aws_lambda_go//lambda",
21 | ],
22 | )
23 |
--------------------------------------------------------------------------------
/func/encrypt_secret_value/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@npm//@bazel/esbuild:index.bzl", "esbuild")
2 | # load("//:node_deps.bzl", "setup_node_repositories")
3 |
4 | # setup_node_repositories()
5 |
6 | esbuild(
7 | name = "encrypt_secret_value",
8 | entry_point = "index.js",
9 | platform = "node",
10 | target = "node10",
11 | tool = select({
12 | "@bazel_tools//src/conditions:darwin": "@esbuild_darwin//:bin/esbuild",
13 | "@bazel_tools//src/conditions:windows": "@esbuild_windows//:esbuild.exe",
14 | "@bazel_tools//src/conditions:linux_x86_64": "@esbuild_linux//:bin/esbuild",
15 | }),
16 | deps = [
17 | "@npm//libsodium-wrappers"
18 | ],
19 | )
--------------------------------------------------------------------------------
/hooks/site_start/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "goclassifieds/lib/entity"
6 | "log"
7 |
8 | "github.com/aws/aws-lambda-go/lambda"
9 | )
10 |
11 | func handler(ctx context.Context, event map[string]interface{}) (entity.AfterSaveExecEntityResponse, error) {
12 |
13 | /**
14 | * This is where all the code goes to create action SECRETS
15 | * for a site. Both for repo and enviironment.
16 | */
17 | log.Print("Start site workflow")
18 |
19 | return entity.AfterSaveExecEntityResponse{}, nil
20 | }
21 |
22 | func main() {
23 | log.SetFlags(0)
24 | // Make the handler available for Remote Procedure Call by AWS Lambda
25 | lambda.Start(handler)
26 | }
27 |
--------------------------------------------------------------------------------
/architecture/sites.md:
--------------------------------------------------------------------------------
1 | Add Site key to top level entities like panel page.
2 |
3 | This could be an array allowing opne piece of content to belong to multiple sites.
4 |
5 | Include site id in query string when applicable.
6 |
7 | Limit result set to items in that site.
8 |
9 | configs
10 | --------
11 |
12 | does this matter not - not really
13 |
14 | all sites share same resources for now.
15 |
16 |
17 |
18 | -----------
19 |
20 | environments (limited - lambda limit)
21 |
22 | - dev
23 | - prod
24 |
25 | ------
26 |
27 | restrict write on entities (pages) user is not allowed to write 0- entity permissions
28 |
29 | * New Authorizer that can be used with entity permissions on entities
--------------------------------------------------------------------------------
/hooks/site_complete/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "goclassifieds/lib/entity"
6 | "log"
7 |
8 | "github.com/aws/aws-lambda-go/lambda"
9 | )
10 |
11 | func handler(ctx context.Context, event map[string]interface{}) (entity.AfterSaveExecEntityResponse, error) {
12 |
13 | /**
14 | * This is where all the code goes to create action SECRETS
15 | * for a site. Both for repo and enviironment.
16 | */
17 | log.Print("Complete site workflow")
18 |
19 | return entity.AfterSaveExecEntityResponse{}, nil
20 | }
21 |
22 | func main() {
23 | log.SetFlags(0)
24 | // Make the handler available for Remote Procedure Call by AWS Lambda
25 | lambda.Start(handler)
26 | }
27 |
--------------------------------------------------------------------------------
/func/enforce_contract/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@npm//@bazel/esbuild:index.bzl", "esbuild")
2 | # load("//:node_deps.bzl", "setup_node_repositories")
3 |
4 | # setup_node_repositories()
5 |
6 | esbuild(
7 | name = "enforce_contract",
8 | entry_point = "index.js",
9 | platform = "node",
10 | target = "node10",
11 | tool = select({
12 | "@bazel_tools//src/conditions:darwin": "@esbuild_darwin//:bin/esbuild",
13 | "@bazel_tools//src/conditions:windows": "@esbuild_windows//:esbuild.exe",
14 | "@bazel_tools//src/conditions:linux_x86_64": "@esbuild_linux//:bin/esbuild",
15 | }),
16 | deps = [
17 | "@npm//ajv",
18 | "@npm//ajv-formats",
19 | ],
20 | )
--------------------------------------------------------------------------------
/iac/precheck-oidc-auth/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "precheck-oidc-auth",
3 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
4 | "projectType": "application",
5 | "sourceRoot": "apps/precheck-oidc-auth",
6 | "targets": {
7 | "destroy": {
8 | "executor": "@telos-iac/nx-pulumi:destroy",
9 | "options": {}
10 | },
11 | "up": {
12 | "executor": "@telos-iac/nx-pulumi:up",
13 | "options": {}
14 | },
15 | "preview": {
16 | "executor": "@telos-iac/nx-pulumi:preview",
17 | "options": {}
18 | },
19 | "refresh": {
20 | "executor": "@telos-iac/nx-pulumi:refresh",
21 | "options": {}
22 | }
23 | },
24 | "tags": []
25 | }
26 |
--------------------------------------------------------------------------------
/manifests/entity-api.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: serving.knative.dev/v1
2 | kind: Service
3 | metadata:
4 | name: entity-api
5 | namespace: default
6 | spec:
7 | template:
8 | spec:
9 | containers:
10 | - image: gcr.io//entity-api:latest # Replace with your actual image URL
11 | ports:
12 | - containerPort: 8080
13 | env:
14 | - name: ELASTIC_URL
15 | value: # Replace with your actual Elastic URL
16 | - name: BUCKET_NAME
17 | value: # Replace with your actual bucket name
18 | - name: STAGE
19 | value: dev # Replace with your desired stage (e.g., dev, prod)
20 |
--------------------------------------------------------------------------------
/hooks/site_staticwebapp/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@npm//@bazel/esbuild:index.bzl", "esbuild")
2 | # load("//:node_deps.bzl", "setup_node_repositories")
3 |
4 | # setup_node_repositories()
5 |
6 | esbuild(
7 | name = "site_staticwebapp",
8 | entry_point = "index.js",
9 | platform = "node",
10 | target = "node18",
11 | tool = select({
12 | "@bazel_tools//src/conditions:darwin": "@esbuild_darwin//:bin/esbuild",
13 | "@bazel_tools//src/conditions:windows": "@esbuild_windows//:esbuild.exe",
14 | "@bazel_tools//src/conditions:linux_x86_64": "@esbuild_linux//:bin/esbuild",
15 | }),
16 | deps = [
17 | "@npm//@azure/arm-appservice",
18 | "@npm//@azure/identity",
19 | ],
20 | )
--------------------------------------------------------------------------------
/iac/precheck-oidc-auth/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "../../.eslintrc.json"
4 | ],
5 | "ignorePatterns": [
6 | "!**/*"
7 | ],
8 | "overrides": [
9 | {
10 | "files": [
11 | "*.ts",
12 | "*.tsx",
13 | "*.js",
14 | "*.jsx"
15 | ],
16 | "parserOptions": {
17 | "project": [
18 | "apps/precheck-oidc-auth/tsconfig.json"
19 | ]
20 | },
21 | "rules": {}
22 | },
23 | {
24 | "files": [
25 | "*.ts",
26 | "*.tsx"
27 | ],
28 | "rules": {}
29 | },
30 | {
31 | "files": [
32 | "*.js",
33 | "*.jsx"
34 | ],
35 | "rules": {}
36 | }
37 | ]
38 | }
39 |
--------------------------------------------------------------------------------
/hooks/site_start/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "site_start",
5 | embed = [":site_start_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/hooks/site_start",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 |
13 | go_library(
14 | name = "site_start_lib",
15 | srcs = ["main.go"],
16 | importpath = "goclassifieds/hooks/site_start",
17 | visibility = ["//visibility:private"],
18 | deps = [
19 | "//lib/entity",
20 | "//lib/repo",
21 | "@com_github_google_go_github_v46//github",
22 | "@com_github_aws_aws_lambda_go//lambda",
23 | ],
24 | )
25 |
--------------------------------------------------------------------------------
/iac/precheck-cognito/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "precheck-cognito",
3 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
4 | "projectType": "application",
5 | "sourceRoot": "apps/precheck-cognito",
6 | "targets": {
7 | "destroy": {
8 | "executor": "@telos-iac/nx-pulumi:destroy",
9 | "options": {}
10 | },
11 | "up": {
12 | "executor": "@telos-iac/nx-pulumi:up",
13 | "options": {}
14 | },
15 | "preview": {
16 | "executor": "@telos-iac/nx-pulumi:preview",
17 | "options": {}
18 | },
19 | "refresh": {
20 | "executor": "@telos-iac/nx-pulumi:refresh",
21 | "options": {}
22 | }
23 | },
24 | "tags": []
25 | }
26 |
--------------------------------------------------------------------------------
/api/proxy/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "proxy",
5 | embed = [":proxy_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/api/proxy",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 | #go_test(
13 | # name = "mainTest",
14 | # srcs = ["main_test.go"],
15 | #)
16 |
17 | go_library(
18 | name = "proxy_lib",
19 | srcs = ["main.go"],
20 | importpath = "goclassifieds/api/proxy",
21 | visibility = ["//visibility:private"],
22 | deps = [
23 | "//lib/utils",
24 | "@com_github_aws_aws_lambda_go//events",
25 | "@com_github_aws_aws_lambda_go//lambda",
26 | ],
27 | )
28 |
--------------------------------------------------------------------------------
/cloudform/bucket.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: '2010-09-09'
2 | Description: 'This template sets up bucket to host automation resources.'
3 |
4 | Parameters:
5 | EnvironmentName:
6 | Description: The name of the environment (e.g., dev, prod)
7 | Type: String
8 | VendorSuffix:
9 | Description: Unique vendor suffix for the bucket.
10 | Type: String
11 |
12 | Resources:
13 | CloudformationTemplatesBucket:
14 | Type: 'AWS::S3::Bucket'
15 | Properties:
16 | BucketName: !Sub 'rtc-classifieds-cloudformation-templates-${EnvironmentName}-${VendorSuffix}'
17 | AccessControl: Private
18 | BucketEncryption:
19 | ServerSideEncryptionConfiguration:
20 | - ServerSideEncryptionByDefault:
21 | SSEAlgorithm: AES256
--------------------------------------------------------------------------------
/func/azure_hedge/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "azure_hedge",
5 | embed = [":azure_hedge_lib"],
6 | goarch = "amd64",
7 | goos = "windows",
8 | importpath = "goclassifieds/func/azure_hedge",
9 | visibility = ["//visibility:public"],
10 | )
11 |
12 | go_library(
13 | name = "azure_hedge_lib",
14 | srcs = ["main.go"],
15 | importpath = "goclassifieds/func/azure_hedge",
16 | visibility = ["//visibility:private"],
17 | deps = [
18 | "//lib/shapeshift",
19 | "@com_github_aws_aws_lambda_go//events",
20 | "@com_github_golang_jwt_jwt_v4//:go_default_library",
21 | "@com_github_micahparks_keyfunc//:go_default_library"
22 | ],
23 | )
24 |
--------------------------------------------------------------------------------
/iac/precheck-cognito/stack-reference.ts:
--------------------------------------------------------------------------------
1 | import * as pulumi from '@pulumi/pulumi';
2 |
3 | type ExportTypes = typeof import('./pulumi');
4 | type ExportTypesKey = keyof ExportTypes;
5 | type ExportTypesValue = ExportTypes[TKey];
6 |
7 | type StrongTypedStackReference = Omit & {
8 | getOutput(name: pulumi.Input): ExportTypesValue;
9 | requireOutput(name: pulumi.Input): ExportTypesValue;
10 | };
11 |
12 | export function getStackReference() {
13 | const org = pulumi.getOrganization();
14 | const stack = pulumi.getStack();
15 | return new pulumi.StackReference(`${org}/precheck-cognito/${stack}`) as StrongTypedStackReference;
16 | }
17 |
--------------------------------------------------------------------------------
/lib/hedge/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@npm//@bazel/esbuild:index.bzl", "esbuild")
2 |
3 | esbuild(
4 | name = "hedge_lib",
5 | entry_point = "index.js",
6 | platform = "node",
7 | target = "node10",
8 | tool = select({
9 | "@bazel_tools//src/conditions:darwin": "@esbuild_darwin//:bin/esbuild",
10 | "@bazel_tools//src/conditions:windows": "@esbuild_windows//:esbuild.exe",
11 | "@bazel_tools//src/conditions:linux_x86_64": "@esbuild_linux//:bin/esbuild",
12 | }),
13 | deps = [
14 | "@npm//matcher",
15 | ],
16 | )
17 |
18 | load("@build_bazel_rules_nodejs//:index.bzl", "pkg_npm")
19 |
20 | pkg_npm(
21 | name = "hedge",
22 | srcs = ["package.json"],
23 | deps = [":hedge_lib"],
24 | substitutions = {"//internal/": "//"},
25 | )
--------------------------------------------------------------------------------
/hooks/site_init/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "site_init",
5 | embed = [":site_init_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/hooks/site_init",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 |
13 | go_library(
14 | name = "site_init_lib",
15 | srcs = ["main.go"],
16 | importpath = "goclassifieds/hooks/site_init",
17 | visibility = ["//visibility:private"],
18 | deps = [
19 | "//lib/entity",
20 | "//lib/repo",
21 | "@org_golang_x_oauth2//:go_default_library",
22 | "@com_github_google_go_github_v46//github",
23 | "@com_github_aws_aws_lambda_go//lambda",
24 | ],
25 | )
26 |
--------------------------------------------------------------------------------
/iac/precheck-cognito/eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "../../.eslintrc.json"
4 | ],
5 | "ignorePatterns": [
6 | "!**/*"
7 | ],
8 | "overrides": [
9 | {
10 | "files": [
11 | "*.ts",
12 | "*.tsx",
13 | "*.js",
14 | "*.jsx"
15 | ],
16 | "parserOptions": {
17 | "project": [
18 | "apps/precheck-cognito/tsconfig.json"
19 | ]
20 | },
21 | "rules": {}
22 | },
23 | {
24 | "files": [
25 | "*.ts",
26 | "*.tsx"
27 | ],
28 | "rules": {}
29 | },
30 | {
31 | "files": [
32 | "*.js",
33 | "*.jsx"
34 | ],
35 | "rules": {}
36 | }
37 | ]
38 | }
39 |
--------------------------------------------------------------------------------
/api/index/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "index",
5 | embed = [":index_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/api/index",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 |
13 | go_library(
14 | name = "index_lib",
15 | srcs = ["main.go"],
16 | importpath = "goclassifieds/api/index",
17 | visibility = ["//visibility:private"],
18 | deps = [
19 | "//lib/repo",
20 | "//lib/search",
21 | "@com_github_aws_aws_lambda_go//events",
22 | "@com_github_aws_aws_lambda_go//lambda",
23 | "@org_golang_x_oauth2//:go_default_library",
24 | "@com_github_google_go_github_v46//github",
25 | ],
26 | )
27 |
--------------------------------------------------------------------------------
/hooks/site_modify/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "site_modify",
5 | embed = [":site_modify_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/hooks/site_modify",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 |
13 | go_library(
14 | name = "site_modify_lib",
15 | srcs = ["main.go"],
16 | importpath = "goclassifieds/hooks/site_modify",
17 | visibility = ["//visibility:private"],
18 | deps = [
19 | "//lib/entity",
20 | "//lib/repo",
21 | "@org_golang_x_oauth2//:go_default_library",
22 | "@com_github_aws_aws_lambda_go//lambda",
23 | "@com_github_google_go_github_v46//github",
24 | ],
25 | )
26 |
--------------------------------------------------------------------------------
/api/entity/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 | #load("@rules_oci//oci:oci.bzl", "oci_image")
3 |
4 | # Build the Go library
5 | go_library(
6 | name = "entity_lib",
7 | srcs = ["main.go"],
8 | importpath = "goclassifieds/api/entity",
9 | visibility = ["//visibility:private"],
10 | deps = [
11 | "//lib/shapeshift",
12 | "@com_github_aws_aws_lambda_go//events",
13 | "@com_github_aws_aws_lambda_go//lambda",
14 | ],
15 | )
16 |
17 | # Build the Go binary
18 | go_binary(
19 | name = "entity",
20 | embed = [":entity_lib"],
21 | goarch = "amd64",
22 | goos = "linux",
23 | importpath = "goclassifieds/api/entity",
24 | visibility = ["//visibility:public"],
25 | out = "bootstrap", # Output binary name
26 | )
--------------------------------------------------------------------------------
/hooks/entity_index/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "entity_index",
5 | embed = [":entity_index_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/hooks/entity_index",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 |
13 | go_library(
14 | name = "entity_index_lib",
15 | srcs = ["main.go"],
16 | importpath = "goclassifieds/hooks/entity_index",
17 | visibility = ["//visibility:private"],
18 | deps = [
19 | "//lib/entity",
20 | "//lib/repo",
21 | "@org_golang_x_oauth2//:go_default_library",
22 | "@com_github_google_go_github_v46//github",
23 | "@com_github_aws_aws_lambda_go//lambda",
24 | ],
25 | )
26 |
--------------------------------------------------------------------------------
/hooks/index_create/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "index_create",
5 | embed = [":index_create_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/hooks/index_create",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 |
13 | go_library(
14 | name = "index_create_lib",
15 | srcs = ["main.go"],
16 | importpath = "goclassifieds/hooks/index_create",
17 | visibility = ["//visibility:private"],
18 | deps = [
19 | "//lib/entity",
20 | "//lib/repo",
21 | "@org_golang_x_oauth2//:go_default_library",
22 | "@com_github_google_go_github_v46//github",
23 | "@com_github_aws_aws_lambda_go//lambda",
24 | ],
25 | )
26 |
--------------------------------------------------------------------------------
/func/grant_access/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "grant_access",
5 | embed = [":grant_access_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/func/grant_access",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 |
13 | go_library(
14 | name = "grant_access_lib",
15 | srcs = ["main.go"],
16 | importpath = "goclassifieds/func/grant_access",
17 | visibility = ["//visibility:private"],
18 | deps = [
19 | "//lib/gov",
20 | "//lib/entity",
21 | "//lib/utils",
22 | "@com_github_gocql_gocql//:gocql",
23 | "@com_github_tangzero_inflector//:inflector",
24 | "@com_github_aws_aws_lambda_go//lambda",
25 | ],
26 | )
27 |
--------------------------------------------------------------------------------
/api/gov/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "gov",
5 | embed = [":gov_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/api/gov",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 |
13 | go_library(
14 | name = "gov_lib",
15 | srcs = ["main.go"],
16 | importpath = "goclassifieds/api/gov",
17 | visibility = ["//visibility:private"],
18 | deps = [
19 | "//lib/gov",
20 | "//lib/utils",
21 | "@com_github_aws_aws_lambda_go//events",
22 | "@com_github_aws_aws_lambda_go//lambda",
23 | "@com_github_aws_aws_sdk_go//aws",
24 | "@com_github_aws_aws_sdk_go//aws/session",
25 | "@com_github_aws_aws_sdk_go//service/lambda",
26 | ],
27 | )
28 |
--------------------------------------------------------------------------------
/hooks/site_write/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "site_write",
5 | embed = [":site_write_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/hooks/site_write",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 |
13 | go_library(
14 | name = "site_write_lib",
15 | srcs = ["main.go"],
16 | importpath = "goclassifieds/hooks/site_write",
17 | visibility = ["//visibility:private"],
18 | deps = [
19 | "//lib/entity",
20 | "//lib/repo",
21 | "@com_github_aws_aws_sdk_go//aws/session",
22 | "@com_github_aws_aws_sdk_go//service/lambda",
23 | "@org_golang_x_oauth2//:go_default_library",
24 | "@com_github_aws_aws_lambda_go//lambda",
25 | ],
26 | )
27 |
--------------------------------------------------------------------------------
/hooks/site_deploy/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "site_deploy",
5 | embed = [":site_deploy_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/hooks/site_deploy",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 |
13 | go_library(
14 | name = "site_deploy_lib",
15 | srcs = ["main.go"],
16 | importpath = "goclassifieds/hooks/site_deploy",
17 | visibility = ["//visibility:private"],
18 | deps = [
19 | "//lib/entity",
20 | "//lib/repo",
21 | "@com_github_aws_aws_sdk_go//aws/session",
22 | "@com_github_aws_aws_sdk_go//service/lambda",
23 | "@org_golang_x_oauth2//:go_default_library",
24 | "@com_github_aws_aws_lambda_go//lambda",
25 | ],
26 | )
27 |
--------------------------------------------------------------------------------
/iac/precheck-oidc-auth/stack-reference.ts:
--------------------------------------------------------------------------------
1 | import * as pulumi from '@pulumi/pulumi';
2 |
3 | type ExportTypes = typeof import('./pulumi');
4 | type ExportTypesKey = keyof ExportTypes;
5 | type ExportTypesValue = ExportTypes[TKey];
6 |
7 | type StrongTypedStackReference = Omit & {
8 | // eslint-disable-next-line no-unused-vars
9 | getOutput(name: pulumi.Input): ExportTypesValue;
10 | // eslint-disable-next-line no-unused-vars
11 | requireOutput(name: pulumi.Input): ExportTypesValue;
12 | };
13 |
14 | export function getStackReference() {
15 | const org = pulumi.getOrganization();
16 | const stack = pulumi.getStack();
17 | return new pulumi.StackReference(`${org}/precheck-oidc-auth/${stack}`) as StrongTypedStackReference;
18 | }
19 |
--------------------------------------------------------------------------------
/hooks/site_environment/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "site_environment",
5 | embed = [":site_environment_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/hooks/site_environment",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 |
13 | go_library(
14 | name = "site_environment_lib",
15 | srcs = ["main.go"],
16 | importpath = "goclassifieds/hooks/site_environment",
17 | visibility = ["//visibility:private"],
18 | deps = [
19 | "//lib/entity",
20 | "//lib/repo",
21 | "@com_github_aws_aws_sdk_go//aws/session",
22 | "@com_github_aws_aws_sdk_go//service/lambda",
23 | "@org_golang_x_oauth2//:go_default_library",
24 | "@com_github_aws_aws_lambda_go//lambda",
25 | ],
26 | )
27 |
--------------------------------------------------------------------------------
/lib/repo/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_library")
2 |
3 | go_library(
4 | name = "repo",
5 | srcs = ["repo.go"],
6 | importpath = "goclassifieds/lib/repo",
7 | visibility = ["//visibility:public"],
8 | deps = [
9 | "//lib/utils",
10 | "@com_github_shurcool_githubv4//:go_default_library",
11 | "@com_github_google_go_github_v46//github",
12 | "@org_golang_x_oauth2//:go_default_library",
13 | "@com_github_golang_jwt_jwt_v4//:go_default_library",
14 | # "@org_golang_x_crypto//nacl/box", # Add this line for golang.org/x/crypto
15 | "@com_github_kevinburke_nacl//box:go_default_library", # THIS IS THE KEY CHANGE
16 | "@com_github_aws_aws_sdk_go//service/lambda",
17 | "@com_github_aws_aws_sdk_go//aws",
18 | "@org_golang_x_crypto//ssh:go_default_library", # Add ssh library dependency
19 | ],
20 | )
--------------------------------------------------------------------------------
/func/renewable_record/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "renewable_record",
5 | embed = [":renewable_record_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/func/renewable_record",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 |
13 | go_library(
14 | name = "renewable_record_lib",
15 | srcs = ["main.go"],
16 | importpath = "goclassifieds/func/renewable_record",
17 | visibility = ["//visibility:private"],
18 | deps = [
19 | "//lib/entity",
20 | "//lib/sign",
21 | "//lib/utils",
22 | "@com_github_aws_aws_lambda_go//lambda",
23 | "@com_github_aws_aws_lambda_go//events",
24 | "@com_github_aws_aws_sdk_go//aws/session",
25 | "@com_github_opensearch_project_opensearch_go//:opensearch-go",
26 | ],
27 | )
28 |
--------------------------------------------------------------------------------
/func/validate_entity/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "validate_entity",
5 | embed = [":validate_entity_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/func/validate_entity",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 |
13 | go_library(
14 | name = "validate_entity_lib",
15 | srcs = ["main.go"],
16 | importpath = "goclassifieds/func/validate_entity",
17 | visibility = ["//visibility:private"],
18 | deps = [
19 | "//lib/ads",
20 | "//lib/cc",
21 | "//lib/chat",
22 | "//lib/entity",
23 | "//lib/profiles",
24 | "//lib/utils",
25 | "//lib/vocab",
26 | "@com_github_aws_aws_lambda_go//lambda",
27 | "@com_github_go_playground_validator_v10//:validator",
28 | ],
29 | )
30 |
--------------------------------------------------------------------------------
/api/authorizer/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "authorizer",
5 | embed = [":authorizer_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/api/authorizer",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 | #go_test(
13 | # name = "mainTest",
14 | # srcs = ["main_test.go"],
15 | #)
16 |
17 | go_library(
18 | name = "authorizer_lib",
19 | srcs = ["main.go"],
20 | importpath = "goclassifieds/api/authorizer",
21 | visibility = ["//visibility:private"],
22 | deps = [
23 | "//lib/utils",
24 | "@com_github_aws_aws_lambda_go//events",
25 | "@com_github_aws_aws_lambda_go//lambda",
26 | "@com_github_golang_jwt_jwt_v4//:go_default_library",
27 | "@com_github_micahparks_keyfunc//:go_default_library"
28 | ],
29 | )
30 |
--------------------------------------------------------------------------------
/api/authorizer2/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "authorizer2",
5 | embed = [":authorizer2_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/api/authorizer2",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 | #go_test(
13 | # name = "mainTest",
14 | # srcs = ["main_test.go"],
15 | #)
16 |
17 | go_library(
18 | name = "authorizer2_lib",
19 | srcs = ["main.go"],
20 | importpath = "goclassifieds/api/authorizer2",
21 | visibility = ["//visibility:private"],
22 | deps = [
23 | "//lib/utils",
24 | "@com_github_aws_aws_lambda_go//events",
25 | "@com_github_aws_aws_lambda_go//lambda",
26 | "@com_github_golang_jwt_jwt_v4//:go_default_library",
27 | "@com_github_micahparks_keyfunc//:go_default_library"
28 | ],
29 | )
30 |
--------------------------------------------------------------------------------
/api/authorizer3/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "authorizer3",
5 | embed = [":authorizer3_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/api/authorizer3",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 | #go_test(
13 | # name = "mainTest",
14 | # srcs = ["main_test.go"],
15 | #)
16 |
17 | go_library(
18 | name = "authorizer3_lib",
19 | srcs = ["main.go"],
20 | importpath = "goclassifieds/api/authorizer3",
21 | visibility = ["//visibility:private"],
22 | deps = [
23 | "//lib/utils",
24 | "@com_github_aws_aws_lambda_go//events",
25 | "@com_github_aws_aws_lambda_go//lambda",
26 | "@com_github_golang_jwt_jwt_v4//:go_default_library",
27 | "@com_github_micahparks_keyfunc//:go_default_library"
28 | ],
29 | )
30 |
--------------------------------------------------------------------------------
/func/convert_media/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "convert_media",
5 | embed = [":convert_media_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/func/convert_media",
9 | visibility = ["//visibility:public"],
10 | )
11 |
12 | go_library(
13 | name = "convert_media_lib",
14 | srcs = ["main.go"],
15 | importpath = "goclassifieds/func/convert_media",
16 | visibility = ["//visibility:private"],
17 | deps = [
18 | "@com_github_aws_aws_lambda_go//events",
19 | "@com_github_aws_aws_lambda_go//lambda",
20 | "@com_github_aws_aws_sdk_go//aws",
21 | "@com_github_aws_aws_sdk_go//aws/session",
22 | "@com_github_aws_aws_sdk_go//service/lambda",
23 | "@com_github_aws_aws_sdk_go//service/s3",
24 | "@com_github_aws_aws_sdk_go//service/s3/s3manager",
25 | ],
26 | )
27 |
--------------------------------------------------------------------------------
/api/stream/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "stream",
5 | embed = [":stream_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/api/stream",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 | #go_test(
13 | # name = "mainTest",
14 | # srcs = ["main_test.go"],
15 | #)
16 |
17 | go_library(
18 | name = "stream_lib",
19 | srcs = ["main.go"],
20 | importpath = "goclassifieds/api/stream",
21 | visibility = ["//visibility:private"],
22 | deps = [
23 | "//lib/entity",
24 | "//lib/utils",
25 | "@com_github_aws_aws_lambda_go//events",
26 | "@com_github_aws_aws_lambda_go//lambda",
27 | "@com_github_aws_aws_sdk_go//aws/session",
28 | "@com_github_aws_aws_sdk_go//service/lambda",
29 | "@com_github_gocql_gocql//:gocql",
30 | ],
31 | )
32 |
--------------------------------------------------------------------------------
/api/entity/test_data/post_shapeshift.json:
--------------------------------------------------------------------------------
1 | {
2 | "resource": "/{owner}/{repo}/shapeshifter/{proxy+}",
3 | "path": "/my-owner/my-repo/shapeshifter/example/550e8400-e29b-41d4-a716-446655440000",
4 | "httpMethod": "POST",
5 | "headers": {
6 | "Content-Type": "application/json",
7 | "Authorization": "Bearer eyJraWQiOiJVTlRrNGhWaFVycTBrOHVBR3ZEWXNcL3cwZ1wvaVcrYjlQd2VPYk1iTTZTXC93PSIsImFsZyI6IlJTMjU2In0..."
8 | },
9 | "pathParameters": {
10 | "owner": "my-owner",
11 | "repo": "my-repo",
12 | "proxy": "example/550e8400-e29b-41d4-a716-446655440000"
13 | },
14 | "queryStringParameters": {},
15 | "requestContext": {
16 | "authorizer": {
17 | "claims": {
18 | "sub": "mock-user-id",
19 | "username": "mock-username"
20 | }
21 | },
22 | "requestId": "mock-request-id"
23 | },
24 | "body": "{\"id\": \"550e8400-e29b-41d4-a716-446655440000\", \"name\": \"GenericEntity\", \"type\": \"example\", \"attributes\": {\"key\": \"value\", \"key2\": \"value2\"}}"
25 | }
--------------------------------------------------------------------------------
/job/renewable_report/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "renewable_report",
5 | embed = [":renewable_report_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/job/renewable_report",
9 | visibility = ["//visibility:public"],
10 | )
11 |
12 | go_library(
13 | name = "renewable_report_lib",
14 | srcs = ["main.go"],
15 | importpath = "goclassifieds/job/renewable_report",
16 | visibility = ["//visibility:private"],
17 | deps = [
18 | "//lib/entity",
19 | "//lib/sign",
20 | "//lib/utils",
21 | "//lib/repo",
22 | "//lib/watttime",
23 | "@com_github_aws_aws_lambda_go//lambda",
24 | "@com_github_aws_aws_lambda_go//events",
25 | "@com_github_aws_aws_sdk_go//aws/session",
26 | "@org_golang_x_oauth2//:go_default_library",
27 | "@com_github_shurcool_githubv4//:go_default_library",
28 | "@com_github_opensearch_project_opensearch_go//:opensearch-go",
29 | ],
30 | )
31 |
--------------------------------------------------------------------------------
/api/chat/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "chat",
5 | embed = [":chat_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/api/chat",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 | #go_test(
13 | # name = "mainTest",
14 | # srcs = ["main_test.go"],
15 | #)
16 |
17 | go_library(
18 | name = "chat_lib",
19 | srcs = ["main.go"],
20 | importpath = "goclassifieds/api/chat",
21 | visibility = ["//visibility:private"],
22 | deps = [
23 | "//lib/entity",
24 | "//lib/utils",
25 | "@com_github_aws_aws_lambda_go//events",
26 | "@com_github_aws_aws_lambda_go//lambda",
27 | "@com_github_aws_aws_sdk_go//aws",
28 | "@com_github_aws_aws_sdk_go//aws/session",
29 | "@com_github_aws_aws_sdk_go//service/apigatewaymanagementapi",
30 | "@com_github_aws_aws_sdk_go//service/lambda",
31 | "@com_github_gocql_gocql//:gocql",
32 | "@com_github_tangzero_inflector//:inflector",
33 | ],
34 | )
35 |
--------------------------------------------------------------------------------
/func/index_entity/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "index_entity",
5 | embed = [":index_entity_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/func/index_entity",
9 | visibility = ["//visibility:public"],
10 | )
11 |
12 | go_library(
13 | name = "index_entity_lib",
14 | srcs = ["main.go"],
15 | importpath = "goclassifieds/func/index_entity",
16 | visibility = ["//visibility:private"],
17 | deps = [
18 | "//lib/ads",
19 | "//lib/attr",
20 | "//lib/cc",
21 | "//lib/entity",
22 | "//lib/vocab",
23 | "//lib/sign",
24 | "@com_github_aws_aws_lambda_go//events",
25 | "@com_github_aws_aws_lambda_go//lambda",
26 | "@com_github_aws_aws_sdk_go//aws/session",
27 | "@com_github_elastic_go_elasticsearch_v7//:go-elasticsearch",
28 | "@com_github_opensearch_project_opensearch_go//:opensearch-go",
29 | "@com_github_mitchellh_mapstructure//:mapstructure",
30 | "@com_github_tangzero_inflector//:inflector",
31 | ],
32 | )
33 |
--------------------------------------------------------------------------------
/lib/search/stats.go:
--------------------------------------------------------------------------------
1 | package search
2 |
3 | // IndexStats holds pre-calculated global statistics for IDF scoring.
4 | type IndexStats struct {
5 | TotalDocuments uint64 // N: Total number of documents in the index.
6 |
7 | // DocumentFrequency[token] stores the number of documents (df)
8 | // that contain the given analyzed token.
9 | DocumentFrequency map[string]uint64
10 | }
11 |
12 | // GetDocumentFrequency returns the document count for a token, or 1 if unknown.
13 | func (i *IndexStats) GetDocumentFrequency(token string) uint64 {
14 | if df, ok := i.DocumentFrequency[token]; ok && df > 0 {
15 | return df
16 | }
17 | // Default to 1 (or a small smoothing value) to prevent division by zero/log(1).
18 | return 1
19 | }
20 |
21 | // CalculateIDF returns the Inverse Document Frequency score for a given token.
22 | // Formula: log(1 + (N - df + 0.5) / (df + 0.5)) (a common variation for better stability)
23 | func (i *IndexStats) CalculateIDF(token string) float64 {
24 | N := float64(i.TotalDocuments)
25 | df := float64(i.GetDocumentFrequency(token))
26 |
27 | return math.Log(1 + (N - df + 0.5) / (df + 0.5))
28 | }
--------------------------------------------------------------------------------
/api/user/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "user",
5 | embed = [":user_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/api/user",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 | #go_test(
13 | # name = "mainTest",
14 | # srcs = ["main_test.go"],
15 | #)
16 |
17 | go_library(
18 | name = "user_lib",
19 | srcs = ["main.go"],
20 | importpath = "goclassifieds/api/user",
21 | visibility = ["//visibility:private"],
22 | deps = [
23 | "//lib/entity",
24 | "//lib/utils",
25 | "@com_github_aws_aws_lambda_go//events",
26 | "@com_github_aws_aws_lambda_go//lambda",
27 | "@com_github_aws_aws_sdk_go//aws/session",
28 | "@com_github_aws_aws_sdk_go//service/cognitoidentityprovider",
29 | "@com_github_aws_aws_sdk_go//service/ses",
30 | "@org_golang_x_oauth2//:go_default_library",
31 | "@com_github_google_go_github_v46//github",
32 | "@com_github_aws_aws_sdk_go//aws",
33 | "@com_github_sethvargo_go_password//password",
34 | ],
35 | )
36 |
--------------------------------------------------------------------------------
/api/media/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2 |
3 | go_binary(
4 | name = "media",
5 | embed = [":media_lib"],
6 | goarch = "amd64",
7 | goos = "linux",
8 | importpath = "goclassifieds/api/media",
9 | visibility = ["//visibility:public"],
10 | out = "bootstrap"
11 | )
12 | #go_test(
13 | # name = "mainTest",
14 | # srcs = ["main_test.go"],
15 | #)
16 |
17 | go_library(
18 | name = "media_lib",
19 | srcs = ["main.go"],
20 | importpath = "goclassifieds/api/media",
21 | visibility = ["//visibility:private"],
22 | deps = [
23 | "//lib/utils",
24 | "//lib/repo",
25 | "//lib/gov",
26 | "@com_github_aws_aws_lambda_go//events",
27 | "@com_github_aws_aws_lambda_go//lambda",
28 | "@com_github_aws_aws_sdk_go//aws",
29 | "@com_github_aws_aws_sdk_go//aws/session",
30 | "@com_github_aws_aws_sdk_go//service/s3",
31 | "@com_github_aws_aws_sdk_go//service/s3/s3manager",
32 | "@org_golang_x_oauth2//:go_default_library",
33 | "@com_github_shurcool_githubv4//:go_default_library",
34 | "@com_github_google_go_github_v46//github",
35 | "@com_github_aws_aws_sdk_go//service/lambda",
36 | ],
37 | )
38 |
--------------------------------------------------------------------------------
/api/chat/AmazonRootCA1.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF
3 | ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
4 | b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL
5 | MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
6 | b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj
7 | ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM
8 | 9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw
9 | IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6
10 | VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L
11 | 93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm
12 | jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
13 | AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA
14 | A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI
15 | U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs
16 | N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv
17 | o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU
18 | 5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy
19 | rqXRfboQnoZsG4q5WTP468SQvvG5
20 | -----END CERTIFICATE-----
21 |
--------------------------------------------------------------------------------
/api/stream/AmazonRootCA1.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF
3 | ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
4 | b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL
5 | MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
6 | b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj
7 | ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM
8 | 9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw
9 | IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6
10 | VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L
11 | 93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm
12 | jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
13 | AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA
14 | A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI
15 | U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs
16 | N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv
17 | o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU
18 | 5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy
19 | rqXRfboQnoZsG4q5WTP468SQvvG5
20 | -----END CERTIFICATE-----
21 |
--------------------------------------------------------------------------------
/func/grant_access/AmazonRootCA1.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF
3 | ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
4 | b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL
5 | MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
6 | b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj
7 | ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM
8 | 9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw
9 | IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6
10 | VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L
11 | 93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm
12 | jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
13 | AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA
14 | A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI
15 | U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs
16 | N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv
17 | o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU
18 | 5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy
19 | rqXRfboQnoZsG4q5WTP468SQvvG5
20 | -----END CERTIFICATE-----
21 |
--------------------------------------------------------------------------------
/func/readable_profiles/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "goclassifieds/lib/entity"
7 | "log"
8 |
9 | "github.com/aws/aws-lambda-go/lambda"
10 | )
11 |
12 | func handler(ctx context.Context, payload *entity.EntityDataRequest) (entity.EntityDataResponse, error) {
13 | log.Print("Inside readable_profiles")
14 | log.Printf("Entity: %s", payload.EntityName)
15 |
16 | rootIds := make(map[string]bool)
17 | for _, p := range payload.Data {
18 | id := fmt.Sprint(p["id"])
19 | parentId := fmt.Sprint(p["parentId"])
20 | _, ok := rootIds[id]
21 | if !ok && (parentId == "" || parentId == "") {
22 | rootIds[id] = true
23 | }
24 | }
25 | for _, p := range payload.Data {
26 | parentId := fmt.Sprint(p["parentId"])
27 | _, ok := rootIds[parentId]
28 | if !ok && parentId != "" && parentId != "" {
29 | rootIds[parentId] = true
30 | }
31 | }
32 | i := 0
33 | ids := make([]map[string]interface{}, len(rootIds))
34 | for key := range rootIds {
35 | ids[i] = map[string]interface{}{"value": key}
36 | i++
37 | }
38 |
39 | return entity.EntityDataResponse{
40 | Data: ids,
41 | }, nil
42 | }
43 |
44 | func main() {
45 | // Make the handler available for Remote Procedure Call by AWS Lambda
46 | lambda.Start(handler)
47 | }
48 |
--------------------------------------------------------------------------------
/iac/precheck-cognito/post-confirmation/index.js:
--------------------------------------------------------------------------------
1 | const AWS = require("aws-sdk");
2 |
3 | exports.handler = async (event) => {
4 | console.log("Event:", JSON.stringify(event, null, 2));
5 |
6 | const userPoolId = event.userPoolId;
7 | const userName = event.userName;
8 | const email = event.request.userAttributes.email;
9 | const phoneNumberVerified = event.request.userAttributes.phone_number_verified;
10 |
11 | // Only proceed if phone number is verified
12 | if (phoneNumberVerified === "true" && email) {
13 | const cognito = new AWS.CognitoIdentityServiceProvider();
14 |
15 | try {
16 | // Update the email_verified attribute to true
17 | await cognito.adminUpdateUserAttributes({
18 | UserPoolId: userPoolId,
19 | Username: userName,
20 | UserAttributes: [
21 | {
22 | Name: "email_verified",
23 | Value: "true",
24 | },
25 | ],
26 | }).promise();
27 |
28 | console.log(`Email verified for user: ${userName}`);
29 | } catch (error) {
30 | console.error("Error updating email_verified:", error);
31 | throw error;
32 | }
33 | }
34 |
35 | return event;
36 | };
--------------------------------------------------------------------------------
/func/enforce_contract/index.js:
--------------------------------------------------------------------------------
1 | const Ajv = require("ajv")
2 | const addFormats = require("ajv-formats")
3 |
4 | const ajv = new Ajv() // options can be passed, e.g. {allErrors: true}
5 | addFormats(ajv)
6 |
7 | module.exports.handler = function(event, context, callback) {
8 | console.log('hello world');
9 | console.log(event);
10 | console.log(context);
11 |
12 | /*const schema = {
13 | type: "object",
14 | properties: {
15 | foo: {type: "integer"},
16 | bar: {type: "string"}
17 | },
18 | required: ["foo"],
19 | additionalProperties: false
20 | }*/
21 |
22 | const contract = event.Contract;
23 | const schema = contract.schema;
24 | if (typeof(schema['$schema']) !== 'undefined') {
25 | delete schema['$schema'];
26 | }
27 |
28 | console.log('schema', schema);
29 |
30 | const validate = ajv.compile(schema)
31 |
32 | /*const data = {
33 | foo: "blah",
34 | bar: "abc"
35 | }*/
36 |
37 | const data = event.Entity;
38 | console.log('entity', data);
39 |
40 | const valid = validate(data)
41 | console.log('errors', validate.errors)
42 |
43 | const res = {
44 | Entity: { ...data, userId: event.UserId },
45 | Valid: validate.errors == null,
46 | Unauthorized: false,
47 | Errors: validate.errors
48 | };
49 |
50 | //var json = cssjson.toJSON(event.Content, { comments: true });
51 | //console.log(json);
52 |
53 | callback(null, res);
54 | }
--------------------------------------------------------------------------------
/iac/precheck-oidc-auth/shared/logger.js:
--------------------------------------------------------------------------------
1 | // Helper function to format log messages with context variables
2 | const format = (level, contextVars, message, additionalData) => {
3 | const timestamp = new Date().toISOString();
4 | const contextString = Object.entries(contextVars)
5 | .map(([key, value]) => `${key}=${value}`)
6 | .join(" ");
7 | const additionalString = additionalData ? JSON.stringify(additionalData) : "";
8 | return `${timestamp} [${level}] ${contextString} ${message} ${additionalString}`;
9 | };
10 |
11 | // Functional logger setup to override console methods
12 | const logger = (contextVars) => {
13 | const log = console.log;
14 | const error = console.error;
15 | const warn = console.warn;
16 | const debug = console.debug;
17 |
18 | // Wrap each console method with custom formatting
19 | console.log = (message, additionalData) =>
20 | log(format("INFO", contextVars, message, additionalData));
21 |
22 | console.error = (message, additionalData) =>
23 | error(format("ERROR", contextVars, message, additionalData));
24 |
25 | console.warn = (message, additionalData) =>
26 | warn(format("WARN", contextVars, message, additionalData));
27 |
28 | console.debug = (message, additionalData) =>
29 | debug(format("DEBUG", contextVars, message, additionalData));
30 | };
31 |
32 | // Export the setup function
33 | module.exports = logger;
--------------------------------------------------------------------------------
/cloudform/search2.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Variables
4 | STACK_NAME="opensearch-cognito-stack2"
5 | TEMPLATE_FILE="search2.yaml" # Name of the CloudFormation template file
6 | REGION="us-east-1" # Change to your preferred AWS Region
7 |
8 | # Replace these with your existing Cognito resource IDs
9 | COGNITO_USER_POOL_ID="us-east-1_sWRV0kAgS"
10 | COGNITO_IDENTITY_POOL_ID="us-east-1:026caf18-c852-451b-a93e-fb431c4eee6d"
11 | MASTER_USER_ARN="arn:aws:iam::032425924121:user/tzmijewski" # Replace with the actual master user's ARN
12 |
13 | # Deploy the stack
14 | echo "Deploying CloudFormation stack: $STACK_NAME"
15 |
16 | aws cloudformation deploy \
17 | --template-file "$TEMPLATE_FILE" \
18 | --stack-name "$STACK_NAME" \
19 | --region "$REGION" \
20 | --capabilities CAPABILITY_NAMED_IAM \
21 | --parameter-overrides "CognitoUserPoolId=$COGNITO_USER_POOL_ID" "CognitoIdentityPoolId=$COGNITO_IDENTITY_POOL_ID" "MasterUserARN=$MASTER_USER_ARN"
22 |
23 | # Check if the stack creation/update was successful
24 | if [ $? -eq 0 ]; then
25 | echo "CloudFormation stack deployed successfully!"
26 |
27 | # Retrieve the OpenSearch domain endpoint as an output
28 | OPENSEARCH_ENDPOINT=$(aws cloudformation describe-stacks \
29 | --stack-name "$STACK_NAME" \
30 | --region "$REGION" \
31 | --query "Stacks[0].Outputs[?OutputKey=='OpenSearchEndpoint'].OutputValue" \
32 | --output text)
33 |
34 | echo "OpenSearch Domain Endpoint: $OPENSEARCH_ENDPOINT"
35 | else
36 | echo "Failed to deploy CloudFormation stack."
37 | exit 1
38 | fi
--------------------------------------------------------------------------------
/lib/entity/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_library")
2 |
3 | go_library(
4 | name = "entity",
5 | srcs = ["entity.go"],
6 | importpath = "goclassifieds/lib/entity",
7 | visibility = ["//visibility:public"],
8 | deps = [
9 | "//lib/attr",
10 | "//lib/es",
11 | "//lib/os",
12 | "//lib/utils",
13 | "//lib/repo",
14 | "//lib/gov",
15 | "@com_github_aws_aws_lambda_go//events",
16 | "@com_github_aws_aws_sdk_go//aws",
17 | "@com_github_aws_aws_sdk_go//aws/session",
18 | "@com_github_aws_aws_sdk_go//service/cognitoidentityprovider",
19 | "@com_github_aws_aws_sdk_go//service/lambda",
20 | "@com_github_aws_aws_sdk_go//service/sfn",
21 | "@com_github_aws_aws_sdk_go//service/s3",
22 | "@com_github_aws_aws_sdk_go//service/s3/s3manager",
23 | "@com_github_elastic_go_elasticsearch//esapi",
24 | "@com_github_elastic_go_elasticsearch_v7//:go-elasticsearch",
25 | "@com_github_opensearch_project_opensearch_go//:opensearch-go",
26 | "@com_github_opensearch_project_opensearch_go//opensearchapi",
27 | "@com_github_go_playground_validator_v10//:validator",
28 | "@com_github_gocql_gocql//:gocql",
29 | "@com_github_mitchellh_mapstructure//:mapstructure",
30 | "@com_github_tangzero_inflector//:inflector",
31 | "@com_github_shurcool_githubv4//:go_default_library",
32 | "@org_golang_x_oauth2//:go_default_library",
33 | "@com_github_google_go_github_v46//github",
34 | ],
35 | )
36 |
--------------------------------------------------------------------------------
/lib/shapeshift/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_go//go:def.bzl", "go_library")
2 | #go_test(
3 | # name = "mainTest",
4 | # srcs = ["main_test.go"],
5 | #)
6 |
7 | go_library(
8 | name = "shapeshift",
9 | srcs = ["shapeshift.go"],
10 | importpath = "goclassifieds/lib/shapeshift",
11 | visibility = ["//visibility:public"],
12 | deps = [
13 | "//lib/entity",
14 | "//lib/sign",
15 | "//lib/gov",
16 | "//lib/repo",
17 | "//lib/utils",
18 | "@com_github_aws_aws_lambda_go//events",
19 | "@com_github_aws_aws_lambda_go//lambda",
20 | "@com_github_aws_aws_sdk_go//aws/session",
21 | "@com_github_aws_aws_sdk_go//service/lambda",
22 | "@com_github_aws_aws_sdk_go//service/sfn",
23 | "@com_github_aws_aws_sdk_go//aws/credentials",
24 | "@com_github_aws_aws_sdk_go//aws/signer/v4:go_default_library",
25 | "@com_github_elastic_go_elasticsearch_v7//:go-elasticsearch",
26 | "@com_github_opensearch_project_opensearch_go//:opensearch-go",
27 | "@com_github_mitchellh_mapstructure//:mapstructure",
28 | "@com_github_tangzero_inflector//:inflector",
29 | "@com_github_shurcool_githubv4//:go_default_library",
30 | "@org_golang_x_oauth2//:go_default_library",
31 | "@com_github_aws_aws_sdk_go//aws",
32 | "@com_github_aws_aws_sdk_go//service/cognitoidentityprovider",
33 | "@com_github_google_go_github_v46//github",
34 | "@com_github_golang_jwt_jwt_v4//:go_default_library",
35 | "@com_github_gocql_gocql//:gocql",
36 | ],
37 | )
38 |
--------------------------------------------------------------------------------
/cloudform/search_minimal.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Shell script to deploy an AWS CloudFormation template for OpenSearch.
4 |
5 | # Set variables
6 | STACK_NAME="MinimalOpenSearchStack"
7 | TEMPLATE_FILE="search_minimal.yaml" # Ensure the YAML template file is in the same directory as this script
8 | REGION="us-east-1" # Change this to your target AWS region
9 | MASTER_PASSWORD="-Password1!" # Replace with your desired master password
10 |
11 | # Create the CloudFormation Stack
12 | echo "Deploying the CloudFormation stack: ${STACK_NAME} in region: ${REGION}..."
13 | aws cloudformation create-stack \
14 | --stack-name ${STACK_NAME} \
15 | --template-body file://${TEMPLATE_FILE} \
16 | --parameters ParameterKey=MasterPassword,ParameterValue=${MASTER_PASSWORD} \
17 | --capabilities CAPABILITY_NAMED_IAM \
18 | --region ${REGION}
19 |
20 | # Wait for the deployment to complete
21 | if [ $? -eq 0 ]; then
22 | echo "Stack creation initiated successfully. Waiting for completion..."
23 | aws cloudformation wait stack-create-complete --stack-name ${STACK_NAME} --region ${REGION}
24 |
25 | if [ $? -eq 0 ]; then
26 | echo "Stack created successfully!"
27 |
28 | # Fetch and display the OpenSearch endpoint
29 | OPENSEARCH_ENDPOINT=$(aws cloudformation describe-stacks \
30 | --stack-name ${STACK_NAME} \
31 | --query "Stacks[0].Outputs[?OutputKey=='OpenSearchEndpoint'].OutputValue" \
32 | --output text \
33 | --region ${REGION})
34 |
35 | echo "OpenSearch Endpoint: ${OPENSEARCH_ENDPOINT}"
36 | else
37 | echo "Error: Stack creation failed!"
38 | fi
39 | else
40 | echo "Error: Failed to initiate stack creation!"
41 | fi
--------------------------------------------------------------------------------
/lib/attr/attr.go:
--------------------------------------------------------------------------------
1 | package attr
2 |
3 | import "strconv"
4 |
5 | type AttributeTypes int32
6 |
7 | const (
8 | Number AttributeTypes = iota
9 | Text
10 | Complex
11 | Float
12 | Array
13 | Bool
14 | )
15 |
16 | type AttributeValue struct {
17 | Name string `form:"name" json:"name" binding:"required"`
18 | DisplayName string `form:"displayName" json:"displayName" binding:"required"`
19 | Type AttributeTypes `form:"type" json:"type" binding:"required"`
20 | Value string `form:"value" json:"value" binding:"required"`
21 | IntValue int32 `json:"intValue"`
22 | FloatValue float64 `json:"floatValue"`
23 | ComputedValue string `form:"computedValue" json:"computedValue" binding:"required"`
24 | Attributes []AttributeValue `form:"attributes[]" json:"attributes"`
25 | }
26 |
27 | func FlattenAttributeValue(value AttributeValue) []AttributeValue {
28 | leafNodes := make([]AttributeValue, 0)
29 | if value.Attributes == nil || len(value.Attributes) == 0 {
30 | leafNodes = append(leafNodes, value)
31 | } else {
32 | for _, attr := range value.Attributes {
33 | flatChildren := FlattenAttributeValue(attr)
34 | for _, flatChild := range flatChildren {
35 | leafNodes = append(leafNodes, flatChild)
36 | }
37 | }
38 | }
39 | return leafNodes
40 | }
41 |
42 | func FinalizeAttributeValue(value *AttributeValue) {
43 | if value.Type == Number {
44 | computedValue, _ := strconv.ParseInt(value.ComputedValue, 10, 32)
45 | value.IntValue = int32(computedValue)
46 | } else if value.Type == Float {
47 | computedValue, _ := strconv.ParseFloat(value.ComputedValue, 64)
48 | value.FloatValue = computedValue
49 | } else {
50 | value.IntValue = 0
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/func/watttime_demo/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "log"
7 | "os"
8 |
9 | "goclassifieds/lib/watttime"
10 |
11 | "github.com/aws/aws-lambda-go/lambda"
12 | )
13 |
14 | type RegionIntensity struct {
15 | Name string
16 | Ba string
17 | Intensity int
18 | }
19 |
20 | func handler(ctx context.Context, b json.RawMessage) {
21 | log.Print("top handler")
22 |
23 | loginInput := &watttime.LoginInput{
24 | Username: os.Getenv("WATTTIME_USERNAME"),
25 | Password: os.Getenv("WATTTIME_PASSWORD"),
26 | }
27 |
28 | loginRes, err := watttime.Login(loginInput)
29 | if err != nil {
30 | log.Print(err.Error())
31 | }
32 |
33 | // log.Printf("token = %s", loginRes.Token)
34 |
35 | regionsInput := &watttime.GridRegionsInput{
36 | Token: loginRes.Token,
37 | }
38 |
39 | gridRegionsRes, err := watttime.GridRegions(regionsInput)
40 | if err != nil {
41 | log.Print(err.Error())
42 | }
43 |
44 | intensities := make([]*RegionIntensity, len(gridRegionsRes.GridRegions))
45 |
46 | for index, region := range gridRegionsRes.GridRegions {
47 | log.Print("region: " + region.Name + " | Ba: " + region.Ba)
48 | indexInput := &watttime.IndexInput{
49 | Token: loginRes.Token,
50 | Ba: region.Ba,
51 | }
52 | intensity := &RegionIntensity{
53 | Name: region.Name,
54 | Ba: region.Ba,
55 | }
56 | intensities[index] = intensity
57 | indexRes, err := watttime.GetIndex(indexInput)
58 | if err != nil {
59 | log.Print(err.Error())
60 | } else {
61 | intensity.Intensity = indexRes.Percent
62 | log.Printf("Grid Intensity: %d", indexRes.Percent)
63 | log.Printf("Grid MOER: %f", indexRes.Moer)
64 | }
65 | }
66 | }
67 |
68 | func main() {
69 | log.SetFlags(0)
70 | lambda.Start(handler)
71 | }
72 |
--------------------------------------------------------------------------------
/iac/precheck-oidc-auth/user/index.js:
--------------------------------------------------------------------------------
1 | const fetch = require("node-fetch");
2 | const logger = require("./logger");
3 |
4 | exports.handler = async (event) => {
5 | logger({});
6 |
7 | console.log("Received event:", JSON.stringify(event, null, 2));
8 |
9 | // Extract the Authorization header
10 | const authorizationHeader = event.headers?.Authorization || event.headers?.authorization;
11 | if (!authorizationHeader || !authorizationHeader.startsWith("Bearer ")) {
12 | return {
13 | statusCode: 401,
14 | body: JSON.stringify({ error: "Unauthorized: Missing or invalid Authorization header" }),
15 | };
16 | }
17 |
18 | const accessToken = authorizationHeader.split(" ")[1];
19 |
20 | try {
21 | // Fetch user information from Login.gov `/userinfo` endpoint
22 | const userInfoResponse = await fetch(`${process.env.LOGIN_GOV_ISSUER}/api/openid_connect/userinfo`, {
23 | method: "GET",
24 | headers: {
25 | Authorization: `Bearer ${accessToken}`,
26 | },
27 | });
28 |
29 | if (!userInfoResponse.ok) {
30 | console.error("Failed to fetch user info from Login.gov:", userInfoResponse.statusText);
31 | return {
32 | statusCode: userInfoResponse.status,
33 | body: await userInfoResponse.text(),
34 | };
35 | }
36 |
37 | const userInfo = await userInfoResponse.json();
38 |
39 | // Return the user information
40 | return {
41 | statusCode: 200,
42 | body: JSON.stringify(userInfo),
43 | };
44 | } catch (error) {
45 | console.error("Error fetching user info from Login.gov:", error);
46 | return {
47 | statusCode: 500,
48 | body: JSON.stringify({ error: "Failed to fetch user info", details: error.message }),
49 | };
50 | }
51 | };
--------------------------------------------------------------------------------
/cloudform/search_minimal.yaml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: '2010-09-09'
2 | Description: Minimal OpenSearch instance using the smallest node type (t3.small.search) with fine-grained access control.
3 |
4 | Resources:
5 | OpenSearchDomain:
6 | Type: AWS::OpenSearchService::Domain
7 | Properties:
8 | DomainName: minimal-opensearch
9 | EngineVersion: OpenSearch_2.5
10 | ClusterConfig:
11 | InstanceType: t3.small.search
12 | InstanceCount: 1
13 | DedicatedMasterEnabled: false
14 | ZoneAwarenessEnabled: false
15 | EBSOptions:
16 | EBSEnabled: true
17 | VolumeType: gp2
18 | VolumeSize: 10
19 | AdvancedSecurityOptions:
20 | Enabled: true
21 | InternalUserDatabaseEnabled: true
22 | MasterUserOptions:
23 | MasterUserName: admin
24 | MasterUserPassword: !Sub '${MasterPassword}'
25 | EncryptionAtRestOptions:
26 | Enabled: true # Enable encryption at rest
27 | NodeToNodeEncryptionOptions:
28 | Enabled: true # Enable node-to-node encryption
29 | DomainEndpointOptions:
30 | EnforceHTTPS: true # Enable HTTPS enforcement
31 | AccessPolicies:
32 | Version: '2012-10-17'
33 | Statement:
34 | - Effect: Allow
35 | Principal:
36 | AWS: '*'
37 | Action: 'es:*'
38 | Resource: '*'
39 |
40 | Parameters:
41 | MasterPassword:
42 | Type: String
43 | Description: Master user password for OpenSearch fine-grained access control
44 | NoEcho: true
45 | MinLength: 8
46 | MaxLength: 32
47 | AllowedPattern: '[A-Za-z0-9@!?*#_+=^.-]+'
48 | ConstraintDescription: Password must be 8-32 characters long and can include letters, numbers, and special characters.
49 |
50 | Outputs:
51 | OpenSearchEndpoint:
52 | Description: Endpoint of the OpenSearch domain
53 | Value: !GetAtt OpenSearchDomain.Endpoint
--------------------------------------------------------------------------------
/func/encrypt_secret_value/index.js:
--------------------------------------------------------------------------------
1 | const sodium = require("libsodium-wrappers");
2 |
3 | module.exports.handler = async (event) => {
4 | try {
5 | await sodium.ready;
6 |
7 | const { publicKey: publicKeyBase64, secretValue } = event;
8 |
9 | // Validate the inputs
10 | if (!publicKeyBase64 || typeof publicKeyBase64 !== "string") {
11 | throw new Error(`Invalid or missing publicKey: ${publicKeyBase64}`);
12 | }
13 | if (!secretValue || typeof secretValue !== "string") {
14 | throw new Error(`Invalid or missing secretValue: ${secretValue}`);
15 | }
16 |
17 | // Decode public key
18 | const publicKey = Buffer.from(publicKeyBase64, "base64");
19 | if (publicKey.length !== sodium.crypto_box_PUBLICKEYBYTES) {
20 | throw new Error(
21 | `Invalid publicKey length: Expected ${sodium.crypto_box_PUBLICKEYBYTES}, got ${publicKey.length}`
22 | );
23 | }
24 |
25 | // Encrypt the message
26 | const message = Buffer.from(secretValue, "utf8");
27 | const sealedBox = sodium.crypto_box_seal(message, publicKey);
28 |
29 | // Convert the sealed box to Base64
30 | const encryptedValue = Buffer.from(sealedBox).toString("base64");
31 |
32 | // Ensure the encryptedValue is not empty
33 | if (!encryptedValue) {
34 | throw new Error("Failed to encrypt secretValue; Empty encryptedValue.");
35 | }
36 |
37 | console.log("Encrypted Value:", encryptedValue);
38 | return {
39 | statusCode: 200,
40 | body: JSON.stringify({ encryptedValue }),
41 | };
42 | } catch (error) {
43 | console.error("Lambda error:", error.message);
44 | return {
45 | statusCode: 500,
46 | body: JSON.stringify({ message: `Error processing request: ${error.message}` }),
47 | };
48 | }
49 | };
--------------------------------------------------------------------------------
/cloudform/main.yaml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: '2010-09-09'
2 | Description: 'AWS CloudFormation Sample Template: This template automates ase infrastructure.'
3 |
4 | Parameters:
5 | EnvironmentName:
6 | Description: The name of the environment (e.g., dev, prod)
7 | Type: String
8 | EnvironmentNameCamelCase:
9 | Description: The name of the environment (e.g., dev, prod) in CamelCase for supporting multiple environments in same account without conflicts.
10 | Type: String
11 | VendorSuffix:
12 | Description: Unique vendor suffix for the bucket.
13 | Type: String
14 | VendorSuffixCamelCase:
15 | Description: Unique vendor suffix in CamelCase for supporting multiple vendors under the same account.
16 | Type: String
17 | SSODomain:
18 | Description: The domain for hosting sso page.
19 | Type: String
20 |
21 | Resources:
22 | UsersStack:
23 | Type: AWS::CloudFormation::Stack
24 | Properties:
25 | TemplateURL: !Sub 'https://s3.amazonaws.com/rtc-classifieds-cloudformation-templates-${EnvironmentName}-${VendorSuffix}/users.yaml'
26 | Parameters:
27 | EnvironmentName: !Ref EnvironmentName
28 | EnvironmentNamCamelCase: !Ref EnvironmentNameCamelCase
29 | VendorSuffix: !Ref VendorSuffix
30 | VendorSuffixCamelCase: !Ref VendorSuffixCamelCase
31 | SSODomain: !Ref SSODomain
32 |
33 | SearchStack:
34 | Type: AWS::CloudFormation::Stack
35 | Properties:
36 | TemplateURL: !Sub 'https://s3.amazonaws.com/rtc-classifieds-cloudformation-templates-${EnvironmentName}-${VendorSuffix}/search.yaml'
37 | Parameters:
38 | EnvironmentName: !Ref EnvironmentName
39 | EnvironmentNamCamelCase: !Ref EnvironmentNameCamelCase
40 | VendorSuffix: !Ref VendorSuffix
41 | VendorSuffixCamelCase: !Ref VendorSuffixCamelCase
42 | UserPoolId: !GetAtt UsersStack.Outputs.UserPoolId
43 | IdentityPoolId: !GetAtt UsersStack.Outputs.IdentityPoolId
--------------------------------------------------------------------------------
/iac/precheck-oidc-auth/README.md:
--------------------------------------------------------------------------------
1 | implementation of an OIDC adapter (pkce:prononciation of PKCE) to bridge compatibility gaps between Login.gov and AWS Cognito and centralize external auth and/or support internal legacy user databases without requiring explicit migrations. The following changes are included:
2 |
3 | **Pulumi Infrastructure:**
4 |
5 | Created DynamoDB tables for state, code verifier, and redirect URI tracking.
6 | Deployed API Gateway with Lambda-backed endpoints (
7 |
8 | - /login
9 | - /callback
10 | - /token
11 | - /user
12 |
13 | Configured Lambda functions with required IAM permissions and environment variables.
14 |
15 | **Lambda Functions:**
16 |
17 | - loginLambda: Handles the /login endpoint to initiate the OIDC flow with PKCE (Proof Key for Code Exchange) and redirects users to Login.gov.
18 | - callbackLambda: Processes Login.gov responses at the /callback endpoint, exchanges authorization codes for tokens, and redirects users back to Cognito.
19 | - tokenLambda: Implements the /token endpoint for exchanging authorization codes for tokens.
20 | - userLambda: Retrieves user profile information from Cognito's /userinfo endpoint.
21 |
22 | **Features:**
23 |
24 | - Secure state management with short-lived TTL in DynamoDB for mitigating CSRF and replay attacks.
25 | - PKCE-based implementation for enhanced security in OIDC flows.
26 | - Dynamic discovery of Cognito endpoints using the OpenID Connect discovery document.
27 | - Comprehensive error handling and logging for debugging.
28 |
29 | **Testing Notes:**
30 |
31 | - Deployed and tested the full OIDC flow end-to-end, including Login.gov authentication and Cognito redirection.
32 | - Verified DynamoDB entries (state, authorization codes) and their TTL behavior.
33 | - Ensured error propagation for invalid or expired tokens.
34 | - Impact: This implementation enables seamless integration with Login.gov while maintaining compatibility with AWS Cognito, providing a robust and secure authentication solution for our monorepo.
--------------------------------------------------------------------------------
/cloudform/search_distinct.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Shell script to deploy an AWS CloudFormation template for OpenSearch.
4 |
5 | # Set variables
6 | STACK_NAME="DistinctOpenSearchStack" # Updated the stack name to avoid conflicts
7 | TEMPLATE_FILE="search_distinct.yaml" # Ensure the updated YAML template file is in the same directory as this script
8 | REGION="us-east-1" # Change this to your target AWS region
9 | MASTER_PASSWORD="-Password1!" # Replace with your desired master password
10 | COGNITO_USER_POOL_ID="us-east-1_sWRV0kAgS" # Replace with the existing Cognito User Pool ID
11 | COGNITO_IDENTITY_POOL_ID="us-east-1:026caf18-c852-451b-a93e-fb431c4eee6d" # Replace with the existing Cognito Identity Pool ID
12 |
13 | # Create the CloudFormation Stack
14 | echo "Deploying the CloudFormation stack: ${STACK_NAME} in region: ${REGION}..."
15 | aws cloudformation create-stack \
16 | --stack-name ${STACK_NAME} \
17 | --template-body file://${TEMPLATE_FILE} \
18 | --parameters \
19 | ParameterKey=MasterPassword,ParameterValue=${MASTER_PASSWORD} \
20 | ParameterKey=CognitoUserPoolId,ParameterValue=${COGNITO_USER_POOL_ID} \
21 | ParameterKey=CognitoIdentityPoolId,ParameterValue=${COGNITO_IDENTITY_POOL_ID} \
22 | --capabilities CAPABILITY_NAMED_IAM \
23 | --region ${REGION}
24 |
25 | # Wait for the deployment to complete
26 | if [ $? -eq 0 ]; then
27 | echo "Stack creation initiated successfully. Waiting for completion..."
28 | aws cloudformation wait stack-create-complete --stack-name ${STACK_NAME} --region ${REGION}
29 |
30 | if [ $? -eq 0 ]; then
31 | echo "Stack created successfully!"
32 |
33 | # Fetch and display the OpenSearch endpoint
34 | OPENSEARCH_ENDPOINT=$(aws cloudformation describe-stacks \
35 | --stack-name ${STACK_NAME} \
36 | --query "Stacks[0].Outputs[?OutputKey=='DistinctOpenSearchEndpoint'].OutputValue" \
37 | --output text \
38 | --region ${REGION})
39 |
40 | echo "OpenSearch Endpoint: ${OPENSEARCH_ENDPOINT}"
41 | else
42 | echo "Error: Stack creation failed!"
43 | fi
44 | else
45 | echo "Error: Failed to initiate stack creation!"
46 | fi
--------------------------------------------------------------------------------
/cloudform/cognito_role.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Variables
4 | STACK_NAME="CognitoAuthenticatedRoleStack"
5 | TEMPLATE_FILE="cognito_role.yaml" # Ensure the YAML template file is in the same directory
6 | REGION="us-east-1" # Change to your AWS target region
7 | IDENTITY_POOL_ID="us-east-1:026caf18-c852-451b-a93e-fb431c4eee6d" # Replace with your Cognito Identity Pool ID
8 | OPENSEARCH_DOMAIN_NAME="rtc-classifieds-dev-rtc" # Replace with your OpenSearch domain name
9 | ENVIRONMENT_NAME_CAMEL_CASE="Dev" # Replace with your environment name in camel case
10 | VENDOR_SUFFIX_CAMEL_CASE="Rtc" # Replace with your vendor suffix in camel case
11 |
12 | # Deploy the CloudFormation stack
13 | echo "Deploying the CloudFormation stack: ${STACK_NAME} in region: ${REGION}..."
14 | aws cloudformation create-stack \
15 | --stack-name ${STACK_NAME} \
16 | --template-body file://${TEMPLATE_FILE} \
17 | --parameters ParameterKey=IdentityPoolId,ParameterValue=${IDENTITY_POOL_ID} \
18 | ParameterKey=OpenSearchDomainName,ParameterValue=${OPENSEARCH_DOMAIN_NAME} \
19 | ParameterKey=EnvironmentNameCamelCase,ParameterValue=${ENVIRONMENT_NAME_CAMEL_CASE} \
20 | ParameterKey=VendorSuffixCamelCase,ParameterValue=${VENDOR_SUFFIX_CAMEL_CASE} \
21 | --capabilities CAPABILITY_NAMED_IAM \
22 | --region ${REGION}
23 |
24 | # Wait for stack creation to complete
25 | if [ $? -eq 0 ]; then
26 | echo "Stack creation initiated successfully. Waiting for completion..."
27 | aws cloudformation wait stack-create-complete --stack-name ${STACK_NAME} --region ${REGION}
28 |
29 | if [ $? -eq 0 ]; then
30 | echo "Stack created successfully!"
31 |
32 | # Fetch and display the role ARN
33 | COGNITO_ROLE_ARN=$(aws cloudformation describe-stacks \
34 | --stack-name ${STACK_NAME} \
35 | --query "Stacks[0].Outputs[?OutputKey=='CognitoRoleArn'].OutputValue" \
36 | --output text \
37 | --region ${REGION})
38 |
39 | echo "Cognito Authenticated Role ARN: ${COGNITO_ROLE_ARN}"
40 | else
41 | echo "Error: Stack creation failed!"
42 | fi
43 | else
44 | echo "Error: Failed to initiate stack creation!"
45 | fi
--------------------------------------------------------------------------------
/cloudform/cognito_role.yaml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: '2010-09-09'
2 | Description: CloudFormation template to create an IAM role for Cognito-authenticated users to access OpenSearch and OpenSearch Dashboards.
3 |
4 | Resources:
5 | CognitoAuthenticatedRole:
6 | Type: AWS::IAM::Role
7 | Properties:
8 | RoleName: !Sub 'CognitoAuthenticatedRole${EnvironmentNameCamelCase}${VendorSuffixCamelCase}'
9 | AssumeRolePolicyDocument:
10 | Version: '2012-10-17'
11 | Statement:
12 | - Effect: Allow
13 | Principal:
14 | Federated: "cognito-identity.amazonaws.com"
15 | Action: "sts:AssumeRoleWithWebIdentity"
16 | Condition:
17 | StringEquals:
18 | "cognito-identity.amazonaws.com:aud": !Ref IdentityPoolId
19 | ForAnyValue:StringLike:
20 | "cognito-identity.amazonaws.com:amr": "authenticated"
21 |
22 | Policies:
23 | - PolicyName: OpenSearchAccess
24 | PolicyDocument:
25 | Version: "2012-10-17"
26 | Statement:
27 | - Effect: Allow
28 | Action:
29 | - "es:ESHttpGet"
30 | - "es:ESHttpPost"
31 | - "es:ESHttpPut"
32 | - "es:ESHttpDelete"
33 | Resource: !Sub 'arn:aws:es:${AWS::Region}:${AWS::AccountId}:domain/${OpenSearchDomainName}/*'
34 |
35 | Parameters:
36 | IdentityPoolId:
37 | Type: String
38 | Description: The ID of the Cognito Identity Pool (e.g., us-east-1:xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx)
39 | OpenSearchDomainName:
40 | Type: String
41 | Description: Name of the OpenSearch domain (e.g., rtc-classifieds-dev-rtc)
42 | EnvironmentNameCamelCase:
43 | Type: String
44 | Description: The name of the environment in camel case (e.g., Dev, Prod)
45 | VendorSuffixCamelCase:
46 | Type: String
47 | Description: Unique vendor suffix in camel case for multiple vendors under the same account
48 |
49 | Outputs:
50 | CognitoRoleArn:
51 | Description: ARN of the IAM Role created for Cognito-authenticated users
52 | Value: !GetAtt CognitoAuthenticatedRole.Arn
--------------------------------------------------------------------------------
/iac/precheck-oidc-auth/user/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "user",
3 | "version": "1.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "user",
9 | "version": "1.0.0",
10 | "license": "ISC",
11 | "dependencies": {
12 | "node-fetch": "^2.7.0"
13 | }
14 | },
15 | "node_modules/node-fetch": {
16 | "version": "2.7.0",
17 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
18 | "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
19 | "dependencies": {
20 | "whatwg-url": "^5.0.0"
21 | },
22 | "engines": {
23 | "node": "4.x || >=6.0.0"
24 | },
25 | "peerDependencies": {
26 | "encoding": "^0.1.0"
27 | },
28 | "peerDependenciesMeta": {
29 | "encoding": {
30 | "optional": true
31 | }
32 | }
33 | },
34 | "node_modules/tr46": {
35 | "version": "0.0.3",
36 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
37 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
38 | },
39 | "node_modules/webidl-conversions": {
40 | "version": "3.0.1",
41 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
42 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
43 | },
44 | "node_modules/whatwg-url": {
45 | "version": "5.0.0",
46 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
47 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
48 | "dependencies": {
49 | "tr46": "~0.0.3",
50 | "webidl-conversions": "^3.0.0"
51 | }
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/lib/gov/gov.go:
--------------------------------------------------------------------------------
1 | package gov
2 |
3 | import (
4 | "goclassifieds/lib/utils"
5 | "text/template"
6 |
7 | "github.com/gocql/gocql"
8 | )
9 |
10 | const (
11 | User ResourceUserTypes = iota
12 | Site
13 | )
14 |
15 | var UserTypeMap = map[string]ResourceUserTypes{
16 | "user": User,
17 | "site": Site,
18 | }
19 |
20 | type ResourceUserTypes int32
21 |
22 | const (
23 | GithubRepo ResourceTypes = iota
24 | DruidSite
25 | CognitoUserPool
26 | CassandraTable
27 | )
28 |
29 | var ResourceTypeMap = map[string]ResourceTypes{
30 | "githubrepo": GithubRepo,
31 | "druidsite": DruidSite,
32 | "cognitouserpool": CognitoUserPool,
33 | "cassandratable": CassandraTable,
34 | }
35 |
36 | type ResourceTypes int32
37 |
38 | const (
39 | Read ResourceOperations = iota
40 | Write
41 | Delete
42 | )
43 |
44 | var OperationMap = map[string]ResourceOperations{
45 | "read": Read,
46 | "write": Write,
47 | "delete": Delete,
48 | }
49 |
50 | type ResourceOperations int32
51 |
52 | type GrantAccessRequest struct {
53 | User string
54 | Type ResourceUserTypes
55 | Resource ResourceTypes
56 | Asset string
57 | Operation ResourceOperations
58 | AdditionalResources []Resource
59 | LogUsageLambdaInput *utils.LogUsageLambdaInput
60 | }
61 |
62 | type Resource struct {
63 | User string
64 | Type ResourceUserTypes
65 | Resource ResourceTypes
66 | Asset string
67 | Operation ResourceOperations
68 | }
69 |
70 | type GrantAccessResponse struct {
71 | Grant bool `json:"grant"`
72 | }
73 |
74 | type ResourceManagerParams struct {
75 | Session *gocql.Session
76 | Request *GrantAccessRequest
77 | Template *template.Template
78 | // Resource string
79 | // Operation string
80 | }
81 |
82 | func Query() string {
83 | return `
84 | {{ define "grant_access" }}
85 | SELECT
86 | op
87 | FROM
88 | resources2
89 | WHERE
90 | user = {{ bindValue (index .Metadata "user" ) }}
91 | AND
92 | type = {{ bindValue (index .Metadata "type" ) }}
93 | AND
94 | resource = {{ bindValue (index .Metadata "resource" ) }}
95 | AND
96 | asset = {{ bindValue (index .Metadata "asset" ) }}
97 | AND
98 | op = {{ bindValue (index .Metadata "op" ) }}
99 | {{end}}
100 | `
101 | }
102 |
--------------------------------------------------------------------------------
/lib/vocab/vocab.go:
--------------------------------------------------------------------------------
1 | package vocab
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "log"
7 | )
8 |
9 | type Vocabulary struct {
10 | Id string `form:"id" json:"id"`
11 | UserId string `form:"userId" json:"userId"`
12 | MachineName string `form:"machineName" json:"machineName" binding:"required" validate:"required"`
13 | HumanName string `form:"humanName" json:"humanName" binding:"required" validate:"required"`
14 | Terms []Term `form:"terms[]" json:"terms" binding:"required" validate:"required"`
15 | }
16 |
17 | type Term struct {
18 | Id string `form:"id" json:"id" binding:"required" validate:"required"`
19 | VocabularyId string `form:"vocabularyId" json:"vocabularyId" binding:"required" validate:"required"`
20 | ParentId string `form:"parentId" json:"parentId"`
21 | MachineName string `form:"machineName" json:"machineName" binding:"required" validate:"required"`
22 | HumanName string `form:"humanName" json:"humanName" binding:"required" validate:"required"`
23 | Weight int `form:"weight" json:"weight" binding:"required" validate:"required"`
24 | Group bool `form:"group" json:"group" binding:"required" validate:"required"`
25 | Selected bool `form:"selected" json:"selected" binding:"required" validate:"required"`
26 | Level int `form:"level" json:"level" binding:"required" validate:"required"`
27 | Children []Term `form:"children[]" json:"children"`
28 | }
29 |
30 | func ToEntity(vocab *Vocabulary) (map[string]interface{}, error) {
31 | var buf bytes.Buffer
32 | if err := json.NewEncoder(&buf).Encode(vocab); err != nil {
33 | log.Fatalf("Error encoding query: %s", err)
34 | }
35 | jsonData, err := json.Marshal(vocab)
36 | if err != nil {
37 | return nil, err
38 | }
39 | var entity map[string]interface{}
40 | err = json.Unmarshal(jsonData, &entity)
41 | return entity, nil
42 | }
43 |
44 | func FlattenTerm(term Term, selectedOnly bool) []Term {
45 | leafNodes := make([]Term, 0)
46 | if term.Children == nil || len(term.Children) == 0 {
47 | if !selectedOnly || term.Selected {
48 | leafNodes = append(leafNodes, term)
49 | }
50 | } else {
51 | for _, childTerm := range term.Children {
52 | flatChildren := FlattenTerm(childTerm, selectedOnly)
53 | for _, flatChild := range flatChildren {
54 | leafNodes = append(leafNodes, flatChild)
55 | }
56 | }
57 | }
58 | return leafNodes
59 | }
60 |
--------------------------------------------------------------------------------
/api/chat/queries.tmpl:
--------------------------------------------------------------------------------
1 | {{ define "chatconversations" }}
2 | SELECT
3 | recipientid AS recipientId,
4 | recipientlabel AS recipientLabel,
5 | userid AS userId
6 | FROM
7 | chatconversations
8 | WHERE
9 | userid = {{ bindValue .Req.RequestContext.Authorizer.claims.sub }}
10 | {{end}}
11 |
12 | {{ define "chatmessages" }}
13 | SELECT
14 | senderid,
15 | recipientid,
16 | createdat,
17 | message
18 | FROM
19 | chatmessages
20 | WHERE
21 | senderid = {{ bindValue .Req.RequestContext.Authorizer.claims.sub }}
22 | AND
23 | recipientid = {{ bindValue (index .Req.QueryStringParameters "recipientId") }}
24 | {{end}}
25 |
26 | {{ define "_chatmessages_inverse" }}
27 | SELECT
28 | senderid,
29 | recipientid,
30 | createdat,
31 | message
32 | FROM
33 | chatmessages
34 | WHERE
35 | recipientid = {{ bindValue .Req.RequestContext.Authorizer.claims.sub }}
36 | AND
37 | senderid = {{ bindValue (index .Req.QueryStringParameters "recipientId") }}
38 | {{end}}
39 |
40 | {{ define "_chatconnections" }}
41 | SELECT
42 | connid,
43 | userid,
44 | createdat
45 | FROM
46 | chatconnections
47 | WHERE
48 | userid = {{ bindValue .Req.RequestContext.Authorizer.claims.sub }}
49 | {{ end }}
50 |
51 | {{ define "_chatconnections_inverse" }}
52 | SELECT
53 | connid,
54 | userid,
55 | createdat
56 | FROM
57 | chatconnections
58 | WHERE
59 | userid = {{ bindValue (index .Metadata "recipientId" ) }}
60 | {{ end }}
61 |
62 | {{define "leads" }}
63 | SELECT
64 | adid,
65 | profileid,
66 | senderid,
67 | phone,
68 | email,
69 | createdat
70 | FROM
71 | leads
72 | WHERE
73 | profileid = {{ bindValue (index .Req.QueryStringParameters "profileId") }}
74 | {{ end }}
75 |
76 | {{ define "pages" }}
77 | SELECT
78 | site,
79 | path,
80 | title,
81 | body,
82 | published,
83 | createdAt
84 | FROM
85 | pages
86 | WHERE
87 | site = {{ bindValue (index .Req.QueryStringParameters "site") }}
88 | {{ end }}
89 |
90 | {{ define "gridlayouts"}}
91 | SELECT
92 | id,
93 | site,
94 | weight,
95 | cols,
96 | rows,
97 | x,
98 | y
99 | FROM
100 | gridlayouts
101 | WHERE
102 | site = {{ bindValue (index .Req.QueryStringParameters "site") }}
103 | {{ end }}
--------------------------------------------------------------------------------
/lib/chat/chat.go:
--------------------------------------------------------------------------------
1 | package chat
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "log"
7 | "time"
8 | )
9 |
10 | type ChatConnection struct {
11 | ConnId string `form:"connId" json:"connId" binding:"required" validate:"required"`
12 | CreatedAt time.Time `form:"createdAt" json:"createdAt" binding:"required" validate:"required"`
13 | UserId string `form:"userId" json:"userId" binding:"required" validate:"required"`
14 | }
15 |
16 | type ChatConversation struct {
17 | UserId string `form:"userId" json:"userId" binding:"required" validate:"required"`
18 | RecipientId string `form:"recipientId" json:"recipientId" binding:"required" validate:"required"`
19 | RecipientLabel string `form:"recipientLabel" json:"recipientLabel" binding:"required" validate:"required"`
20 | }
21 |
22 | type ChatMessage struct {
23 | SenderId string `form:"senderId" json:"senderId" binding:"required" validate:"required"`
24 | RecipientId string `form:"recipientId" json:"recipientId" binding:"required" validate:"required"`
25 | Message string `form:"message" json:"message" binding:"required" validate:"required"`
26 | CreatedAt time.Time `form:"createdAt" json:"createdAt" binding:"required" validate:"required"`
27 | }
28 |
29 | func ToConnectionEntity(conn *ChatConnection) (map[string]interface{}, error) {
30 | var buf bytes.Buffer
31 | if err := json.NewEncoder(&buf).Encode(conn); err != nil {
32 | log.Fatalf("Error encoding query: %s", err)
33 | }
34 | jsonData, err := json.Marshal(conn)
35 | if err != nil {
36 | return nil, err
37 | }
38 | var entity map[string]interface{}
39 | err = json.Unmarshal(jsonData, &entity)
40 | return entity, nil
41 | }
42 |
43 | func ToConversationEntity(conv *ChatConversation) (map[string]interface{}, error) {
44 | var buf bytes.Buffer
45 | if err := json.NewEncoder(&buf).Encode(conv); err != nil {
46 | log.Fatalf("Error encoding query: %s", err)
47 | }
48 | jsonData, err := json.Marshal(conv)
49 | if err != nil {
50 | return nil, err
51 | }
52 | var entity map[string]interface{}
53 | err = json.Unmarshal(jsonData, &entity)
54 | return entity, nil
55 | }
56 |
57 | func ToMessageEntity(message *ChatMessage) (map[string]interface{}, error) {
58 | var buf bytes.Buffer
59 | if err := json.NewEncoder(&buf).Encode(message); err != nil {
60 | log.Fatalf("Error encoding query: %s", err)
61 | }
62 | jsonData, err := json.Marshal(message)
63 | if err != nil {
64 | return nil, err
65 | }
66 | var entity map[string]interface{}
67 | err = json.Unmarshal(jsonData, &entity)
68 | return entity, nil
69 | }
70 |
--------------------------------------------------------------------------------
/cloudform/search.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Shell script to deploy an AWS CloudFormation template for OpenSearch.
4 |
5 | # Set variables
6 | STACK_NAME="BaseOpenSearchStack"
7 | TEMPLATE_FILE="search.yaml" # Ensure the YAML template file is in the same directory as this script
8 | REGION="us-east-1" # Change this to your target AWS region
9 | MASTER_PASSWORD="" # Replace with your desired master password
10 | ENVIRONMENT_NAME="dev" # Replace with your environment name (e.g., dev, prod)
11 | ENVIRONMENT_NAME_CAMEL_CASE="Dev" # Replace with the CamelCase environment name (e.g., Dev, Prod)
12 | VENDOR_SUFFIX="rtc" # Replace with your vendor suffix
13 | VENDOR_SUFFIX_CAMEL_CASE="Rtc" # Replace with the CamelCase vendor suffix
14 | COGNITO_USER_POOL_ID="us-east-1_sWRV0kAgS"
15 | COGNITO_IDENTITY_POOL_ID="us-east-1:026caf18-c852-451b-a93e-fb431c4eee6d"
16 |
17 | # Create the CloudFormation Stack
18 | echo "Deploying the CloudFormation stack: ${STACK_NAME} in region: ${REGION}..."
19 | aws cloudformation create-stack \
20 | --stack-name ${STACK_NAME} \
21 | --template-body file://${TEMPLATE_FILE} \
22 | --parameters ParameterKey=MasterPassword,ParameterValue=${MASTER_PASSWORD} \
23 | ParameterKey=EnvironmentName,ParameterValue=${ENVIRONMENT_NAME} \
24 | ParameterKey=EnvironmentNameCamelCase,ParameterValue=${ENVIRONMENT_NAME_CAMEL_CASE} \
25 | ParameterKey=VendorSuffix,ParameterValue=${VENDOR_SUFFIX} \
26 | ParameterKey=VendorSuffixCamelCase,ParameterValue=${VENDOR_SUFFIX_CAMEL_CASE} \
27 | ParameterKey=UserPoolId,ParameterValue=${USER_POOL_ID} \
28 | ParameterKey=IdentityPoolId,ParameterValue=${IDENTITY_POOL_ID} \
29 | --capabilities CAPABILITY_NAMED_IAM \
30 | --region ${REGION}
31 |
32 | # Wait for the deployment to complete
33 | if [ $? -eq 0 ]; then
34 | echo "Stack creation initiated successfully. Waiting for completion..."
35 | aws cloudformation wait stack-create-complete --stack-name ${STACK_NAME} --region ${REGION}
36 |
37 | if [ $? -eq 0 ]; then
38 | echo "Stack created successfully!"
39 |
40 | # Fetch and display the OpenSearch endpoint
41 | OPENSEARCH_ENDPOINT=$(aws cloudformation describe-stacks \
42 | --stack-name ${STACK_NAME} \
43 | --query "Stacks[0].Outputs[?OutputKey=='OpenSearchEndpoint'].OutputValue" \
44 | --output text \
45 | --region ${REGION})
46 |
47 | echo "OpenSearch Endpoint: ${OPENSEARCH_ENDPOINT}"
48 | else
49 | echo "Error: Stack creation failed!"
50 | fi
51 | else
52 | echo "Error: Failed to initiate stack creation!"
53 | fi
--------------------------------------------------------------------------------
/HelloWorld.yml:
--------------------------------------------------------------------------------
1 | service: goclassifieds-hellworld
2 | provider:
3 | name: aws
4 | runtime: go1.x
5 | memorySize: 256
6 | timeout: 10
7 | httpApi:
8 | payload: '1.0'
9 | cors: true
10 | package:
11 | individually: false
12 | include:
13 | - bazel-out/darwin_arm64-fastbuild-ST-5fa8105c1248/bin/func/hello_world/hello_world_/hello_world
14 | exclude:
15 | - ./**
16 | - bazel-out/darwin_arm64-fastbuild-ST-5fa8105c1248/bin/func/hello_world/hello_world_/**
17 | resources:
18 | Resources:
19 | HelloWorldRole:
20 | Type: AWS::IAM::Role
21 | Properties:
22 | Path: "/"
23 | RoleName: "goclassifieds-helloworld-us-east-1-lambdaRole"
24 | AssumeRolePolicyDocument:
25 | Version: '2012-10-17'
26 | Statement:
27 | - Effect: Allow
28 | Principal:
29 | Service:
30 | - lambda.amazonaws.com
31 | Action: sts:AssumeRole
32 | ManagedPolicyArns:
33 | - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
34 | Policies:
35 | - PolicyName: goclassifieds-helloworld-lambda
36 | PolicyDocument:
37 | Version: '2012-10-17'
38 | Statement:
39 | - Effect: Allow
40 | Action:
41 | - logs:CreateLogGroup
42 | - logs:CreateLogStream
43 | - logs:PutLogEvents
44 | - logs:DescribeLogGroups
45 | - logs:DescribeLogStreams
46 | - logs:GetLogEvents
47 | - logs:FilterLogEvents
48 | Resource:
49 | - 'Fn::Join':
50 | - ':'
51 | -
52 | - 'arn:aws:logs'
53 | - Ref: 'AWS::Region'
54 | - Ref: 'AWS::AccountId'
55 | - 'log-group:/aws/lambda/*:*:*'
56 | - Effect: "Allow"
57 | Action:
58 | - "lambda:InvokeFunction"
59 | Resource: "*"
60 | - Effect: "Allow"
61 | Action:
62 | - "execute-api:Invoke"
63 | - "execute-api:ManageConnections"
64 | Resource: "*"
65 | functions:
66 | HelloWorld:
67 | handler: bazel-out/darwin_arm64-fastbuild-ST-5fa8105c1248/bin/func/hello_world/hello_world_/hello_world
68 | role: HelloWorldRole
69 | events:
70 | - httpApi:
71 | path: /helloworld
72 | method: GET
--------------------------------------------------------------------------------
/cloudform/users.yaml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: '2010-09-09'
2 | Description: 'AWS CloudFormation Sample Template: This template demonstrates the creation of Cognito User Pools'
3 |
4 | Parameters:
5 | EnvironmentName:
6 | Description: The name of the environment (e.g., dev, prod)
7 | Type: String
8 | EnvironmentNameCamelCase:
9 | Description: The name of the environment (e.g., dev, prod) in CamelCase for supporting multiple environments in same account without conflicts.
10 | Type: String
11 | VendorSuffix:
12 | Description: Unique vendor suffix for the bucket.
13 | Type: String
14 | VendorSuffixCamelCase:
15 | Description: Unique vendor suffix in CamelCase for supporting multiple vendors under the same account.
16 | Type: String
17 | SSODomain:
18 | Description: The domain for hosting sso page.
19 | Type: String
20 |
21 | Resources:
22 | UserPool:
23 | Type: "AWS::Cognito::UserPool"
24 | Properties:
25 | UserPoolName: !Sub 'RtcClassifiedsUserPool${EnvironmentNameCamelCase}${VendorSuffixCamelCase}'
26 | Policies:
27 | PasswordPolicy:
28 | MinimumLength: 8
29 | RequireLowercase: True
30 | RequireNumbers: True
31 | RequireSymbols: True
32 | RequireUppercase: True
33 |
34 | UserPoolClient:
35 | Type: "AWS::Cognito::UserPoolClient"
36 | Properties:
37 | ClientName: !Sub 'RtcClassifiedsUserPoolClient${EnvironmentNameCamelCase}${VendorSuffixCamelCase}'
38 | GenerateSecret: False
39 | UserPoolId:
40 | Ref: UserPool
41 | SupportedIdentityProviders: ["COGNITO"]
42 | CallbackURLs: [!Sub 'https://${SSODomain}/auth-callback']
43 | LogoutURLs: [!Sub 'https://${SSODomain}/logout']
44 | AllowedOAuthFlows: ["code"]
45 | AllowedOAuthScopes: ["email", "openid"]
46 | AllowedOAuthFlowsUserPoolClient: true
47 |
48 | UserPoolDomain:
49 | Type: "AWS::Cognito::UserPoolDomain"
50 | Properties:
51 | UserPoolId:
52 | Ref: UserPool
53 | Domain: !Ref SSODomain
54 |
55 | UserIdentityPool:
56 | Type: "AWS::Cognito::IdentityPool"
57 | Properties:
58 | IdentityPoolName: !Sub 'ClassifiedsIdentityPool${EnvironmentNameCamelCase}${VendorSuffixCamelCase}'
59 | AllowUnauthenticatedIdentities: false
60 | CognitoIdentityProviders:
61 | - ClientId:
62 | Ref: UserPoolClient
63 | ProviderName:
64 | Fn::GetAtt:
65 | - UserPool
66 | - ProviderName
67 |
68 | Outputs:
69 | UserPoolId:
70 | Description: The ID of the user pool
71 | Value: !Ref UserPool
72 | IdentityPoolId:
73 | Description: The ID of the identity pool
74 | Value: !Ref UserIdentityPool
--------------------------------------------------------------------------------
/lib/utils/adub.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "encoding/json" // For working with JSON (e.g., unmarshaling request bodies)
5 | "fmt" // For string formatting and creating errors
6 | "github.com/aws/aws-lambda-go/events" // For APIGatewayProxyRequest & APIGatewayProxyResponse
7 | )
8 |
9 | func CopyRequest(req *events.APIGatewayProxyRequest, modifications func(*events.APIGatewayProxyRequest)) *events.APIGatewayProxyRequest {
10 | // Create a deep copy of the original request
11 | newReq := &events.APIGatewayProxyRequest{
12 | Resource: req.Resource,
13 | Path: req.Path,
14 | HTTPMethod: req.HTTPMethod,
15 | Headers: copyMap(req.Headers),
16 | QueryStringParameters: copyMap(req.QueryStringParameters),
17 | PathParameters: copyMap(req.PathParameters),
18 | StageVariables: copyMap(req.StageVariables),
19 | RequestContext: req.RequestContext,
20 | Body: req.Body,
21 | IsBase64Encoded: req.IsBase64Encoded,
22 | MultiValueHeaders: copyMultiMap(req.MultiValueHeaders),
23 | MultiValueQueryStringParameters: copyMultiMap(req.MultiValueQueryStringParameters),
24 | }
25 |
26 | // Apply modifications
27 | if modifications != nil {
28 | modifications(newReq)
29 | }
30 |
31 | return newReq
32 | }
33 |
34 | // Helper functions to copy maps to maintain immutability
35 | func copyMap(original map[string]string) map[string]string {
36 | if original == nil {
37 | return nil
38 | }
39 | copied := make(map[string]string)
40 | for k, v := range original {
41 | copied[k] = v
42 | }
43 | return copied
44 | }
45 |
46 | func copyMultiMap(original map[string][]string) map[string][]string {
47 | if original == nil {
48 | return nil
49 | }
50 | copied := make(map[string][]string)
51 | for k, v := range original {
52 | copied[k] = append([]string{}, v...)
53 | }
54 | return copied
55 | }
56 |
57 | func PluckPropertyFromJSON(body string, propertyName string) (string, error) {
58 | if body == "" {
59 | return "", fmt.Errorf("request body is empty")
60 | }
61 |
62 | // Parse the JSON body into a map
63 | var data map[string]interface{}
64 | err := json.Unmarshal([]byte(body), &data)
65 | if err != nil {
66 | return "", fmt.Errorf("failed to parse JSON: %v", err)
67 | }
68 |
69 | // Extract the desired property
70 | value, ok := data[propertyName]
71 | if !ok {
72 | return "", fmt.Errorf("property %s not found in JSON body", propertyName)
73 | }
74 |
75 | // Ensure the value is a string
76 | strValue, ok := value.(string)
77 | if !ok {
78 | return "", fmt.Errorf("property %s is not a string", propertyName)
79 | }
80 |
81 | return strValue, nil
82 | }
--------------------------------------------------------------------------------
/serverless-watttime-demo.yml:
--------------------------------------------------------------------------------
1 | service: watttime-demo
2 | frameworkVersion: '3'
3 | plugins:
4 | - serverless-prune-plugin
5 | custom:
6 | wattTimeUsername: ${file(./private.${opt:stage, 'dev'}.json):wattTimeUsername}
7 | wattTimePassword: ${file(./private.${opt:stage, 'dev'}.json):wattTimePassword}
8 | provider:
9 | name: aws
10 | runtime: go1.x
11 | memorySize: 256
12 | timeout: 10
13 | package:
14 | individually: false
15 | patterns:
16 | - '!./**'
17 | - bazel-out/darwin_arm64-fastbuild-ST-5fa8105c1248/bin/func/watttime_demo/watttime_demo_/watttime_demo
18 | resources:
19 | Resources:
20 | WattTimeDemoRole:
21 | Type: AWS::IAM::Role
22 | Properties:
23 | Path: "/"
24 | RoleName: watttime-demo-${opt:region, 'us-east-1'}-lambdaRole
25 | AssumeRolePolicyDocument:
26 | Version: '2012-10-17'
27 | Statement:
28 | - Effect: Allow
29 | Principal:
30 | Service:
31 | - lambda.amazonaws.com
32 | - apigateway.amazonaws.com
33 | Action: sts:AssumeRole
34 | ManagedPolicyArns:
35 | - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
36 | Policies:
37 | - PolicyName: watttime-demo-lambda
38 | PolicyDocument:
39 | Version: '2012-10-17'
40 | Statement:
41 | - Effect: Allow
42 | Action:
43 | - logs:CreateLogGroup
44 | - logs:CreateLogStream
45 | - logs:PutLogEvents
46 | - logs:DescribeLogGroups
47 | - logs:DescribeLogStreams
48 | - logs:GetLogEvents
49 | - logs:FilterLogEvents
50 | Resource:
51 | - 'Fn::Join':
52 | - ':'
53 | -
54 | - 'arn:aws:logs'
55 | - Ref: 'AWS::Region'
56 | - Ref: 'AWS::AccountId'
57 | - 'log-group:/aws/lambda/*:*:*'
58 | - Effect: "Allow"
59 | Action:
60 | - "lambda:InvokeFunction"
61 | Resource: "*"
62 | - Effect: "Allow"
63 | Action:
64 | - "execute-api:Invoke"
65 | - "execute-api:ManageConnections"
66 | Resource: "*"
67 | functions:
68 | WattTimeDemo:
69 | handler: bazel-out/darwin_arm64-fastbuild-ST-5fa8105c1248/bin/func/watttime_demo/watttime_demo_/watttime_demo
70 | role: WattTimeDemoRole
71 | environment:
72 | WATTTIME_USERNAME: ${self:custom.wattTimeUsername}
73 | WATTTIME_PASSWORD: ${self:custom.wattTimePassword}
74 | STAGE: ${opt:stage, 'dev'}
--------------------------------------------------------------------------------
/func/convert_media/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | "encoding/json"
7 | "errors"
8 | "log"
9 | "os"
10 |
11 | "github.com/aws/aws-lambda-go/events"
12 | "github.com/aws/aws-lambda-go/lambda"
13 | "github.com/aws/aws-sdk-go/aws"
14 | "github.com/aws/aws-sdk-go/aws/session"
15 | lambda2 "github.com/aws/aws-sdk-go/service/lambda"
16 | "github.com/aws/aws-sdk-go/service/s3"
17 | "github.com/aws/aws-sdk-go/service/s3/s3manager"
18 | )
19 |
20 | var stage string
21 |
22 | type CssToJsonRequest struct {
23 | Content string
24 | }
25 |
26 | func handler(ctx context.Context, s3Event events.S3Event) {
27 |
28 | sess := session.Must(session.NewSession())
29 | lClient := lambda2.New(sess)
30 |
31 | for _, record := range s3Event.Records {
32 |
33 | log.Printf("%+v", record)
34 |
35 | content, _ := download(sess, &record)
36 | payload, _ := request(content)
37 |
38 | log.Printf("%s", payload)
39 |
40 | res, err := lClient.Invoke(&lambda2.InvokeInput{FunctionName: aws.String("goclassifieds-api-" + stage + "-CssToJson"), Payload: payload})
41 | if err != nil {
42 | log.Printf("error invoking entity validation: %s", err.Error())
43 | log.Printf("response: %s", res)
44 | }
45 |
46 | log.Printf("json: %s", res.Payload)
47 | write(sess, res.Payload, &record)
48 |
49 | }
50 | }
51 |
52 | func request(content []byte) ([]byte, error) {
53 |
54 | request := CssToJsonRequest{
55 | Content: string(content),
56 | }
57 |
58 | payload, err := json.Marshal(request)
59 | if err != nil {
60 | log.Printf("Error marshalling css to json request: %s", err.Error())
61 | return nil, errors.New("Error marshalling css to json request")
62 | }
63 |
64 | return payload, nil
65 | }
66 |
67 | func download(sess *session.Session, record *events.S3EventRecord) ([]byte, error) {
68 |
69 | buf := aws.NewWriteAtBuffer([]byte{})
70 |
71 | downloader := s3manager.NewDownloader(sess)
72 |
73 | _, err := downloader.Download(buf, &s3.GetObjectInput{
74 | Bucket: aws.String(record.S3.Bucket.Name),
75 | Key: aws.String(record.S3.Object.Key),
76 | })
77 |
78 | if err != nil {
79 | return []byte(""), err
80 | }
81 |
82 | return buf.Bytes(), nil
83 |
84 | }
85 |
86 | func write(sess *session.Session, content []byte, record *events.S3EventRecord) error {
87 |
88 | uploader := s3manager.NewUploader(sess)
89 |
90 | _, err := uploader.Upload(&s3manager.UploadInput{
91 | Bucket: aws.String(record.S3.Bucket.Name),
92 | Key: aws.String(record.S3.Object.Key + ".json"),
93 | Body: bytes.NewReader(content),
94 | ContentType: aws.String("application/json"),
95 | })
96 | if err != nil {
97 | return err
98 | }
99 | return nil
100 | }
101 |
102 | func main() {
103 | stage = os.Getenv("STAGE")
104 | lambda.Start(handler)
105 | }
106 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module goclassifieds
2 |
3 | go 1.17
4 |
5 | require (
6 | github.com/aws/aws-lambda-go v1.26.0
7 | github.com/aws/aws-sdk-go v1.42.27
8 | github.com/dgrijalva/jwt-go v3.2.0+incompatible
9 | github.com/elastic/go-elasticsearch v0.0.0
10 | github.com/elastic/go-elasticsearch/v7 v7.14.0
11 | github.com/gin-gonic/gin v1.7.4
12 | github.com/go-playground/validator/v10 v10.9.0
13 | github.com/gocql/gocql v0.0.0-20210817081954-bc256bbb90de
14 | github.com/golang/protobuf v1.5.2 // indirect
15 | github.com/google/uuid v1.3.0
16 | github.com/lestrrat-go/jwx v1.2.6
17 | github.com/mitchellh/mapstructure v1.4.1
18 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
19 | github.com/modern-go/reflect2 v1.0.1 // indirect
20 | github.com/tangzero/inflector v1.0.0
21 | golang.org/x/sys v0.34.0 // indirect
22 | gopkg.in/yaml.v2 v2.4.0 // indirect
23 | )
24 |
25 | require (
26 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d // indirect
27 | github.com/gin-contrib/sse v0.1.0 // indirect
28 | github.com/go-playground/locales v0.14.0 // indirect
29 | github.com/go-playground/universal-translator v0.18.0 // indirect
30 | github.com/goccy/go-json v0.7.6 // indirect
31 | github.com/golang/snappy v0.0.3 // indirect
32 | github.com/google/go-cmp v0.7.0 // indirect
33 | github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect
34 | github.com/jmespath/go-jmespath v0.4.0 // indirect
35 | github.com/json-iterator/go v1.1.10 // indirect
36 | github.com/leodido/go-urn v1.2.1 // indirect
37 | github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect
38 | github.com/lestrrat-go/blackmagic v1.0.0 // indirect
39 | github.com/lestrrat-go/httpcc v1.0.0 // indirect
40 | github.com/lestrrat-go/iter v1.0.1 // indirect
41 | github.com/lestrrat-go/option v1.0.0 // indirect
42 | github.com/mattn/go-isatty v0.0.12 // indirect
43 | github.com/opensearch-project/opensearch-go v1.1.0 // indirect
44 | github.com/pkg/errors v0.9.1 // indirect
45 | github.com/shurcooL/githubv4 v0.0.0-20220520033151-0b4e3294ff00
46 | github.com/ugorji/go/codec v1.1.7 // indirect
47 | golang.org/x/crypto v0.40.0 // indirect
48 | //golang.org/x/crypto/curve25519 v0.0.0-20210817164053-32db794688a5 // indirect
49 | golang.org/x/text v0.27.0 // indirect
50 | google.golang.org/protobuf v1.26.0 // indirect
51 | gopkg.in/inf.v0 v0.9.1 // indirect
52 | )
53 |
54 | require (
55 | github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29 // indirect
56 | golang.org/x/net v0.41.0 // indirect
57 | golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb
58 | )
59 |
60 | require (
61 | github.com/GoKillers/libsodium-go v0.0.0-20171022220152-dd733721c3cb // indirect
62 | github.com/MicahParks/keyfunc v1.5.1 // indirect
63 | github.com/golang-jwt/jwt/v4 v4.4.2 // indirect
64 | github.com/google/go-github/v46 v46.0.0 // indirect
65 | github.com/kevinburke/nacl v0.0.0-20250518034207-4fa338b68f84 // indirect
66 | github.com/sethvargo/go-password v0.2.0 // indirect
67 | google.golang.org/appengine v1.6.7 // indirect
68 | )
69 |
--------------------------------------------------------------------------------
/api/authorizer2/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "goclassifieds/lib/utils"
5 | "log"
6 | "os"
7 |
8 | "github.com/MicahParks/keyfunc"
9 | "github.com/aws/aws-lambda-go/events"
10 | "github.com/aws/aws-lambda-go/lambda"
11 | "github.com/golang-jwt/jwt/v4"
12 | )
13 |
14 | var handler Handler
15 |
16 | type Handler func(req *events.APIGatewayProxyRequest) (events.APIGatewayCustomAuthorizerResponse, error)
17 |
18 | type ActionContext struct {
19 | UserPoolId string
20 | }
21 |
22 | // Authorizer custom api authorizer
23 | func Authorizer(request *events.APIGatewayProxyRequest, ac *ActionContext) (events.APIGatewayCustomAuthorizerResponse, error) {
24 | token1 := request.Headers["authorization"]
25 |
26 | utils.LogUsageForHttpRequest(request)
27 |
28 | log.Printf("%v", request)
29 | log.Printf("token is %s", token1)
30 |
31 | if token1 == "" {
32 | log.Print("unauthorized request pass thru")
33 | return events.APIGatewayCustomAuthorizerResponse{
34 | PrincipalID: "me",
35 | PolicyDocument: events.APIGatewayCustomAuthorizerPolicy{
36 | Version: "2012-10-17",
37 | Statement: []events.IAMPolicyStatement{
38 | {
39 | Action: []string{"execute-api:*"},
40 | Effect: "Allow",
41 | Resource: []string{"*"},
42 | // Resource: []string{request.Re},
43 | },
44 | },
45 | },
46 | }, nil
47 | }
48 |
49 | token := token1[7:]
50 | log.Printf("token after is %s", token)
51 |
52 | jwks, err := keyfunc.Get("https://cognito-idp.us-east-1.amazonaws.com/"+ac.UserPoolId+"/.well-known/jwks.json", keyfunc.Options{})
53 | if err != nil {
54 | log.Fatalln("Unable to fetch keys")
55 | }
56 |
57 | log.Print("fectehd keys")
58 |
59 | // Verify
60 | t, err := jwt.Parse(token, jwks.Keyfunc)
61 | if err != nil || !t.Valid {
62 | log.Print(err)
63 | log.Fatalln("Unauthorized")
64 | }
65 |
66 | log.Print("authorized")
67 |
68 | claims := t.Claims.(jwt.MapClaims)
69 |
70 | log.Print("got claims")
71 |
72 | claims["cognito:groups"] = nil //claims["cognito:groups"]
73 | claims["cognito:roles"] = nil //claims["cognito:groups"]
74 |
75 | return events.APIGatewayCustomAuthorizerResponse{
76 | PrincipalID: "me",
77 | PolicyDocument: events.APIGatewayCustomAuthorizerPolicy{
78 | Version: "2012-10-17",
79 | Statement: []events.IAMPolicyStatement{
80 | {
81 | Action: []string{"execute-api:*"},
82 | Effect: "Allow",
83 | Resource: []string{"*"},
84 | // Resource: []string{request.Re},
85 | },
86 | },
87 | },
88 | Context: claims,
89 | }, nil
90 | }
91 |
92 | func InitializeHandler(ac ActionContext) Handler {
93 | return func(req *events.APIGatewayProxyRequest) (events.APIGatewayCustomAuthorizerResponse, error) {
94 | return Authorizer(req, &ac)
95 | }
96 | }
97 |
98 | func init() {
99 | log.Printf("Gin cold start")
100 | actionContext := ActionContext{
101 | UserPoolId: os.Getenv("USER_POOL_ID"),
102 | }
103 | handler = InitializeHandler(actionContext)
104 | }
105 |
106 | func main() {
107 | log.SetFlags(0)
108 | lambda.Start(handler)
109 | }
110 |
--------------------------------------------------------------------------------
/serverless-versait.yml:
--------------------------------------------------------------------------------
1 | service: goclassifieds-versait-api
2 | frameworkVersion: '3'
3 | plugins:
4 | - serverless-prune-plugin
5 | - serverless-custom-packaging-plugin
6 | custom:
7 | githubToken: ${file(./private.${opt:stage, 'dev'}.json):githubToken}
8 | versaitUsername: ${file(./private.${opt:stage, 'dev'}.json):versaitUsername}
9 | webhookSecret: ${file(./private.${opt:stage, 'dev'}.json):webhookSecret}
10 | openAiApiKey: ${file(./private.${opt:stage, 'dev'}.json):openAiApiKey}
11 | provider:
12 | name: aws
13 | runtime: provided.al2023
14 | memorySize: 512
15 | timeout: 45
16 | #logs:
17 | #websocket: true
18 | httpApi:
19 | payload: '1.0'
20 | cors: true
21 | package:
22 | individually: true
23 | resources:
24 | Resources:
25 | VersaitRole:
26 | Type: AWS::IAM::Role
27 | Properties:
28 | Path: "/"
29 | RoleName: verait-${opt:region, 'us-east-1'}-lambdaRole
30 | AssumeRolePolicyDocument:
31 | Version: '2012-10-17'
32 | Statement:
33 | - Effect: Allow
34 | Principal:
35 | Service:
36 | - lambda.amazonaws.com
37 | - apigateway.amazonaws.com
38 | Action: sts:AssumeRole
39 | ManagedPolicyArns:
40 | - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
41 | Policies:
42 | - PolicyName: goclassifieds-versait-api-lambda
43 | PolicyDocument:
44 | Version: '2012-10-17'
45 | Statement:
46 | - Effect: Allow
47 | Action:
48 | - logs:CreateLogGroup
49 | - logs:CreateLogStream
50 | - logs:PutLogEvents
51 | - logs:DescribeLogGroups
52 | - logs:DescribeLogStreams
53 | - logs:GetLogEvents
54 | - logs:FilterLogEvents
55 | Resource:
56 | - 'Fn::Join':
57 | - ':'
58 | -
59 | - 'arn:aws:logs'
60 | - Ref: 'AWS::Region'
61 | - Ref: 'AWS::AccountId'
62 | - 'log-group:/aws/lambda/*:*:*'
63 | - Effect: "Allow"
64 | Action:
65 | - "lambda:InvokeFunction"
66 | Resource: "*"
67 | - Effect: "Allow"
68 | Action:
69 | - "execute-api:Invoke"
70 | - "execute-api:ManageConnections"
71 | Resource: "*"
72 | functions:
73 | VersaitApi:
74 | handler: bootstrap
75 | role: VersaitRole
76 | package:
77 | path: bazel-bin/api/versait
78 | artifact: .serverless/VersaitApi.zip
79 | libs: api/entity
80 | include_globs:
81 | - "**/bootstrap"
82 | - "**/*.json.tmpl"
83 | - "**/*.pem"
84 | environment:
85 | GITHUB_TOKEN: ${self:custom.githubToken}
86 | VERSAIT_USERNAME: ${self:custom.versaitUsername}
87 | OPENAI_API_KEY: ${self:custom.openAiApiKey}
88 | #WEBHOOK_SECRET: ${self:custom.webhookSecret}
89 | STAGE: ${opt:stage, 'dev'}
90 | events:
91 | - httpApi:
92 | path: /versait/{proxy+}
93 | method: POST
--------------------------------------------------------------------------------
/lib/ads/ads.go:
--------------------------------------------------------------------------------
1 | package ads
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "goclassifieds/lib/attr"
7 | "goclassifieds/lib/vocab"
8 | "log"
9 | "time"
10 | )
11 |
12 | type AdStatuses int32
13 |
14 | const (
15 | Submitted AdStatuses = iota
16 | Approved
17 | Rejected
18 | Expired
19 | Deleted
20 | )
21 |
22 | type AdListitemsRequest struct {
23 | TypeId string `form:"typeId" binding:"required"`
24 | SearchString string `form:"searchString"`
25 | Location string `form:"location"`
26 | Features []string `form:"features[]"`
27 | Page int `form:"page"`
28 | }
29 |
30 | type Ad struct {
31 | Id string `json:"id" validate:"required"`
32 | TypeId string `form:"typeId" json:"typeId" binding:"typeId" validate:"required"`
33 | Status *AdStatuses `form:"status" json:"status" validate:"required"`
34 | Title string `form:"title" json:"title" binding:"required" validate:"required"`
35 | Description string `form:"description" json:"description" binding:"required" validate:"required"`
36 | Location [2]float64 `form:"location[]" json:"location" binding:"required" validate:"required"`
37 | UserId string `form:"userId" json:"userId" validate:"required"`
38 | ProfileId string `form:"profileId" json:"profileId"`
39 | CityDisplay string `form:"cityDisplay" json:"cityDisplay" binding:"required" validate:"required"`
40 | Images []AdImage `form:"images[]" json:"images" validate:"dive"`
41 | Attributes []attr.AttributeValue `form:"attributes[]" json:"attributes" validate:"dive"`
42 | FeatureSets []vocab.Vocabulary `form:"featureSets[]" json:"featureSets" validate:"dive"`
43 | }
44 |
45 | type AdImage struct {
46 | Id string `form:"id" json:"id" binding:"required" validate:"required"`
47 | Path string `form:"path" json:"path" binding:"required" validate:"required"`
48 | Weight int `form:"weight" json:"weight" binding:"required" validate:"required"`
49 | }
50 |
51 | type AdLead struct {
52 | ProfileId string `form:"profileId" json:"profileId" binding:"required" validate:"required"`
53 | AdId string `form:"adId" json:"adId" binding:"required" validate:"required"`
54 | SenderId string `form:"senderId" json:"senderId"`
55 | Email string `form:"email" json:"email" binding:"required" validate:"required,email"`
56 | Phone string `form:"phone" json:"phone" binding:"required" validate:"required"`
57 | Message string `form:"message" json:"message" binding:"required" validate:"required"`
58 | CreatedAt time.Time `form:"createdAt" json:"createdAt" binding:"required" validate:"required"`
59 | }
60 |
61 | func ToEntity(ad *Ad) (map[string]interface{}, error) {
62 | var buf bytes.Buffer
63 | if err := json.NewEncoder(&buf).Encode(ad); err != nil {
64 | log.Fatalf("Error encoding query: %s", err)
65 | }
66 | jsonData, err := json.Marshal(ad)
67 | if err != nil {
68 | return nil, err
69 | }
70 | var entity map[string]interface{}
71 | err = json.Unmarshal(jsonData, &entity)
72 | return entity, nil
73 | }
74 |
75 | func ToLeadEntity(lead *AdLead) (map[string]interface{}, error) {
76 | jsonData, err := json.Marshal(lead)
77 | if err != nil {
78 | return nil, err
79 | }
80 | var entity map[string]interface{}
81 | err = json.Unmarshal(jsonData, &entity)
82 | return entity, nil
83 | }
84 |
--------------------------------------------------------------------------------
/api/authorizer/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "os"
6 |
7 | "goclassifieds/lib/utils"
8 |
9 | "github.com/MicahParks/keyfunc"
10 | "github.com/aws/aws-lambda-go/events"
11 | "github.com/aws/aws-lambda-go/lambda"
12 | "github.com/golang-jwt/jwt/v4"
13 | )
14 |
15 | var handler Handler
16 |
17 | type Handler func(req *events.APIGatewayWebsocketProxyRequest) (events.APIGatewayCustomAuthorizerResponse, error)
18 |
19 | type ActionContext struct {
20 | UserPoolId string
21 | }
22 |
23 | // Authorizer custom api authorizer
24 | func Authorizer(request *events.APIGatewayWebsocketProxyRequest, ac *ActionContext) (events.APIGatewayCustomAuthorizerResponse, error) {
25 | token := request.QueryStringParameters["token"]
26 |
27 | usageLog := &utils.LogUsageLambdaInput{
28 | // UserId: GetUserId(req),
29 | //Username: GetUsername(req),
30 | UserId: "null",
31 | Username: "null",
32 | Resource: request.RequestContext.RouteKey,
33 | Path: request.RequestContext.EventType,
34 | RequestId: request.RequestContext.RequestID,
35 | Intensities: "null",
36 | Regions: "null",
37 | Region: "null",
38 | Service: "null",
39 | Repository: "null",
40 | Organization: "null",
41 | }
42 | _, hedged := request.Headers["x-hedge-region"]
43 | if hedged {
44 | usageLog.Intensities = request.Headers["x-hedge-intensities"]
45 | usageLog.Regions = request.Headers["x-hedge-regions"]
46 | usageLog.Region = request.Headers["x-hedge-region"]
47 | usageLog.Service = request.Headers["x-hedge-service"]
48 | }
49 |
50 | utils.LogUsageForLambdaWithInput(usageLog)
51 |
52 | // ctx := context.Background()
53 | // Fetch all keys
54 | // jwkSet, err := jwk.Fetch(ctx, "https://cognito-idp.us-east-1.amazonaws.com/"+ac.UserPoolId+"/.well-known/jwks.json")
55 | jwks, err := keyfunc.Get("https://cognito-idp.us-east-1.amazonaws.com/"+ac.UserPoolId+"/.well-known/jwks.json", keyfunc.Options{})
56 | if err != nil {
57 | log.Fatalln("Unable to fetch keys")
58 | }
59 |
60 | log.Print("fectehd keys")
61 |
62 | // Verify
63 | t, err := jwt.Parse(token, jwks.Keyfunc)
64 | if err != nil || !t.Valid {
65 | log.Print(err)
66 | log.Fatalln("Unauthorized")
67 | }
68 |
69 | log.Print("authorized")
70 |
71 | claims := t.Claims.(jwt.MapClaims)
72 |
73 | log.Print("got claims")
74 |
75 | claims["cognito:groups"] = nil //claims["cognito:groups"]
76 | claims["cognito:roles"] = nil //claims["cognito:groups"]
77 |
78 | return events.APIGatewayCustomAuthorizerResponse{
79 | PrincipalID: "me",
80 | PolicyDocument: events.APIGatewayCustomAuthorizerPolicy{
81 | Version: "2012-10-17",
82 | Statement: []events.IAMPolicyStatement{
83 | {
84 | Action: []string{"execute-api:Invoke"},
85 | Effect: "Allow",
86 | Resource: []string{"*"},
87 | // Resource: []string{request.Resource},
88 | },
89 | },
90 | },
91 | Context: claims,
92 | }, nil
93 | }
94 |
95 | func InitializeHandler(ac ActionContext) Handler {
96 | return func(req *events.APIGatewayWebsocketProxyRequest) (events.APIGatewayCustomAuthorizerResponse, error) {
97 | return Authorizer(req, &ac)
98 | }
99 | }
100 |
101 | func init() {
102 | log.Printf("Gin cold start")
103 | actionContext := ActionContext{
104 | UserPoolId: os.Getenv("USER_POOL_ID"),
105 | }
106 | handler = InitializeHandler(actionContext)
107 | }
108 |
109 | func main() {
110 | log.SetFlags(0)
111 | lambda.Start(handler)
112 | }
113 |
--------------------------------------------------------------------------------
/lib/es/es.go:
--------------------------------------------------------------------------------
1 | package es
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | "encoding/json"
7 | "log"
8 | "strings"
9 | "text/template"
10 |
11 | elasticsearch7 "github.com/elastic/go-elasticsearch/v7"
12 | )
13 |
14 | type SearchQueryBuilder interface {
15 | Build() map[string]interface{}
16 | GetIndex() string
17 | GetCollectionKey() string
18 | }
19 |
20 | type TemplateBuilder struct {
21 | Index string
22 | Data interface{}
23 | Template *template.Template
24 | Name string
25 | CollectionKey string
26 | }
27 |
28 | func (t TemplateBuilder) GetIndex() string {
29 | return t.Index
30 | }
31 |
32 | func (t TemplateBuilder) GetCollectionKey() string {
33 | return t.CollectionKey
34 | }
35 |
36 | func (t TemplateBuilder) Build() map[string]interface{} {
37 | var tb bytes.Buffer
38 | err := t.Template.ExecuteTemplate(&tb, t.Name, t.Data)
39 | if err != nil {
40 | log.Printf("Build Query Error: %s", err.Error())
41 | }
42 |
43 | var query map[string]interface{}
44 | err = json.Unmarshal(tb.Bytes(), &query)
45 | if err != nil {
46 | log.Printf("Unmarshall Query Error: %s", err.Error())
47 | }
48 |
49 | return query
50 | }
51 |
52 | func ExecuteQuery(esClient *elasticsearch7.Client, builder SearchQueryBuilder) []interface{} {
53 | query := builder.Build()
54 |
55 | var qb bytes.Buffer
56 | if err := json.NewEncoder(&qb).Encode(query); err != nil {
57 | log.Fatalf("Error encoding search query: %s", err)
58 | }
59 | log.Printf("Search Query: %s", qb.String())
60 |
61 | return ExecuteSearch(esClient, &query, builder.GetIndex(), builder.GetCollectionKey())
62 | }
63 |
64 | func ExecuteSearch(esClient *elasticsearch7.Client, query *map[string]interface{}, index string, collectionKey string) []interface{} {
65 | var buf bytes.Buffer
66 | if err := json.NewEncoder(&buf).Encode(query); err != nil {
67 | log.Fatalf("Error encoding query: %s", err)
68 | }
69 | res, err := esClient.Search(
70 | esClient.Search.WithContext(context.Background()),
71 | esClient.Search.WithIndex(index),
72 | esClient.Search.WithBody(&buf),
73 | // esClient.Search.WithTrackTotalHits(true),
74 | esClient.Search.WithPretty(),
75 | )
76 | if err != nil {
77 | log.Fatalf("Error getting response: %s", err)
78 | }
79 | if res.IsError() {
80 | var e map[string]interface{}
81 | if err := json.NewDecoder(res.Body).Decode(&e); err != nil {
82 | log.Printf("Response: %s", res.Body)
83 | log.Fatalf("Error parsing the response body: %s", err)
84 | } else {
85 | // Print the response status and error information.
86 | log.Fatalf("[%s] %s: %s",
87 | res.Status(),
88 | e["error"].(map[string]interface{})["type"],
89 | e["error"].(map[string]interface{})["reason"],
90 | )
91 | }
92 | }
93 | defer res.Body.Close()
94 | var r map[string]interface{}
95 | if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
96 | log.Printf("Response: %s", res.Body)
97 | log.Fatalf("Error parsing the response body: %s", err)
98 | }
99 | pieces := strings.Split(collectionKey, ".")
100 | target := r[pieces[0]]
101 | for i, piece := range pieces {
102 | if i > 0 {
103 | target = target.(map[string]interface{})[piece]
104 | }
105 | }
106 | return target.([]interface{})
107 | // return r["hits"].(map[string]interface{})["hits"].([]interface{})
108 | /*var docs []interface{}
109 | for _, hit := range r["hits"].(map[string]interface{})["hits"].([]interface{}) {
110 | docs = append(docs, hit)
111 | }
112 | return docs*/
113 | }
114 |
--------------------------------------------------------------------------------
/lib/utils/compact.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "errors"
7 | "os"
8 | )
9 |
10 | // EncodeStringToFixedBytes encodes a string into a fixed number of bytes.
11 | func EncodeStringToFixedBytes(input string, fixedSize int) ([]byte, error) {
12 | if fixedSize <= 0 {
13 | return nil, errors.New("fixed size must be greater than zero")
14 | }
15 |
16 | encoded := []byte(input)
17 |
18 | // If the string is longer than the fixed size, truncate it.
19 | if len(encoded) > fixedSize {
20 | return encoded[:fixedSize], nil
21 | }
22 |
23 | // If the string is shorter, pad it with zero bytes.
24 | padding := make([]byte, fixedSize-len(encoded))
25 | return append(encoded, padding...), nil
26 | }
27 |
28 | // DecodeFixedBytesToString decodes a fixed-size byte array back into a string.
29 | func DecodeFixedBytesToString(data []byte) string {
30 | return string(bytes.TrimRight(data, "\x00")) // Remove trailing zero bytes (padding)
31 | }
32 |
33 | // WriteFixedLengthStrings writes an array of strings to a binary file with fixed-length encoding.
34 | func WriteFixedLengthStrings(filename string, strings []string, fixedSize int) error {
35 | file, err := os.Create(filename)
36 | if err != nil {
37 | return err
38 | }
39 | defer file.Close()
40 |
41 | for _, s := range strings {
42 | encoded, err := EncodeStringToFixedBytes(s, fixedSize)
43 | if err != nil {
44 | return err
45 | }
46 | _, err = file.Write(encoded)
47 | if err != nil {
48 | return err
49 | }
50 | }
51 | return nil
52 | }
53 |
54 | // ReadFixedLengthString reads a single fixed-length string from a binary file at a given index.
55 | func ReadFixedLengthString(filename string, index int, fixedSize int) (string, error) {
56 | file, err := os.Open(filename)
57 | if err != nil {
58 | return "", err
59 | }
60 | defer file.Close()
61 |
62 | offset := index * fixedSize
63 | data := make([]byte, fixedSize)
64 |
65 | // Seek to the correct position in the file
66 | _, err = file.Seek(int64(offset), 0)
67 | if err != nil {
68 | return "", err
69 | }
70 |
71 | // Read the fixed-length string
72 | _, err = file.Read(data)
73 | if err != nil {
74 | return "", err
75 | }
76 |
77 | // Decode and return the string
78 | return DecodeFixedBytesToString(data), nil
79 | }
80 |
81 | func main() {
82 | // Fixed size for each string (128 bytes)
83 | fixedSize := 128
84 |
85 | // Strings to store in the file
86 | strings := []string{"Hello", "World", "Golang", "This is a longer string!"}
87 |
88 | // File to store the fixed-length encoded strings
89 | filename := "fixed_length_strings_128.bin"
90 |
91 | // Write the strings to the file
92 | err := WriteFixedLengthStrings(filename, strings, fixedSize)
93 | if err != nil {
94 | fmt.Printf("Error writing strings to file: %v\n", err)
95 | return
96 | }
97 | fmt.Println("Strings written to file successfully.")
98 |
99 | // Read specific strings from the file
100 | for i := 0; i < len(strings); i++ {
101 | result, err := ReadFixedLengthString(filename, i, fixedSize)
102 | if err != nil {
103 | fmt.Printf("Error reading string at index %d: %v\n", i, err)
104 | continue
105 | }
106 | fmt.Printf("String at index %d: %s\n", i, result)
107 | }
108 |
109 | // Random access: Read the 2nd string (index 1, zero-based)
110 | index := 1
111 | randomResult, err := ReadFixedLengthString(filename, index, fixedSize)
112 | if err != nil {
113 | fmt.Printf("Error reading string at index %d: %v\n", index, err)
114 | return
115 | }
116 | fmt.Printf("Random access result (index %d): %s\n", index, randomResult)
117 | }
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Use a base image with Debian/Ubuntu for compatibility
2 | FROM ubuntu:20.04
3 |
4 | # Set environment variables for non-interactive installation
5 | ENV DEBIAN_FRONTEND=noninteractive
6 | ENV NVM_DIR=/usr/local/nvm
7 |
8 | # Install dependencies required for Bazel, Go, Node.js, AWS CLI, Serverless Framework, and additional tools
9 | RUN apt-get update && \
10 | apt-get install -y \
11 | curl \
12 | wget \
13 | gnupg \
14 | unzip \
15 | git \
16 | build-essential \
17 | software-properties-common \
18 | apt-transport-https \
19 | python3 \
20 | python3-pip \
21 | libssl-dev
22 |
23 | # --- NEW BLOCK START: INSTALL DOCKER CLI ---
24 | # This installs only the 'docker' command line client, allowing it to talk
25 | # to the host's daemon via the socket defined in docker-compose.yml.
26 | RUN apt-get update && \
27 | # 1. Add Docker's official GPG key
28 | install -m 0755 -d /etc/apt/keyrings && \
29 | curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg && \
30 | chmod a+r /etc/apt/keyrings/docker.gpg && \
31 | \
32 | # 2. Add the Docker repository to Apt sources
33 | echo \
34 | "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
35 | $(. /etc/os-release && echo "$UBUNTU_CODENAME") stable" | \
36 | tee /etc/apt/sources.list.d/docker.list > /dev/null && \
37 | \
38 | # 3. Install only the CLI package
39 | apt-get update && \
40 | apt-get install -y docker-ce-cli
41 | # --- NEW BLOCK END ---
42 |
43 | # Clean up package lists after all installations
44 | RUN rm -rf /var/lib/apt/lists/*
45 |
46 | # Install Bazel 5.4.0
47 | RUN wget https://github.com/bazelbuild/bazel/releases/download/5.4.0/bazel-5.4.0-linux-x86_64 -O /usr/local/bin/bazel && \
48 | chmod +x /usr/local/bin/bazel && \
49 | bazel --version
50 |
51 | # Install Go 1.18 instead of Go 1.7
52 | RUN wget https://go.dev/dl/go1.18.10.linux-amd64.tar.gz && \
53 | tar -C /usr/local -xzf go1.18.10.linux-amd64.tar.gz && \
54 | rm go1.18.10.linux-amd64.tar.gz
55 | ENV PATH="/usr/local/go/bin:${PATH}"
56 | RUN go version
57 |
58 |
59 | # Set GOPATH environment variable
60 | ENV GOPATH=/workspace/go
61 | ENV PATH="$GOPATH/bin:${PATH}"
62 |
63 | # Create GOPATH directory
64 | RUN mkdir -p $GOPATH/src $GOPATH/bin $GOPATH/pkg
65 |
66 | # Install Node.js using nvm (Node Version Manager)
67 | ENV NODE_VERSION=20
68 | RUN mkdir -p $NVM_DIR && \
69 | curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash && \
70 | . "$NVM_DIR/nvm.sh" && \
71 | nvm install $NODE_VERSION && \
72 | nvm alias default $NODE_VERSION && \
73 | ln -s "$NVM_DIR/versions/node/$(nvm current)/bin/node" /usr/local/bin/node && \
74 | ln -s "$NVM_DIR/versions/node/$(nvm current)/bin/npm" /usr/local/bin/npm && \
75 | ln -s "$NVM_DIR/versions/node/$(nvm current)/bin/npx" /usr/local/bin/npx
76 | ENV PATH="$NVM_DIR/versions/node/$(nvm current)/bin:$PATH"
77 | RUN . "$NVM_DIR/nvm.sh" && npm install -g npm@latest
78 | RUN node -v && npm -v
79 |
80 | # Install AWS CLI v2
81 | RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && \
82 | unzip awscliv2.zip && \
83 | ./aws/install && \
84 | rm -rf awscliv2.zip aws
85 | RUN aws --version
86 |
87 | # Install Serverless Framework version 3
88 | RUN . "$NVM_DIR/nvm.sh" && \
89 | npm install -g serverless@3 --unsafe-perm --verbose && \
90 | echo "Serverless installed to: $(which serverless)" && \
91 | serverless --version
92 |
93 | # Set default workdir inside the container
94 | WORKDIR /workspace
95 |
96 | # Default command to keep the container running
97 | CMD ["bash"]
--------------------------------------------------------------------------------
/func/index_entity/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "goclassifieds/lib/ads"
6 | "goclassifieds/lib/attr"
7 | "goclassifieds/lib/cc"
8 | "goclassifieds/lib/entity"
9 | "goclassifieds/lib/sign"
10 | "goclassifieds/lib/vocab"
11 | "os"
12 | "strings"
13 |
14 | "github.com/aws/aws-lambda-go/events"
15 | "github.com/aws/aws-lambda-go/lambda"
16 | session "github.com/aws/aws-sdk-go/aws/session"
17 | elasticsearch7 "github.com/elastic/go-elasticsearch/v7"
18 | opensearch "github.com/opensearch-project/opensearch-go"
19 |
20 | "github.com/mitchellh/mapstructure"
21 | "github.com/tangzero/inflector"
22 | )
23 |
24 | func handler(ctx context.Context, s3Event events.S3Event) {
25 |
26 | elasticCfg := elasticsearch7.Config{
27 | Addresses: []string{os.Getenv("ELASTIC_URL")},
28 | }
29 |
30 | esClient, err := elasticsearch7.NewClient(elasticCfg)
31 | if err != nil {
32 |
33 | }
34 |
35 | awsSigner := sign.AwsSigner{
36 | Service: "es",
37 | Region: "us-east-1",
38 | }
39 |
40 | opensearchCfg := opensearch.Config{
41 | Addresses: []string{os.Getenv("ELASTIC_URL")},
42 | Signer: awsSigner,
43 | }
44 |
45 | osClient, err := opensearch.NewClient(opensearchCfg)
46 | if err != nil {
47 |
48 | }
49 |
50 | sess := session.Must(session.NewSession())
51 |
52 | for _, record := range s3Event.Records {
53 |
54 | pieces := strings.Split(record.S3.Object.Key, "/")
55 |
56 | pluralName := inflector.Pluralize(pieces[0])
57 | singularName := inflector.Singularize(pieces[0])
58 |
59 | entityManager := entity.NewDefaultManager(entity.DefaultManagerConfig{
60 | SingularName: singularName,
61 | PluralName: pluralName,
62 | Index: "classified_" + pluralName,
63 | EsClient: esClient,
64 | OsClient: osClient,
65 | Session: sess,
66 | UserId: "",
67 | Stage: os.Getenv("STAGE"),
68 | BucketName: os.Getenv("BUCKET_NAME"),
69 | })
70 |
71 | id := pieces[1][0 : len(pieces[1])-8]
72 | ent := entityManager.Load(id, "default")
73 |
74 | if singularName == "ad" {
75 | ent = IndexAd(ent)
76 | } else if singularName == "panelpage" {
77 | ent = IndexPanelPage(ent)
78 | }
79 |
80 | entityManager.Save(ent, "elastic")
81 | }
82 | }
83 |
84 | func IndexAd(obj map[string]interface{}) map[string]interface{} {
85 |
86 | var item ads.Ad
87 | mapstructure.Decode(obj, &item)
88 |
89 | allAttrValues := make([]attr.AttributeValue, 0)
90 | for _, attrValue := range item.Attributes {
91 | attributesFlattened := attr.FlattenAttributeValue(attrValue)
92 | for _, flatAttr := range attributesFlattened {
93 | attr.FinalizeAttributeValue(&flatAttr)
94 | allAttrValues = append(allAttrValues, flatAttr)
95 | }
96 | }
97 | item.Attributes = allAttrValues
98 |
99 | for index, featureSet := range item.FeatureSets {
100 | allFeatureTerms := make([]vocab.Term, 0)
101 | for _, term := range featureSet.Terms {
102 | flatTerms := vocab.FlattenTerm(term, true)
103 | for _, flatTerm := range flatTerms {
104 | allFeatureTerms = append(allFeatureTerms, flatTerm)
105 | }
106 | }
107 | item.FeatureSets[index].Terms = allFeatureTerms
108 | }
109 |
110 | ent, _ := ads.ToEntity(&item)
111 | return ent
112 |
113 | }
114 |
115 | func IndexPanelPage(obj map[string]interface{}) map[string]interface{} {
116 |
117 | var item cc.PanelPage
118 | mapstructure.Decode(obj, &item)
119 |
120 | item.GridItems = make([]cc.GridItem, 0)
121 | item.Contexts = make([]cc.InlineContext, 0)
122 | item.Panels = make([]cc.Panel, 0)
123 | item.RowSettings = make([]cc.LayoutSetting, 0)
124 |
125 | ent, _ := cc.ToPanelPageEntity(&item)
126 | return ent
127 |
128 | }
129 |
130 | func main() {
131 | lambda.Start(handler)
132 | }
133 |
--------------------------------------------------------------------------------
/iac/precheck-oidc-auth/token/index.js:
--------------------------------------------------------------------------------
1 | const AWS = require("aws-sdk");
2 | const querystring = require("querystring"); // Used to parse form-encoded data
3 | const logger = require("./logger");
4 |
5 | const dynamodb = new AWS.DynamoDB.DocumentClient();
6 |
7 | exports.handler = async (event) => {
8 | try {
9 | // Parse the form-encoded body
10 | const body = querystring.parse(event.body);
11 | const { code /*, state , redirect_uri, client_id*/ } = body;
12 |
13 | logger({});
14 |
15 | // Validate the input parameters
16 | if (!code) {
17 | console.error("Missing authorization code");
18 | return {
19 | statusCode: 400,
20 | body: JSON.stringify({ error: "Missing authorization code" }),
21 | };
22 | }
23 |
24 | // Retrieve the token data from DynamoDB using the authorization code
25 | const tokenData = await dynamodb
26 | .get({
27 | TableName: process.env.AUTH_CODE_TABLE_NAME,
28 | Key: { auth_code: code },
29 | })
30 | .promise();
31 |
32 | // Check if token data exists
33 | if (!tokenData.Item) {
34 | console.error("Invalid or expired authorization code");
35 | return {
36 | statusCode: 400,
37 | body: JSON.stringify({ error: "Invalid or expired authorization code" }),
38 | };
39 | }
40 |
41 | // No state provided in request for cognito but maybe others so leave it here
42 | /*if (tokenData.Item.state !== state) {
43 | console.error("State mismatch");
44 | return {
45 | statusCode: 400,
46 | body: JSON.stringify({ error: "State mismatch" }),
47 | };
48 | }*/
49 |
50 | // Optional: Validate redirect_uri
51 | /*if (redirect_uri && tokenData.Item.redirect_uri !== redirect_uri) {
52 | console.error("Redirect URI mismatch");
53 | return {
54 | statusCode: 400,
55 | body: JSON.stringify({ error: "Redirect URI mismatch" }),
56 | };
57 | }*/
58 |
59 | // Optional: Validate client_id
60 | /*if (client_id && process.env.COGNITO_APP_CLIENT_ID !== client_id) {
61 | console.error("Client ID mismatch");
62 | return {
63 | statusCode: 400,
64 | body: JSON.stringify({ error: "Client ID mismatch" }),
65 | };
66 | }*/
67 |
68 | // Extract the tokens from the retrieved data
69 | const { id_token, access_token, refresh_token, expiration } = tokenData.Item;
70 |
71 | // Optional: Check if the tokens are expired
72 | const currentTimestamp = Math.floor(Date.now() / 1000);
73 | if (expiration < currentTimestamp) {
74 | console.error("Authorization code has expired");
75 | return {
76 | statusCode: 400,
77 | body: JSON.stringify({ error: "Authorization code has expired" }),
78 | };
79 | }
80 |
81 | // Return the tokens to the client
82 | return {
83 | statusCode: 200,
84 | body: JSON.stringify({
85 | id_token,
86 | access_token,
87 | refresh_token,
88 | expires_in: expiration - currentTimestamp, // Remaining time until expiration
89 | token_type: 'Bearer'
90 | }),
91 | };
92 | } catch (error) {
93 | console.error("Error retrieving token:", error);
94 | return {
95 | statusCode: 500,
96 | body: JSON.stringify({ error: "Internal server error", details: error.message }),
97 | };
98 | }
99 | };
--------------------------------------------------------------------------------
/lib/os/os.go:
--------------------------------------------------------------------------------
1 | package os
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | "encoding/json"
7 | "log"
8 | "strings"
9 | "text/template"
10 |
11 | opensearch "github.com/opensearch-project/opensearch-go"
12 | )
13 |
14 | type SearchQueryBuilder interface {
15 | Build() map[string]interface{}
16 | GetIndex() string
17 | GetCollectionKey() string
18 | }
19 |
20 | type TemplateBuilder struct {
21 | Index string
22 | Data interface{}
23 | Template *template.Template
24 | Name string
25 | CollectionKey string
26 | }
27 |
28 | func (t TemplateBuilder) GetIndex() string {
29 | return t.Index
30 | }
31 |
32 | func (t TemplateBuilder) GetCollectionKey() string {
33 | return t.CollectionKey
34 | }
35 |
36 | func (t TemplateBuilder) Build() map[string]interface{} {
37 | var tb bytes.Buffer
38 | err := t.Template.ExecuteTemplate(&tb, t.Name, t.Data)
39 | if err != nil {
40 | log.Printf("Build Query Error: %s", err.Error())
41 | }
42 |
43 | var query map[string]interface{}
44 | err = json.Unmarshal(tb.Bytes(), &query)
45 | if err != nil {
46 | log.Printf("Unmarshall Query Error: %s", err.Error())
47 | }
48 |
49 | return query
50 | }
51 |
52 | func ExecuteQuery(esClient *opensearch.Client, builder SearchQueryBuilder) []interface{} {
53 | query := builder.Build()
54 |
55 | var qb bytes.Buffer
56 | if err := json.NewEncoder(&qb).Encode(query); err != nil {
57 | log.Fatalf("Error encoding search query: %s", err)
58 | }
59 | log.Printf("Search Query: %s", qb.String())
60 |
61 | return ExecuteSearch(esClient, &query, builder.GetIndex(), builder.GetCollectionKey())
62 | }
63 |
64 | func ExecuteSearch(esClient *opensearch.Client, query *map[string]interface{}, index string, collectionKey string) []interface{} {
65 | var buf bytes.Buffer
66 | if err := json.NewEncoder(&buf).Encode(query); err != nil {
67 | log.Fatalf("Error encoding query: %s", err)
68 | }
69 | res, err := esClient.Search(
70 | esClient.Search.WithContext(context.Background()),
71 | esClient.Search.WithIndex(index),
72 | esClient.Search.WithBody(&buf),
73 | // esClient.Search.WithTrackTotalHits(true),
74 | esClient.Search.WithPretty(),
75 | )
76 | if err != nil {
77 | log.Fatalf("Error getting response: %s", err)
78 | }
79 | if res.IsError() {
80 | var e map[string]interface{}
81 | if err := json.NewDecoder(res.Body).Decode(&e); err != nil {
82 | buf := new(bytes.Buffer)
83 | buf.ReadFrom(res.Body)
84 | newStr := buf.String()
85 | log.Printf("Response: %s", newStr)
86 | log.Fatalf("Error parsing the response body: %s", err)
87 | } else {
88 | // Print the response status and error information.
89 | buf := new(bytes.Buffer)
90 | buf.ReadFrom(res.Body)
91 | msg := buf.String()
92 | log.Printf("open search request failure statuc: %s", res.Status())
93 | log.Printf("open search request failure message: %s", msg)
94 | log.Fatalf("[%s] %s: %s",
95 | res.Status(),
96 | e["error"].(map[string]interface{})["type"],
97 | e["error"].(map[string]interface{})["reason"],
98 | )
99 | }
100 | }
101 | defer res.Body.Close()
102 | var r map[string]interface{}
103 | if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
104 | buf := new(bytes.Buffer)
105 | buf.ReadFrom(res.Body)
106 | newStr := buf.String()
107 | log.Printf("Response: %s", newStr)
108 | log.Fatalf("Error parsing the response body: %s", err)
109 | }
110 | pieces := strings.Split(collectionKey, ".")
111 | target := r[pieces[0]]
112 | for i, piece := range pieces {
113 | if i > 0 {
114 | target = target.(map[string]interface{})[piece]
115 | }
116 | }
117 | return target.([]interface{})
118 | // return r["hits"].(map[string]interface{})["hits"].([]interface{})
119 | /*var docs []interface{}
120 | for _, hit := range r["hits"].(map[string]interface{})["hits"].([]interface{}) {
121 | docs = append(docs, hit)
122 | }
123 | return docs*/
124 | }
125 |
--------------------------------------------------------------------------------
/hooks/index_create/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "goclassifieds/lib/entity"
6 | "log"
7 | "encoding/json"
8 | "os"
9 | "fmt"
10 |
11 | "goclassifieds/lib/repo"
12 |
13 | "github.com/aws/aws-lambda-go/lambda"
14 | "golang.org/x/oauth2"
15 | "github.com/google/go-github/v46/github"
16 | )
17 |
18 | func handler(ctx context.Context, event entity.AfterSaveExecEntityRequest) (entity.AfterSaveExecEntityResponse, error) {
19 |
20 | /**
21 | * This is where all the code goes to create action SECRETS
22 | * for a site. Both for repo and enviironment.
23 | */
24 | log.Printf("Create index for entity %s in repo %s and owner %s", event.Contract, event.Repo, event.Owner)
25 |
26 | // Log the entire content of event.Entity
27 | entityJSON, err := json.Marshal(event.Entity) // Convert map[string]interface{} to JSON string for readable logging
28 | if err != nil {
29 | log.Printf("Error marshalling event.Entity: %s", err)
30 | } else {
31 | log.Printf("Entity content: %s", entityJSON)
32 | }
33 |
34 | repoName, ok := event.Entity["repoName"].(string)
35 | if ok {
36 | log.Printf("The new repo name will be %s", repoName)
37 | }
38 |
39 | githubAppID := os.Getenv("GITHUB_APP_ID")
40 | if githubAppID == "" {
41 | err := fmt.Errorf("environment variable GITHUB_APP_ID is missing")
42 | log.Print(err)
43 | return entity.AfterSaveExecEntityResponse{}, err
44 | }
45 |
46 | // Load GitHub app PEM file
47 | pemFilePath := fmt.Sprintf("rtc-vertigo-%s.private-key.pem", os.Getenv("STAGE"))
48 | pem, err := os.ReadFile(pemFilePath)
49 | if err != nil {
50 | log.Printf("Failed to read PEM file '%s': %v", pemFilePath, err)
51 | return entity.AfterSaveExecEntityResponse{}, fmt.Errorf("failed to load GitHub app PEM file: %w", err)
52 | }
53 | log.Print("GitHub app PEM file loaded successfully.")
54 |
55 | // Generate GitHub Installation Token
56 | getTokenInput := &repo.GetInstallationTokenInput{
57 | GithubAppPem: pem,
58 | Owner: event.Owner,
59 | GithubAppId: githubAppID,
60 | }
61 | installationToken, err := repo.GetInstallationToken(getTokenInput)
62 | if err != nil {
63 | log.Printf("Error generating GitHub installation token for owner '%s': %v", event.Owner, err)
64 | return entity.AfterSaveExecEntityResponse{}, fmt.Errorf("failed to generate GitHub installation token: %w", err)
65 | }
66 | log.Print("GitHub installation token generated successfully.")
67 |
68 | // Create OAuth2 HTTP client
69 | srcToken := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: *installationToken.Token})
70 | httpClient := oauth2.NewClient(ctx, srcToken)
71 | githubRestClient := github.NewClient(httpClient)
72 |
73 | // Step 1: Create repository from template
74 | templateOwner := "rollthecloudinc"
75 | templateRepo := "spearhead-index"
76 | description := "index repository"
77 | private := false // index repositories are private by default content should only be accessed via searches. Ah... make it public for now
78 | err = repo.CreateFromTemplate(ctx, githubRestClient.Client(), templateOwner, templateRepo, event.Owner, repoName, description, private)
79 | if err != nil {
80 | log.Printf("Error creating index repository: %s", err.Error())
81 | return entity.AfterSaveExecEntityResponse{}, fmt.Errorf("failed to create repository from template: %w", err)
82 | }
83 |
84 | log.Printf("Repository '%s/%s' successfully created from template. Proceeding with dev branch creation...", event.Owner, repoName)
85 |
86 | // Step 2: Create the "dev" branch
87 | err = repo.CreateDevBranch(githubRestClient, event.Owner, repoName)
88 | if err != nil {
89 | log.Printf("Error provisioning dev branch for index repository.")
90 | return entity.AfterSaveExecEntityResponse{}, fmt.Errorf("failed to create dev branch: %w", err)
91 | }
92 |
93 | return entity.AfterSaveExecEntityResponse{}, nil
94 | }
95 |
96 | func main() {
97 | log.SetFlags(0)
98 | // Make the handler available for Remote Procedure Call by AWS Lambda
99 | lambda.Start(handler)
100 | }
--------------------------------------------------------------------------------
/api/entity/types.json.tmpl:
--------------------------------------------------------------------------------
1 | {{ define "all" }}
2 | [
3 | {
4 | "id": "27b8e62c-fcec-450c-9774-579a9549917f",
5 | "owner": "system",
6 | "overlay": false,
7 | "name": "general",
8 | "target": "ad",
9 | "attributes": [],
10 | "filters": []
11 | },
12 | {
13 | "id": "5665f5f8-5533-4aa1-96f4-b78e02714de0",
14 | "owner": "system",
15 | "overlay": false,
16 | "name": "realestate",
17 | "target": "ad",
18 | "attributes": [
19 | {
20 | "name": "price",
21 | "type": 0,
22 | "label": "Asking Price",
23 | "required": true,
24 | "widget": "text",
25 | "attributes": []
26 | },
27 | {
28 | "name": "beds",
29 | "type": 0,
30 | "label": "Beds",
31 | "required": true,
32 | "widget": "text",
33 | "attributes": []
34 | },
35 | {
36 | "name": "baths",
37 | "type": 0,
38 | "label": "Baths",
39 | "required": true,
40 | "widget": "text",
41 | "attributes": []
42 | },
43 | {
44 | "name": "sqft",
45 | "type": 0,
46 | "label": "Sqft",
47 | "required": true,
48 | "widget": "text",
49 | "attributes": []
50 | }
51 | ],
52 | "filters": [
53 | {
54 | "name": "beds",
55 | "type": 0,
56 | "label": "Beds",
57 | "required": false,
58 | "widget": "text",
59 | "attributes": []
60 | },
61 | {
62 | "name": "baths",
63 | "type": 0,
64 | "label": "Baths",
65 | "required": false,
66 | "widget": "text",
67 | "attributes": []
68 | }
69 | ]
70 | },
71 | {
72 | "id": "d9bf054c-2a43-4976-a35e-25bd4c58417e",
73 | "owner": "system",
74 | "overlay": false,
75 | "name": "rentals",
76 | "target": "ad",
77 | "attributes": [],
78 | "filters": []
79 | },
80 | {
81 | "id": "ef5c1cdb-95b3-45be-ba01-b6745d9744f9",
82 | "owner": "system",
83 | "overlay": false,
84 | "name": "autos",
85 | "target": "ad",
86 | "attributes": [
87 | {
88 | "name": "ymm",
89 | "type": 2,
90 | "label": "YMM",
91 | "required": false,
92 | "widget": "ymm_selector",
93 | "attributes": [
94 | {
95 | "name": "year",
96 | "type": 0,
97 | "label": "Year",
98 | "required": false,
99 | "widget": "text",
100 | "attributes": []
101 | },
102 | {
103 | "name": "make",
104 | "type": 1,
105 | "label": "Make",
106 | "required": false,
107 | "widget": "text",
108 | "attributes": []
109 | },
110 | {
111 | "name": "model",
112 | "type": 1,
113 | "label": "Model",
114 | "required": false,
115 | "widget": "text",
116 | "attributes": []
117 | }
118 | ]
119 | }
120 | ],
121 | "filters": [
122 | {
123 | "name": "ymm",
124 | "label": "YMM",
125 | "type": 2,
126 | "required": false,
127 | "attributes": [
128 | {
129 | "name": "year",
130 | "label": "Year",
131 | "type": 0,
132 | "required": false,
133 | "attributes": []
134 | },
135 | {
136 | "name": "make",
137 | "label": "Make",
138 | "type": 1,
139 | "required": false,
140 | "attributes": []
141 | },
142 | {
143 | "name":"model",
144 | "label":"model",
145 | "type": 1,
146 | "required": false,
147 | "attributes": []
148 | }
149 | ],
150 | "widget": "ymm_selector"
151 | }
152 | ]
153 | }
154 | ]
155 | {{end}}
--------------------------------------------------------------------------------
/iac/precheck-cognito/message/index.js:
--------------------------------------------------------------------------------
1 | const AWS = require('aws-sdk');
2 | const b64 = require('base64-js');
3 | const encryptionSdk = require('@aws-crypto/client-node');
4 |
5 | // Configure the encryption SDK client with the KMS key from the environment variables
6 | const { encrypt, decrypt } = encryptionSdk.buildClient(encryptionSdk.CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT);
7 | const generatorKeyId = process.env.KEY_ALIAS; // KMS Key Alias (e.g., alias/your-key)
8 | const keyIds = [process.env.KEY_ARN]; // KMS Key ARN (e.g., arn:aws:kms:region:account-id:key/key-id)
9 | const keyring = new encryptionSdk.KmsKeyringNode({ generatorKeyId, keyIds });
10 |
11 | const sns = new AWS.SNS({ region: process.env.REGION }); // Default SNS region
12 | const ses = new AWS.SES({ region: process.env.REGION }); // Default SES region
13 |
14 | exports.handler = async (event) => {
15 | console.log('Received event:', JSON.stringify(event, null, 2));
16 |
17 | const phoneNumber = event.request.userAttributes.phone_number; // E.164 format
18 | const encryptedCode = event.request.code; // Encrypted verification code
19 | const fallbackEmail = event.request.userAttributes.email; // Fallback email, if needed
20 |
21 | // Ensure required attributes are present
22 | if (!phoneNumber || !encryptedCode) {
23 | console.error('Missing required parameters: phone number or encrypted code');
24 | throw new Error('Missing required parameters');
25 | }
26 |
27 | let plainTextCode;
28 | try {
29 | // Decrypt the secret code using the encryption SDK
30 | console.log('Decrypting verification code...');
31 | const { plaintext, messageHeader } = await decrypt(keyring, b64.toByteArray(encryptedCode));
32 | plainTextCode = plaintext.toString('utf-8'); // Convert plaintext buffer to a string
33 | console.log('Decrypted verification code:', plainTextCode);
34 | } catch (decryptError) {
35 | console.error('Failed to decrypt verification code:', decryptError);
36 | throw new Error('Failed to decrypt verification code');
37 | }
38 |
39 | try {
40 | // Step 1: Attempt to send SMS via SNS
41 | console.log('Attempting to send SMS via SNS...');
42 | const smsResult = await sns
43 | .publish({
44 | Message: `Your verification code is: ${plainTextCode}`, // The decrypted code
45 | PhoneNumber: phoneNumber,
46 | })
47 | .promise();
48 |
49 | console.log('SMS sent successfully:', smsResult);
50 | return event; // Return the unmodified event object to indicate success
51 | } catch (snsError) {
52 | console.error('Failed to send SMS via SNS:', snsError);
53 |
54 | // Step 2: Fallback to Email via SES
55 | if (fallbackEmail) {
56 | console.log('Falling back to email...');
57 | try {
58 | const emailResult = await ses
59 | .sendEmail({
60 | Source: process.env.SES_VERIFIED_EMAIL, // SES verified sender email
61 | Destination: {
62 | ToAddresses: [fallbackEmail],
63 | },
64 | Message: {
65 | Subject: {
66 | Data: 'Your Verification Code',
67 | },
68 | Body: {
69 | Text: {
70 | Data: `We couldn't send an SMS to your phone number. Your verification code is: ${plainTextCode}`,
71 | },
72 | },
73 | },
74 | })
75 | .promise();
76 | console.log('Fallback email sent successfully:', emailResult);
77 | return event; // Return the unmodified event object to indicate success
78 | } catch (emailError) {
79 | console.error('Failed to send fallback email:', emailError);
80 | throw new Error('Failed to send SMS and fallback email');
81 | }
82 | }
83 |
84 | throw new Error('Failed to send SMS and no fallback email is available');
85 | }
86 | };
--------------------------------------------------------------------------------
/api/gov/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "goclassifieds/lib/gov"
6 | "goclassifieds/lib/utils"
7 | "log"
8 | "os"
9 |
10 | "github.com/aws/aws-lambda-go/events"
11 | "github.com/aws/aws-lambda-go/lambda"
12 | "github.com/aws/aws-sdk-go/aws"
13 | "github.com/aws/aws-sdk-go/aws/session"
14 | lambda2 "github.com/aws/aws-sdk-go/service/lambda"
15 | )
16 |
17 | var handler Handler
18 |
19 | type Handler func(req *events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error)
20 |
21 | type ActionContext struct {
22 | Stage string
23 | Lambda *lambda2.Lambda
24 | }
25 |
26 | func GetGrant(req *events.APIGatewayProxyRequest, ac *ActionContext) (events.APIGatewayProxyResponse, error) {
27 | var res events.APIGatewayProxyResponse
28 |
29 | grantAccessRequest := gov.GrantAccessRequest{
30 | User: req.PathParameters["user"],
31 | Type: gov.UserTypeMap[req.PathParameters["type"]],
32 | Resource: gov.ResourceTypeMap[req.PathParameters["resource"]],
33 | Operation: gov.OperationMap[req.PathParameters["op"]],
34 | Asset: req.PathParameters["proxy"],
35 | }
36 |
37 | log.Print(req)
38 |
39 | payload, err := json.Marshal(grantAccessRequest)
40 | if err != nil {
41 | log.Printf("Error marshalling grant access request: %s", err.Error())
42 | res.StatusCode = 500
43 | return res, err
44 | }
45 |
46 | lambdaRes, err := ac.Lambda.Invoke(&lambda2.InvokeInput{FunctionName: aws.String("goclassifieds-api-" + ac.Stage + "-GrantAccess"), Payload: payload})
47 | if err != nil {
48 | log.Printf("error invoking grant_access: %s", err.Error())
49 | res.StatusCode = 500
50 | return res, err
51 | }
52 |
53 | //var grantRes gov.GrantAccessResponse
54 | //json.Unmarshal(lambdaRes.Payload, &grantRes)
55 |
56 | res.Body = string(lambdaRes.Payload)
57 | res.StatusCode = 200
58 | return res, nil
59 | }
60 |
61 | func InitializeHandler(c *ActionContext) Handler {
62 | return func(req *events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
63 |
64 | usageLog := &utils.LogUsageLambdaInput{
65 | UserId: req.PathParameters["user"],
66 | Username: "null",
67 | Resource: req.Resource,
68 | Path: req.Path,
69 | RequestId: req.RequestContext.RequestID,
70 | Intensities: "null",
71 | Regions: "null",
72 | Region: "null",
73 | Service: "null",
74 | Repository: "null",
75 | Organization: "null",
76 | }
77 | _, hedged := req.Headers["x-hedge-region"]
78 | if hedged {
79 | usageLog.Intensities = req.Headers["x-hedge-intensities"]
80 | usageLog.Regions = req.Headers["x-hedge-regions"]
81 | usageLog.Region = req.Headers["x-hedge-region"]
82 | usageLog.Service = req.Headers["x-hedge-service"]
83 | }
84 | _, hasOwner := req.PathParameters["owner"]
85 | if hasOwner {
86 | usageLog.Organization = req.PathParameters["owner"]
87 | }
88 | _, hasRepo := req.PathParameters["repo"]
89 | if hasRepo {
90 | usageLog.Repository = req.PathParameters["repo"]
91 | }
92 |
93 | utils.LogUsageForLambdaWithInput(usageLog)
94 |
95 | ac := RequestActionContext(c)
96 |
97 | //ac.UserId = GetUserId(req)
98 |
99 | if req.HTTPMethod == "GET" {
100 | return GetGrant(req, ac)
101 | } /*else if entityName == pluralName && req.HTTPMethod == "GET" {
102 | return GetEntities(req, ac)
103 | }*/
104 |
105 | return events.APIGatewayProxyResponse{StatusCode: 400}, nil
106 | }
107 | }
108 |
109 | func RequestActionContext(c *ActionContext) *ActionContext {
110 |
111 | ac := &ActionContext{
112 | Stage: c.Stage,
113 | Lambda: c.Lambda,
114 | }
115 |
116 | return ac
117 |
118 | }
119 |
120 | func init() {
121 | log.Printf("gov start")
122 |
123 | sess := session.Must(session.NewSession())
124 | lClient := lambda2.New(sess)
125 | //gateway := apigatewaymanagementapi.New(sess, aws.NewConfig().WithEndpoint(os.Getenv("APIGATEWAY_ENDPOINT")))
126 |
127 | actionContext := ActionContext{
128 | Stage: os.Getenv("STAGE"),
129 | Lambda: lClient,
130 | }
131 |
132 | handler = InitializeHandler(&actionContext)
133 |
134 | log.Print("gov started")
135 | }
136 |
137 | func main() {
138 | log.SetFlags(0)
139 | lambda.Start(handler)
140 | }
141 |
--------------------------------------------------------------------------------
/lib/watttime/watttime.go:
--------------------------------------------------------------------------------
1 | package watttime
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | "io/ioutil"
7 | "net/http"
8 | "strconv"
9 | "time"
10 | )
11 |
12 | type LoginInput struct {
13 | Username string
14 | Password string
15 | }
16 |
17 | type LoginOutput struct {
18 | Token string
19 | }
20 |
21 | type GridRegionsInput struct {
22 | Token string
23 | All bool
24 | }
25 |
26 | type GridRegionsOutput struct {
27 | GridRegions []GridRegion
28 | }
29 |
30 | type IndexInput struct {
31 | Token string
32 | Ba string
33 | }
34 |
35 | type IndexOutput struct {
36 | Freq string `json:"freq"`
37 | Ba string `json:"ba"`
38 | Percent int `json:"percent"`
39 | Moer float64 `json:"moer"`
40 | PointTime time.Time `json:"point_time"`
41 | }
42 |
43 | type IndexResponse struct {
44 | Freq string `json:"freq"`
45 | Ba string `json:"ba"`
46 | Percent string `json:"percent"`
47 | Moer string `json:"moer"`
48 | PointTime string `json:"point_time"`
49 | }
50 |
51 | type GridRegion struct {
52 | Ba string `json:"ba"`
53 | Name string `json:"name"`
54 | Access string `json:"access"`
55 | Datatype string `json:"datatype"`
56 | }
57 |
58 | type Result []interface{}
59 |
60 | type WattLoginResponse struct {
61 | Token string `json:"token"`
62 | }
63 |
64 | func Login(input *LoginInput) (*LoginOutput, error) {
65 | output := &LoginOutput{}
66 |
67 | h := http.Client{}
68 | req, _ := http.NewRequest("GET", "https://api2.watttime.org/v2/login", nil)
69 | req.SetBasicAuth(input.Username, input.Password)
70 | r, err := h.Do(req)
71 | if err != nil {
72 | return nil, err
73 | } else if r.Status != "200 OK" {
74 | return nil, errors.New("Status: " + r.Status)
75 | }
76 |
77 | defer r.Body.Close()
78 | body, err := ioutil.ReadAll(r.Body)
79 | if err != nil {
80 | return nil, err
81 | }
82 |
83 | wattRes := &WattLoginResponse{}
84 | err = json.Unmarshal(body, wattRes)
85 | if err != nil {
86 | return nil, err
87 | }
88 |
89 | output.Token = wattRes.Token
90 |
91 | return output, nil
92 | }
93 |
94 | func GridRegions(input *GridRegionsInput) (*GridRegionsOutput, error) {
95 | output := &GridRegionsOutput{}
96 | url := "https://api2.watttime.org/v2/ba-access"
97 | if input.All {
98 | url += "?all=true"
99 | }
100 |
101 | h := http.Client{}
102 | req, _ := http.NewRequest("GET", url, nil)
103 | req.Header.Set("Authorization", "Bearer "+input.Token)
104 | r, err := h.Do(req)
105 | if err != nil {
106 | return nil, err
107 | } else if r.Status != "200 OK" {
108 | return nil, errors.New("Status: " + r.Status)
109 | }
110 |
111 | defer r.Body.Close()
112 | body, err := ioutil.ReadAll(r.Body)
113 | if err != nil {
114 | return nil, err
115 | }
116 |
117 | var wattRes Result
118 | err = json.Unmarshal(body, &wattRes)
119 | if err != nil {
120 | return nil, err
121 | }
122 |
123 | typedResult := make([]GridRegion, len(wattRes))
124 | for index, item := range wattRes {
125 | var gridRegion GridRegion
126 | b, _ := json.Marshal(item)
127 | json.Unmarshal(b, &gridRegion)
128 | typedResult[index] = gridRegion
129 | }
130 |
131 | output.GridRegions = typedResult
132 |
133 | return output, nil
134 | }
135 |
136 | func GetIndex(input *IndexInput) (*IndexOutput, error) {
137 |
138 | output := &IndexOutput{}
139 | url := "https://api2.watttime.org/index?ba=" + input.Ba
140 |
141 | h := http.Client{}
142 | req, _ := http.NewRequest("GET", url, nil)
143 | req.Header.Set("Authorization", "Bearer "+input.Token)
144 | r, err := h.Do(req)
145 | if err != nil {
146 | return nil, err
147 | } else if r.Status != "200 OK" {
148 | return nil, errors.New("Status: " + r.Status)
149 | }
150 |
151 | defer r.Body.Close()
152 | body, err := ioutil.ReadAll(r.Body)
153 | if err != nil {
154 | return nil, err
155 | }
156 |
157 | indexRes := &IndexResponse{}
158 | err = json.Unmarshal(body, indexRes)
159 | if err != nil {
160 | return nil, err
161 | }
162 |
163 | output.Freq = indexRes.Freq
164 | output.Ba = indexRes.Ba
165 | output.Percent, _ = strconv.Atoi(indexRes.Percent)
166 | output.Moer, _ = strconv.ParseFloat(indexRes.Moer, 64)
167 | output.PointTime, _ = time.Parse(time.RFC3339, indexRes.PointTime)
168 |
169 | return output, nil
170 |
171 | }
172 |
--------------------------------------------------------------------------------
/cloudform/search.yaml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: '2010-09-09'
2 | Description: Hedge OpenSearch instance using the smallest node type (t3.small.search) with fine-grained access control.
3 |
4 | Resources:
5 | CognitoAuthRole:
6 | Type: AWS::IAM::Role
7 | Properties:
8 | RoleName: !Sub 'RtcCognitoOpenSearchRole${EnvironmentNameCamelCase}${VendorSuffixCamelCase}'
9 | AssumeRolePolicyDocument:
10 | Version: '2012-10-17'
11 | Statement:
12 | - Effect: "Allow"
13 | Principal:
14 | Service: "opensearchservice.amazonaws.com"
15 | Action: "sts:AssumeRole"
16 |
17 | Policies:
18 | - PolicyName: !Sub 'RtcCognitoOpenSearchAccess${EnvironmentNameCamelCase}${VendorSuffixCamelCase}'
19 | PolicyDocument:
20 | Version: "2012-10-17"
21 | Statement:
22 | - Effect: "Allow"
23 | Action:
24 | - "cognito-idp:DescribeUserPool"
25 | - "cognito-idp:CreateUserPoolClient"
26 | - "cognito-idp:DescribeUserPoolClient"
27 | - "cognito-identity:DescribeIdentityPool"
28 | - "cognito-identity:ListIdentityPools"
29 | Resource: !Sub 'arn:aws:es:${AWS::Region}:${AWS::AccountId}:domain/rtc-classifieds-${EnvironmentName}-${VendorSuffix}/*'
30 |
31 | - PolicyName: AllowCognitoActions
32 | PolicyDocument:
33 | Version: '2012-10-17'
34 | Statement:
35 | - Effect: Allow
36 | Action:
37 | - 'es:ESHttp*'
38 | Resource:
39 | - !Sub 'arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/${UserPoolId}'
40 | - !Sub 'arn:aws:cognito-identity:${AWS::Region}:${AWS::AccountId}:identitypool/${IdentityPoolId}'
41 | OpenSearchDomain:
42 | Type: AWS::OpenSearchService::Domain
43 | Properties:
44 | DomainName: !Sub 'rtc-classifieds-${EnvironmentName}-${VendorSuffix}'
45 | EngineVersion: OpenSearch_2.19
46 | ClusterConfig:
47 | InstanceType: t3.small.search
48 | InstanceCount: 1
49 | DedicatedMasterEnabled: false
50 | ZoneAwarenessEnabled: false
51 | EBSOptions:
52 | EBSEnabled: true
53 | VolumeType: gp2
54 | VolumeSize: 10
55 | AdvancedSecurityOptions:
56 | Enabled: true
57 | InternalUserDatabaseEnabled: true
58 | MasterUserOptions:
59 | MasterUserName: admin
60 | MasterUserPassword: !Sub '${MasterPassword}'
61 | EncryptionAtRestOptions:
62 | Enabled: true # Enable encryption at rest
63 | NodeToNodeEncryptionOptions:
64 | Enabled: true # Enable node-to-node encryption
65 | DomainEndpointOptions:
66 | EnforceHTTPS: true # Enable HTTPS enforcement
67 | AccessPolicies:
68 | Version: '2012-10-17'
69 | Statement:
70 | - Effect: Allow
71 | Principal:
72 | AWS: '*'
73 | Action: 'es:*'
74 | Resource: '*'
75 |
76 | Parameters:
77 | EnvironmentName:
78 | Description: The name of the environment (e.g., dev, prod)
79 | Type: String
80 | EnvironmentNameCamelCase:
81 | Description: The name of the environment (e.g., dev, prod) in CamelCase for supporting multiple environments in same account without conflicts.
82 | Type: String
83 | VendorSuffix:
84 | Description: Unique vendor suffix for the bucket.
85 | Type: String
86 | VendorSuffixCamelCase:
87 | Description: Unique vendor suffix in CamelCase for supporting multiple vendors under the same account.
88 | Type: String
89 | UserPoolId:
90 | Description: The ID of the Cognito User Pool
91 | Type: String
92 | IdentityPoolId:
93 | Description: The ID of the Cognito Identity Pool
94 | Type: String
95 | MasterPassword:
96 | Type: String
97 | Description: Master user password for OpenSearch fine-grained access control
98 | NoEcho: true
99 | MinLength: 8
100 | MaxLength: 32
101 | AllowedPattern: '[A-Za-z0-9@!?*#_+=^.-]+'
102 | ConstraintDescription: Password must be 8-32 characters long and can include letters, numbers, and special characters.
103 |
104 | Outputs:
105 | OpenSearchEndpoint:
106 | Description: Endpoint of the OpenSearch domain
107 | Value: !GetAtt OpenSearchDomain.DomainEndpoint
--------------------------------------------------------------------------------
/iac/precheck-oidc-auth/login/index.js:
--------------------------------------------------------------------------------
1 | const crypto = require("crypto");
2 | const AWS = require("aws-sdk");
3 | const logger = require("./logger");
4 |
5 | const loginGovBaseUrl = process.env.LOGIN_GOV_BASE_URL;
6 | const loginGovClientId = process.env.LOGIN_GOV_CLIENT_ID;
7 | const loginGovRedirectUri = process.env.LOGIN_GOV_REDIRECT_URI;
8 | const stateTableName = process.env.STATE_TABLE_NAME;
9 | const redirectTableName = process.env.REDIRECT_TABLE_NAME; // New table for storing redirect URLs
10 |
11 | /**
12 | * Generates a random string for the PKCE code verifier.
13 | */
14 | function generateCodeVerifier() {
15 | return crypto.randomBytes(32).toString("hex");
16 | }
17 |
18 | function generateNonce() {
19 | return crypto.randomBytes(16).toString("hex"); // Generate a random 16-byte nonce
20 | }
21 |
22 | /**
23 | * Generates a code challenge from the code verifier using SHA256.
24 | */
25 | function generateCodeChallenge(codeVerifier) {
26 | return crypto
27 | .createHash("sha256")
28 | .update(codeVerifier)
29 | .digest("base64")
30 | .replace(/\+/g, "-")
31 | .replace(/\//g, "_")
32 | .replace(/=/g, "");
33 | }
34 |
35 | exports.handler = async (event) => {
36 | try {
37 |
38 | // Parse the query string to extract the original redirect URL
39 | const { queryStringParameters } = event;
40 | const originalRedirectUri = queryStringParameters?.redirect_uri;
41 | const state = queryStringParameters?.state;
42 |
43 | logger({ state });
44 |
45 | if (!state || state == '') {
46 | console.error("Missing state in query string");
47 | return {
48 | statusCode: 400,
49 | body: JSON.stringify({ error: "Missing state in query string" }),
50 | };
51 | }
52 |
53 | if (!originalRedirectUri || originalRedirectUri == '') {
54 | console.error("Missing redirect_uri in query string");
55 | return {
56 | statusCode: 400,
57 | body: JSON.stringify({ error: "Missing redirect_uri in query string" }),
58 | };
59 | }
60 |
61 |
62 | // Generate a nonce
63 | const nonce = generateNonce();
64 |
65 | // const state = crypto.randomBytes(16).toString("hex"); // Generate a random state
66 | const codeVerifier = generateCodeVerifier();
67 | const codeChallenge = generateCodeChallenge(codeVerifier);
68 |
69 | // Store the state and code verifier in DynamoDB
70 | const dynamodb = new AWS.DynamoDB.DocumentClient();
71 |
72 | await dynamodb
73 | .put({
74 | TableName: stateTableName,
75 | Item: {
76 | state,
77 | codeVerifier,
78 | nonce,
79 | ttl: Math.floor(Date.now() / 1000) + 300, // 5-minute TTL
80 | },
81 | })
82 | .promise();
83 |
84 |
85 | // Store the original redirect URL in the redirect table
86 | await dynamodb
87 | .put({
88 | TableName: redirectTableName,
89 | Item: {
90 | state,
91 | redirectUri: originalRedirectUri,
92 | ttl: Math.floor(Date.now() / 1000) + 300, // 5-minute TTL
93 | },
94 | })
95 | .promise();
96 |
97 | // Build the Login.gov authorization URL
98 | const redirectUrl = `${loginGovBaseUrl}?client_id=${loginGovClientId}&response_type=code&redirect_uri=${encodeURIComponent(
99 | loginGovRedirectUri
100 | )}&scope=openid+email+verified_at+x509_presented+x509_subject&state=${state}&code_challenge=${codeChallenge}&code_challenge_method=S256&acr_values=${encodeURIComponent('urn:acr.login.gov:auth-only urn:gov:gsa:ac:classes:sp:PasswordProtectedTransport:duo')}&nonce=${nonce}`;
101 |
102 | // Return a redirect response
103 | return {
104 | statusCode: 302,
105 | headers: {
106 | Location: redirectUrl,
107 | },
108 | };
109 | } catch (error) {
110 | console.error("Error in Login Lambda:", error);
111 | return {
112 | statusCode: 500,
113 | body: JSON.stringify({ error: "Internal Server Error" }),
114 | };
115 | }
116 | };
--------------------------------------------------------------------------------
/hooks/site_write/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "log"
7 | "os"
8 |
9 | "goclassifieds/lib/entity"
10 | "goclassifieds/lib/repo"
11 |
12 | "golang.org/x/oauth2"
13 | "github.com/aws/aws-lambda-go/lambda" // Used for defining the Lambda handler
14 | lambdaSvc "github.com/aws/aws-sdk-go/service/lambda" // AWS SDK for interacting with Lambda
15 | "github.com/aws/aws-sdk-go/aws/session" // AWS session for SDK service creation
16 | )
17 |
18 | func handler(ctx context.Context, event map[string]interface{}) (entity.AfterSaveExecEntityResponse, error) {
19 |
20 | // Navigate through the map to get the 'repoName'
21 | input, ok := event["Input"].(map[string]interface{})
22 | if !ok {
23 | return entity.AfterSaveExecEntityResponse{}, fmt.Errorf("failed to parse 'Input' field from event")
24 | }
25 |
26 | ent, ok := input["entity"].(map[string]interface{})
27 | if !ok {
28 | return entity.AfterSaveExecEntityResponse{}, fmt.Errorf("failed to parse 'entity' field from Input")
29 | }
30 |
31 | repoName, ok := ent["repoName"].(string)
32 | if !ok {
33 | return entity.AfterSaveExecEntityResponse{}, fmt.Errorf("failed to parse 'repoName' from entity")
34 | }
35 |
36 | // Log or use the 'repoName'
37 | log.Printf("Repository Name: %s", repoName)
38 |
39 | owner := "rollthecloudinc" // Hardcoded owner (to be replaced by payload.Entity in later iterations)
40 | // repoName := "site12"
41 | repoBuildName := repoName + "-build" // Hardcoded repo name (to be replaced by payload.Entity)
42 |
43 | // Validate environment variables
44 | stage := os.Getenv("STAGE")
45 | if stage == "" {
46 | return entity.AfterSaveExecEntityResponse{}, fmt.Errorf("environment variable STAGE is missing")
47 | }
48 |
49 | githubAppID := os.Getenv("GITHUB_APP_ID")
50 | if githubAppID == "" {
51 | return entity.AfterSaveExecEntityResponse{}, fmt.Errorf("environment variable GITHUB_APP_ID is missing")
52 | }
53 |
54 | // Create an AWS session for Lambda client
55 | sess := session.Must(session.NewSession())
56 | lClient := lambdaSvc.New(sess)
57 |
58 | // Load GitHub app PEM file
59 | pemFilePath := fmt.Sprintf("rtc-vertigo-%s.private-key.pem", stage)
60 | pem, err := os.ReadFile(pemFilePath)
61 | if err != nil {
62 | log.Printf("Failed to read PEM file '%s': %v", pemFilePath, err)
63 | return entity.AfterSaveExecEntityResponse{}, fmt.Errorf("failed to load GitHub app PEM file: %w", err)
64 | }
65 | log.Print("GitHub app PEM file loaded successfully.")
66 |
67 | // Generate GitHub Installation Token
68 | getTokenInput := &repo.GetInstallationTokenInput{
69 | GithubAppPem: pem,
70 | Owner: owner,
71 | GithubAppId: githubAppID,
72 | }
73 | installationToken, err := repo.GetInstallationToken(getTokenInput)
74 | if err != nil {
75 | log.Printf("Error generating GitHub installation token for owner '%s': %v", owner, err)
76 | return entity.AfterSaveExecEntityResponse{}, fmt.Errorf("failed to generate GitHub installation token: %w", err)
77 | }
78 |
79 | // Create OAuth2 HTTP client
80 | srcToken := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: *installationToken.Token})
81 | httpClient := oauth2.NewClient(ctx, srcToken)
82 |
83 | privateKey, publicKey, err := repo.GenerateSSHKeyPair()
84 | if err != nil {
85 | log.Print("Failed to ssh key pair")
86 | return entity.AfterSaveExecEntityResponse{}, err
87 | }
88 |
89 | log.Printf("ssh private key: %s", privateKey)
90 | log.Printf("ssh public key: %s", publicKey)
91 |
92 | err = repo.CreateGithubDeployKey(context.Background(), httpClient, owner, repoBuildName, "Automated SSH Deploy Key", publicKey, false)
93 | if err != nil {
94 | log.Print("Failed to create deploy deploy")
95 | return entity.AfterSaveExecEntityResponse{}, err
96 | }
97 |
98 | // Now add the private key to the source code repo as a repo secret.
99 | // Step: Create environment secrets
100 | err = repo.CreateGithubRepositorySecret(context.Background(), httpClient, lClient, owner, repoName, "SSH_DEPLOY_KEY", privateKey, stage)
101 | if err != nil {
102 | log.Print("Failed to create SSH_DEPLOY_KEY repository secret")
103 | return entity.AfterSaveExecEntityResponse{}, err
104 | }
105 |
106 | log.Printf("Repository secret %s created successfullly.", "SSH_DEPLOY_KEY")
107 |
108 | return entity.AfterSaveExecEntityResponse{}, nil
109 | }
110 |
111 | func main() {
112 | log.SetFlags(0) // Output logs without timestamps
113 | // Make the handler available for invocation by AWS Lambda
114 | lambda.Start(handler)
115 | }
--------------------------------------------------------------------------------
/serverless-splits.yml:
--------------------------------------------------------------------------------
1 | service: goclassifieds-splits-api
2 | frameworkVersion: '3'
3 | plugins:
4 | - serverless-prune-plugin
5 | - serverless-custom-packaging-plugin
6 | custom:
7 | githubToken: ${file(./private.${opt:stage, 'dev'}.json):githubToken}
8 | githubAppId: ${file(./private.${opt:stage, 'dev'}.json):githubAppId}
9 | provider:
10 | name: aws
11 | runtime: provided.al2023
12 | memorySize: 512
13 | timeout: 45
14 | #logs:
15 | #websocket: true
16 | httpApi:
17 | payload: '1.0'
18 | cors: true
19 | package:
20 | individually: true
21 | resources:
22 | Resources:
23 | SplitsRole:
24 | Type: AWS::IAM::Role
25 | Properties:
26 | Path: "/"
27 | RoleName: splits-${opt:region, 'us-east-1'}-lambdaRole
28 | AssumeRolePolicyDocument:
29 | Version: '2012-10-17'
30 | Statement:
31 | - Effect: Allow
32 | Principal:
33 | Service:
34 | - lambda.amazonaws.com
35 | - apigateway.amazonaws.com
36 | Action: sts:AssumeRole
37 | ManagedPolicyArns:
38 | - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
39 | Policies:
40 | - PolicyName: goclassifieds-splits-api-lambda
41 | PolicyDocument:
42 | Version: '2012-10-17'
43 | Statement:
44 | - Effect: Allow
45 | Action:
46 | - logs:CreateLogGroup
47 | - logs:CreateLogStream
48 | - logs:PutLogEvents
49 | - logs:DescribeLogGroups
50 | - logs:DescribeLogStreams
51 | - logs:GetLogEvents
52 | - logs:FilterLogEvents
53 | Resource:
54 | - 'Fn::Join':
55 | - ':'
56 | -
57 | - 'arn:aws:logs'
58 | - Ref: 'AWS::Region'
59 | - Ref: 'AWS::AccountId'
60 | - 'log-group:/aws/lambda/*:*:*'
61 | - Effect: "Allow"
62 | Action:
63 | - "lambda:InvokeFunction"
64 | Resource: "*"
65 | - Effect: "Allow"
66 | Action:
67 | - "execute-api:Invoke"
68 | - "execute-api:ManageConnections"
69 | Resource: "*"
70 | functions:
71 | ModelApi:
72 | handler: bootstrap
73 | role: SplitsRole
74 | package:
75 | path: bazel-bin/api/model
76 | artifact: .serverless/ModelApi.zip
77 | libs: api/entity
78 | include_globs:
79 | - "**/bootstrap"
80 | - "**/*.json.tmpl"
81 | - "**/*.pem"
82 | environment:
83 | GITHUB_TOKEN: ${self:custom.githubToken}
84 | STAGE: ${opt:stage, 'dev'}
85 | events:
86 | - httpApi:
87 | path: /model/{proxy+}
88 | method: POST
89 | IndexApi:
90 | handler: bootstrap
91 | role: SplitsRole
92 | package:
93 | path: bazel-bin/api/index
94 | artifact: .serverless/IndexApi.zip
95 | libs: api/entity
96 | include_globs:
97 | - "**/bootstrap"
98 | - "**/*.json.tmpl"
99 | - "**/*.pem"
100 | environment:
101 | GITHUB_APP_ID: ${self:custom.githubAppId}
102 | STAGE: ${opt:stage, 'dev'}
103 | events:
104 | - httpApi:
105 | path: /{owner}/{repo}/index/{index}
106 | method: GET
107 | - httpApi:
108 | path: /{owner}/{repo}/search
109 | method: POST
110 | IndexCreateHook:
111 | handler: bootstrap
112 | role: SplitsRole
113 | package:
114 | path: bazel-bin/hooks/index_create
115 | artifact: .serverless/Index_Create.zip
116 | libs: api/entity
117 | include_globs:
118 | - "**/bootstrap"
119 | - "**/*.json.tmpl"
120 | - "**/*.pem"
121 | environment:
122 | GITHUB_APP_ID: ${self:custom.githubAppId}
123 | STAGE: ${opt:stage, 'dev'}
124 | EntityIndexHook:
125 | handler: bootstrap
126 | role: SplitsRole
127 | package:
128 | path: bazel-bin/hooks/entity_index
129 | artifact: .serverless/Entity_Index.zip
130 | libs: api/entity
131 | include_globs:
132 | - "**/bootstrap"
133 | - "**/*.json.tmpl"
134 | - "**/*.pem"
135 | environment:
136 | GITHUB_APP_ID: ${self:custom.githubAppId}
137 | STAGE: ${opt:stage, 'dev'}
--------------------------------------------------------------------------------
/cloudform/search_distinct.yaml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: '2010-09-09'
2 | Description: Creates a Distinct OpenSearch instance with Cognito authentication and fine-grained access control.
3 |
4 | Resources:
5 | # IAM Role for Cognito and OpenSearch Authentication
6 | DistinctCognitoAuthRole:
7 | Type: AWS::IAM::Role
8 | Properties:
9 | RoleName: DistinctOpenSearchAuthRole
10 | AssumeRolePolicyDocument:
11 | Version: '2012-10-17'
12 | Statement:
13 | - Effect: Allow
14 | Principal:
15 | Federated: "cognito-identity.amazonaws.com"
16 | Action: 'sts:AssumeRoleWithWebIdentity'
17 | Condition:
18 | StringEquals:
19 | 'cognito-identity.amazonaws.com:aud': !Ref CognitoIdentityPoolId
20 | ForAnyValue:StringLike:
21 | 'cognito-identity.amazonaws.com:amr': authenticated
22 | - Effect: Allow
23 | Principal:
24 | Service: "es.amazonaws.com" # Allow OpenSearch Service to assume the role
25 | Action: 'sts:AssumeRole'
26 | Policies:
27 | - PolicyName: DistinctOpenSearchAccessPolicy
28 | PolicyDocument:
29 | Version: '2012-10-17'
30 | Statement:
31 | - Effect: Allow
32 | Action:
33 | - 'es:ESHttp*'
34 | Resource: '*' # Further restriction is recommended based on access control requirements
35 | - Effect: Allow
36 | Action:
37 | - 'cognito-idp:DescribeUserPool' # Permission to describe the Cognito User Pool
38 | - 'cognito-identity:ListIdentityPools' # Permission to list identity pools
39 | - 'cognito-identity:GetIdentityPoolRoles' # Permission to get identity pool roles
40 | - 'cognito-identity:DescribeIdentityPool' # Permission to describe the Cognito Identity Pool
41 | Resource:
42 | - !Sub 'arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/${CognitoUserPoolId}' # Restrict to the Cognito User Pool
43 | - !Sub 'arn:aws:cognito-identity:${AWS::Region}:${AWS::AccountId}:identitypool/${CognitoIdentityPoolId}' # Restrict to the Cognito Identity Pool
44 |
45 | # OpenSearch Domain with Cognito Authentication Enabled
46 | DistinctOpenSearchDomain:
47 | Type: AWS::OpenSearchService::Domain
48 | Properties:
49 | DomainName: distinct-opensearch
50 | EngineVersion: OpenSearch_2.5
51 | ClusterConfig:
52 | InstanceType: t3.small.search
53 | InstanceCount: 1
54 | DedicatedMasterEnabled: false
55 | ZoneAwarenessEnabled: false
56 | EBSOptions:
57 | EBSEnabled: true
58 | VolumeType: gp2
59 | VolumeSize: 10
60 | AdvancedSecurityOptions:
61 | Enabled: true
62 | InternalUserDatabaseEnabled: true
63 | MasterUserOptions:
64 | MasterUserName: admin
65 | MasterUserPassword: !Sub '${MasterPassword}'
66 | CognitoOptions:
67 | Enabled: true
68 | IdentityPoolId: !Ref CognitoIdentityPoolId
69 | UserPoolId: !Ref CognitoUserPoolId
70 | RoleArn: !GetAtt DistinctCognitoAuthRole.Arn # Reference the corrected IAM Role here
71 | EncryptionAtRestOptions:
72 | Enabled: true # Enable encryption at rest
73 | NodeToNodeEncryptionOptions:
74 | Enabled: true # Enable node-to-node encryption
75 | DomainEndpointOptions:
76 | EnforceHTTPS: true # Enforce HTTPS
77 | AccessPolicies:
78 | Version: '2012-10-17'
79 | Statement:
80 | - Effect: Allow
81 | Principal:
82 | AWS: '*'
83 | Action: 'es:*'
84 | Resource: '*'
85 |
86 | Parameters:
87 | MasterPassword:
88 | Type: String
89 | Description: Master user password for OpenSearch fine-grained access control
90 | NoEcho: true
91 | MinLength: 8
92 | MaxLength: 32
93 | AllowedPattern: '[A-Za-z0-9@!?*#_+=^.-]+'
94 | ConstraintDescription: Password must be 8-32 characters long and can include letters, numbers, and special characters.
95 |
96 | CognitoUserPoolId:
97 | Type: String
98 | Description: ID of the existing Cognito User Pool
99 |
100 | CognitoIdentityPoolId:
101 | Type: String
102 | Description: ID of the existing Cognito Identity Pool
103 |
104 | Outputs:
105 | DistinctOpenSearchEndpoint:
106 | Description: Endpoint of the Distinct OpenSearch domain
107 | Value: !GetAtt DistinctOpenSearchDomain.Endpoint
--------------------------------------------------------------------------------
/api/authorizer3/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "goclassifieds/lib/utils"
5 | "log"
6 | "os"
7 | "errors"
8 |
9 | "github.com/MicahParks/keyfunc"
10 | "github.com/aws/aws-lambda-go/events"
11 | "github.com/aws/aws-lambda-go/lambda"
12 | "github.com/golang-jwt/jwt/v4"
13 | )
14 |
15 | var handler Handler
16 |
17 | type Handler func(req *events.APIGatewayProxyRequest) (events.APIGatewayCustomAuthorizerResponse, error)
18 |
19 | type ActionContext struct {
20 | UserPoolId string
21 | }
22 |
23 | // Authorizer custom api authorizer
24 | func Authorizer(request *events.APIGatewayProxyRequest, ac *ActionContext) (events.APIGatewayCustomAuthorizerResponse, error) {
25 | token1 := request.Headers["authorization"]
26 |
27 | utils.LogUsageForHttpRequest(request)
28 |
29 | log.Printf("%v", request)
30 | log.Printf("token is %s", token1)
31 |
32 | if token1 == "" {
33 | // For now lock it down but eventually needs to support anonymous users.
34 | /*log.Print("unauthorized request pass thru")
35 | return events.APIGatewayCustomAuthorizerResponse{
36 | PrincipalID: "me",
37 | PolicyDocument: events.APIGatewayCustomAuthorizerPolicy{
38 | Version: "2012-10-17",
39 | Statement: []events.IAMPolicyStatement{
40 | {
41 | Action: []string{"execute-api:*"},
42 | Effect: "Allow",
43 | Resource: []string{"*"},
44 | // Resource: []string{request.Re},
45 | },
46 | },
47 | },
48 | }, nil*/
49 | log.Print("Unauthorized requires token")
50 | return events.APIGatewayCustomAuthorizerResponse{}, errors.New("Unauthorized requires token")
51 | }
52 |
53 | token := token1[7:]
54 | log.Printf("token after is %s", token)
55 |
56 | // jwks, err := keyfunc.Get("https://cognito-idp.us-east-1.amazonaws.com/"+ac.UserPoolId+"/.well-known/jwks.json", keyfunc.Options{})
57 |
58 | // --------------------- Canva integration for jwks ----------------------
59 |
60 | appId := "AAGJBJc7pZs"
61 | jwks, err := keyfunc.Get("https://api.canva.com/rest/v1/apps/" + appId + "/jwks", keyfunc.Options{})
62 |
63 | // --------------------- End Canva integration for jwks ----------------------
64 |
65 | if err != nil {
66 | log.Print("Unable to fetch keys")
67 | }
68 |
69 | log.Print("fectehd keys")
70 |
71 | // Verify
72 | t, err := jwt.Parse(token, jwks.Keyfunc)
73 | if err != nil || !t.Valid {
74 | log.Print(err)
75 | log.Print("Unauthorized")
76 | return events.APIGatewayCustomAuthorizerResponse{}, err
77 | }
78 |
79 | log.Print("authorized")
80 |
81 | claims := t.Claims.(jwt.MapClaims)
82 |
83 | log.Print("got claims")
84 |
85 | // -------- Canva Integration for Claims Compatibility ---------------------------------
86 | // remap to source of truth when shared between providers like: user id/sub, user name, etc.
87 | // Otherwise overload with claims that can be pulled out once request identified as a specific auth host ie.cognito, canva, etc.
88 |
89 | // log expected claims for canva
90 | log.Printf("Canva userId: %s", claims["userId"])
91 | log.Printf("Canva designId: %s", claims["brandId"])
92 | log.Printf("Canva teamId: %s", claims["aud"])
93 |
94 | // Cognito is the source of truth so map Canva userId to sub which means the same thing.
95 | claims["sub"] = claims["userId"]
96 |
97 | // Canva does not have usernames just optional display names. So for now just reuse the userId.
98 | // We could use the connect api to grab the display name if it exists... but why really...
99 | claims["cognito:username"] = claims["userId"]
100 |
101 | // --------- End Canva Integration for Claims Compatibility -------------------
102 |
103 | claims["cognito:groups"] = nil //claims["cognito:groups"]
104 | claims["cognito:roles"] = nil //claims["cognito:groups"]
105 |
106 | return events.APIGatewayCustomAuthorizerResponse{
107 | PrincipalID: "me",
108 | PolicyDocument: events.APIGatewayCustomAuthorizerPolicy{
109 | Version: "2012-10-17",
110 | Statement: []events.IAMPolicyStatement{
111 | {
112 | Action: []string{"execute-api:*"},
113 | Effect: "Allow",
114 | Resource: []string{"*"},
115 | // Resource: []string{request.Re},
116 | },
117 | },
118 | },
119 | Context: claims,
120 | }, nil
121 | }
122 |
123 | func InitializeHandler(ac ActionContext) Handler {
124 | return func(req *events.APIGatewayProxyRequest) (events.APIGatewayCustomAuthorizerResponse, error) {
125 | return Authorizer(req, &ac)
126 | }
127 | }
128 |
129 | func init() {
130 | log.Printf("Gin cold start")
131 | actionContext := ActionContext{
132 | UserPoolId: os.Getenv("USER_POOL_ID"),
133 | }
134 | handler = InitializeHandler(actionContext)
135 | }
136 |
137 | func main() {
138 | log.SetFlags(0)
139 | lambda.Start(handler)
140 | }
141 |
--------------------------------------------------------------------------------