├── .gitignore ├── .vim └── coc-settings.json ├── LICENSE ├── README.md ├── deno-deploy ├── README.md └── api.ts ├── fly-go-cockroach ├── README.md ├── api.go ├── fly.toml ├── go.mod └── go.sum ├── fly-go-crunchy-bridge ├── README.md ├── api.go ├── fly.toml ├── go.mod └── go.sum ├── fly-go-crunchy-replicas ├── Dockerfile ├── README.md ├── api.go ├── fly.toml ├── go.mod ├── go.sum └── start.sh ├── fly-go-neon ├── README.md ├── api.go ├── fly.toml ├── go.mod └── go.sum ├── fly-go-sqlite ├── Dockerfile ├── api.go ├── etc │ └── litefs.yml ├── fly.toml ├── go.mod └── go.sum ├── fly-go-supabase ├── README.md ├── api.go ├── fly.toml ├── go.mod └── go.sum ├── fly-go ├── README.md ├── api.go ├── fly.toml ├── go.mod └── go.sum ├── fly-planetscale ├── README.md ├── api.go ├── fly.toml ├── go.mod └── go.sum ├── fly-read-replicas ├── README.md ├── api.go ├── fly.toml ├── go.mod ├── go.sum └── url_test.go ├── fly-ruby ├── Gemfile ├── Gemfile.lock ├── Procfile ├── README.md ├── api.rb └── fly.toml ├── heroku-crystal ├── .gitignore ├── README.md ├── shard.lock ├── shard.yml └── src │ └── api.cr ├── heroku-go-crunchy ├── Dockerfile ├── README.md ├── go.mod ├── go.sum ├── main.go └── start.sh ├── heroku-go ├── Procfile ├── README.md ├── api.go ├── go.mod └── go.sum ├── heroku-node ├── Procfile ├── README.md ├── api.js ├── package-lock.json └── package.json ├── heroku-ruby ├── Gemfile ├── Gemfile.lock ├── Procfile ├── README.md └── api.rb ├── heroku.yml ├── htmlx-go ├── README.md ├── go.mod ├── go.sum ├── html │ ├── index.html │ └── version.html └── main.go ├── northflank-go ├── README.md ├── go.mod ├── go.sum └── main.go ├── railway-crystal ├── .gitignore ├── README.md ├── shard.lock ├── shard.yml └── src │ └── api.cr ├── railway-deno ├── README.md └── main.ts ├── railway-go-tailscale ├── Dockerfile ├── README.md ├── api.go ├── go.mod ├── go.sum ├── railway.toml └── start.sh ├── railway-go ├── README.md ├── go.mod ├── go.sum └── main.go ├── railway-node ├── README.md ├── api.js ├── package-lock.json └── package.json ├── railway-ruby ├── Gemfile ├── Gemfile.lock ├── Procfile ├── README.md └── api.rb ├── render-go ├── README.md ├── go.mod ├── go.sum └── main.go ├── render-ruby ├── Gemfile ├── Gemfile.lock ├── README.md └── api.rb ├── vercel-edge-planetscale ├── .gitignore ├── README.md ├── api │ └── index.ts ├── lib │ └── wake.js ├── package-lock.json └── package.json ├── vercel-go-cockroach ├── README.md ├── api │ └── index.go ├── go.mod └── go.sum └── vercel-go-neon ├── README.md ├── api └── index.go ├── go.mod └── go.sum /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | public/* 3 | -------------------------------------------------------------------------------- /.vim/coc-settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true, 3 | "deno.lint": true, 4 | "deno.unstable": true, 5 | "prettier.disableLanguages": ["typescript", "javascript"], 6 | "tsserver.enable": false 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT No Attribution 2 | 3 | Copyright 2022 Dan Croak 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | software and associated documentation files (the "Software"), to deal in the Software 7 | without restriction, including without limitation the rights to use, copy, modify, 8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 13 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 14 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 16 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # webstack 2 | 3 | A project to prototype web stacks. 4 | -------------------------------------------------------------------------------- /deno-deploy/README.md: -------------------------------------------------------------------------------- 1 | # Deno Deploy 2 | 3 | Follow tutorial at . 4 | -------------------------------------------------------------------------------- /deno-deploy/api.ts: -------------------------------------------------------------------------------- 1 | import { serve } from "https://deno.land/std@0.114.0/http/server.ts"; 2 | import * as postgres from "https://deno.land/x/postgres@v0.15.0/mod.ts"; 3 | 4 | const dbURL = Deno.env.get("DATABASE_URL")!; 5 | const pool = new postgres.Pool(dbURL, 3, true); 6 | 7 | serve(async (req) => { 8 | const db = await pool.connect(); 9 | 10 | try { 11 | await db.queryArray`SELECT 1`; 12 | return new Response('{"status": "ok"}', { 13 | headers: { "content-type": "application/json" }, 14 | }); 15 | } catch (err) { 16 | console.error(err); 17 | return new Response(`Internal Server Error\n\n${err.message}`, { 18 | status: 500, 19 | }); 20 | } finally { 21 | db.release(); 22 | } 23 | }); 24 | -------------------------------------------------------------------------------- /fly-go-cockroach/README.md: -------------------------------------------------------------------------------- 1 | # Fly Go Cockroach 2 | 3 | Develop: 4 | 5 | ```bash 6 | go run api.go 7 | ``` 8 | 9 | Create a Postgres database: 10 | 11 | * Go to to create a new Postgres database. 12 | * Choose a region to be close to app server. 13 | * Copy "Connection string". 14 | 15 | Set up Fly: 16 | 17 | ```bash 18 | brew install flyctl 19 | fly auth login 20 | rm -rf ~/.docker 21 | fly launch --remote-only 22 | ``` 23 | -------------------------------------------------------------------------------- /fly-go-cockroach/api.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | 10 | "github.com/jackc/pgx/v4/pgxpool" 11 | ) 12 | 13 | func main() { 14 | // env 15 | port, ok := os.LookupEnv("PORT") 16 | if !ok { 17 | port = "8080" 18 | } 19 | dbUrl, ok := os.LookupEnv("DATABASE_URL") 20 | if !ok { 21 | dbUrl = "postgres:///webstack_dev" 22 | } 23 | 24 | // db 25 | db, err := pgxpool.Connect(context.Background(), dbUrl) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | defer db.Close() 30 | 31 | // routes 32 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 33 | var col int 34 | db.QueryRow(r.Context(), "SELECT 1").Scan(&col) 35 | w.Header().Set("Content-Type", "application/json") 36 | fmt.Fprintf(w, "{\"status\":\"ok\"}") 37 | }) 38 | 39 | // listen 40 | log.Println("Listening at http://localhost:" + port) 41 | log.Fatal(http.ListenAndServe(":"+port, nil)) 42 | } 43 | -------------------------------------------------------------------------------- /fly-go-cockroach/fly.toml: -------------------------------------------------------------------------------- 1 | # fly.toml file generated for webstack-go-cockroach on 2022-11-05T11:58:54-04:00 2 | 3 | app = "webstack-go-cockroach" 4 | kill_signal = "SIGINT" 5 | kill_timeout = 5 6 | processes = [] 7 | 8 | [build] 9 | builder = "paketobuildpacks/builder:base" 10 | buildpacks = ["gcr.io/paketo-buildpacks/go"] 11 | 12 | [env] 13 | PORT = "8080" 14 | 15 | [experimental] 16 | allowed_public_ports = [] 17 | auto_rollback = true 18 | 19 | [[services]] 20 | http_checks = [] 21 | internal_port = 8080 22 | processes = ["app"] 23 | protocol = "tcp" 24 | script_checks = [] 25 | [services.concurrency] 26 | hard_limit = 25 27 | soft_limit = 20 28 | type = "connections" 29 | 30 | [[services.ports]] 31 | force_https = true 32 | handlers = ["http"] 33 | port = 80 34 | 35 | [[services.ports]] 36 | handlers = ["tls", "http"] 37 | port = 443 38 | 39 | [[services.tcp_checks]] 40 | grace_period = "1s" 41 | interval = "15s" 42 | restart_limit = 0 43 | timeout = "2s" 44 | -------------------------------------------------------------------------------- /fly-go-cockroach/go.mod: -------------------------------------------------------------------------------- 1 | module server 2 | 3 | go 1.19 4 | 5 | require github.com/jackc/pgx/v4 v4.17.2 6 | 7 | require ( 8 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 9 | github.com/jackc/pgconn v1.13.0 // indirect 10 | github.com/jackc/pgio v1.0.0 // indirect 11 | github.com/jackc/pgpassfile v1.0.0 // indirect 12 | github.com/jackc/pgproto3/v2 v2.3.1 // indirect 13 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect 14 | github.com/jackc/pgtype v1.12.0 // indirect 15 | github.com/jackc/puddle v1.3.0 // indirect 16 | github.com/lib/pq v1.10.4 // indirect 17 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect 18 | golang.org/x/text v0.3.7 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /fly-go-crunchy-bridge/README.md: -------------------------------------------------------------------------------- 1 | # Fly Go Crunchy Bridge 2 | 3 | Develop: 4 | 5 | ```bash 6 | go run api.go 7 | ``` 8 | 9 | Create a Postgres database: 10 | 11 | * Go to to create a new Postgres database. 12 | * Choose a region to be close to app server. 13 | * Copy "Connection string". 14 | 15 | Set up Fly: 16 | 17 | ```bash 18 | brew install flyctl 19 | fly auth login 20 | rm -rf ~/.docker 21 | fly launch --remote-only 22 | ``` 23 | -------------------------------------------------------------------------------- /fly-go-crunchy-bridge/api.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | 10 | "github.com/jackc/pgx/v4/pgxpool" 11 | ) 12 | 13 | func main() { 14 | // env 15 | port, ok := os.LookupEnv("PORT") 16 | if !ok { 17 | port = "8080" 18 | } 19 | dbUrl, ok := os.LookupEnv("DATABASE_URL") 20 | if !ok { 21 | dbUrl = "postgres:///webstack_dev" 22 | } 23 | 24 | // db 25 | db, err := pgxpool.Connect(context.Background(), dbUrl) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | defer db.Close() 30 | 31 | // routes 32 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 33 | var col int 34 | db.QueryRow(r.Context(), "SELECT 1").Scan(&col) 35 | w.Header().Set("Content-Type", "application/json") 36 | fmt.Fprintf(w, "{\"status\":\"ok\"}") 37 | }) 38 | 39 | // listen 40 | log.Println("Listening at http://localhost:" + port) 41 | log.Fatal(http.ListenAndServe(":"+port, nil)) 42 | } 43 | -------------------------------------------------------------------------------- /fly-go-crunchy-bridge/fly.toml: -------------------------------------------------------------------------------- 1 | # fly.toml file generated for webstack-go-crunchy-bridge on 2022-11-05T20:32:25-04:00 2 | 3 | app = "webstack-go-crunchy-bridge" 4 | kill_signal = "SIGINT" 5 | kill_timeout = 5 6 | processes = [] 7 | 8 | [build] 9 | builder = "paketobuildpacks/builder:base" 10 | buildpacks = ["gcr.io/paketo-buildpacks/go"] 11 | 12 | [env] 13 | PORT = "8080" 14 | 15 | [experimental] 16 | allowed_public_ports = [] 17 | auto_rollback = true 18 | 19 | [[services]] 20 | http_checks = [] 21 | internal_port = 8080 22 | processes = ["app"] 23 | protocol = "tcp" 24 | script_checks = [] 25 | [services.concurrency] 26 | hard_limit = 25 27 | soft_limit = 20 28 | type = "connections" 29 | 30 | [[services.ports]] 31 | force_https = true 32 | handlers = ["http"] 33 | port = 80 34 | 35 | [[services.ports]] 36 | handlers = ["tls", "http"] 37 | port = 443 38 | 39 | [[services.tcp_checks]] 40 | grace_period = "1s" 41 | interval = "15s" 42 | restart_limit = 0 43 | timeout = "2s" 44 | -------------------------------------------------------------------------------- /fly-go-crunchy-bridge/go.mod: -------------------------------------------------------------------------------- 1 | module server 2 | 3 | go 1.19 4 | 5 | require github.com/jackc/pgx/v4 v4.17.2 6 | 7 | require ( 8 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 9 | github.com/jackc/pgconn v1.13.0 // indirect 10 | github.com/jackc/pgio v1.0.0 // indirect 11 | github.com/jackc/pgpassfile v1.0.0 // indirect 12 | github.com/jackc/pgproto3/v2 v2.3.1 // indirect 13 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect 14 | github.com/jackc/pgtype v1.12.0 // indirect 15 | github.com/jackc/puddle v1.3.0 // indirect 16 | github.com/lib/pq v1.10.4 // indirect 17 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect 18 | golang.org/x/text v0.3.7 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /fly-go-crunchy-replicas/Dockerfile: -------------------------------------------------------------------------------- 1 | # Build app 2 | FROM golang:1.20-alpine as builder 3 | WORKDIR /app 4 | COPY . ./ 5 | RUN go build -o /app/server 6 | 7 | # Build Tailscale 8 | ENV TSFILE=tailscale_1.40.0_amd64.tgz 9 | RUN wget https://pkgs.tailscale.com/stable/${TSFILE} && tar xzf ${TSFILE} --strip-components=1 10 | 11 | # https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds 12 | FROM alpine:latest 13 | RUN apk update && apk add ca-certificates iptables ip6tables && rm -rf /var/cache/apk/* 14 | 15 | # Copy binary to production image 16 | COPY --from=builder /app/start.sh /app/start.sh 17 | COPY --from=builder /app/server /app/server 18 | COPY --from=builder /app/tailscaled /app/tailscaled 19 | COPY --from=builder /app/tailscale /app/tailscale 20 | RUN mkdir -p /var/run/tailscale /var/cache/tailscale /var/lib/tailscale 21 | 22 | # Run on container startup 23 | EXPOSE 8080 24 | CMD ["/app/start.sh"] 25 | -------------------------------------------------------------------------------- /fly-go-crunchy-replicas/README.md: -------------------------------------------------------------------------------- 1 | # Fly Crunchy Bridge Read Replicas 2 | 3 | This stack involves: 4 | 5 | - a web application written in Go 6 | - deployed to 2 regions on Fly.io 7 | - querying a Postgres primary and read replica on Crunchy Bridge 8 | - networked with Tailscale 9 | 10 | Develop: 11 | 12 | ```bash 13 | DATABASE_URL="postgres:///webstack_dev" go run api.go 14 | ``` 15 | 16 | Set up Crunchy Bridge: 17 | 18 | - Go to [Crunchy Bridge](https://crunchybridge.com/). 19 | - Create a cluster in AWS `us-west-1` (N. California). 20 | - Click "Connection" tab. 21 | - Select "Role: postgres" and "Format: psql". 22 | - Click "Copy", then paste into a shell: 23 | 24 | ```bash 25 | psql postgres://postgres:password@host.db.postgresbridge.com:5432/postgres 26 | ``` 27 | 28 | In the Postgres shell as the `postgres` superuser, 29 | [enable PgBouncer](https://docs.crunchybridge.com/how-to/pgbouncer/): 30 | 31 | ```sql 32 | CREATE EXTENSION crunchy_pooler; 33 | ``` 34 | 35 | Create a reusable, ephemeral 36 | [Tailscale auth key](https://login.tailscale.com/admin/settings/keys). 37 | 38 | In the Crunchy Bridge web UI: 39 | 40 | - Click "Networking". 41 | - Delete the two "fully open firewall" rules `::/0` and `0.0.0.0/0`. 42 | - Click "Tailscale". 43 | - Paste the Tailscale auth key. 44 | - Click "Connect Tailscale". 45 | 46 | In the Tailscale web UI: 47 | 48 | - Go to the [Machines](https://login.tailscale.com/admin/machines) page. 49 | - Click the three dots next to the newly-connected Crunchy database. 50 | - Click "Edit machine name..". 51 | - Rename the machine something like `crunchy-n-california`. 52 | - Click the machine name. 53 | - Copy the domain name into a temporary text file. 54 | It will look something like `crunchy-n-california.taile1234.ts.net`. 55 | 56 | In the Crunchy Bridge web UI: 57 | 58 | - Click "Connection" 59 | - Select "Role: application". 60 | - Copy the connection string and paste into the temporary text file. 61 | - Replace the Crunchy Bridge host (e.g. `p.abc123.db.postgresbridge.com`) 62 | with the Tailscale domain name copied above. 63 | - Replace port `5432` with `5431` so the connection will use PgBouncer. 64 | - Copy the edited connection string, which will look something like: 65 | 66 | ``` 67 | postgres://application:password@crunchy-n-california.taile1234.ts.net:5431/postgres 68 | ``` 69 | 70 | Create Fly app: 71 | 72 | ```bash 73 | fly launch --machine remote-only 74 | ``` 75 | 76 | Set the primary database URL connection string: 77 | 78 | ```bash 79 | fly secrets set DATABASE_URL="" 80 | ``` 81 | 82 | Add a London region near the read replica: 83 | 84 | ```bash 85 | fly regions add lhr 86 | ``` 87 | 88 | In the Crunchy Bridge web UI from the cluster overview page: 89 | 90 | - Click "Actions". 91 | - Click "Create Replica". 92 | - Select AWS region "EU West 2 (London)". 93 | - Set an environment variable on Fly for the London region, 94 | using the Crunchy `application` user and `5431` PgBouncer port: 95 | 96 | ```bash 97 | fly secrets set LONDON_URL="" 98 | ``` 99 | 100 | Deploy: 101 | 102 | ```bash 103 | fly deploy --remote-only 104 | ``` 105 | 106 | Scale 1 instance per region: 107 | 108 | ```bash 109 | fly autoscale set min=2 max=2 110 | ``` 111 | -------------------------------------------------------------------------------- /fly-go-crunchy-replicas/api.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | 10 | "github.com/jackc/pgx/v4/pgxpool" 11 | ) 12 | 13 | func main() { 14 | // env 15 | port, ok := os.LookupEnv("PORT") 16 | if !ok { 17 | port = "8080" 18 | } 19 | dbUrl, ok := os.LookupEnv("DATABASE_URL") 20 | if !ok { 21 | dbUrl = "postgres:///webstack_dev" 22 | } 23 | region := os.Getenv("FLY_REGION") 24 | londonUrl := os.Getenv("LONDON_URL") 25 | 26 | // if not in primary region... 27 | if region == "lhr" && londonUrl != "" { 28 | dbUrl = londonUrl 29 | } 30 | 31 | // db 32 | db, err := pgxpool.Connect(context.Background(), dbUrl) 33 | if err != nil { 34 | log.Fatal(err) 35 | } 36 | defer db.Close() 37 | 38 | // routes 39 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 40 | var col int 41 | db.QueryRow(r.Context(), "SELECT 1").Scan(&col) 42 | w.Header().Set("Content-Type", "application/json") 43 | fmt.Fprintf(w, "{\"status\":\"ok\"}") 44 | }) 45 | 46 | // listen 47 | log.Println("Listening at http://localhost:" + port) 48 | log.Fatal(http.ListenAndServe(":"+port, nil)) 49 | } 50 | -------------------------------------------------------------------------------- /fly-go-crunchy-replicas/fly.toml: -------------------------------------------------------------------------------- 1 | # fly.toml file generated for webstack-go-crunchy-replicas on 2022-11-14T20:45:53-05:00 2 | 3 | app = "webstack-go-crunchy-replicas" 4 | kill_signal = "SIGINT" 5 | kill_timeout = 5 6 | processes = [] 7 | 8 | [env] 9 | PORT = "8080" 10 | 11 | [experimental] 12 | allowed_public_ports = [] 13 | auto_rollback = true 14 | 15 | [[services]] 16 | http_checks = [] 17 | internal_port = 8080 18 | processes = ["app"] 19 | protocol = "tcp" 20 | script_checks = [] 21 | [services.concurrency] 22 | hard_limit = 25 23 | soft_limit = 20 24 | type = "connections" 25 | 26 | [[services.ports]] 27 | force_https = true 28 | handlers = ["http"] 29 | port = 80 30 | 31 | [[services.ports]] 32 | handlers = ["tls", "http"] 33 | port = 443 34 | 35 | [[services.tcp_checks]] 36 | grace_period = "1s" 37 | interval = "15s" 38 | restart_limit = 0 39 | timeout = "2s" 40 | -------------------------------------------------------------------------------- /fly-go-crunchy-replicas/go.mod: -------------------------------------------------------------------------------- 1 | module server 2 | 3 | go 1.19 4 | 5 | require github.com/jackc/pgx/v4 v4.17.2 6 | 7 | require ( 8 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 9 | github.com/jackc/pgconn v1.13.0 // indirect 10 | github.com/jackc/pgio v1.0.0 // indirect 11 | github.com/jackc/pgpassfile v1.0.0 // indirect 12 | github.com/jackc/pgproto3/v2 v2.3.1 // indirect 13 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect 14 | github.com/jackc/pgtype v1.12.0 // indirect 15 | github.com/jackc/puddle v1.3.0 // indirect 16 | github.com/lib/pq v1.10.4 // indirect 17 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect 18 | golang.org/x/text v0.3.7 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /fly-go-crunchy-replicas/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Connect machine to Tailscale tailnet and auto-remove immediately when machine shuts down. 4 | # https://tailscale.com/blog/ephemeral-logout/ 5 | /app/tailscaled --state=/var/lib/tailscale/tailscaled.state --socket=/var/run/tailscale/tailscaled.sock & 6 | ID=$(echo "${FLY_ALLOC_ID}" | cut -d '-' -f 1) 7 | /app/tailscale up --authkey="${TAILSCALE_AUTHKEY}" --hostname="fly-${FLY_REGION}-${ID}" 8 | echo "Tailscale started" 9 | 10 | # Run server 11 | /app/server 12 | -------------------------------------------------------------------------------- /fly-go-neon/README.md: -------------------------------------------------------------------------------- 1 | # Fly Neon 2 | 3 | [Neon Postgres](https://console.neon.tech/). 4 | 5 | ## Setup 6 | 7 | Develop: 8 | 9 | ``` 10 | go run api.go 11 | ``` 12 | 13 | Set up Fly: 14 | 15 | ``` 16 | brew install flyctl 17 | fly auth login 18 | fly launch --remote-only 19 | ``` 20 | 21 | Set up Neon: 22 | 23 | - [Create a project](https://console.neon.tech/app/projects). 24 | - Copy the `psql` string value. 25 | - Append `sslmode=verify-full` 26 | 27 | Set the primary database URL connection string: 28 | 29 | ```bash 30 | fly secrets set DATABASE_URL="" 31 | ``` 32 | 33 | Deploy: 34 | 35 | ```bash 36 | fly deploy --remote-only 37 | ``` 38 | -------------------------------------------------------------------------------- /fly-go-neon/api.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | 10 | "github.com/jackc/pgx/v4/pgxpool" 11 | ) 12 | 13 | func main() { 14 | // env 15 | port, ok := os.LookupEnv("PORT") 16 | if !ok { 17 | port = "8080" 18 | } 19 | dbUrl, ok := os.LookupEnv("DATABASE_URL") 20 | if !ok { 21 | dbUrl = "postgres:///webstack_dev" 22 | } 23 | 24 | // db 25 | db, err := pgxpool.Connect(context.Background(), dbUrl) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | defer db.Close() 30 | 31 | // routes 32 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 33 | var col int 34 | db.QueryRow(r.Context(), "SELECT 1").Scan(&col) 35 | w.Header().Set("Content-Type", "application/json") 36 | fmt.Fprintf(w, "{\"status\":\"ok\"}") 37 | }) 38 | 39 | // listen 40 | log.Println("Listening at http://localhost:" + port) 41 | log.Fatal(http.ListenAndServe(":"+port, nil)) 42 | } 43 | -------------------------------------------------------------------------------- /fly-go-neon/fly.toml: -------------------------------------------------------------------------------- 1 | # fly.toml file generated for webstack-go-neon on 2023-04-08T20:01:59-04:00 2 | 3 | app = "webstack-go-neon" 4 | kill_signal = "SIGINT" 5 | kill_timeout = 5 6 | primary_region = "bos" 7 | processes = [] 8 | 9 | [build] 10 | builder = "paketobuildpacks/builder:base" 11 | buildpacks = ["gcr.io/paketo-buildpacks/go"] 12 | 13 | [env] 14 | PORT = "8080" 15 | 16 | [experimental] 17 | allowed_public_ports = [] 18 | auto_rollback = true 19 | 20 | [[services]] 21 | http_checks = [] 22 | internal_port = 8080 23 | processes = ["app"] 24 | protocol = "tcp" 25 | script_checks = [] 26 | [services.concurrency] 27 | hard_limit = 25 28 | soft_limit = 20 29 | type = "connections" 30 | 31 | [[services.ports]] 32 | force_https = true 33 | handlers = ["http"] 34 | port = 80 35 | 36 | [[services.ports]] 37 | handlers = ["tls", "http"] 38 | port = 443 39 | 40 | [[services.tcp_checks]] 41 | grace_period = "1s" 42 | interval = "15s" 43 | restart_limit = 0 44 | timeout = "2s" 45 | -------------------------------------------------------------------------------- /fly-go-neon/go.mod: -------------------------------------------------------------------------------- 1 | module server 2 | 3 | go 1.19 4 | 5 | require github.com/jackc/pgx/v4 v4.16.0 6 | 7 | require ( 8 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 9 | github.com/jackc/pgconn v1.12.0 // indirect 10 | github.com/jackc/pgio v1.0.0 // indirect 11 | github.com/jackc/pgpassfile v1.0.0 // indirect 12 | github.com/jackc/pgproto3/v2 v2.3.0 // indirect 13 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect 14 | github.com/jackc/pgtype v1.11.0 // indirect 15 | github.com/jackc/puddle v1.2.1 // indirect 16 | github.com/lib/pq v1.10.4 // indirect 17 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect 18 | golang.org/x/text v0.3.7 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /fly-go-neon/go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 2 | github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= 3 | github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= 4 | github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= 5 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 6 | github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 7 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 8 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 9 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 10 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 11 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 12 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 13 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 14 | github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= 15 | github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 16 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 17 | github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= 18 | github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 19 | github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= 20 | github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 21 | github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= 22 | github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= 23 | github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= 24 | github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= 25 | github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= 26 | github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= 27 | github.com/jackc/pgconn v1.12.0 h1:/RvQ24k3TnNdfBSW0ou9EOi5jx2cX7zfE8n2nLKuiP0= 28 | github.com/jackc/pgconn v1.12.0/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono= 29 | github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= 30 | github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= 31 | github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= 32 | github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= 33 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= 34 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= 35 | github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= 36 | github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= 37 | github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= 38 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= 39 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= 40 | github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 41 | github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 42 | github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 43 | github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 44 | github.com/jackc/pgproto3/v2 v2.3.0 h1:brH0pCGBDkBW07HWlN/oSBXrmo3WB0UvZd1pIuDcL8Y= 45 | github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 46 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= 47 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= 48 | github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= 49 | github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= 50 | github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= 51 | github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= 52 | github.com/jackc/pgtype v1.11.0 h1:u4uiGPz/1hryuXzyaBhSk6dnIyyG2683olG2OV+UUgs= 53 | github.com/jackc/pgtype v1.11.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= 54 | github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= 55 | github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= 56 | github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= 57 | github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= 58 | github.com/jackc/pgx/v4 v4.16.0 h1:4k1tROTJctHotannFYzu77dY3bgtMRymQP7tXQjqpPk= 59 | github.com/jackc/pgx/v4 v4.16.0/go.mod h1:N0A9sFdWzkw/Jy1lwoiB64F2+ugFZi987zRxcPez/wI= 60 | github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 61 | github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 62 | github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 63 | github.com/jackc/puddle v1.2.1 h1:gI8os0wpRXFd4FiAY2dWiqRK037tjj3t7rKFeO4X5iw= 64 | github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 65 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 66 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 67 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 68 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 69 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 70 | github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 71 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 72 | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 73 | github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 74 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 75 | github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 76 | github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= 77 | github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 78 | github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= 79 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 80 | github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 81 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 82 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 83 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 84 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 85 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 86 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 87 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 88 | github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= 89 | github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= 90 | github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= 91 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 92 | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= 93 | github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= 94 | github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 95 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 96 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 97 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 98 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 99 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 100 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 101 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 102 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 103 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 104 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 105 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 106 | github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= 107 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 108 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 109 | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 110 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 111 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 112 | go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 113 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 114 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 115 | go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 116 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 117 | go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 118 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 119 | golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= 120 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 121 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 122 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 123 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 124 | golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 125 | golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 126 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= 127 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 128 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 129 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 130 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 131 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 132 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 133 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 134 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 135 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 136 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 137 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 138 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 139 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 140 | golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 141 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 142 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 143 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 144 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 145 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 146 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 147 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 148 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 149 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 150 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 151 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 152 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 153 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 154 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 155 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 156 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 157 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 158 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 159 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 160 | golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 161 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 162 | golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 163 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 164 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 165 | golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 166 | golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 167 | golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 168 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 169 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 170 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 171 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 172 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 173 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 174 | gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= 175 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 176 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 177 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 178 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 179 | -------------------------------------------------------------------------------- /fly-go-sqlite/Dockerfile: -------------------------------------------------------------------------------- 1 | # Fetch the LiteFS binary using a multi-stage build. 2 | FROM flyio/litefs:pr-109 AS litefs 3 | 4 | # Build our application using a Go builder. 5 | FROM golang:1.19 AS builder 6 | WORKDIR /src/fly-go-sqlite 7 | COPY . . 8 | RUN go build -ldflags "-s -w -extldflags '-static'" -tags osusergo,netgo -o /usr/local/bin/fly-go-sqlite . 9 | 10 | # Our final Docker image stage starts here. 11 | FROM alpine 12 | 13 | # Copy binaries from the previous build stages. 14 | COPY --from=builder /usr/local/bin/fly-go-sqlite /usr/local/bin/fly-go-sqlite 15 | COPY --from=litefs /usr/local/bin/litefs /usr/local/bin/litefs 16 | 17 | # Copy our LiteFS configuration. 18 | ADD etc/litefs.yml /etc/litefs.yml 19 | 20 | # Setup our environment to include FUSE & SQLite. 21 | RUN apk add bash curl fuse sqlite 22 | 23 | # Ensure our mount & data directories exists before mounting with LiteFS. 24 | RUN mkdir -p /data /mnt/data 25 | 26 | # Run LiteFS as the entrypoint so it can execute "fly-go-sqlite" as a subprocess. 27 | ENTRYPOINT "litefs" 28 | -------------------------------------------------------------------------------- /fly-go-sqlite/api.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | _ "embed" 6 | "flag" 7 | "fmt" 8 | "log" 9 | "net/http" 10 | 11 | _ "github.com/mattn/go-sqlite3" 12 | ) 13 | 14 | func main() { 15 | // flag 16 | dsn := flag.String("dsn", "", "datasource name") 17 | flag.Parse() 18 | if *dsn == "" { 19 | log.Fatalf("dsn required") 20 | } 21 | 22 | // db 23 | db, err := sql.Open("sqlite3", *dsn) 24 | if err != nil { 25 | log.Fatalf("open db: %v", err) 26 | } 27 | defer db.Close() 28 | 29 | // route 30 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 31 | db.Query("SELECT 1") 32 | w.Header().Set("Content-Type", "application/json") 33 | fmt.Fprintf(w, "{\"status\":\"ok\"}") 34 | }) 35 | 36 | log.Println("http server listening on :8080") 37 | http.ListenAndServe(":8080", nil) 38 | } 39 | -------------------------------------------------------------------------------- /fly-go-sqlite/etc/litefs.yml: -------------------------------------------------------------------------------- 1 | # The path to where the SQLite database will be accessed. 2 | mount-dir: "/data" 3 | 4 | # The path to where the underlying volume mount is. 5 | data-dir: "/mnt/data" 6 | 7 | # Execute this subprocess once LiteFS connects to the cluster. 8 | exec: "fly-go-sqlite -dsn /data/db" 9 | 10 | # These environment variables will be available in your Fly.io application. 11 | # You must specify "experiement.enable_consul" for FLY_CONSUL_URL to be available. 12 | consul: 13 | url: "${FLY_CONSUL_URL}" 14 | advertise-url: "http://${HOSTNAME}.vm.${FLY_APP_NAME}.internal:20202" 15 | -------------------------------------------------------------------------------- /fly-go-sqlite/fly.toml: -------------------------------------------------------------------------------- 1 | # fly.toml file generated for webstack-go-sqlite on 2022-09-21T22:12:34-04:00 2 | 3 | app = "webstack-go-sqlite" 4 | 5 | [experimental] 6 | enable_consul = true 7 | 8 | [[services]] 9 | internal_port = 8080 10 | protocol = "tcp" 11 | 12 | [[services.ports]] 13 | force_https = true 14 | handlers = ["http"] 15 | port = 80 16 | 17 | [[services.ports]] 18 | handlers = ["tls", "http"] 19 | port = "443" 20 | -------------------------------------------------------------------------------- /fly-go-sqlite/go.mod: -------------------------------------------------------------------------------- 1 | module server 2 | 3 | go 1.19 4 | 5 | require github.com/mattn/go-sqlite3 v1.14.15 6 | -------------------------------------------------------------------------------- /fly-go-sqlite/go.sum: -------------------------------------------------------------------------------- 1 | github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= 2 | github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= 3 | -------------------------------------------------------------------------------- /fly-go-supabase/README.md: -------------------------------------------------------------------------------- 1 | # Fly Go w/ Supabase 2 | 3 | Develop: 4 | 5 | ``` 6 | go run api.go 7 | ``` 8 | 9 | Set up Fly: 10 | 11 | ``` 12 | brew install flyctl 13 | flyctl auth login 14 | rm -rf ~/.docker 15 | flyctl launch --remote-only 16 | ``` 17 | 18 | Choose Virginia for a region. 19 | Don't choose to add a Postgres database or deploy now. 20 | 21 | Set up Supabase: 22 | 23 | * Create a database at [Supabase](https://supabase.com/). 24 | * Go to "Settings" > "Database" > "Connection String" > "URI" > "Copy". 25 | 26 | Add connection string to Fly: 27 | 28 | ```bash 29 | fly secrets set DATABASE_URL="PASTE" 30 | ``` 31 | -------------------------------------------------------------------------------- /fly-go-supabase/api.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | 10 | "github.com/jackc/pgx/v4/pgxpool" 11 | ) 12 | 13 | func main() { 14 | // env 15 | port, ok := os.LookupEnv("PORT") 16 | if !ok { 17 | port = "8080" 18 | } 19 | dbUrl, ok := os.LookupEnv("DATABASE_URL") 20 | if !ok { 21 | dbUrl = "postgres:///webstack_dev" 22 | } 23 | 24 | // db 25 | db, err := pgxpool.Connect(context.Background(), dbUrl) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | defer db.Close() 30 | 31 | // routes 32 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 33 | var col int 34 | db.QueryRow(r.Context(), "SELECT 1").Scan(&col) 35 | w.Header().Set("Content-Type", "application/json") 36 | fmt.Fprintf(w, "{\"status\":\"ok\"}") 37 | }) 38 | 39 | // listen 40 | log.Println("Listening at http://localhost:" + port) 41 | log.Fatal(http.ListenAndServe(":"+port, nil)) 42 | } 43 | -------------------------------------------------------------------------------- /fly-go-supabase/fly.toml: -------------------------------------------------------------------------------- 1 | # fly.toml file generated for webstack-go-supabase on 2022-10-10T21:16:37-04:00 2 | 3 | app = "webstack-go-supabase" 4 | kill_signal = "SIGINT" 5 | kill_timeout = 5 6 | processes = [] 7 | 8 | [build] 9 | builder = "paketobuildpacks/builder:base" 10 | buildpacks = ["gcr.io/paketo-buildpacks/go"] 11 | 12 | [env] 13 | PORT = "8080" 14 | 15 | [experimental] 16 | allowed_public_ports = [] 17 | auto_rollback = true 18 | 19 | [[services]] 20 | http_checks = [] 21 | internal_port = 8080 22 | processes = ["app"] 23 | protocol = "tcp" 24 | script_checks = [] 25 | [services.concurrency] 26 | hard_limit = 25 27 | soft_limit = 20 28 | type = "connections" 29 | 30 | [[services.ports]] 31 | force_https = true 32 | handlers = ["http"] 33 | port = 80 34 | 35 | [[services.ports]] 36 | handlers = ["tls", "http"] 37 | port = 443 38 | 39 | [[services.tcp_checks]] 40 | grace_period = "1s" 41 | interval = "15s" 42 | restart_limit = 0 43 | timeout = "2s" 44 | -------------------------------------------------------------------------------- /fly-go-supabase/go.mod: -------------------------------------------------------------------------------- 1 | module server 2 | 3 | go 1.19 4 | 5 | require github.com/jackc/pgx/v4 v4.17.2 6 | 7 | require ( 8 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 9 | github.com/jackc/pgconn v1.13.0 // indirect 10 | github.com/jackc/pgio v1.0.0 // indirect 11 | github.com/jackc/pgpassfile v1.0.0 // indirect 12 | github.com/jackc/pgproto3/v2 v2.3.1 // indirect 13 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect 14 | github.com/jackc/pgtype v1.12.0 // indirect 15 | github.com/jackc/puddle v1.3.0 // indirect 16 | github.com/lib/pq v1.10.4 // indirect 17 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect 18 | golang.org/x/text v0.3.7 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /fly-go/README.md: -------------------------------------------------------------------------------- 1 | # Fly Go 2 | 3 | Develop: 4 | 5 | ``` 6 | go run api.go 7 | ``` 8 | 9 | Set up Fly: 10 | 11 | ``` 12 | brew install flyctl 13 | flyctl auth login 14 | rm -rf ~/.docker 15 | flyctl launch --remote-only 16 | ``` 17 | 18 | I prefer to not install Docker on my local machine. Fly.io has a cool 19 | `--remote-only` option that will use a remote Docker builder that they set up in 20 | your account. One "gotcha" is a leftover `~/.docker` directory can cause these 21 | errors when using Fly: 22 | 23 | ``` 24 | Error failed to fetch builder image 'index.docker.io/heroku/buildpacks:20': resolve auth for ref index.docker.io/heroku/buildpacks:20: error getting credentials - err: exec: "docker-credential-desktop": executable file not found in $PATH, out: `` 25 | ``` 26 | -------------------------------------------------------------------------------- /fly-go/api.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | 10 | "github.com/jackc/pgx/v4/pgxpool" 11 | ) 12 | 13 | func main() { 14 | // env 15 | port, ok := os.LookupEnv("PORT") 16 | if !ok { 17 | port = "8080" 18 | } 19 | dbUrl, ok := os.LookupEnv("DATABASE_URL") 20 | if !ok { 21 | dbUrl = "postgres:///webstack_dev" 22 | } 23 | 24 | // db 25 | db, err := pgxpool.Connect(context.Background(), dbUrl) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | defer db.Close() 30 | 31 | // routes 32 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 33 | var col int 34 | db.QueryRow(r.Context(), "SELECT 1").Scan(&col) 35 | w.Header().Set("Content-Type", "application/json") 36 | fmt.Fprintf(w, "{\"status\":\"ok\"}") 37 | }) 38 | 39 | // listen 40 | log.Println("Listening at http://localhost:" + port) 41 | log.Fatal(http.ListenAndServe(":"+port, nil)) 42 | } 43 | -------------------------------------------------------------------------------- /fly-go/fly.toml: -------------------------------------------------------------------------------- 1 | # fly.toml file generated for webstack-go on 2022-04-06T14:37:01-07:00 2 | 3 | app = "webstack-go" 4 | 5 | kill_signal = "SIGINT" 6 | kill_timeout = 5 7 | processes = [] 8 | 9 | [env] 10 | PORT = "8080" 11 | 12 | [experimental] 13 | allowed_public_ports = [] 14 | auto_rollback = true 15 | 16 | [[services]] 17 | http_checks = [] 18 | internal_port = 8080 19 | processes = ["app"] 20 | protocol = "tcp" 21 | script_checks = [] 22 | 23 | [services.concurrency] 24 | hard_limit = 25 25 | soft_limit = 20 26 | type = "connections" 27 | 28 | [[services.ports]] 29 | force_https = true 30 | handlers = ["http"] 31 | port = 80 32 | 33 | [[services.ports]] 34 | handlers = ["tls", "http"] 35 | port = 443 36 | 37 | [[services.tcp_checks]] 38 | grace_period = "1s" 39 | interval = "15s" 40 | restart_limit = 0 41 | timeout = "2s" 42 | -------------------------------------------------------------------------------- /fly-go/go.mod: -------------------------------------------------------------------------------- 1 | module server 2 | 3 | go 1.19 4 | 5 | require github.com/jackc/pgx/v4 v4.17.2 6 | 7 | require ( 8 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 9 | github.com/jackc/pgconn v1.13.0 // indirect 10 | github.com/jackc/pgio v1.0.0 // indirect 11 | github.com/jackc/pgpassfile v1.0.0 // indirect 12 | github.com/jackc/pgproto3/v2 v2.3.1 // indirect 13 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect 14 | github.com/jackc/pgtype v1.12.0 // indirect 15 | github.com/jackc/puddle v1.3.0 // indirect 16 | github.com/lib/pq v1.10.4 // indirect 17 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect 18 | golang.org/x/text v0.3.7 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /fly-planetscale/README.md: -------------------------------------------------------------------------------- 1 | # Fly Go PlanetScale 2 | 3 | Develop: 4 | 5 | ``` 6 | go run api.go 7 | ``` 8 | 9 | Set up Fly: 10 | 11 | ``` 12 | brew install flyctl 13 | fly auth login 14 | rm -rf ~/.docker 15 | fly launch --remote-only 16 | ``` 17 | -------------------------------------------------------------------------------- /fly-planetscale/api.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | 10 | _ "github.com/go-sql-driver/mysql" 11 | ) 12 | 13 | func main() { 14 | // env 15 | port, ok := os.LookupEnv("PORT") 16 | if !ok { 17 | port = "8080" 18 | } 19 | dbUrl, ok := os.LookupEnv("DATABASE_URL") 20 | if !ok { 21 | dbUrl = "postgres:///webstack_dev" 22 | } 23 | 24 | // db 25 | db, err := sql.Open("mysql", dbUrl) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | defer db.Close() 30 | 31 | // routes 32 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 33 | var col int 34 | db.QueryRow("SELECT 1").Scan(&col) 35 | w.Header().Set("Content-Type", "application/json") 36 | fmt.Fprintf(w, "{\"status\":\"ok\"}") 37 | }) 38 | 39 | // listen 40 | log.Println("Listening at http://localhost:" + port) 41 | log.Fatal(http.ListenAndServe(":"+port, nil)) 42 | } 43 | -------------------------------------------------------------------------------- /fly-planetscale/fly.toml: -------------------------------------------------------------------------------- 1 | # fly.toml file generated for webstack-go-planetscale on 2022-10-15T17:49:23-04:00 2 | 3 | app = "webstack-go-planetscale" 4 | kill_signal = "SIGINT" 5 | kill_timeout = 5 6 | processes = [] 7 | 8 | [build] 9 | builder = "paketobuildpacks/builder:base" 10 | buildpacks = ["gcr.io/paketo-buildpacks/go"] 11 | 12 | [env] 13 | PORT = "8080" 14 | 15 | [experimental] 16 | allowed_public_ports = [] 17 | auto_rollback = true 18 | 19 | [[services]] 20 | http_checks = [] 21 | internal_port = 8080 22 | processes = ["app"] 23 | protocol = "tcp" 24 | script_checks = [] 25 | [services.concurrency] 26 | hard_limit = 25 27 | soft_limit = 20 28 | type = "connections" 29 | 30 | [[services.ports]] 31 | force_https = true 32 | handlers = ["http"] 33 | port = 80 34 | 35 | [[services.ports]] 36 | handlers = ["tls", "http"] 37 | port = 443 38 | 39 | [[services.tcp_checks]] 40 | grace_period = "1s" 41 | interval = "15s" 42 | restart_limit = 0 43 | timeout = "2s" 44 | -------------------------------------------------------------------------------- /fly-planetscale/go.mod: -------------------------------------------------------------------------------- 1 | module server 2 | 3 | go 1.19 4 | 5 | require github.com/go-sql-driver/mysql v1.6.0 6 | -------------------------------------------------------------------------------- /fly-planetscale/go.sum: -------------------------------------------------------------------------------- 1 | github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= 2 | github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 3 | -------------------------------------------------------------------------------- /fly-read-replicas/README.md: -------------------------------------------------------------------------------- 1 | # Fly Read Replicas 2 | 3 | Develop: 4 | 5 | ```bash 6 | bundle 7 | createdb webstack_dev 8 | DATABASE_URL="postgres:///webstack_dev" bundle exec ruby api.rb 9 | ``` 10 | 11 | Set up Fly: 12 | 13 | ```bash 14 | brew install flyctl 15 | flyctl auth login 16 | ``` 17 | 18 | I prefer to not install Docker on my local machine. 19 | One "gotcha" is a leftover `~/.docker` directory can cause errors on Fly. 20 | Remove it: 21 | 22 | ```bash 23 | rm -rf ~/.docker 24 | ``` 25 | 26 | Now we can use Fly.io's `--remote-only` option. 27 | It will use a remote Docker builder that they set up in our account: 28 | 29 | ```bash 30 | flyctl launch --remote-only 31 | ``` 32 | 33 | Configure primary region where the primary database is: 34 | 35 | ```bash 36 | flyctl secrets set PRIMARY_REGION=sjc 37 | ``` 38 | 39 | Create the regions where the read replicas will go: 40 | 41 | ```bash 42 | fly regions add ams lhr syd yul 43 | ``` 44 | 45 | Create [read replicas](https://fly.io/docs/getting-started/multi-region-databases/#connect-to-regional-replicas) 46 | in those regions: 47 | 48 | ```bash 49 | fly volumes create pg_data -a webstack-read-replicas-db --size 1 --region ams 50 | fly volumes create pg_data -a webstack-read-replicas-db --size 1 --region lhr 51 | fly volumes create pg_data -a webstack-read-replicas-db --size 1 --region syd 52 | fly volumes create pg_data -a webstack-read-replicas-db --size 1 --region yul 53 | ``` 54 | 55 | Scale 1 instance per region: 56 | 57 | ```bash 58 | fly autoscale standard min=5 max=5 59 | ``` 60 | -------------------------------------------------------------------------------- /fly-read-replicas/api.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net" 8 | "net/http" 9 | "net/url" 10 | "os" 11 | 12 | "github.com/jackc/pgx/v4/pgxpool" 13 | ) 14 | 15 | func main() { 16 | // env 17 | port, ok := os.LookupEnv("PORT") 18 | if !ok { 19 | port = "8080" 20 | } 21 | dbUrl, ok := os.LookupEnv("DATABASE_URL") 22 | if !ok { 23 | dbUrl = "postgres:///webstack_dev" 24 | } 25 | primary := os.Getenv("PRIMARY_REGION") 26 | current := os.Getenv("FLY_REGION") 27 | 28 | // if not in primary region... 29 | if primary != "" && current != "" && primary != current { 30 | u, err := url.Parse(dbUrl) 31 | if err != nil { 32 | log.Fatalf(`could not parse %s`, dbUrl) 33 | } 34 | 35 | host, _, err := net.SplitHostPort(u.Host) 36 | if err != nil { 37 | log.Fatalf(`could not split host port %s`, u.Host) 38 | } 39 | u.Host = host + ":5433" 40 | dbUrl = u.String() 41 | } 42 | 43 | // db 44 | db, err := pgxpool.Connect(context.Background(), dbUrl) 45 | if err != nil { 46 | log.Fatal(err) 47 | } 48 | defer db.Close() 49 | 50 | // routes 51 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 52 | var col int 53 | db.QueryRow(r.Context(), "SELECT 1").Scan(&col) 54 | w.Header().Set("Content-Type", "application/json") 55 | fmt.Fprintf(w, "{\"status\":\"ok\"}") 56 | }) 57 | 58 | // listen 59 | log.Println("Listening at http://localhost:" + port) 60 | log.Fatal(http.ListenAndServe(":"+port, nil)) 61 | } 62 | -------------------------------------------------------------------------------- /fly-read-replicas/fly.toml: -------------------------------------------------------------------------------- 1 | # fly.toml file generated for webstack-read-replicas on 2022-09-12T14:26:12-04:00 2 | 3 | app = "webstack-read-replicas" 4 | kill_signal = "SIGINT" 5 | kill_timeout = 5 6 | processes = [] 7 | 8 | [env] 9 | PORT = "8080" 10 | 11 | [experimental] 12 | allowed_public_ports = [] 13 | auto_rollback = true 14 | 15 | [[services]] 16 | http_checks = [] 17 | internal_port = 8080 18 | processes = ["app"] 19 | protocol = "tcp" 20 | script_checks = [] 21 | [services.concurrency] 22 | hard_limit = 25 23 | soft_limit = 20 24 | type = "connections" 25 | 26 | [[services.ports]] 27 | force_https = true 28 | handlers = ["http"] 29 | port = 80 30 | 31 | [[services.ports]] 32 | handlers = ["tls", "http"] 33 | port = 443 34 | 35 | [[services.tcp_checks]] 36 | grace_period = "1s" 37 | interval = "15s" 38 | restart_limit = 0 39 | timeout = "2s" 40 | -------------------------------------------------------------------------------- /fly-read-replicas/go.mod: -------------------------------------------------------------------------------- 1 | module server 2 | 3 | go 1.19 4 | 5 | require github.com/jackc/pgx/v4 v4.17.2 6 | 7 | require ( 8 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 9 | github.com/jackc/pgconn v1.13.0 // indirect 10 | github.com/jackc/pgio v1.0.0 // indirect 11 | github.com/jackc/pgpassfile v1.0.0 // indirect 12 | github.com/jackc/pgproto3/v2 v2.3.1 // indirect 13 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect 14 | github.com/jackc/pgtype v1.12.0 // indirect 15 | github.com/jackc/puddle v1.3.0 // indirect 16 | github.com/lib/pq v1.10.4 // indirect 17 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect 18 | golang.org/x/text v0.3.7 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /fly-read-replicas/url_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net" 5 | "net/url" 6 | "testing" 7 | ) 8 | 9 | func TestPortReplacement(t *testing.T) { 10 | dbUrl := "postgres://user:pass@subdomain.host.com:5432/db_name" 11 | u, err := url.Parse(dbUrl) 12 | if err != nil { 13 | t.Fatalf(`could not parse %s`, dbUrl) 14 | } 15 | 16 | host, _, err := net.SplitHostPort(u.Host) 17 | if err != nil { 18 | t.Fatalf(`could not split host port %s`, u.Host) 19 | } 20 | u.Host = host + ":5433" 21 | got := u.String() 22 | 23 | want := "postgres://user:pass@subdomain.host.com:5433/db_name" 24 | 25 | if got != want { 26 | t.Fatalf(`got %s, want %s`, got, want) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /fly-ruby/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | ruby "~> 3" 4 | 5 | gem "connection_pool" 6 | gem "pg" 7 | gem "puma" 8 | gem "sinatra" 9 | -------------------------------------------------------------------------------- /fly-ruby/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | connection_pool (2.2.5) 5 | mustermann (3.0.0) 6 | ruby2_keywords (~> 0.0.1) 7 | nio4r (2.5.8) 8 | pg (1.3.5) 9 | puma (5.6.4) 10 | nio4r (~> 2.0) 11 | rack (2.2.4) 12 | rack-protection (3.0.4) 13 | rack 14 | ruby2_keywords (0.0.5) 15 | sinatra (3.0.4) 16 | mustermann (~> 3.0) 17 | rack (~> 2.2, >= 2.2.4) 18 | rack-protection (= 3.0.4) 19 | tilt (~> 2.0) 20 | tilt (2.0.11) 21 | 22 | PLATFORMS 23 | arm64-darwin-20 24 | ruby 25 | x86_64-linux 26 | 27 | DEPENDENCIES 28 | connection_pool 29 | pg 30 | puma 31 | sinatra 32 | 33 | RUBY VERSION 34 | ruby 3.0.3p157 35 | 36 | BUNDLED WITH 37 | 2.3.7 38 | -------------------------------------------------------------------------------- /fly-ruby/Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec ruby api.rb 2 | -------------------------------------------------------------------------------- /fly-ruby/README.md: -------------------------------------------------------------------------------- 1 | # Fly Ruby 2 | 3 | Develop: 4 | 5 | ``` 6 | bundle 7 | createdb webstack_dev 8 | DATABASE_URL="postgres:///webstack_dev" bundle exec ruby api.rb 9 | ``` 10 | 11 | Set up Fly: 12 | 13 | ``` 14 | brew install flyctl 15 | flyctl auth login 16 | rm -rf ~/.docker 17 | flyctl launch --remote-only 18 | ``` 19 | 20 | I prefer to not install Docker on my local machine. Fly.io has a cool 21 | `--remote-only` option that will use a remote Docker builder that they set up in 22 | your account. One "gotcha" is a leftover `~/.docker` directory can cause these 23 | errors when using Fly: 24 | 25 | ``` 26 | Error failed to fetch builder image 'index.docker.io/heroku/buildpacks:20': resolve auth for ref index.docker.io/heroku/buildpacks:20: error getting credentials - err: exec: "docker-credential-desktop": executable file not found in $PATH, out: `` 27 | ``` 28 | -------------------------------------------------------------------------------- /fly-ruby/api.rb: -------------------------------------------------------------------------------- 1 | require "connection_pool" 2 | require "pg" 3 | require "sinatra" 4 | 5 | class DB 6 | def initialize 7 | connect 8 | end 9 | 10 | def exec(sql) 11 | do_exec(sql) 12 | rescue PG::ConnectionBad 13 | connect 14 | do_exec(sql) 15 | end 16 | 17 | private 18 | 19 | def connect 20 | @pool = ConnectionPool.new(size: 5, timeout: 5) { 21 | PG.connect(ENV.fetch("DATABASE_URL")) 22 | } 23 | end 24 | 25 | def do_exec(sql) 26 | @pool.with do |conn| 27 | conn.exec(sql) 28 | end 29 | end 30 | end 31 | 32 | db = DB.new 33 | 34 | get "/" do 35 | db.exec "SELECT 1" 36 | content_type :json 37 | {status: "ok"}.to_json 38 | end 39 | -------------------------------------------------------------------------------- /fly-ruby/fly.toml: -------------------------------------------------------------------------------- 1 | # fly.toml file generated for webstack-ruby-sinatra on 2022-04-06T18:22:35-07:00 2 | 3 | app = "webstack-ruby-sinatra" 4 | 5 | kill_signal = "SIGINT" 6 | kill_timeout = 5 7 | processes = [] 8 | 9 | [build] 10 | builder = "heroku/buildpacks:20" 11 | 12 | [env] 13 | PORT = "8080" 14 | 15 | [experimental] 16 | allowed_public_ports = [] 17 | auto_rollback = true 18 | 19 | [[services]] 20 | http_checks = [] 21 | internal_port = 8080 22 | processes = ["app"] 23 | protocol = "tcp" 24 | script_checks = [] 25 | 26 | [services.concurrency] 27 | hard_limit = 25 28 | soft_limit = 20 29 | type = "connections" 30 | 31 | [[services.ports]] 32 | force_https = true 33 | handlers = ["http"] 34 | port = 80 35 | 36 | [[services.ports]] 37 | handlers = ["tls", "http"] 38 | port = 443 39 | 40 | [[services.tcp_checks]] 41 | grace_period = "1s" 42 | interval = "15s" 43 | restart_limit = 0 44 | timeout = "2s" 45 | -------------------------------------------------------------------------------- /heroku-crystal/.gitignore: -------------------------------------------------------------------------------- 1 | lib 2 | -------------------------------------------------------------------------------- /heroku-crystal/README.md: -------------------------------------------------------------------------------- 1 | # Heroku Crystal 2 | 3 | Setup: 4 | 5 | ```bash 6 | shards install 7 | createdb webstack_dev 8 | DATABASE_URL="postgres:///webstack_dev" crystal run src/api.cr 9 | ``` 10 | 11 | Go to . 12 | Click "New" > "Create new app". 13 | Give it a name and submit. 14 | Click "GitHub" and select this repo, then "Connect". 15 | Click "Enable Automatic Deploys" 16 | 17 | ```bash 18 | heroku login 19 | heroku git:remote -a 20 | git remote rename heroku heroku-crystal 21 | heroku buildpacks:add -a https://github.com/lstoll/heroku-buildpack-monorepo 22 | heroku buildpacks:add -a https://github.com/crystal-lang/heroku-buildpack-crystal.git 23 | heroku config:set APP_BASE=heroku-crystal -a 24 | heroku addons:create heroku-postgresql:hobby-dev -a 25 | ``` 26 | -------------------------------------------------------------------------------- /heroku-crystal/shard.lock: -------------------------------------------------------------------------------- 1 | version: 2.0 2 | shards: 3 | backtracer: 4 | git: https://github.com/sija/backtracer.cr.git 5 | version: 1.2.2 6 | 7 | db: 8 | git: https://github.com/crystal-lang/crystal-db.git 9 | version: 0.11.0 10 | 11 | exception_page: 12 | git: https://github.com/crystal-loot/exception_page.git 13 | version: 0.3.0 14 | 15 | kemal: 16 | git: https://github.com/kemalcr/kemal.git 17 | version: 1.3.0 18 | 19 | pg: 20 | git: https://github.com/will/crystal-pg.git 21 | version: 0.26.0 22 | 23 | radix: 24 | git: https://github.com/luislavena/radix.git 25 | version: 0.4.1 26 | 27 | -------------------------------------------------------------------------------- /heroku-crystal/shard.yml: -------------------------------------------------------------------------------- 1 | name: api 2 | version: 0.0.1 3 | crystal: 1.4.0 4 | 5 | dependencies: 6 | kemal: 7 | github: kemalcr/kemal 8 | pg: 9 | github: will/crystal-pg 10 | 11 | targets: 12 | api: 13 | main: "src/api.cr" 14 | -------------------------------------------------------------------------------- /heroku-crystal/src/api.cr: -------------------------------------------------------------------------------- 1 | require "db" 2 | require "kemal" 3 | require "json" 4 | require "pg" 5 | 6 | DB.open(ENV.fetch("DATABASE_URL", "postgres:///webstack_dev")) do |db| 7 | get "/" do |env| 8 | db.exec "SELECT 1" 9 | env.response.content_type = "application/json" 10 | {"status": "ok"}.to_json 11 | end 12 | 13 | Kemal.run 14 | end 15 | -------------------------------------------------------------------------------- /heroku-go-crunchy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.20-alpine as builder 2 | WORKDIR /app 3 | COPY . ./ 4 | RUN go build -o /app/server 5 | 6 | FROM alpine:latest as tailscale 7 | WORKDIR /app 8 | ENV TSFILE=tailscale_1.42.0_amd64.tgz 9 | RUN wget https://pkgs.tailscale.com/stable/${TSFILE} && tar xzf ${TSFILE} --strip-components=1 10 | 11 | FROM alpine:latest 12 | RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/* 13 | 14 | # Copy binaries to production image 15 | COPY --from=builder /app/start.sh /app/start.sh 16 | COPY --from=builder /app/server /app/server 17 | COPY --from=tailscale /app/tailscaled /app/tailscaled 18 | COPY --from=tailscale /app/tailscale /app/tailscale 19 | RUN mkdir -p /var/run/tailscale /var/cache/tailscale /var/lib/tailscale 20 | 21 | # Run on container startup 22 | CMD ["/app/start.sh"] 23 | -------------------------------------------------------------------------------- /heroku-go-crunchy/README.md: -------------------------------------------------------------------------------- 1 | # Heroku Crunchy 2 | 3 | This stack involves: 4 | 5 | - a web application written in Go 6 | - deployed to Heroku 7 | - connecting to a Postgres database via PgBouncer on Crunchy Bridge 8 | - networked with Tailscale 9 | 10 | Develop: 11 | 12 | ```bash 13 | createddb webstack_dev 14 | DATABASE_URL="postgres:///webstack_dev" go run api.go 15 | ``` 16 | 17 | Set up Crunchy Bridge: 18 | 19 | - Go to [Crunchy Bridge](https://crunchybridge.com/). 20 | - Create a cluster in AWS `us-east-1` (Virginia). 21 | - Click "Connection" tab. 22 | - Select "Role: postgres" and "Format: psql". 23 | - Click "Copy", then paste into a shell: 24 | 25 | ```bash 26 | psql postgres://postgres:password@host.db.postgresbridge.com:5432/postgres 27 | ``` 28 | 29 | In the Postgres shell as the `postgres` superuser, 30 | [enable PgBouncer](https://docs.crunchybridge.com/how-to/pgbouncer/): 31 | 32 | ```sql 33 | CREATE EXTENSION crunchy_pooler; 34 | ``` 35 | 36 | Create a reusable, ephemeral 37 | [Tailscale auth key](https://login.tailscale.com/admin/settings/keys). 38 | 39 | In the Crunchy Bridge web UI: 40 | 41 | - Click "Networking". 42 | - Delete the two "fully open firewall" rules `::/0` and `0.0.0.0/0`. 43 | - Click "Tailscale". 44 | - Paste the Tailscale auth key. 45 | - Click "Connect Tailscale". 46 | 47 | In the Tailscale web UI: 48 | 49 | - Go to the [Machines](https://login.tailscale.com/admin/machines) page. 50 | - Click the three dots next to the newly-connected Crunchy database. 51 | - Click "Edit machine name..". 52 | - Rename the machine something like `crunchy-virginia`. 53 | - Click the machine name. 54 | - Copy the domain name into a temporary text file. 55 | It will look something like `crunchy-virginia.taile1234.ts.net`. 56 | 57 | In the Crunchy Bridge web UI: 58 | 59 | - Click "Connection" 60 | - Select "Role: application". 61 | - Copy the connection string and paste into the temporary text file. 62 | - Replace the Crunchy Bridge host (e.g. `p.abc123.db.postgresbridge.com`) 63 | with the Tailscale domain name copied above. 64 | - Replace port `5432` with `5431` so the connection will use PgBouncer. 65 | - Copy the edited connection string, which will look something like: 66 | 67 | ``` 68 | postgres://application:password@crunchy-n-california.taile1234.ts.net:5431/postgres 69 | ``` 70 | 71 | Go to : 72 | 73 | - Click "New" > "Create new app". 74 | - Give it a name and submit. 75 | - Click "GitHub" and select this repo, then "Connect". 76 | - Click "Enable Automatic Deploys" 77 | 78 | ```bash 79 | heroku login 80 | heroku git:remote -a 81 | git remote rename heroku heroku-go-crunchy 82 | heroku config:set TAILSCALE_AUTHKEY= -a 83 | heroku labs:enable runtime-dyno-metadata -a 84 | heroku stack:set container -a 85 | ``` 86 | 87 | Commit to the repo to deploy. 88 | -------------------------------------------------------------------------------- /heroku-go-crunchy/go.mod: -------------------------------------------------------------------------------- 1 | // +heroku goVersion 1.20 2 | 3 | module server 4 | 5 | go 1.20 6 | 7 | require github.com/jackc/pgx/v4 v4.17.2 8 | 9 | require ( 10 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 11 | github.com/jackc/pgconn v1.13.0 // indirect 12 | github.com/jackc/pgio v1.0.0 // indirect 13 | github.com/jackc/pgpassfile v1.0.0 // indirect 14 | github.com/jackc/pgproto3/v2 v2.3.1 // indirect 15 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect 16 | github.com/jackc/pgtype v1.12.0 // indirect 17 | github.com/jackc/puddle v1.3.0 // indirect 18 | github.com/lib/pq v1.10.4 // indirect 19 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect 20 | golang.org/x/text v0.3.7 // indirect 21 | ) 22 | -------------------------------------------------------------------------------- /heroku-go-crunchy/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | 10 | "github.com/jackc/pgx/v4/pgxpool" 11 | ) 12 | 13 | func main() { 14 | // env 15 | port, ok := os.LookupEnv("PORT") 16 | if !ok { 17 | port = "8080" 18 | } 19 | dbUrl, ok := os.LookupEnv("DATABASE_URL") 20 | if !ok { 21 | dbUrl = "postgres:///webstack_dev" 22 | } 23 | 24 | // db 25 | db, err := pgxpool.Connect(context.Background(), dbUrl) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | defer db.Close() 30 | 31 | // routes 32 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 33 | var col int 34 | db.QueryRow(r.Context(), "SELECT 1").Scan(&col) 35 | w.Header().Set("Content-Type", "application/json") 36 | fmt.Fprintf(w, "{\"status\":\"ok\"}") 37 | }) 38 | 39 | // listen 40 | log.Println("Listening at http://localhost:" + port) 41 | log.Fatal(http.ListenAndServe(":"+port, nil)) 42 | } 43 | -------------------------------------------------------------------------------- /heroku-go-crunchy/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Connect machine to Tailscale tailnet and auto-remove immediately when machine shuts down. 4 | # https://tailscale.com/blog/ephemeral-logout/ 5 | /app/tailscaled --tun=userspace-networking --socks5-server=localhost:1055 & 6 | ID=$(echo "${HEROKU_DYNO_ID}" | cut -d '-' -f 1) 7 | /app/tailscale up --authkey="${TAILSCALE_AUTHKEY}" --hostname="heroku-${ID}" 8 | echo "Tailscale started" 9 | 10 | # Run server 11 | ALL_PROXY=socks5://localhost:1055/ /app/server 12 | -------------------------------------------------------------------------------- /heroku-go/Procfile: -------------------------------------------------------------------------------- 1 | web: bin/server 2 | -------------------------------------------------------------------------------- /heroku-go/README.md: -------------------------------------------------------------------------------- 1 | # Heroku Go 2 | 3 | Setup: 4 | 5 | ```bash 6 | go run api.go 7 | ``` 8 | 9 | Go to : 10 | 11 | * Click "New" > "Create new app". 12 | * Give it a name and submit. 13 | * Click "GitHub" and select this repo, then "Connect". 14 | * Click "Enable Automatic Deploys" 15 | 16 | ```bash 17 | heroku login 18 | heroku buildpacks:add -a https://github.com/lstoll/heroku-buildpack-monorepo 19 | heroku buildpacks:add -a heroku/go 20 | heroku config:set APP_BASE=heroku-go -a 21 | heroku addons:create heroku-postgresql -a 22 | ``` 23 | 24 | Editorial: 25 | 26 | * I always forget to put the magic comment `// +heroku goVersion 1.18` 27 | at the top of `go.mod`. Why isn't `go 1.18` later in the file enough? 28 | * From `git push` to build finishing and process restarting is ~10s. 29 | * Databases are backed up continuously. 30 | * HA databases available. 31 | -------------------------------------------------------------------------------- /heroku-go/api.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | 10 | "github.com/jackc/pgx/v4/pgxpool" 11 | ) 12 | 13 | func main() { 14 | // env 15 | port, ok := os.LookupEnv("PORT") 16 | if !ok { 17 | port = "8080" 18 | } 19 | dbUrl, ok := os.LookupEnv("DATABASE_URL") 20 | if !ok { 21 | dbUrl = "postgres:///webstack_dev" 22 | } 23 | 24 | // db 25 | db, err := pgxpool.Connect(context.Background(), dbUrl) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | defer db.Close() 30 | 31 | // routes 32 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 33 | var col int 34 | db.QueryRow(r.Context(), "SELECT 1").Scan(&col) 35 | w.Header().Set("Content-Type", "application/json") 36 | fmt.Fprintf(w, "{\"status\":\"ok\"}") 37 | }) 38 | 39 | // listen 40 | log.Println("Listening at http://localhost:" + port) 41 | log.Fatal(http.ListenAndServe(":"+port, nil)) 42 | } 43 | -------------------------------------------------------------------------------- /heroku-go/go.mod: -------------------------------------------------------------------------------- 1 | // +heroku goVersion 1.19 2 | 3 | module server 4 | 5 | go 1.19 6 | 7 | require github.com/jackc/pgx/v4 v4.16.0 8 | 9 | require ( 10 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 11 | github.com/jackc/pgconn v1.12.0 // indirect 12 | github.com/jackc/pgio v1.0.0 // indirect 13 | github.com/jackc/pgpassfile v1.0.0 // indirect 14 | github.com/jackc/pgproto3/v2 v2.3.0 // indirect 15 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect 16 | github.com/jackc/pgtype v1.11.0 // indirect 17 | github.com/jackc/puddle v1.2.1 // indirect 18 | github.com/lib/pq v1.10.4 // indirect 19 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect 20 | golang.org/x/text v0.3.7 // indirect 21 | ) 22 | -------------------------------------------------------------------------------- /heroku-go/go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 2 | github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= 3 | github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= 4 | github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= 5 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 6 | github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 7 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 8 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 9 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 10 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 11 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 12 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 13 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 14 | github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= 15 | github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 16 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 17 | github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= 18 | github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 19 | github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= 20 | github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 21 | github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= 22 | github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= 23 | github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= 24 | github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= 25 | github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= 26 | github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= 27 | github.com/jackc/pgconn v1.12.0 h1:/RvQ24k3TnNdfBSW0ou9EOi5jx2cX7zfE8n2nLKuiP0= 28 | github.com/jackc/pgconn v1.12.0/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono= 29 | github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= 30 | github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= 31 | github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= 32 | github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= 33 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= 34 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= 35 | github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= 36 | github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= 37 | github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= 38 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= 39 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= 40 | github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 41 | github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 42 | github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 43 | github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 44 | github.com/jackc/pgproto3/v2 v2.3.0 h1:brH0pCGBDkBW07HWlN/oSBXrmo3WB0UvZd1pIuDcL8Y= 45 | github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 46 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= 47 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= 48 | github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= 49 | github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= 50 | github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= 51 | github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= 52 | github.com/jackc/pgtype v1.11.0 h1:u4uiGPz/1hryuXzyaBhSk6dnIyyG2683olG2OV+UUgs= 53 | github.com/jackc/pgtype v1.11.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= 54 | github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= 55 | github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= 56 | github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= 57 | github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= 58 | github.com/jackc/pgx/v4 v4.16.0 h1:4k1tROTJctHotannFYzu77dY3bgtMRymQP7tXQjqpPk= 59 | github.com/jackc/pgx/v4 v4.16.0/go.mod h1:N0A9sFdWzkw/Jy1lwoiB64F2+ugFZi987zRxcPez/wI= 60 | github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 61 | github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 62 | github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 63 | github.com/jackc/puddle v1.2.1 h1:gI8os0wpRXFd4FiAY2dWiqRK037tjj3t7rKFeO4X5iw= 64 | github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 65 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 66 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 67 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 68 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 69 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 70 | github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 71 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 72 | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 73 | github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 74 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 75 | github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 76 | github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= 77 | github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 78 | github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= 79 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 80 | github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 81 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 82 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 83 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 84 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 85 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 86 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 87 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 88 | github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= 89 | github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= 90 | github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= 91 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 92 | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= 93 | github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= 94 | github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 95 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 96 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 97 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 98 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 99 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 100 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 101 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 102 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 103 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 104 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 105 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 106 | github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= 107 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 108 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 109 | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 110 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 111 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 112 | go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 113 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 114 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 115 | go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 116 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 117 | go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 118 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 119 | golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= 120 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 121 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 122 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 123 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 124 | golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 125 | golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 126 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= 127 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 128 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 129 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 130 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 131 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 132 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 133 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 134 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 135 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 136 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 137 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 138 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 139 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 140 | golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 141 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 142 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 143 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 144 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 145 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 146 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 147 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 148 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 149 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 150 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 151 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 152 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 153 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 154 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 155 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 156 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 157 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 158 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 159 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 160 | golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 161 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 162 | golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 163 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 164 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 165 | golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 166 | golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 167 | golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 168 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 169 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 170 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 171 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 172 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 173 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 174 | gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= 175 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 176 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 177 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 178 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 179 | -------------------------------------------------------------------------------- /heroku-node/Procfile: -------------------------------------------------------------------------------- 1 | web: node api.js 2 | -------------------------------------------------------------------------------- /heroku-node/README.md: -------------------------------------------------------------------------------- 1 | # Heroku Node 2 | 3 | Setup: 4 | 5 | ```bash 6 | npm install 7 | createdb webstack_dev 8 | DATABASE_URL="postgres:///webstack_dev" node api.js 9 | ``` 10 | 11 | Go to . 12 | Click "New" > "Create new app". 13 | Give it a name and submit. 14 | Click "GitHub" and select this repo, then "Connect". 15 | Click "Enable Automatic Deploys" 16 | 17 | ```bash 18 | heroku login 19 | heroku git:remote -a 20 | git remote rename heroku heroku-node 21 | heroku buildpacks:add -a https://github.com/lstoll/heroku-buildpack-monorepo 22 | heroku buildpacks:add -a heroku/nodejs 23 | heroku config:set APP_BASE=heroku-node -a 24 | heroku addons:create heroku-postgresql:hobby-dev -a 25 | ``` 26 | -------------------------------------------------------------------------------- /heroku-node/api.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const { Client } = require("pg"); 3 | 4 | // env 5 | const conn = process.env.DATABASE_URL || "postgres:///webstack_dev"; 6 | const port = process.env.PORT || 3000; 7 | 8 | // db 9 | const client = new Client({ 10 | connectionString: conn, 11 | ssl: { rejectUnauthorized: false } 12 | }); 13 | client.connect(); 14 | 15 | // routes 16 | const app = express(); 17 | 18 | app.get("/", (_, resp) => { 19 | const status = "ok"; 20 | 21 | client.query("SELECT 1", (err, _) => { 22 | if (err !== null) { 23 | console.log(err); 24 | } 25 | }); 26 | 27 | resp.json({ status: status }); 28 | }); 29 | 30 | // listen 31 | app.listen(port, () => { 32 | console.log(`Listening at http://localhost:${port}`); 33 | }); 34 | -------------------------------------------------------------------------------- /heroku-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "railway-node-express", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "api.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "express": "^4.17.3", 13 | "pg": "^8.7.3" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /heroku-ruby/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | ruby "~> 3.2" 6 | 7 | gem "connection_pool" 8 | gem "pg" 9 | gem "puma" 10 | gem "sinatra" 11 | -------------------------------------------------------------------------------- /heroku-ruby/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | connection_pool (2.2.5) 5 | mustermann (3.0.0) 6 | ruby2_keywords (~> 0.0.1) 7 | nio4r (2.5.8) 8 | pg (1.3.5) 9 | puma (5.6.4) 10 | nio4r (~> 2.0) 11 | rack (2.2.4) 12 | rack-protection (3.0.4) 13 | rack 14 | ruby2_keywords (0.0.5) 15 | sinatra (3.0.4) 16 | mustermann (~> 3.0) 17 | rack (~> 2.2, >= 2.2.4) 18 | rack-protection (= 3.0.4) 19 | tilt (~> 2.0) 20 | tilt (2.0.11) 21 | 22 | PLATFORMS 23 | arm64-darwin-20 24 | arm64-darwin-21 25 | arm64-darwin-22 26 | x86_64-linux 27 | 28 | DEPENDENCIES 29 | connection_pool 30 | pg 31 | puma 32 | sinatra 33 | 34 | RUBY VERSION 35 | ruby 3.2.0p0 36 | 37 | BUNDLED WITH 38 | 2.4.1 39 | -------------------------------------------------------------------------------- /heroku-ruby/Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec ruby api.rb 2 | -------------------------------------------------------------------------------- /heroku-ruby/README.md: -------------------------------------------------------------------------------- 1 | # Heroku Ruby 2 | 3 | Setup: 4 | 5 | ```bash 6 | bundle 7 | createdb webstack_dev 8 | DATABASE_URL="postgres:///webstack_dev" bundle exec ruby api.rb 9 | ``` 10 | 11 | Go to . 12 | Click "New" > "Create new app". 13 | Give it a name and submit. 14 | Click "GitHub" and select this repo, then "Connect". 15 | Click "Enable Automatic Deploys" 16 | 17 | ```bash 18 | heroku login 19 | heroku git:remote -a 20 | git remote rename heroku heroku-ruby 21 | heroku buildpacks:add -a https://github.com/lstoll/heroku-buildpack-monorepo 22 | heroku buildpacks:add -a heroku/ruby 23 | heroku config:set APP_BASE=heroku-ruby -a 24 | heroku addons:create heroku-postgresql -a 25 | ``` 26 | -------------------------------------------------------------------------------- /heroku-ruby/api.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "connection_pool" 4 | require "pg" 5 | require "sinatra" 6 | 7 | class DB 8 | def initialize 9 | @pool = ConnectionPool.new(size: 5, timeout: 5) { 10 | PG.connect(ENV.fetch("DATABASE_URL", "postgres:///webstack_dev")) 11 | } 12 | end 13 | 14 | def exec(sql) 15 | @pool.with do |conn| 16 | conn.exec(sql) 17 | end 18 | end 19 | end 20 | 21 | db = DB.new 22 | 23 | configure do 24 | set :protection, :except => [:json_csrf] 25 | end 26 | 27 | get "/" do 28 | db.exec "SELECT 1" 29 | content_type :json 30 | {status: "ok"}.to_json 31 | end 32 | -------------------------------------------------------------------------------- /heroku.yml: -------------------------------------------------------------------------------- 1 | build: 2 | docker: 3 | web: heroku-go-crunchy/Dockerfile 4 | -------------------------------------------------------------------------------- /htmlx-go/README.md: -------------------------------------------------------------------------------- 1 | # HTMLX Go 2 | 3 | 4 | 5 | ``` 6 | go run main.go 7 | ``` 8 | -------------------------------------------------------------------------------- /htmlx-go/go.mod: -------------------------------------------------------------------------------- 1 | module server 2 | 3 | go 1.19 4 | 5 | require github.com/jackc/pgx/v4 v4.16.0 6 | 7 | require ( 8 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 9 | github.com/jackc/pgconn v1.12.0 // indirect 10 | github.com/jackc/pgio v1.0.0 // indirect 11 | github.com/jackc/pgpassfile v1.0.0 // indirect 12 | github.com/jackc/pgproto3/v2 v2.3.0 // indirect 13 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect 14 | github.com/jackc/pgtype v1.11.0 // indirect 15 | github.com/jackc/puddle v1.2.1 // indirect 16 | github.com/lib/pq v1.10.4 // indirect 17 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect 18 | golang.org/x/text v0.3.7 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /htmlx-go/go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 2 | github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= 3 | github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= 4 | github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= 5 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 6 | github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 7 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 8 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 9 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 10 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 11 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 12 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 13 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 14 | github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= 15 | github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 16 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 17 | github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= 18 | github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 19 | github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= 20 | github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 21 | github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= 22 | github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= 23 | github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= 24 | github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= 25 | github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= 26 | github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= 27 | github.com/jackc/pgconn v1.12.0 h1:/RvQ24k3TnNdfBSW0ou9EOi5jx2cX7zfE8n2nLKuiP0= 28 | github.com/jackc/pgconn v1.12.0/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono= 29 | github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= 30 | github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= 31 | github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= 32 | github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= 33 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= 34 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= 35 | github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= 36 | github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= 37 | github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= 38 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= 39 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= 40 | github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 41 | github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 42 | github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 43 | github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 44 | github.com/jackc/pgproto3/v2 v2.3.0 h1:brH0pCGBDkBW07HWlN/oSBXrmo3WB0UvZd1pIuDcL8Y= 45 | github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 46 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= 47 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= 48 | github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= 49 | github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= 50 | github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= 51 | github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= 52 | github.com/jackc/pgtype v1.11.0 h1:u4uiGPz/1hryuXzyaBhSk6dnIyyG2683olG2OV+UUgs= 53 | github.com/jackc/pgtype v1.11.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= 54 | github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= 55 | github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= 56 | github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= 57 | github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= 58 | github.com/jackc/pgx/v4 v4.16.0 h1:4k1tROTJctHotannFYzu77dY3bgtMRymQP7tXQjqpPk= 59 | github.com/jackc/pgx/v4 v4.16.0/go.mod h1:N0A9sFdWzkw/Jy1lwoiB64F2+ugFZi987zRxcPez/wI= 60 | github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 61 | github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 62 | github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 63 | github.com/jackc/puddle v1.2.1 h1:gI8os0wpRXFd4FiAY2dWiqRK037tjj3t7rKFeO4X5iw= 64 | github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 65 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 66 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 67 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 68 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 69 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 70 | github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 71 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 72 | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 73 | github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 74 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 75 | github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 76 | github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= 77 | github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 78 | github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= 79 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 80 | github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 81 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 82 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 83 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 84 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 85 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 86 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 87 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 88 | github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= 89 | github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= 90 | github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= 91 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 92 | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= 93 | github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= 94 | github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 95 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 96 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 97 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 98 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 99 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 100 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 101 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 102 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 103 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 104 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 105 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 106 | github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= 107 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 108 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 109 | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 110 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 111 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 112 | go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 113 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 114 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 115 | go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 116 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 117 | go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 118 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 119 | golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= 120 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 121 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 122 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 123 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 124 | golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 125 | golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 126 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= 127 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 128 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 129 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 130 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 131 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 132 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 133 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 134 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 135 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 136 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 137 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 138 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 139 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 140 | golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 141 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 142 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 143 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 144 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 145 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 146 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 147 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 148 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 149 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 150 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 151 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 152 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 153 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 154 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 155 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 156 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 157 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 158 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 159 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 160 | golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 161 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 162 | golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 163 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 164 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 165 | golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 166 | golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 167 | golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 168 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 169 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 170 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 171 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 172 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 173 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 174 | gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= 175 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 176 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 177 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 178 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 179 | -------------------------------------------------------------------------------- /htmlx-go/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 |
8 |
9 | -------------------------------------------------------------------------------- /htmlx-go/html/version.html: -------------------------------------------------------------------------------- 1 | {{.Version}} 2 | -------------------------------------------------------------------------------- /htmlx-go/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "embed" 6 | "html/template" 7 | "log" 8 | "net/http" 9 | "os" 10 | 11 | "github.com/jackc/pgx/v4/pgxpool" 12 | ) 13 | 14 | var ( 15 | //go:embed html/*.html 16 | views embed.FS 17 | 18 | html = template.Must(template.ParseFS(views, "html/*.html")) 19 | ) 20 | 21 | func main() { 22 | // env 23 | port, ok := os.LookupEnv("PORT") 24 | if !ok { 25 | port = "8080" 26 | } 27 | dbUrl, ok := os.LookupEnv("DATABASE_URL") 28 | if !ok { 29 | dbUrl = "postgres:///webstack_dev" 30 | } 31 | 32 | // db 33 | db, err := pgxpool.Connect(context.Background(), dbUrl) 34 | if err != nil { 35 | log.Fatal(err) 36 | } 37 | defer db.Close() 38 | 39 | // routes 40 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 41 | err := html.ExecuteTemplate(w, "index.html", nil) 42 | if err != nil { 43 | http.Error(w, err.Error(), http.StatusInternalServerError) 44 | } 45 | }) 46 | 47 | http.HandleFunc("/clicked", func(w http.ResponseWriter, r *http.Request) { 48 | var version string 49 | db.QueryRow(r.Context(), "SELECT version()").Scan(&version) 50 | resp := struct { 51 | Version string 52 | }{ 53 | Version: version, 54 | } 55 | err := html.ExecuteTemplate(w, "version.html", resp) 56 | if err != nil { 57 | http.Error(w, err.Error(), http.StatusInternalServerError) 58 | } 59 | }) 60 | 61 | // listen 62 | log.Println("Listening at http://localhost:" + port) 63 | log.Fatal(http.ListenAndServe(":"+port, nil)) 64 | } 65 | -------------------------------------------------------------------------------- /northflank-go/README.md: -------------------------------------------------------------------------------- 1 | # Northflank Go 2 | 3 | Develop: 4 | 5 | ```bash 6 | go run api.go 7 | ``` 8 | 9 | Create Northflank project: 10 | 11 | * [Create a project](https://northflank.com/docs/v1/application/getting-started/create-a-project) 12 | * Choose a name and region "US - Central" or "Europe - West". 13 | 14 | Add Postgres database: 15 | 16 | * Click "Addons" > "Create new addon" > "PostgreSQL". 17 | * Give it a name, choose Postgres v 14.5.0. 18 | * Keep networking defaults: "Deploy with TLS" enabled and "Publicly accessible" disabled. 19 | * Click "Create addon". 20 | 21 | Expose Postgres connection to project via secret group: 22 | 23 | * Click "Connection details" > "Link to secret groups" > "Create a new secret group". 24 | * Give it a name. 25 | * Within "Linked addons", click "Show addons". 26 | * Click "Configure" next to the Postgres database. 27 | * Click `POSTGRES_URI` > "Aliases" and enter `DATABASE_URL`. 28 | * Click "Create secret group". 29 | 30 | Create new service: 31 | 32 | * Choose "Add new service". 33 | * Choose "Build: Build a Git repo and store image". 34 | * Choose GitHub repository and `main` branch. 35 | * For build options, choose "Buildpack" > `heroku/buildpacks:20` and "Build context" of `/northflank-go`. 36 | * In "Advanced", "CMD override" to `go run api.go`. 37 | * Click "Create service". 38 | 39 | ## Editorial 40 | 41 | Little things I came across: 42 | 43 | * I could get heroku/buildpacks:20 working, but not 44 | heroku/builder:22. 45 | * HTTP/2 works for the app from browser to Northflank's routing layer. 46 | I had to use the default HTTP/1.1 option in Northflank, which confused me 47 | but is specific to Go's HTTP package and what you'd need to do to get TLS 48 | working from Northflank's router to your app process. 49 | * Within "Services" > "Builds", when I click "Start build", 50 | I'm redirected to the "Commits" tab and no build seems to start. 51 | * I'm confused about how I set up pull request deploys and staging-production 52 | pipelines. "Combined" services don't let me select `*` pull requests but let 53 | me auto-deploy `main`. In both "Build" and "Combined" services, 54 | I haven't figured out yet how to add a service to a pipeline. I can select 55 | select a service in a "Build" pipeline but am unable to add. 56 | -------------------------------------------------------------------------------- /northflank-go/go.mod: -------------------------------------------------------------------------------- 1 | module server 2 | 3 | go 1.19 4 | 5 | require github.com/jackc/pgx/v4 v4.17.2 6 | 7 | require ( 8 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 9 | github.com/jackc/pgconn v1.13.0 // indirect 10 | github.com/jackc/pgio v1.0.0 // indirect 11 | github.com/jackc/pgpassfile v1.0.0 // indirect 12 | github.com/jackc/pgproto3/v2 v2.3.1 // indirect 13 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect 14 | github.com/jackc/pgtype v1.12.0 // indirect 15 | github.com/jackc/puddle v1.3.0 // indirect 16 | github.com/lib/pq v1.10.4 // indirect 17 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect 18 | golang.org/x/text v0.3.7 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /northflank-go/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | 10 | "github.com/jackc/pgx/v4/pgxpool" 11 | ) 12 | 13 | func main() { 14 | // env 15 | port, ok := os.LookupEnv("PORT") 16 | if !ok { 17 | port = "8080" 18 | } 19 | dbURL, ok := os.LookupEnv("DATABASE_URL") 20 | if !ok { 21 | dbURL = "postgres:///webstack_dev" 22 | } 23 | 24 | // db 25 | db, err := pgxpool.Connect(context.Background(), dbURL) 26 | if err != nil { 27 | fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err) 28 | os.Exit(1) 29 | } 30 | defer db.Close() 31 | 32 | // routes 33 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 34 | var col int 35 | db.QueryRow(r.Context(), "SELECT 1").Scan(&col) 36 | w.Header().Set("Content-Type", "application/json") 37 | fmt.Fprintf(w, "{\"status\":\"ok\"}") 38 | }) 39 | 40 | // listen 41 | log.Println("Listening at http://localhost:" + port) 42 | log.Fatal(http.ListenAndServe(":"+port, nil)) 43 | } 44 | -------------------------------------------------------------------------------- /railway-crystal/.gitignore: -------------------------------------------------------------------------------- 1 | lib 2 | -------------------------------------------------------------------------------- /railway-crystal/README.md: -------------------------------------------------------------------------------- 1 | # Railway Crystal 2 | 3 | Setup: 4 | 5 | ```bash 6 | shards install 7 | createdb webstack_dev 8 | DATABASE_URL="postgres:///webstack_dev" crystal run src/api.cr 9 | ``` 10 | -------------------------------------------------------------------------------- /railway-crystal/shard.lock: -------------------------------------------------------------------------------- 1 | version: 2.0 2 | shards: 3 | backtracer: 4 | git: https://github.com/sija/backtracer.cr.git 5 | version: 1.2.2 6 | 7 | db: 8 | git: https://github.com/crystal-lang/crystal-db.git 9 | version: 0.11.0 10 | 11 | exception_page: 12 | git: https://github.com/crystal-loot/exception_page.git 13 | version: 0.3.0 14 | 15 | kemal: 16 | git: https://github.com/kemalcr/kemal.git 17 | version: 1.3.0 18 | 19 | pg: 20 | git: https://github.com/will/crystal-pg.git 21 | version: 0.26.0 22 | 23 | radix: 24 | git: https://github.com/luislavena/radix.git 25 | version: 0.4.1 26 | 27 | -------------------------------------------------------------------------------- /railway-crystal/shard.yml: -------------------------------------------------------------------------------- 1 | name: api 2 | version: 0.0.1 3 | crystal: 1.4.0 4 | 5 | dependencies: 6 | kemal: 7 | github: kemalcr/kemal 8 | pg: 9 | github: will/crystal-pg 10 | 11 | targets: 12 | api: 13 | main: "src/api.cr" 14 | -------------------------------------------------------------------------------- /railway-crystal/src/api.cr: -------------------------------------------------------------------------------- 1 | require "db" 2 | require "kemal" 3 | require "json" 4 | require "pg" 5 | 6 | DB.open(ENV.fetch("DATABASE_URL", "postgres:///webstack_dev")) do |db| 7 | get "/" do |env| 8 | db.exec "SELECT 1" 9 | env.response.content_type = "application/json" 10 | {"status": "ok"}.to_json 11 | end 12 | 13 | Kemal.run 14 | end 15 | -------------------------------------------------------------------------------- /railway-deno/README.md: -------------------------------------------------------------------------------- 1 | # Railway Deno 2 | 3 | ## Setup 4 | 5 | Create a new Railway project with a web service: 6 | 7 | * Go to your [Railway dashboard](https://railway.app/dashboard). 8 | * Click "+ New Project". 9 | * Select "Deploy from GitHub repo". 10 | * Select this repo. 11 | * Select "Deploy Now". The first build will fail. 12 | * Click "Settings". 13 | * Click "Generate Domain" to expose the service to the public internet. 14 | * Fill in "Root Directory" with `/railway-deno`. 15 | * Fill in "Start Command" with `deno run --allow-env --allow-net ./railway-deno/main.ts`. 16 | * Leave "Healthcheck Path" blank for now. 17 | * For "Builder", select "Nixpacks". 18 | * Type "Esc". 19 | 20 | Add a Postgres database to the Railway project: 21 | 22 | * Click "+ New" 23 | * Click "Database" 24 | * Click "Add PostgreSQL" 25 | * Click the "PostgreSQL" rectangle on the Railway palette. 26 | * Click "Connect". 27 | * Copy "Postgres Connection URL". 28 | * Type "Esc". 29 | * Click your web service rectangle on the Railway palette. 30 | * Click "Variables". 31 | * Enter `DATABASE_URL` for the variable name and paste the value. 32 | * Append `?sslmode=disable` to the value to avoid the error noted below. 33 | * Click "Add". 34 | 35 | ## Editorial 36 | 37 | The Deno Postgres database driver threw this error when trying to connect 38 | to the Railway Postgres instance: 39 | 40 | ``` 41 | Sending fatal alert BadCertificate 42 | TLS connection failed with message: invalid peer certificate contents: invalid peer certificate: UnknownIssuer 43 | Defaulting to non-encrypted connection 44 | ``` 45 | 46 | I didn't see a similar issue for Go's or Ruby's Postgres drivers on Railway. 47 | Obviously, it would be preferable to not disable SSL! 48 | -------------------------------------------------------------------------------- /railway-deno/main.ts: -------------------------------------------------------------------------------- 1 | import { serve } from "https://deno.land/std@0.143.0/http/server.ts"; 2 | import { Pool } from "https://deno.land/x/postgres@v0.16.1/mod.ts"; 3 | 4 | // env 5 | const port = Number(Deno.env.get("PORT") || 8080); 6 | const dbUrl = 7 | Deno.env.get("DATABASE_URL") || 8 | "postgres://postgres:postgres@0.0.0.0:5432/webstack_dev"; 9 | 10 | // db 11 | const pool = new Pool(dbUrl, 5, true); 12 | const db = await pool.connect(); 13 | 14 | // routes 15 | const handler = async (_req: Request): Promise => { 16 | await db.queryArray("SELECT 1"); 17 | 18 | return new Response(`{"status":"ok"}`, { 19 | headers: { "Content-Type": "application/json" }, 20 | status: 200, 21 | }); 22 | }; 23 | 24 | // listen 25 | console.log(`Listening at http://localhost:${port}/`); 26 | await serve(handler, { port }); 27 | -------------------------------------------------------------------------------- /railway-go-tailscale/Dockerfile: -------------------------------------------------------------------------------- 1 | # Build app 2 | FROM golang:1.19-alpine as builder 3 | WORKDIR /app 4 | COPY . ./ 5 | RUN go build -o /app/server 6 | 7 | # Build Tailscale 8 | ENV TSFILE=tailscale_1.32.2_amd64.tgz 9 | RUN wget https://pkgs.tailscale.com/stable/${TSFILE} && tar xzf ${TSFILE} --strip-components=1 10 | 11 | # Copy binaries to production image 12 | FROM alpine:latest 13 | COPY --from=builder /app/start.sh /app/start.sh 14 | COPY --from=builder /app/server /app/server 15 | COPY --from=builder /app/tailscaled /app/tailscaled 16 | COPY --from=builder /app/tailscale /app/tailscale 17 | RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/* 18 | RUN mkdir -p /var/run/tailscale /var/cache/tailscale /var/lib/tailscale 19 | 20 | # Run on container startup 21 | EXPOSE 8080 22 | CMD ["/app/start.sh"] 23 | -------------------------------------------------------------------------------- /railway-go-tailscale/README.md: -------------------------------------------------------------------------------- 1 | # Railway Go Tailscale 2 | 3 | This stack involves: 4 | 5 | - a web application written in Go 6 | - deployed to Railway 7 | - querying a Postgres database on Crunchy Bridge 8 | - networked with Tailscale 9 | 10 | Develop: 11 | 12 | ```bash 13 | createdb webstack_dev 14 | DATABASE_URL="postgres:///webstack_dev" go run api.go 15 | ``` 16 | 17 | Set up Crunchy Bridge: 18 | 19 | - Go to [Crunchy Bridge](https://crunchybridge.com/). 20 | - Create a cluster in AWS `us-east-1` (N. Virginia). 21 | - Click "Connection" tab. 22 | - Select "Role: postgres" and "Format: psql". 23 | - Click "Copy", then paste into a shell: 24 | 25 | ```bash 26 | psql postgres://postgres:password@host.db.postgresbridge.com:5432/postgres 27 | ``` 28 | 29 | In the Postgres shell as the `postgres` superuser, 30 | [enable PgBouncer](https://docs.crunchybridge.com/how-to/pgbouncer/): 31 | 32 | ```sql 33 | CREATE EXTENSION crunchy_pooler; 34 | ``` 35 | 36 | Create a reusable, ephemeral 37 | [Tailscale auth key](https://login.tailscale.com/admin/settings/keys). 38 | 39 | In the Crunchy Bridge web UI: 40 | 41 | - Click "Networking". 42 | - Delete the two "fully open firewall" rules `::/0` and `0.0.0.0/0`. 43 | - Click "Tailscale". 44 | - Paste the Tailscale auth key. 45 | - Click "Connect Tailscale". 46 | 47 | In the Tailscale web UI: 48 | 49 | - Go to the [Machines](https://login.tailscale.com/admin/machines) page. 50 | - Click the three dots next to the newly-connected Crunchy database. 51 | - Click "Edit machine name..". 52 | - Rename the machine something like `crunchy-n-california`. 53 | - Click the machine name. 54 | - Copy the domain name into a temporary text file. 55 | It will look something like `crunchy-n-california.taile1234.ts.net`. 56 | 57 | In the Crunchy Bridge web UI: 58 | 59 | - Click "Connection" 60 | - Select "Role: application". 61 | - Copy the connection string and paste into the temporary text file. 62 | - Replace the Crunchy Bridge host (e.g. `p.abc123.db.postgresbridge.com`) 63 | with the Tailscale domain name copied above. 64 | - Replace port `5432` with `5431` so the connection will use PgBouncer. 65 | - Copy the edited connection string, which will look something like: 66 | 67 | ``` 68 | postgres://application:password@crunchy-n-california.taile1234.ts.net:5431/postgres 69 | ``` 70 | 71 | Go to : 72 | 73 | - Click "Deploy from Repo". 74 | - Select this repo. 75 | - Select `main` branch. 76 | - Click "Deploy now". 77 | - Click "Settings" tab. 78 | - Set "Root directory" to `/railway-go-tailscale`. 79 | - Click "Variables". 80 | - Click "+ New Variable". 81 | - Enter "DATABASE_URL" and paste in the Tailscale'd Crunchy connection string. 82 | -------------------------------------------------------------------------------- /railway-go-tailscale/api.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | 10 | "github.com/jackc/pgx/v4/pgxpool" 11 | ) 12 | 13 | func main() { 14 | // env 15 | port, ok := os.LookupEnv("PORT") 16 | if !ok { 17 | port = "8080" 18 | } 19 | dbUrl, ok := os.LookupEnv("DATABASE_URL") 20 | if !ok { 21 | dbUrl = "postgres:///webstack_dev" 22 | } 23 | 24 | // db 25 | db, err := pgxpool.Connect(context.Background(), dbUrl) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | defer db.Close() 30 | 31 | // routes 32 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 33 | var col int 34 | db.QueryRow(r.Context(), "SELECT 1").Scan(&col) 35 | w.Header().Set("Content-Type", "application/json") 36 | fmt.Fprintf(w, "{\"status\":\"ok\"}") 37 | }) 38 | 39 | // listen 40 | log.Println("Listening at http://localhost:" + port) 41 | log.Fatal(http.ListenAndServe(":"+port, nil)) 42 | } 43 | -------------------------------------------------------------------------------- /railway-go-tailscale/go.mod: -------------------------------------------------------------------------------- 1 | module server 2 | 3 | go 1.19 4 | 5 | require github.com/jackc/pgx/v4 v4.17.2 6 | 7 | require ( 8 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 9 | github.com/jackc/pgconn v1.13.0 // indirect 10 | github.com/jackc/pgio v1.0.0 // indirect 11 | github.com/jackc/pgpassfile v1.0.0 // indirect 12 | github.com/jackc/pgproto3/v2 v2.3.1 // indirect 13 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect 14 | github.com/jackc/pgtype v1.12.0 // indirect 15 | github.com/jackc/puddle v1.3.0 // indirect 16 | github.com/lib/pq v1.10.4 // indirect 17 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect 18 | golang.org/x/text v0.3.7 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /railway-go-tailscale/railway.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | builder = "DOCKERFILE" 3 | dockerfilePath = "railway-go-tailscale/Dockerfile" 4 | 5 | [deploy] 6 | restartPolicyType = "ON_FAILURE" 7 | restartPolicyMaxRetries = 1 8 | -------------------------------------------------------------------------------- /railway-go-tailscale/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Connect machine to Tailscale tailnet 4 | /app/tailscaled --state=mem: --tun=userspace-networking --socks5-server="localhost:1055" & 5 | ID="${RAILWAY_GIT_COMMIT_SHA:0:8}" 6 | /app/tailscale up --authkey="${TAILSCALE_AUTHKEY}" --hostname="railway-app-${ID}" 7 | 8 | # Run server 9 | ALL_PROXY="socks5://localhost:1055/" /app/server 10 | -------------------------------------------------------------------------------- /railway-go/README.md: -------------------------------------------------------------------------------- 1 | # Railway Go 2 | 3 | ```bash 4 | go run main.go 5 | ``` 6 | 7 | Go to . 8 | Click "Deploy from Repo". 9 | Select this repo. 10 | Select `main` branch. 11 | Click "Deploy". 12 | 13 | Click "+ New" button. 14 | Click "Database". 15 | Click "Add PostgreSQL". 16 | Click "Connect" tab. 17 | Copy "Postgres Connection URL". 18 | 19 | Click the original web service. 20 | Click "Variables" tab. 21 | Click "+ New Variable". 22 | Enter `DATABASE_URL` and paste copied string. 23 | 24 | Click "Settings" tab. 25 | Set "Root directory" to `/railway-go`. 26 | Set "Healthcheck Path" to `/`. 27 | -------------------------------------------------------------------------------- /railway-go/go.mod: -------------------------------------------------------------------------------- 1 | module server 2 | 3 | go 1.20 4 | 5 | require github.com/jackc/pgx/v4 v4.16.0 6 | 7 | require ( 8 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 9 | github.com/jackc/pgconn v1.12.0 // indirect 10 | github.com/jackc/pgio v1.0.0 // indirect 11 | github.com/jackc/pgpassfile v1.0.0 // indirect 12 | github.com/jackc/pgproto3/v2 v2.3.0 // indirect 13 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect 14 | github.com/jackc/pgtype v1.11.0 // indirect 15 | github.com/jackc/puddle v1.2.1 // indirect 16 | github.com/lib/pq v1.10.4 // indirect 17 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect 18 | golang.org/x/text v0.3.7 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /railway-go/go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 2 | github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= 3 | github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= 4 | github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= 5 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 6 | github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 7 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 8 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 9 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 10 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 11 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 12 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 13 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 14 | github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= 15 | github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 16 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 17 | github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= 18 | github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 19 | github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= 20 | github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 21 | github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= 22 | github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= 23 | github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= 24 | github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= 25 | github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= 26 | github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= 27 | github.com/jackc/pgconn v1.12.0 h1:/RvQ24k3TnNdfBSW0ou9EOi5jx2cX7zfE8n2nLKuiP0= 28 | github.com/jackc/pgconn v1.12.0/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono= 29 | github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= 30 | github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= 31 | github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= 32 | github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= 33 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= 34 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= 35 | github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= 36 | github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= 37 | github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= 38 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= 39 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= 40 | github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 41 | github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 42 | github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 43 | github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 44 | github.com/jackc/pgproto3/v2 v2.3.0 h1:brH0pCGBDkBW07HWlN/oSBXrmo3WB0UvZd1pIuDcL8Y= 45 | github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 46 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= 47 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= 48 | github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= 49 | github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= 50 | github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= 51 | github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= 52 | github.com/jackc/pgtype v1.11.0 h1:u4uiGPz/1hryuXzyaBhSk6dnIyyG2683olG2OV+UUgs= 53 | github.com/jackc/pgtype v1.11.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= 54 | github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= 55 | github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= 56 | github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= 57 | github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= 58 | github.com/jackc/pgx/v4 v4.16.0 h1:4k1tROTJctHotannFYzu77dY3bgtMRymQP7tXQjqpPk= 59 | github.com/jackc/pgx/v4 v4.16.0/go.mod h1:N0A9sFdWzkw/Jy1lwoiB64F2+ugFZi987zRxcPez/wI= 60 | github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 61 | github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 62 | github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 63 | github.com/jackc/puddle v1.2.1 h1:gI8os0wpRXFd4FiAY2dWiqRK037tjj3t7rKFeO4X5iw= 64 | github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 65 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 66 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 67 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 68 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 69 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 70 | github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 71 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 72 | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 73 | github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 74 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 75 | github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 76 | github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= 77 | github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 78 | github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= 79 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 80 | github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 81 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 82 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 83 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 84 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 85 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 86 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 87 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 88 | github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= 89 | github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= 90 | github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= 91 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 92 | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= 93 | github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= 94 | github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 95 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 96 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 97 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 98 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 99 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 100 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 101 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 102 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 103 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 104 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 105 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 106 | github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= 107 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 108 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 109 | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 110 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 111 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 112 | go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 113 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 114 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 115 | go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 116 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 117 | go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 118 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 119 | golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= 120 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 121 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 122 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 123 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 124 | golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 125 | golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 126 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= 127 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 128 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 129 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 130 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 131 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 132 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 133 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 134 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 135 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 136 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 137 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 138 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 139 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 140 | golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 141 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 142 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 143 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 144 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 145 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 146 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 147 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 148 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 149 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 150 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 151 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 152 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 153 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 154 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 155 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 156 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 157 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 158 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 159 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 160 | golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 161 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 162 | golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 163 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 164 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 165 | golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 166 | golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 167 | golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 168 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 169 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 170 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 171 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 172 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 173 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 174 | gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= 175 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 176 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 177 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 178 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 179 | -------------------------------------------------------------------------------- /railway-go/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | 10 | "github.com/jackc/pgx/v4/pgxpool" 11 | ) 12 | 13 | func main() { 14 | // env 15 | port, ok := os.LookupEnv("PORT") 16 | if !ok { 17 | port = "8080" 18 | } 19 | dbUrl, ok := os.LookupEnv("DATABASE_URL") 20 | if !ok { 21 | dbUrl = "postgres:///webstack_dev" 22 | } 23 | 24 | // db 25 | db, err := pgxpool.Connect(context.Background(), dbUrl) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | defer db.Close() 30 | 31 | // routes 32 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 33 | var col int 34 | db.QueryRow(r.Context(), "SELECT 1").Scan(&col) 35 | w.Header().Set("Content-Type", "application/json") 36 | fmt.Fprintf(w, "{\"status\":\"ok\"}") 37 | }) 38 | 39 | // listen 40 | log.Println("Listening at http://localhost:" + port) 41 | log.Fatal(http.ListenAndServe(":"+port, nil)) 42 | } 43 | -------------------------------------------------------------------------------- /railway-node/README.md: -------------------------------------------------------------------------------- 1 | # Railway Node 2 | 3 | Setup: 4 | 5 | ```bash 6 | npm install 7 | createdb webstack_dev 8 | DATABASE_URL="postgres:///webstack_dev" node api.js 9 | ``` 10 | -------------------------------------------------------------------------------- /railway-node/api.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const { Client } = require("pg"); 3 | 4 | // env 5 | const conn = process.env.DATABASE_URL || "postgres:///webstack_dev"; 6 | const port = process.env.PORT || 3000; 7 | 8 | // db 9 | const client = new Client({ 10 | connectionString: conn, 11 | }); 12 | client.connect(); 13 | 14 | // routes 15 | const app = express(); 16 | 17 | app.get("/", (_, resp) => { 18 | const status = "ok"; 19 | 20 | client.query("SELECT 1", (err, _) => { 21 | if (err !== null) { 22 | console.log(err); 23 | } 24 | }); 25 | 26 | resp.json({ status: status }); 27 | }); 28 | 29 | // listen 30 | app.listen(port, () => { 31 | console.log(`Listening at http://localhost:${port}`); 32 | }); 33 | -------------------------------------------------------------------------------- /railway-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "railway-node-express", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "api.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "express": "^4.17.3", 13 | "pg": "^8.7.3" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /railway-ruby/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | ruby "~> 3" 4 | 5 | gem "connection_pool" 6 | gem "pg" 7 | gem "puma" 8 | gem "sinatra" 9 | -------------------------------------------------------------------------------- /railway-ruby/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | connection_pool (2.2.5) 5 | mustermann (3.0.0) 6 | ruby2_keywords (~> 0.0.1) 7 | nio4r (2.5.8) 8 | pg (1.3.5) 9 | puma (5.6.4) 10 | nio4r (~> 2.0) 11 | rack (2.2.4) 12 | rack-protection (3.0.4) 13 | rack 14 | ruby2_keywords (0.0.5) 15 | sinatra (3.0.4) 16 | mustermann (~> 3.0) 17 | rack (~> 2.2, >= 2.2.4) 18 | rack-protection (= 3.0.4) 19 | tilt (~> 2.0) 20 | tilt (2.0.11) 21 | 22 | PLATFORMS 23 | arm64-darwin-20 24 | arm64-darwin-21 25 | x86_64-linux 26 | 27 | DEPENDENCIES 28 | connection_pool 29 | pg 30 | puma 31 | sinatra 32 | 33 | RUBY VERSION 34 | ruby 3.0.3p157 35 | 36 | BUNDLED WITH 37 | 2.3.4 38 | -------------------------------------------------------------------------------- /railway-ruby/Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec ruby api.rb 2 | -------------------------------------------------------------------------------- /railway-ruby/README.md: -------------------------------------------------------------------------------- 1 | # Railway Ruby 2 | 3 | Setup: 4 | 5 | ```bash 6 | bundle 7 | createdb webstack_dev 8 | DATABASE_URL="postgres:///webstack_dev" bundle exec ruby api.rb 9 | ``` 10 | 11 | Go to . 12 | Click "Deploy from Repo". 13 | Select this repo. 14 | Select `main` branch. 15 | Click "Deploy". 16 | 17 | Click "+ New" button. 18 | Click "Database". 19 | Click "Add PostgreSQL". 20 | Click "Connect" tab. 21 | Copy "Postgres Connection URL". 22 | 23 | Click the original web service. 24 | Click "Variables" tab. 25 | Click "+ New Variable". 26 | Enter `DATABASE_URL` and paste copied string. 27 | 28 | Click "Settings" tab. 29 | Set "Root directory" to `/railway-ruby`. 30 | Set "Healthcheck Path" to `/`. 31 | -------------------------------------------------------------------------------- /railway-ruby/api.rb: -------------------------------------------------------------------------------- 1 | require "connection_pool" 2 | require "pg" 3 | require "sinatra" 4 | 5 | class DB 6 | def initialize 7 | @pool = ConnectionPool.new(size: 5, timeout: 5) { 8 | PG.connect(ENV.fetch("DATABASE_URL")) 9 | } 10 | end 11 | 12 | def exec(sql) 13 | @pool.with do |conn| 14 | conn.exec(sql) 15 | end 16 | end 17 | end 18 | 19 | db = DB.new 20 | 21 | get "/" do 22 | db.exec "SELECT 1" 23 | content_type :json 24 | {status: "ok"}.to_json 25 | end 26 | -------------------------------------------------------------------------------- /render-go/README.md: -------------------------------------------------------------------------------- 1 | # Render Go 2 | 3 | Develop: 4 | 5 | ```bash 6 | go run main.go 7 | ``` 8 | 9 | Postgres: 10 | 11 | * Go to 12 | * Create a new database. 13 | * Select a "Plan". 14 | * Once the database is available, copy "Internal Connection String". 15 | 16 | Web service: 17 | 18 | * Go to . 19 | * Select this repo. 20 | * Select `main` branch. 21 | * Name the service. 22 | * Select "Go" environment. 23 | * Set "Build command" to `cd render-go && go build -tags netgo -ldflags '-s -w' -o api`. 24 | * Set "Start command" to `cd render-go && ./api`. 25 | * Set "Plans" to "Free". 26 | * Click "Advanced". 27 | * Click "Add Environment Variable". 28 | * Paste "Internal Connection String" value from Postgres section above into new `DATABASE_URL` env var 29 | -------------------------------------------------------------------------------- /render-go/go.mod: -------------------------------------------------------------------------------- 1 | module server 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/jackc/pgx/v4 v4.16.0 7 | github.com/lib/pq v1.10.4 // indirect 8 | ) 9 | 10 | require ( 11 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 12 | github.com/jackc/pgconn v1.12.0 // indirect 13 | github.com/jackc/pgio v1.0.0 // indirect 14 | github.com/jackc/pgpassfile v1.0.0 // indirect 15 | github.com/jackc/pgproto3/v2 v2.3.0 // indirect 16 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect 17 | github.com/jackc/pgtype v1.11.0 // indirect 18 | github.com/jackc/puddle v1.2.1 // indirect 19 | github.com/stretchr/testify v1.8.0 // indirect 20 | golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be // indirect 21 | golang.org/x/text v0.3.7 // indirect 22 | ) 23 | -------------------------------------------------------------------------------- /render-go/go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 2 | github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= 3 | github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= 4 | github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= 5 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 6 | github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 7 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 8 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 9 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 10 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 11 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 12 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 13 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 14 | github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= 15 | github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 16 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 17 | github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= 18 | github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 19 | github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= 20 | github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 21 | github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= 22 | github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= 23 | github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= 24 | github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= 25 | github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= 26 | github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= 27 | github.com/jackc/pgconn v1.12.0 h1:/RvQ24k3TnNdfBSW0ou9EOi5jx2cX7zfE8n2nLKuiP0= 28 | github.com/jackc/pgconn v1.12.0/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono= 29 | github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= 30 | github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= 31 | github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= 32 | github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= 33 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= 34 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= 35 | github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= 36 | github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= 37 | github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= 38 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= 39 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= 40 | github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 41 | github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 42 | github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 43 | github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 44 | github.com/jackc/pgproto3/v2 v2.3.0 h1:brH0pCGBDkBW07HWlN/oSBXrmo3WB0UvZd1pIuDcL8Y= 45 | github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 46 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= 47 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= 48 | github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= 49 | github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= 50 | github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= 51 | github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= 52 | github.com/jackc/pgtype v1.11.0 h1:u4uiGPz/1hryuXzyaBhSk6dnIyyG2683olG2OV+UUgs= 53 | github.com/jackc/pgtype v1.11.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= 54 | github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= 55 | github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= 56 | github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= 57 | github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= 58 | github.com/jackc/pgx/v4 v4.16.0 h1:4k1tROTJctHotannFYzu77dY3bgtMRymQP7tXQjqpPk= 59 | github.com/jackc/pgx/v4 v4.16.0/go.mod h1:N0A9sFdWzkw/Jy1lwoiB64F2+ugFZi987zRxcPez/wI= 60 | github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 61 | github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 62 | github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 63 | github.com/jackc/puddle v1.2.1 h1:gI8os0wpRXFd4FiAY2dWiqRK037tjj3t7rKFeO4X5iw= 64 | github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 65 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 66 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 67 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 68 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 69 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 70 | github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 71 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 72 | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 73 | github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 74 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 75 | github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 76 | github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= 77 | github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 78 | github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= 79 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 80 | github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 81 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 82 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 83 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 84 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 85 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 86 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 87 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 88 | github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= 89 | github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= 90 | github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= 91 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 92 | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= 93 | github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= 94 | github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 95 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 96 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 97 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 98 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 99 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 100 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 101 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 102 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 103 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 104 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 105 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 106 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 107 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 108 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 109 | github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= 110 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 111 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 112 | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 113 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 114 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 115 | go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 116 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 117 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 118 | go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 119 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 120 | go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 121 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 122 | golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= 123 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 124 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 125 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 126 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 127 | golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 128 | golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 129 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 130 | golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A= 131 | golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 132 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 133 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 134 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 135 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 136 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 137 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 138 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 139 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 140 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 141 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 142 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 143 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 144 | golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 145 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 146 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 147 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 148 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 149 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 150 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 151 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 152 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 153 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 154 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 155 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 156 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 157 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 158 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 159 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 160 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 161 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 162 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 163 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 164 | golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 165 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 166 | golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 167 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 168 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 169 | golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 170 | golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 171 | golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 172 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 173 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 174 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 175 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 176 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 177 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 178 | gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= 179 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 180 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 181 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 182 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 183 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 184 | -------------------------------------------------------------------------------- /render-go/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | 10 | "github.com/jackc/pgx/v4/pgxpool" 11 | ) 12 | 13 | func main() { 14 | // env 15 | port, ok := os.LookupEnv("PORT") 16 | if !ok { 17 | port = "8080" 18 | } 19 | dbUrl, ok := os.LookupEnv("DATABASE_URL") 20 | if !ok { 21 | dbUrl = "postgres:///webstack_dev" 22 | } 23 | 24 | // db 25 | db, err := pgxpool.Connect(context.Background(), dbUrl) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | defer db.Close() 30 | 31 | // routes 32 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 33 | var col int 34 | db.QueryRow(r.Context(), "SELECT 1").Scan(&col) 35 | w.Header().Set("Content-Type", "application/json") 36 | fmt.Fprintf(w, "{\"status\":\"ok\"}") 37 | }) 38 | 39 | // listen 40 | log.Println("Listening at http://localhost:" + port) 41 | log.Fatal(http.ListenAndServe(":"+port, nil)) 42 | } 43 | -------------------------------------------------------------------------------- /render-ruby/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | ruby "3.2.2" 6 | 7 | gem "connection_pool" 8 | gem "pg" 9 | gem "puma" 10 | gem "sinatra" 11 | -------------------------------------------------------------------------------- /render-ruby/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | connection_pool (2.4.1) 5 | mustermann (3.0.0) 6 | ruby2_keywords (~> 0.0.1) 7 | nio4r (2.5.9) 8 | pg (1.5.3) 9 | puma (6.3.0) 10 | nio4r (~> 2.0) 11 | rack (2.2.7) 12 | rack-protection (3.0.6) 13 | rack 14 | ruby2_keywords (0.0.5) 15 | sinatra (3.0.6) 16 | mustermann (~> 3.0) 17 | rack (~> 2.2, >= 2.2.4) 18 | rack-protection (= 3.0.6) 19 | tilt (~> 2.0) 20 | tilt (2.1.0) 21 | 22 | PLATFORMS 23 | arm64-darwin-20 24 | ruby 25 | x86_64-linux 26 | 27 | DEPENDENCIES 28 | connection_pool 29 | pg 30 | puma 31 | sinatra 32 | 33 | RUBY VERSION 34 | ruby 3.2.2p53 35 | 36 | BUNDLED WITH 37 | 2.3.10 38 | -------------------------------------------------------------------------------- /render-ruby/README.md: -------------------------------------------------------------------------------- 1 | # Render Ruby 2 | 3 | ## Setup 4 | 5 | ```bash 6 | bundle 7 | createdb webstack_dev 8 | DATABASE_URL="postgres:///webstack_dev" bundle exec ruby api.rb 9 | ``` 10 | 11 | Go to 12 | Name the database "webstack". 13 | Set "Plans" to "Free". 14 | Copy "Internal Connection String". 15 | 16 | Go to . 17 | Select this repo. 18 | Select `main` branch. 19 | Name the service. 20 | Select "Ruby" environment. 21 | Set "Build command" to `cd render-ruby && bundle install`. 22 | Set "Start command" to `cd render-ruby && bundle exec ruby api.rb`. 23 | Set "Plans" to "Free". 24 | Click "Advanced". 25 | Click "Add Environment Variable". 26 | Paste "Internal Connection String" value from above into new `DATABASE_URL` env var. 27 | 28 | ## Editorial 29 | 30 | * The [Render docs](https://render.com/docs/deploy-rails#additional-notes) say 31 | "By default, Render uses the latest LTS version of Ruby" but using 32 | `ruby "~> 3"` in the `Gemfile` resulted in build errors: 33 | "Your Ruby version is 2.6.8, but your Gemfile specified ~> 3". 34 | * Databases are backed up daily, not continuously. 35 | * HA databases are not available. 36 | -------------------------------------------------------------------------------- /render-ruby/api.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "connection_pool" 4 | require "pg" 5 | require "sinatra" 6 | 7 | class DB 8 | def initialize 9 | @pool = ConnectionPool.new(size: 5, timeout: 5) { 10 | PG.connect(ENV.fetch("DATABASE_URL", "postgres:///webstack_dev")) 11 | } 12 | end 13 | 14 | def exec(sql) 15 | @pool.with do |conn| 16 | conn.exec(sql) 17 | end 18 | end 19 | end 20 | 21 | db = DB.new 22 | 23 | get "/" do 24 | db.exec "SELECT 1" 25 | content_type :json 26 | {status: "ok"}.to_json 27 | end 28 | -------------------------------------------------------------------------------- /vercel-edge-planetscale/.gitignore: -------------------------------------------------------------------------------- 1 | .vercel 2 | -------------------------------------------------------------------------------- /vercel-edge-planetscale/README.md: -------------------------------------------------------------------------------- 1 | # Vercel Edge Functions w/ PlanetScale serverless driver for JavaScript 2 | 3 | Set up PlanetScale: 4 | 5 | * Create a [PlanetScale](https://planetscale.com) account. 6 | * Create a database. 7 | * Go to "Settings" > "Beta features". 8 | Click "Enroll" for "PlanetScale serverless driver for JavaScript". 9 | * Go to "Settings" > Passwords". 10 | Click "New password". 11 | * Compose a `DATABASE_URL` from the newly created password. 12 | 13 | I chose to create one environment variable because the driver 14 | will decompose the username, password, and host: 15 | 16 | ```ts 17 | if (config.url) { 18 | const url = new URL(config.url) 19 | this.config.username = url.username 20 | this.config.password = url.password 21 | this.config.host = url.hostname 22 | } 23 | ``` 24 | 25 | The host looks like `aws.connect.psdb.cloud` 26 | instead of a region like `us-east-1`. 27 | I'm guessing that's the [global router](https://planetscale.com/blog/introducing-the-planetscale-serverless-driver-for-javascript)? 28 | 29 | > Similar to a CDN, global routing reduces latency drastically in situations 30 | > where a client is connecting from a geographically distant location, which is 31 | > common within serverless and edge compute environments. A client connects to 32 | > the closest geographic edge in our network, then backhauls over long-held 33 | > connection pools over our internal network to reach the actual destination. 34 | > Within the US, connections from US West to a database in US East reduce 35 | > latency by 100ms or more in some cases, even more as the distance increases. 36 | 37 | Set up Vercel: 38 | 39 | * Create a [Vercel](https://vercel.com) account. 40 | * Add a `DATABASE_URL` environment variable from PlanetScale. 41 | 42 | Deploy: 43 | 44 | ```bash 45 | npm i -g vercel 46 | npm i 47 | vercel deploy 48 | vercel --prod 49 | ``` 50 | -------------------------------------------------------------------------------- /vercel-edge-planetscale/api/index.ts: -------------------------------------------------------------------------------- 1 | import { Client } from "@planetscale/database"; 2 | 3 | export const config = { 4 | runtime: "experimental-edge", 5 | }; 6 | 7 | const db = new Client({ 8 | url: process.env["DATABASE_URL"], 9 | }); 10 | 11 | export default async function handler(_req: Request) { 12 | const conn = db.connection(); 13 | await conn.execute("SELECT 1"); 14 | 15 | return new Response('{"status":"ok"}', { 16 | headers: { 17 | "Content-Type": "application/json", 18 | }, 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /vercel-edge-planetscale/lib/wake.js: -------------------------------------------------------------------------------- 1 | import fetch from "node-fetch"; 2 | import { Client } from "@planetscale/database"; 3 | import * as dotenv from "dotenv"; 4 | 5 | dotenv.config(); 6 | 7 | const db = new Client({ 8 | fetch: fetch, 9 | url: process.env["DATABASE_URL"], 10 | }); 11 | 12 | (async () => { 13 | const conn = db.connection(); 14 | await conn.execute("INSERT INTO checks (at) VALUES (now())"); 15 | })(); 16 | -------------------------------------------------------------------------------- /vercel-edge-planetscale/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vercel-edge-planetscale", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "dependencies": { 8 | "@planetscale/database": "^1.4.0", 9 | "dotenv": "^16.0.3", 10 | "node-fetch": "^3.2.10" 11 | }, 12 | "devDependencies": { 13 | "typescript": "^4.8.4" 14 | } 15 | }, 16 | "node_modules/@planetscale/database": { 17 | "version": "1.4.0", 18 | "resolved": "https://registry.npmjs.org/@planetscale/database/-/database-1.4.0.tgz", 19 | "integrity": "sha512-lva57Z/Nz57ocB3dwaWPDGMbb+dj5HtooVB9c0DPcAF9SHbQyfovDyGK5bDgcmCMVoonfblcYv7fr+WBLc3xNw==", 20 | "engines": { 21 | "node": ">=16" 22 | } 23 | }, 24 | "node_modules/data-uri-to-buffer": { 25 | "version": "4.0.0", 26 | "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", 27 | "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==", 28 | "engines": { 29 | "node": ">= 12" 30 | } 31 | }, 32 | "node_modules/dotenv": { 33 | "version": "16.0.3", 34 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", 35 | "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", 36 | "engines": { 37 | "node": ">=12" 38 | } 39 | }, 40 | "node_modules/fetch-blob": { 41 | "version": "3.2.0", 42 | "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", 43 | "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", 44 | "funding": [ 45 | { 46 | "type": "github", 47 | "url": "https://github.com/sponsors/jimmywarting" 48 | }, 49 | { 50 | "type": "paypal", 51 | "url": "https://paypal.me/jimmywarting" 52 | } 53 | ], 54 | "dependencies": { 55 | "node-domexception": "^1.0.0", 56 | "web-streams-polyfill": "^3.0.3" 57 | }, 58 | "engines": { 59 | "node": "^12.20 || >= 14.13" 60 | } 61 | }, 62 | "node_modules/formdata-polyfill": { 63 | "version": "4.0.10", 64 | "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", 65 | "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", 66 | "dependencies": { 67 | "fetch-blob": "^3.1.2" 68 | }, 69 | "engines": { 70 | "node": ">=12.20.0" 71 | } 72 | }, 73 | "node_modules/node-domexception": { 74 | "version": "1.0.0", 75 | "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", 76 | "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", 77 | "funding": [ 78 | { 79 | "type": "github", 80 | "url": "https://github.com/sponsors/jimmywarting" 81 | }, 82 | { 83 | "type": "github", 84 | "url": "https://paypal.me/jimmywarting" 85 | } 86 | ], 87 | "engines": { 88 | "node": ">=10.5.0" 89 | } 90 | }, 91 | "node_modules/node-fetch": { 92 | "version": "3.2.10", 93 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.10.tgz", 94 | "integrity": "sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==", 95 | "dependencies": { 96 | "data-uri-to-buffer": "^4.0.0", 97 | "fetch-blob": "^3.1.4", 98 | "formdata-polyfill": "^4.0.10" 99 | }, 100 | "engines": { 101 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 102 | }, 103 | "funding": { 104 | "type": "opencollective", 105 | "url": "https://opencollective.com/node-fetch" 106 | } 107 | }, 108 | "node_modules/typescript": { 109 | "version": "4.8.4", 110 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", 111 | "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", 112 | "dev": true, 113 | "bin": { 114 | "tsc": "bin/tsc", 115 | "tsserver": "bin/tsserver" 116 | }, 117 | "engines": { 118 | "node": ">=4.2.0" 119 | } 120 | }, 121 | "node_modules/web-streams-polyfill": { 122 | "version": "3.2.1", 123 | "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", 124 | "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", 125 | "engines": { 126 | "node": ">= 8" 127 | } 128 | } 129 | }, 130 | "dependencies": { 131 | "@planetscale/database": { 132 | "version": "1.4.0", 133 | "resolved": "https://registry.npmjs.org/@planetscale/database/-/database-1.4.0.tgz", 134 | "integrity": "sha512-lva57Z/Nz57ocB3dwaWPDGMbb+dj5HtooVB9c0DPcAF9SHbQyfovDyGK5bDgcmCMVoonfblcYv7fr+WBLc3xNw==" 135 | }, 136 | "data-uri-to-buffer": { 137 | "version": "4.0.0", 138 | "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", 139 | "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==" 140 | }, 141 | "dotenv": { 142 | "version": "16.0.3", 143 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", 144 | "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" 145 | }, 146 | "fetch-blob": { 147 | "version": "3.2.0", 148 | "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", 149 | "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", 150 | "requires": { 151 | "node-domexception": "^1.0.0", 152 | "web-streams-polyfill": "^3.0.3" 153 | } 154 | }, 155 | "formdata-polyfill": { 156 | "version": "4.0.10", 157 | "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", 158 | "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", 159 | "requires": { 160 | "fetch-blob": "^3.1.2" 161 | } 162 | }, 163 | "node-domexception": { 164 | "version": "1.0.0", 165 | "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", 166 | "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" 167 | }, 168 | "node-fetch": { 169 | "version": "3.2.10", 170 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.10.tgz", 171 | "integrity": "sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==", 172 | "requires": { 173 | "data-uri-to-buffer": "^4.0.0", 174 | "fetch-blob": "^3.1.4", 175 | "formdata-polyfill": "^4.0.10" 176 | } 177 | }, 178 | "typescript": { 179 | "version": "4.8.4", 180 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", 181 | "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", 182 | "dev": true 183 | }, 184 | "web-streams-polyfill": { 185 | "version": "3.2.1", 186 | "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", 187 | "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==" 188 | } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /vercel-edge-planetscale/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "type": "module", 4 | "dependencies": { 5 | "@planetscale/database": "^1.4.0", 6 | "dotenv": "^16.0.3", 7 | "node-fetch": "^3.2.10" 8 | }, 9 | "devDependencies": { 10 | "typescript": "^4.8.4" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /vercel-go-cockroach/README.md: -------------------------------------------------------------------------------- 1 | # Vercel Go w/ Cockroach serverless Postgres 2 | 3 | Vercel supports [serverless functions written in 4 | Go](https://vercel.com/docs/concepts/functions/supported-languages#go). 5 | 6 | ## Setup 7 | 8 | * Go to . 9 | * Click "New project". 10 | * Select this Git repo. 11 | * Select `vercel-go` as root directory. 12 | * Click "Deploy". 13 | * Test your app in a web browser at the `/api` path. 14 | 15 | Create a Postgres database: 16 | 17 | * Go to to create a new Postgres database. 18 | * Choose "AWS > N. Virginia" region 19 | to be close to Vercel Washington DC functions. 20 | * Copy "Connection string". 21 | 22 | Back in Vercel: 23 | 24 | * Click "Settings" tab. 25 | * Click "Environment Variables" 26 | * Create a new env var `DATABASE_URL` and paste value from Neon. 27 | 28 | ## Editorial 29 | 30 | Vercel: 31 | 32 | * Preview deploys in branches and pull requests are nice. 33 | * Deploys take ~20s. 34 | * Build and deployment logs are easy to read. 35 | * My functions appear to be in Washington, DC: 36 | 37 | ``` 38 | Generated build outputs: 39 | - Static files: 3 40 | - Serverless Functions: 1 41 | - Edge Functions: 0 42 | Serverless regions: Washington, D.C., USA 43 | ``` 44 | 45 | Cockroach Serverless: 46 | 47 | * Impressive low latency. Lowest I've been able to achieve so far for any 48 | serverless function w/ SQL database. 49 | * Unclear under what conditions it idles. 50 | * `sslmode=verify-full` works and is the default in Go template. 51 | * Not totally compatible w/ Postgres, 52 | doesn't support Postgres functions or triggers. 53 | -------------------------------------------------------------------------------- /vercel-go-cockroach/api/index.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | 10 | "github.com/jackc/pgx/v4" 11 | ) 12 | 13 | func Handler(w http.ResponseWriter, r *http.Request) { 14 | w.Header().Set("Content-Type", "application/json") 15 | 16 | dsn, ok := os.LookupEnv("DATABASE_URL") 17 | if !ok { 18 | log.Fatal("DATABASE_URL not set") 19 | fmt.Fprintf(w, "{\"status\":\"internal server error\"}") 20 | return 21 | } 22 | 23 | ctx := context.Background() 24 | db, err := pgx.Connect(ctx, dsn) 25 | defer db.Close(ctx) 26 | if err != nil { 27 | log.Fatal(err) 28 | fmt.Fprintf(w, "{\"status\":\"internal server error\"}") 29 | return 30 | } 31 | 32 | var col int 33 | err = db.QueryRow(ctx, "SELECT 1").Scan(&col) 34 | if err != nil { 35 | log.Fatal(err) 36 | fmt.Fprintf(w, "{\"status\":\"internal server error\"}") 37 | return 38 | } 39 | 40 | fmt.Fprintf(w, "{\"status\":\"ok\"}") 41 | } 42 | -------------------------------------------------------------------------------- /vercel-go-cockroach/go.mod: -------------------------------------------------------------------------------- 1 | module api 2 | 3 | go 1.18 4 | 5 | require github.com/jackc/pgx/v4 v4.17.2 6 | 7 | require ( 8 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 9 | github.com/jackc/pgconn v1.13.0 // indirect 10 | github.com/jackc/pgio v1.0.0 // indirect 11 | github.com/jackc/pgpassfile v1.0.0 // indirect 12 | github.com/jackc/pgproto3/v2 v2.3.1 // indirect 13 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect 14 | github.com/jackc/pgtype v1.12.0 // indirect 15 | github.com/lib/pq v1.10.5 // indirect 16 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect 17 | golang.org/x/text v0.3.7 // indirect 18 | ) 19 | -------------------------------------------------------------------------------- /vercel-go-neon/README.md: -------------------------------------------------------------------------------- 1 | # Vercel Go w/ Neon Postgres database 2 | 3 | Vercel supports [serverless functions written in 4 | Go](https://vercel.com/docs/concepts/functions/supported-languages#go). 5 | 6 | ## Setup 7 | 8 | * Go to . 9 | * Click "New project". 10 | * Select this Git repo. 11 | * Select `vercel-go` as root directory. 12 | * Click "Deploy". 13 | * Test your app in a web browser at the `/api` path. 14 | 15 | Create a Postgres database: 16 | 17 | * Go to to create a new Postgres database. 18 | * Ensure connection pooling is disabled 19 | * Copy basic `psql` connection string. 20 | 21 | Back in Vercel: 22 | 23 | * Click "Settings" tab. 24 | * Click "Environment Variables" 25 | * Create a new env var `DATABASE_URL` and paste value from Neon. 26 | Append `?sslmode=verify-full&options=project%3DYOUR-PROJECT-ID-HERE`. 27 | See [project ID in options docs](https://neon.tech/docs/how-to-guides/connectivity-issues/#a-pass-project-id-in-options) 28 | 29 | ## Editorial 30 | 31 | Vercel: 32 | 33 | * Preview deploys in branches and pull requests are nice. 34 | * Deploys take ~20s. 35 | * Build and deployment logs are easy to read. 36 | 37 | Neon: 38 | 39 | * Pretty awesome that the database can "scale to zero", 40 | idles after 5m of inactivity. 41 | * `sslmode=verify-full` works. 42 | * Normal Postgres, uses version 14.5 as of Oct 6, 2022. 43 | * Latency is suffering from Vercel function being in Washington DC 44 | and database in Oregon. Not currently possible to choose a region 45 | for the Neon database. 46 | -------------------------------------------------------------------------------- /vercel-go-neon/api/index.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | 10 | "github.com/jackc/pgx/v4" 11 | ) 12 | 13 | func Handler(w http.ResponseWriter, r *http.Request) { 14 | w.Header().Set("Content-Type", "application/json") 15 | 16 | dsn, ok := os.LookupEnv("DATABASE_URL") 17 | if !ok { 18 | log.Fatal("DATABASE_URL not set") 19 | fmt.Fprintf(w, "{\"status\":\"internal server error\"}") 20 | return 21 | } 22 | 23 | ctx := context.Background() 24 | db, err := pgx.Connect(ctx, dsn) 25 | defer db.Close(ctx) 26 | if err != nil { 27 | log.Fatal(err) 28 | fmt.Fprintf(w, "{\"status\":\"internal server error\"}") 29 | return 30 | } 31 | 32 | var col int 33 | err = db.QueryRow(ctx, "SELECT 1").Scan(&col) 34 | if err != nil { 35 | log.Fatal(err) 36 | fmt.Fprintf(w, "{\"status\":\"internal server error\"}") 37 | return 38 | } 39 | 40 | fmt.Fprintf(w, "{\"status\":\"ok\"}") 41 | } 42 | -------------------------------------------------------------------------------- /vercel-go-neon/go.mod: -------------------------------------------------------------------------------- 1 | module api 2 | 3 | go 1.18 4 | 5 | require github.com/jackc/pgx/v4 v4.17.2 6 | 7 | require ( 8 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 9 | github.com/jackc/pgconn v1.13.0 // indirect 10 | github.com/jackc/pgio v1.0.0 // indirect 11 | github.com/jackc/pgpassfile v1.0.0 // indirect 12 | github.com/jackc/pgproto3/v2 v2.3.1 // indirect 13 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect 14 | github.com/jackc/pgtype v1.12.0 // indirect 15 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect 16 | golang.org/x/text v0.3.7 // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /vercel-go-neon/go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 2 | github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= 3 | github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= 4 | github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= 5 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 6 | github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 7 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 8 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 9 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 10 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 11 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 12 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 13 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 14 | github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= 15 | github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 16 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 17 | github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= 18 | github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 19 | github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= 20 | github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 21 | github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= 22 | github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= 23 | github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= 24 | github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= 25 | github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= 26 | github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= 27 | github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys= 28 | github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI= 29 | github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= 30 | github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= 31 | github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= 32 | github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= 33 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= 34 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= 35 | github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= 36 | github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= 37 | github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= 38 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= 39 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= 40 | github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 41 | github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 42 | github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 43 | github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 44 | github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y= 45 | github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 46 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= 47 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= 48 | github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= 49 | github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= 50 | github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= 51 | github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= 52 | github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w= 53 | github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= 54 | github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= 55 | github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= 56 | github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= 57 | github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= 58 | github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E= 59 | github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw= 60 | github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 61 | github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 62 | github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 63 | github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 64 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 65 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 66 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 67 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 68 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 69 | github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 70 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 71 | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 72 | github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 73 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 74 | github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= 75 | github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 76 | github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= 77 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 78 | github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 79 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 80 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 81 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 82 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 83 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 84 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 85 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 86 | github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= 87 | github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= 88 | github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= 89 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 90 | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= 91 | github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= 92 | github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 93 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 94 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 95 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 96 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 97 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 98 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 99 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 100 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 101 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 102 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 103 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 104 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 105 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 106 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 107 | github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= 108 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 109 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 110 | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 111 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 112 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 113 | go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 114 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 115 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 116 | go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 117 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 118 | go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 119 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 120 | golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= 121 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 122 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 123 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 124 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 125 | golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 126 | golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 127 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 128 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= 129 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 130 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 131 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 132 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 133 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 134 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 135 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 136 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 137 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 138 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 139 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 140 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 141 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 142 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 143 | golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 144 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 145 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 146 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 147 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 148 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 149 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 150 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 151 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 152 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 153 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 154 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 155 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 156 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 157 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 158 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 159 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 160 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 161 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 162 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 163 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 164 | golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 165 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 166 | golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 167 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 168 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 169 | golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 170 | golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 171 | golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 172 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 173 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 174 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 175 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 176 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 177 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 178 | gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= 179 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 180 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 181 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 182 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 183 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 184 | --------------------------------------------------------------------------------