feat: build entire backend into single executable (including migrations etc)
This commit is contained in:
parent
f94bc67f3d
commit
ded9d06e4a
12 changed files with 137 additions and 32 deletions
2
Makefile
2
Makefile
|
@ -8,7 +8,7 @@ seeddb:
|
|||
|
||||
.PHONY: backend
|
||||
backend:
|
||||
CGO_ENABLED=0 go build -v -o pronouns -ldflags="-buildid= -X codeberg.org/u1f320/pronouns.cc/backend/server.Revision=`git rev-parse --short HEAD`" ./backend
|
||||
CGO_ENABLED=0 go build -v -o pronouns -ldflags="-buildid= -X codeberg.org/u1f320/pronouns.cc/backend/server.Revision=`git rev-parse --short HEAD` -X codeberg.org/u1f320/pronouns.cc/backend/server.Tag=`git describe --tags --long`" .
|
||||
|
||||
.PHONY: generate
|
||||
generate:
|
||||
|
|
|
@ -63,6 +63,14 @@ PORT=8080 # Port the API will listen on. Default is 8080, this is also default f
|
|||
MINIO_ENDPOINT=localhost:9000 # This always needs to be set, it *does not* need to point to a running MinIO server.
|
||||
```
|
||||
|
||||
## Updating in production
|
||||
|
||||
1. Build the backend with `make backend`
|
||||
2. Build the frontend with `cd frontend && pnpm install && pnpm build`
|
||||
3. Stop the servers: `systemctl stop pronouns-api pronouns-fe`
|
||||
4. Run migrations: `./pronouns database migrate`
|
||||
5. Start the servers: `systemctl start pronouns-api pronouns-fe`
|
||||
|
||||
## License
|
||||
|
||||
Copyright (C) 2022 Sam <u1f320>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package backend
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -12,14 +12,22 @@ import (
|
|||
|
||||
"github.com/go-chi/render"
|
||||
_ "github.com/joho/godotenv/autoload"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var Command = &cli.Command{
|
||||
Name: "web",
|
||||
Usage: "Run the API server",
|
||||
Action: run,
|
||||
}
|
||||
|
||||
func run(c *cli.Context) error {
|
||||
port := ":" + os.Getenv("PORT")
|
||||
|
||||
s, err := server.New()
|
||||
if err != nil {
|
||||
log.Fatalf("Error creating server: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// set render.Decode to a custom one that checks content length
|
||||
|
@ -44,10 +52,12 @@ func main() {
|
|||
case <-ctx.Done():
|
||||
log.Info("Interrupt signal received, shutting down...")
|
||||
s.DB.Close()
|
||||
return
|
||||
return nil
|
||||
case err := <-e:
|
||||
log.Fatalf("Error running server: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const MaxContentLength = 2 * 1024 * 1024
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package backend
|
||||
|
||||
import (
|
||||
"codeberg.org/u1f320/pronouns.cc/backend/routes/auth"
|
||||
|
|
|
@ -16,7 +16,10 @@ import (
|
|||
)
|
||||
|
||||
// Revision is the git commit, filled at build time
|
||||
var Revision = "[unknown]"
|
||||
var (
|
||||
Revision = "[unknown]"
|
||||
Tag = "[unknown]"
|
||||
)
|
||||
|
||||
// Repository is the URL of the git repository
|
||||
const Repository = "https://codeberg.org/u1f320/pronouns.cc"
|
||||
|
|
4
go.mod
4
go.mod
|
@ -19,12 +19,14 @@ require (
|
|||
github.com/minio/minio-go/v7 v7.0.37
|
||||
github.com/rs/xid v1.4.0
|
||||
github.com/rubenv/sql-migrate v1.1.1
|
||||
github.com/urfave/cli/v2 v2.25.0
|
||||
go.uber.org/zap v1.21.0
|
||||
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/go-gorp/gorp/v3 v3.0.2 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
|
@ -47,8 +49,10 @@ require (
|
|||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/tilinna/clock v1.0.2 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
|
||||
|
|
10
go.sum
10
go.sum
|
@ -78,6 +78,8 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7
|
|||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
|
@ -407,6 +409,8 @@ github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThC
|
|||
github.com/rubenv/sql-migrate v1.1.1 h1:haR5Hn8hbW9/SpAICrXoZqXnywS7Q5WijwkQENPeNWY=
|
||||
github.com/rubenv/sql-migrate v1.1.1/go.mod h1:/7TZymwxN8VWumcIxw1jjHEcR1djpdkMHQPT4FWdnbQ=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
|
@ -442,6 +446,10 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tilinna/clock v1.0.2 h1:6BO2tyAC9JbPExKH/z9zl44FLu1lImh3nDNKA0kgrkI=
|
||||
github.com/tilinna/clock v1.0.2/go.mod h1:ZsP7BcY7sEEz7ktc0IVy8Us6boDrK8VradlKRUGfOao=
|
||||
github.com/urfave/cli/v2 v2.25.0 h1:ykdZKuQey2zq0yin/l7JOm9Mh+pg72ngYMeB0ABn6q8=
|
||||
github.com/urfave/cli/v2 v2.25.0/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
|
@ -859,8 +867,8 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gorm.io/driver/postgres v1.0.8/go.mod h1:4eOzrI1MUfm6ObJU/UcmbXyiHSs8jSwH95G5P5dxcAg=
|
||||
gorm.io/gorm v1.20.12/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||
gorm.io/gorm v1.21.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||
|
|
40
main.go
Normal file
40
main.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"codeberg.org/u1f320/pronouns.cc/backend"
|
||||
"codeberg.org/u1f320/pronouns.cc/backend/server"
|
||||
"codeberg.org/u1f320/pronouns.cc/scripts/cleandb"
|
||||
"codeberg.org/u1f320/pronouns.cc/scripts/migrate"
|
||||
"codeberg.org/u1f320/pronouns.cc/scripts/seeddb"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var app = &cli.App{
|
||||
HelpName: "pronouns.cc",
|
||||
Usage: "Pronoun card website and API",
|
||||
Version: server.Tag,
|
||||
Commands: []*cli.Command{
|
||||
backend.Command,
|
||||
{
|
||||
Name: "database",
|
||||
Aliases: []string{"db"},
|
||||
Usage: "Manage the database",
|
||||
Subcommands: []*cli.Command{
|
||||
migrate.Command,
|
||||
seeddb.Command,
|
||||
cleandb.Command,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
|
@ -1,22 +1,28 @@
|
|||
package main
|
||||
package cleandb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
dbpkg "codeberg.org/u1f320/pronouns.cc/backend/db"
|
||||
"github.com/georgysavva/scany/pgxscan"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/rs/xid"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var Command = &cli.Command{
|
||||
Name: "clean",
|
||||
Usage: "Clean deleted tokens + users daily",
|
||||
Action: run,
|
||||
}
|
||||
|
||||
func run(c *cli.Context) error {
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
fmt.Println("error loading .env file:", err)
|
||||
os.Exit(1)
|
||||
return err
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
@ -24,7 +30,7 @@ func main() {
|
|||
db, err := dbpkg.New()
|
||||
if err != nil {
|
||||
fmt.Println("error opening database:", err)
|
||||
os.Exit(1)
|
||||
return err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
|
@ -35,7 +41,7 @@ func main() {
|
|||
ct, err := db.Exec(ctx, "DELETE FROM tokens WHERE invalidated = true OR expires < $1", time.Now())
|
||||
if err != nil {
|
||||
fmt.Println("executing query:", err)
|
||||
os.Exit(1)
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("deleted %v invalidated or expired tokens\n", ct.RowsAffected())
|
||||
|
@ -48,12 +54,12 @@ func main() {
|
|||
ORDER BY id`, time.Now().Add(-dbpkg.SelfDeleteAfter), time.Now().Add(-dbpkg.ModDeleteAfter))
|
||||
if err != nil {
|
||||
fmt.Println("error getting to-be-deleted users:", err)
|
||||
os.Exit(1)
|
||||
return err
|
||||
}
|
||||
|
||||
if len(users) == 0 {
|
||||
fmt.Println("there are no users pending deletion")
|
||||
os.Exit(0)
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, u := range users {
|
||||
|
@ -102,8 +108,9 @@ func main() {
|
|||
ct, err = db.Exec(ctx, "DELETE FROM users WHERE id = ANY($1)", ids)
|
||||
if err != nil {
|
||||
fmt.Printf("error deleting users: %v\n", err)
|
||||
os.Exit(1)
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("deleted %v users!\n", ct.RowsAffected())
|
||||
return nil
|
||||
}
|
||||
|
|
10
scripts/migrate/008_data_exports.sql
Normal file
10
scripts/migrate/008_data_exports.sql
Normal file
|
@ -0,0 +1,10 @@
|
|||
-- +migrate Up
|
||||
|
||||
-- 2023-03-15: Add data export
|
||||
|
||||
create table data_exports (
|
||||
id serial primary key,
|
||||
user_id text not null references users (id) on delete cascade,
|
||||
hash text not null,
|
||||
created_at timestamptz not null default now()
|
||||
);
|
|
@ -1,5 +1,5 @@
|
|||
// migrate runs (forward) migrations
|
||||
package main
|
||||
package migrate
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
"github.com/joho/godotenv"
|
||||
migrate "github.com/rubenv/sql-migrate"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
// SQL driver
|
||||
_ "github.com/jackc/pgx/v4/stdlib"
|
||||
|
@ -17,7 +18,13 @@ import (
|
|||
//go:embed *.sql
|
||||
var migrations embed.FS
|
||||
|
||||
func main() {
|
||||
var Command = &cli.Command{
|
||||
Name: "migrate",
|
||||
Usage: "Migrate the database",
|
||||
Action: run,
|
||||
}
|
||||
|
||||
func run(c *cli.Context) error {
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
fmt.Println("error loading .env file:", err)
|
||||
|
@ -42,7 +49,7 @@ func main() {
|
|||
|
||||
if err := db.Ping(); err != nil {
|
||||
fmt.Println("error pinging database:", err)
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
src := &migrate.EmbedFileSystemMigrationSource{
|
||||
|
@ -54,7 +61,7 @@ func main() {
|
|||
n, err := migrate.Exec(db, "postgres", src, migrate.Up)
|
||||
if err != nil {
|
||||
fmt.Println("error executing migrations:", err)
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
|
@ -62,4 +69,5 @@ func main() {
|
|||
} else {
|
||||
fmt.Printf("executed %v migrations!\n", n)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,28 +1,34 @@
|
|||
package main
|
||||
package seeddb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"codeberg.org/u1f320/pronouns.cc/backend/db"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var Command = &cli.Command{
|
||||
Name: "seed",
|
||||
Usage: "Seed the database with test data",
|
||||
Action: run,
|
||||
}
|
||||
|
||||
func run(c *cli.Context) error {
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
fmt.Println("error loading .env file:", err)
|
||||
os.Exit(1)
|
||||
return err
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ctx := c.Context
|
||||
|
||||
pool, err := pgxpool.Connect(ctx, os.Getenv("DATABASE_URL"))
|
||||
if err != nil {
|
||||
fmt.Println("error opening database:", err)
|
||||
os.Exit(1)
|
||||
return err
|
||||
}
|
||||
defer pool.Close()
|
||||
|
||||
|
@ -33,19 +39,19 @@ func main() {
|
|||
tx, err := pg.Begin(ctx)
|
||||
if err != nil {
|
||||
fmt.Println("error beginning transaction:", err)
|
||||
os.Exit(1)
|
||||
return err
|
||||
}
|
||||
|
||||
u, err := pg.CreateUser(ctx, tx, "test")
|
||||
if err != nil {
|
||||
fmt.Println("error creating user:", err)
|
||||
os.Exit(1)
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = pg.UpdateUser(ctx, tx, u.ID, ptr("testing"), ptr("This is a bio!"), &[]string{"https://pronouns.cc"}, nil)
|
||||
if err != nil {
|
||||
fmt.Println("error setting user info:", err)
|
||||
os.Exit(1)
|
||||
return err
|
||||
}
|
||||
|
||||
err = pg.SetUserNamesPronouns(ctx, tx, u.ID, []db.FieldEntry{
|
||||
|
@ -57,7 +63,7 @@ func main() {
|
|||
})
|
||||
if err != nil {
|
||||
fmt.Println("error setting pronouns:", err)
|
||||
os.Exit(1)
|
||||
return err
|
||||
}
|
||||
|
||||
err = pg.SetUserFields(ctx, tx, u.ID, []db.Field{
|
||||
|
@ -114,16 +120,17 @@ func main() {
|
|||
})
|
||||
if err != nil {
|
||||
fmt.Println("error setting fields:", err)
|
||||
os.Exit(1)
|
||||
return err
|
||||
}
|
||||
|
||||
err = tx.Commit(ctx)
|
||||
if err != nil {
|
||||
fmt.Println("error committing transaction:", err)
|
||||
os.Exit(1)
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Created testing user with ID", u.ID, "and name", u.Username)
|
||||
return nil
|
||||
}
|
||||
|
||||
func ptr[T any](v T) *T {
|
||||
|
|
Loading…
Reference in a new issue