diff --git a/.github/labeler.yml b/.github/labeler.yml index 97d6cae..af4cd14 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,32 +1,24 @@ # Labels applied automatically by actions/labeler@v5. # Matching uses glob patterns; any matching file in the PR triggers the label. -# Manually-applied labels (bug, feature, breaking-change, etc.) are not listed here. +# Manually-applied labels (type, priority, needs, blocked, etc.) are not listed here. -backend: +"area: backend": - changed-files: - any-glob-to-any-file: ["backend/**"] -web: +"area: web": - changed-files: - any-glob-to-any-file: ["web/**"] -mobile: +"area: mobile": - changed-files: - any-glob-to-any-file: ["mobile/**"] -documentation: - - changed-files: - - any-glob-to-any-file: - - "**/*.md" - - "backend/docs/**" - - "web/docs/**" - - "mobile/docs/**" - -ci: +"area: infra": - changed-files: - any-glob-to-any-file: [".github/**"] -dependencies: +"type: chore": - changed-files: - any-glob-to-any-file: - "backend/go.mod" diff --git a/CLAUDE.md b/CLAUDE.md index 697b370..a32fa78 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -24,18 +24,30 @@ cd mobile && ./gradlew lint && ./gradlew test # mobile quality gate cd mobile && ./gradlew connectedAndroidTest # instrumented tests (emulator/device required) ``` +## Migration commands (run from backend/) +```bash +make migrate-create name= # create a new timestamped SQL migration file +make migrate-status # show applied vs. pending migrations +make migrate-up # apply all pending migrations +make migrate-up-one # apply the next pending migration only +make migrate-down # roll back the last applied migration +make migrate-reset # roll back everything to version 0 +make migrate-version # print current schema version +``` + ## Feature development workflow — always follow this 1. **Check docs first** — delegate to `docs` agent: find relevant topic docs, verify they match the current code 2. **Fix stale docs** — if docs diverge from code, update docs before implementing -3. **Implement** — delegate to `backend` or `web` agent, passing the relevant doc content as context -4. **Update docs** — delegate to `docs` agent: update `last_verified`, add new topics if introduced -5. **Quality gate** — run `/project:check` before declaring done +3. **Migrate** — if the feature needs new or changed tables: `make migrate-create name=`, write the SQL, `make migrate-up` +4. **Implement** — delegate to `backend` or `web` agent, passing the relevant doc content as context +5. **Update docs** — delegate to `docs` agent: update `last_verified`, add new topics if introduced +6. **Quality gate** — run `/project:check` before declaring done Use `/project:implement` to run this workflow end-to-end. ## Documentation locations ``` -backend/docs/ # database, routing, testing, error-handling, environment +backend/docs/ # database, migrations, routing, testing, error-handling, environment web/docs/ # routing, data-fetching, styling, components mobile/docs/ # compose-conventions, architecture, testing ``` @@ -59,6 +71,8 @@ Each doc file has `last_verified` and `sources` frontmatter. The `docs` agent ma ``` backend/ cmd/api/main.go # entry point — wires layers, graceful shutdown + cmd/migrate/main.go # migration CLI — wraps goose, reads BLUEPRINT_DB_* env vars + migrations/ # SQL migration files (goose) — YYYYMMDDHHMMSS_.sql internal/ domain/ # Layer 1: entities + repository interfaces (no external deps) health.go # HealthStats type @@ -98,6 +112,14 @@ mobile/ - Return errors up the stack. Never `log.Fatal` or `os.Exit` inside `internal/`. - Run `go vet ./...` before committing. +## Migrations (goose) +- Any feature that introduces or changes a table **must** include a goose migration. +- `make migrate-create name=` → edit the generated SQL → `make migrate-up`. +- SQL only — no Go migrations. No DDL inside repository methods. +- Never edit or delete an applied migration; add a new one to fix it. +- Integration tests do NOT run migrations — Testcontainers starts blank and tests create their own schema via `testDB.Exec`. +- See `backend/docs/migrations.md` for the full workflow and format rules. + ## TypeScript/React conventions - App Router only. Default to Server Components. - Add `"use client"` only when browser APIs or React hooks are required. diff --git a/backend/Makefile b/backend/Makefile index c46b538..8ffe711 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -5,13 +5,12 @@ all: build test build: @echo "Building..." - - @go build -o main.exe cmd/api/main.go # Run the application run: @go run cmd/api/main.go + # Create DB container docker-run: @docker compose up --build @@ -24,7 +23,8 @@ docker-down: test: @echo "Testing..." @go test ./... -v -# Integrations Tests for the application + +# Integration tests (requires Docker) itest: @echo "Running integration tests..." @go test ./internal/repository/postgres/... -v @@ -46,4 +46,32 @@ watch: Write-Output 'Watching...'; \ }" -.PHONY: all build run test clean watch docker-run docker-down itest +# ---- Migrations (goose) ---- + +migrate-up: + @go run ./cmd/migrate up + +migrate-up-one: + @go run ./cmd/migrate up-by-one + +migrate-down: + @go run ./cmd/migrate down + +migrate-down-to: + @go run ./cmd/migrate down-to $(version) + +migrate-reset: + @go run ./cmd/migrate down-to 0 + +migrate-status: + @go run ./cmd/migrate status + +migrate-version: + @go run ./cmd/migrate version + +migrate-create: + @go run ./cmd/migrate create $(name) + +.PHONY: all build run test clean watch docker-run docker-down itest \ + migrate-up migrate-up-one migrate-down migrate-down-to migrate-reset \ + migrate-status migrate-version migrate-create diff --git a/backend/cmd/migrate/main.go b/backend/cmd/migrate/main.go new file mode 100644 index 0000000..be8d25d --- /dev/null +++ b/backend/cmd/migrate/main.go @@ -0,0 +1,60 @@ +package main + +import ( + "context" + "fmt" + "log" + "os" + + _ "github.com/joho/godotenv/autoload" + "github.com/pressly/goose/v3" + + "backend/internal/repository/postgres" +) + +const migrationsDir = "migrations" + +func main() { + if len(os.Args) < 2 { + fmt.Fprintln(os.Stderr, "usage: migrate [args]") + fmt.Fprintln(os.Stderr, "commands: up, up-by-one, down, down-to , reset, status, version, create ") + os.Exit(1) + } + + if err := goose.SetDialect("postgres"); err != nil { + log.Fatalf("migrate: set dialect: %v", err) + } + + command, args := os.Args[1], os.Args[2:] + + // create only touches the filesystem — connect to DB is not needed + if command == "create" { + if len(args) == 0 { + log.Fatal("migrate create: name is required (e.g. make migrate-create name=add_users)") + } + if err := goose.Create(nil, migrationsDir, args[0], "sql"); err != nil { + log.Fatalf("migrate create: %v", err) + } + return + } + + cfg := postgres.DBConfig{ + Host: os.Getenv("BLUEPRINT_DB_HOST"), + Port: os.Getenv("BLUEPRINT_DB_PORT"), + Database: os.Getenv("BLUEPRINT_DB_DATABASE"), + Username: os.Getenv("BLUEPRINT_DB_USERNAME"), + Password: os.Getenv("BLUEPRINT_DB_PASSWORD"), + Schema: os.Getenv("BLUEPRINT_DB_SCHEMA"), + SSLMode: os.Getenv("BLUEPRINT_DB_SSLMODE"), + } + + db, err := postgres.NewPostgresDB(cfg) + if err != nil { + log.Fatalf("migrate: database: %v", err) + } + defer db.Close() + + if err := goose.RunContext(context.Background(), command, db, migrationsDir, args...); err != nil { + log.Fatalf("migrate %s: %v", command, err) + } +} diff --git a/backend/docs/_index.md b/backend/docs/_index.md index e4d3145..c6cd9d1 100644 --- a/backend/docs/_index.md +++ b/backend/docs/_index.md @@ -6,6 +6,7 @@ The `docs` agent reads this index first to locate the right file before diving i | Topic | File | Source files covered | |---|---|---| | Database connection & query patterns | [database.md](database.md) | `internal/repository/postgres/db.go`, `internal/repository/postgres/health_repository.go`, `internal/domain/health.go`, `internal/usecase/health_usecase.go` | +| Schema migrations (goose) | [migrations.md](migrations.md) | `cmd/migrate/main.go`, `migrations/`, `Makefile` | | HTTP routing & handler patterns | [routing.md](routing.md) | `internal/handler/handler.go`, `internal/handler/routes.go`, `internal/handler/hello_handler.go`, `internal/handler/health_handler.go`, `internal/server/server.go` | | Integration testing with Testcontainers | [testing.md](testing.md) | `internal/repository/postgres/health_repository_test.go`, `internal/handler/hello_handler_test.go` | | Error handling conventions | [error-handling.md](error-handling.md) | `internal/repository/postgres/health_repository.go`, `internal/handler/health_handler.go`, `cmd/api/main.go` | diff --git a/backend/docs/migrations.md b/backend/docs/migrations.md new file mode 100644 index 0000000..a599abe --- /dev/null +++ b/backend/docs/migrations.md @@ -0,0 +1,72 @@ +--- +topic: migrations +last_verified: 2026-06-14 +sources: + - cmd/migrate/main.go + - migrations/ + - Makefile +--- + +# Migrations + +## Tool +goose v3 (`github.com/pressly/goose/v3`). +Entry point: `cmd/migrate/main.go` — a thin wrapper that reuses `postgres.NewPostgresDB` and reads the same `BLUEPRINT_DB_*` env vars as the server. No separate goose binary installation needed. + +## File location +`backend/migrations/` — SQL files only. Naming: `YYYYMMDDHHMMSS_.sql`, created automatically by `make migrate-create`. + +## Makefile targets +| Target | What it does | +|---|---| +| `make migrate-create name=` | Create a new timestamped SQL file in `migrations/` | +| `make migrate-status` | Show applied vs. pending migrations | +| `make migrate-up` | Apply all pending migrations | +| `make migrate-up-one` | Apply only the next pending migration | +| `make migrate-down` | Roll back the last applied migration | +| `make migrate-down-to version=N` | Roll back to a specific version number | +| `make migrate-reset` | Roll back all migrations to version 0 | +| `make migrate-version` | Print the current schema version | + +## SQL migration format +Each file must have exactly one `-- +goose Up` annotation. `-- +goose Down` is optional but should always be included. + +```sql +-- +goose Up +CREATE TABLE users ( + id BIGSERIAL PRIMARY KEY, + email TEXT NOT NULL UNIQUE, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +-- +goose Down +DROP TABLE users; +``` + +Rules: +- Every statement must end with `;` +- DDL only — no application data mutations in migrations +- For multi-statement blocks (PL/pgSQL), wrap with `-- +goose StatementBegin` / `-- +goose StatementEnd` +- Avoid `-- +goose NO TRANSACTION` unless the statement genuinely cannot run in a transaction (e.g. `CREATE INDEX CONCURRENTLY`) + +## Workflow for any new table +1. `make migrate-create name=add_` — generates the timestamped file +2. Fill in `CREATE TABLE` (Up) and `DROP TABLE` (Down) +3. `make migrate-up` — applies to local DB +4. Build the repository layer against the new schema +5. `make itest` to verify integration tests pass + +## Go migrations — not supported +Go migrations require the migration functions to be registered and compiled into the binary. `go run ./cmd/migrate` produces a fresh binary on each invocation with no registered functions. Use SQL migrations for all schema changes. + +## Testcontainers and migrations +Repository integration tests do **not** run goose migrations. Testcontainers starts a blank Postgres instance; tests create their own schema via `testDB.Exec(...)` in `TestMain` or per-test setup. This keeps tests independent of migration history and fast. + +## Goose tracking table +Goose creates `goose_db_version` in the schema set by `BLUEPRINT_DB_SCHEMA` (via `search_path` in the connection string). Never modify this table manually. + +## Hard rules +- No DDL inside Go code — `CREATE TABLE` belongs in a migration file, not in a repository method +- No Go migration files — SQL only +- Never edit or delete an applied migration — add a new one to correct it diff --git a/backend/go.mod b/backend/go.mod index 7696602..9d21ac3 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -1,12 +1,13 @@ module backend -go 1.25.6 +go 1.25.7 require ( github.com/gin-contrib/cors v1.7.7 github.com/gin-gonic/gin v1.12.0 github.com/jackc/pgx/v5 v5.10.0 github.com/joho/godotenv v1.5.1 + github.com/pressly/goose/v3 v3.27.1 github.com/testcontainers/testcontainers-go v0.42.0 github.com/testcontainers/testcontainers-go/modules/postgres v0.42.0 ) @@ -28,7 +29,7 @@ require ( github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/docker/go-connections v0.6.0 // indirect + github.com/docker/go-connections v0.7.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect @@ -53,10 +54,11 @@ require ( github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.10 // indirect github.com/mattn/go-isatty v0.0.22 // indirect + github.com/mfridman/interpolate v0.0.2 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/go-archive v0.2.0 // indirect - github.com/moby/moby/api v1.54.1 // indirect - github.com/moby/moby/client v0.4.0 // indirect + github.com/moby/moby/api v1.54.2 // indirect + github.com/moby/moby/client v0.4.1 // indirect github.com/moby/patternmatcher v0.6.1 // indirect github.com/moby/sys/sequential v0.6.0 // indirect github.com/moby/sys/user v0.4.0 // indirect @@ -71,6 +73,7 @@ require ( github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/quic-go v0.60.0 // indirect + github.com/sethvargo/go-retry v0.3.0 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/sirupsen/logrus v1.9.4 // indirect github.com/stretchr/testify v1.11.1 // indirect @@ -81,10 +84,11 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.mongodb.org/mongo-driver/v2 v2.6.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect - go.opentelemetry.io/otel v1.41.0 // indirect - go.opentelemetry.io/otel/metric v1.41.0 // indirect - go.opentelemetry.io/otel/trace v1.41.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect + go.opentelemetry.io/otel v1.43.0 // indirect + go.opentelemetry.io/otel/metric v1.43.0 // indirect + go.opentelemetry.io/otel/trace v1.43.0 // indirect + go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.28.0 // indirect golang.org/x/crypto v0.53.0 // indirect golang.org/x/net v0.56.0 // indirect diff --git a/backend/go.sum b/backend/go.sum index 2ed3e5c..4913d03 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -35,10 +35,12 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= -github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= +github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c= +github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -108,14 +110,16 @@ github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= github.com/mdelapenya/tlscert v0.2.0 h1:7H81W6Z/4weDvZBNOfQte5GpIMo0lGYEeWbkGp5LJHI= github.com/mdelapenya/tlscert v0.2.0/go.mod h1:O4njj3ELLnJjGdkN7M/vIVCpZ+Cf0L6muqOG4tLSl8o= +github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY= +github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8= github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU= -github.com/moby/moby/api v1.54.1 h1:TqVzuJkOLsgLDDwNLmYqACUuTehOHRGKiPhvH8V3Nn4= -github.com/moby/moby/api v1.54.1/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= -github.com/moby/moby/client v0.4.0 h1:S+2XegzHQrrvTCvF6s5HFzcrywWQmuVnhOXe2kiWjIw= -github.com/moby/moby/client v0.4.0/go.mod h1:QWPbvWchQbxBNdaLSpoKpCdf5E+WxFAgNHogCWDoa7g= +github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg= +github.com/moby/moby/api v1.54.2/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= +github.com/moby/moby/client v0.4.1 h1:DMQgisVoMkmMs7fp3ROSdiBnoAu8+vo3GggFl06M/wY= +github.com/moby/moby/client v0.4.1/go.mod h1:z52C9O2POPOsnxZAy//WtKcQ32P+jT/NGeXu/7nfjGQ= github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U= github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= @@ -131,6 +135,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= +github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= @@ -141,14 +147,20 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/pressly/goose/v3 v3.27.1 h1:6uEvcprBybDmW4hcz3gYujhARhye+GoWKhEWyzD5sh4= +github.com/pressly/goose/v3 v3.27.1/go.mod h1:maruOxsPnIG2yHHyo8UqKWXYKFcH7Q76csUV7+7KYoM= github.com/quic-go/go-ossfuzz-seeds v0.1.0 h1:APacT+iIaNF6fd8AGEiN3bT/Jtkd2jz4v4TzM7MFjy0= github.com/quic-go/go-ossfuzz-seeds v0.1.0/go.mod h1:3IOHRbJIc+L6YKMwfDtJAM9Vj9k0YY4muhuyUYk5tbk= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= github.com/quic-go/quic-go v0.60.0 h1:xcQioE8OM66UQLeUMHltK1CCcOu3JbVB4JAQdDQSB+0= github.com/quic-go/quic-go v0.60.0/go.mod h1:wpKpjmPpftl30sL6pFh7REVpjbcCVy4zt2vDyK1TuJk= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= +github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= @@ -185,20 +197,22 @@ go.mongodb.org/mongo-driver/v2 v2.6.0 h1:b9sJOYrkmt4l8bY43ZenFBcPlhYIjaOfYHLtbB/ go.mongodb.org/mongo-driver/v2 v2.6.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= -go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c= -go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE= -go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ= -go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= -go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0= -go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= +go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/arch v0.28.0 h1:wVwVdqsTuUbJvhYVCspQYwZXHNYeLSoZnmHD+ggddpQ= golang.org/x/arch v0.28.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8= golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= @@ -227,5 +241,13 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= +modernc.org/libc v1.72.1 h1:db1xwJ6u1kE3KHTFTTbe2GCrczHPKzlURP0aDC4NGD0= +modernc.org/libc v1.72.1/go.mod h1:HRMiC/PhPGLIPM7GzAFCbI+oSgE3dhZ8FWftmRrHVlY= +modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= +modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= +modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= +modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= +modernc.org/sqlite v1.49.1 h1:dYGHTKcX1sJ+EQDnUzvz4TJ5GbuvhNJa8Fg6ElGx73U= +modernc.org/sqlite v1.49.1/go.mod h1:m0w8xhwYUVY3H6pSDwc3gkJ/irZT/0YEXwBlhaxQEew= pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= diff --git a/backend/migrations/.gitkeep b/backend/migrations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/backend/migrations/20260614201325_init.sql b/backend/migrations/20260614201325_init.sql new file mode 100644 index 0000000..35d0d0e --- /dev/null +++ b/backend/migrations/20260614201325_init.sql @@ -0,0 +1,10 @@ +-- +goose Up +CREATE TABLE IF NOT EXISTS users ( + id BIGSERIAL PRIMARY KEY, + name TEXT NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +-- +goose Down +DROP TABLE IF EXISTS users;