diff --git a/.gitignore b/.gitignore index 3f6b01e..29d4243 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,3 @@ -/data.db -/data.db-* -/.test.db -/.test.db-* /bin/ /tmp/ .env diff --git a/internal/store/postgres.go b/internal/store/postgres.go index bd14f7d..44b1069 100644 --- a/internal/store/postgres.go +++ b/internal/store/postgres.go @@ -10,17 +10,13 @@ import ( _ "github.com/jackc/pgx/v5/stdlib" ) -// applySchema runs semicolon-separated DDL statements, skipping PRAGMA lines. +// applySchema runs semicolon-separated DDL statements. func applySchema(db *sql.DB, schema string) error { for _, stmt := range strings.Split(schema, ";") { stmt = strings.TrimSpace(stmt) if stmt == "" { continue } - upper := strings.ToUpper(stmt) - if strings.HasPrefix(upper, "PRAGMA") { - continue - } if _, err := db.Exec(stmt); err != nil { return fmt.Errorf("%w: %s", err, stmt) } diff --git a/schema.sql b/schema.sql index 29bd809..e3a8e89 100644 --- a/schema.sql +++ b/schema.sql @@ -1,5 +1,3 @@ -PRAGMA foreign_keys = ON; - CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY, username TEXT NOT NULL UNIQUE, diff --git a/todo.md b/todo.md index b4d0601..5cac465 100644 --- a/todo.md +++ b/todo.md @@ -4,24 +4,23 @@ From the project review. Priority order within each section. ## Fix soon -- [x] **Persist sessions** — Sessions live in the app DB (`sessions` table): SQLite locally, `postgresstore` when `DATABASE_URL` is set. Opaque cookie unchanged; unused `SESSION_SECRET` removed from config / `.env.example`. +- [x] **Persist sessions** — Sessions live in the app DB (`sessions` table) via `postgresstore`. Opaque cookie unchanged; unused `SESSION_SECRET` removed from config / `.env.example`. - [x] **Drop Dockerfile** — Deploying on DigitalOcean App Platform (buildpack from `go.mod`); no container image needed. - [ ] **Rune-safe truncation** — `title[:120]`, `body[:8000]`, `city[:80]`, answer body, etc. can split multi-byte UTF-8. Truncate by runes (or safely). - [x] **Admin bootstrap** — `ADMIN_USERNAME` seeds the first admin on register only when no admin exists. Promote/demote via `/admin/users` (admins only); roles stay in `users.role`. ## Docs & ops -- [ ] **README** — How to run locally, env vars (from `.env.example`), admin bootstrap, SQLite vs PlanetScale `DATABASE_URL`, App Platform notes (`PORT`, `SECURE_COOKIE=1`). -- [ ] **Migrations story** — Schema is applied on boot from `schema.sql` (+ sessions DDL). OK for v1; plan real migrations before schema drifts between SQLite and Postgres. +- [ ] **README** — How to run locally, env vars (from `.env.example`), admin bootstrap, PlanetScale `DATABASE_URL`, App Platform notes (`PORT`, `SECURE_COOKIE=1`). +- [ ] **Migrations story** — Schema is applied on boot from `schema.sql` (+ sessions DDL). OK for v1; plan real migrations before schema drifts. - [x] **App Platform listen port** — Prefers `PORT`, then `LISTEN`, then `:8080`. -- [x] **Prod DB = PlanetScale Postgres** — App already opens Postgres when `DATABASE_URL` is set; DSN cleanup strips PlanetScale/libpq-only params (`sslrootcert=system`, `sslnegotiation`). Use dashboard URI on **5432** for boot schema create; **6432** (PgBouncer) later if you need pooling. +- [x] **Prod DB = PlanetScale Postgres** — App opens Postgres via required `DATABASE_URL`; DSN cleanup strips PlanetScale/libpq-only params (`sslrootcert=system`, `sslnegotiation`). Use dashboard URI on **5432** for boot schema create; **6432** (PgBouncer) later if you need pooling. ## Smaller / later - [ ] Rate-limit login/register (bcrypt helps; still open to brute-force). - [ ] Graceful shutdown instead of bare `ListenAndServe`. - [ ] More tests: vote HTMX paths, admin answer/hide, archive redirects; optional Postgres integration test. -- [ ] Watch dual-dialect schema — one SQL file works now; expect divergence later. ## Suggested order of attack