feat: build entire backend into single executable (including migrations etc)

This commit is contained in:
Sam 2023-03-15 10:04:48 +01:00
parent f94bc67f3d
commit ded9d06e4a
No known key found for this signature in database
GPG Key ID: B4EF20DDE721CAA1
12 changed files with 137 additions and 32 deletions

View File

@ -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:

View File

@ -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>

View File

@ -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

View File

@ -1,4 +1,4 @@
package main
package backend
import (
"codeberg.org/u1f320/pronouns.cc/backend/routes/auth"

View File

@ -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
View File

@ -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
View File

@ -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
View 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)
}
}

View File

@ -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
}

View 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()
);

View File

@ -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
}

View File

@ -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 {