chore: starter template
This commit is contained in:
21
db/annotations.ts
Normal file
21
db/annotations.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
// Column annotations — compiled by `pikku db migrate` into db/annotations.gen.json,
|
||||
// which drives BOTH the generated types/zod AND runtime value coercion.
|
||||
//
|
||||
// Declare a `kind` for every semantic date/bool/json column you add:
|
||||
// - kind: 'date' → the column types as `Date` end-to-end. Write real `Date`
|
||||
// objects, read back `Date` objects; the TEXT storage is coerced both ways.
|
||||
// - kind: 'bool' → types as `boolean` over the INTEGER 0/1 storage.
|
||||
// - kind: 'json' → parsed/serialized automatically.
|
||||
// Without a kind, a SQLite date column is just `string` (write ISO strings via
|
||||
// `new Date().toISOString()`) and a flag is `number` — that default is fine too;
|
||||
// just follow the generated type, never fight it with `new Date()` writes or casts.
|
||||
//
|
||||
// Optional per column: security ('public' | 'private' | 'pii' | 'secret'),
|
||||
// classification (anonymize strategy), tsType, format, description.
|
||||
export const classifications = {
|
||||
// Example — after adding a `todos` table in a migration:
|
||||
// todos: {
|
||||
// created_at: { kind: 'date' },
|
||||
// done: { kind: 'bool' },
|
||||
// },
|
||||
}
|
||||
3
db/sqlite-seed.sql
Normal file
3
db/sqlite-seed.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
-- Dev seed (applied via `pikku db seed` after `pikku db migrate`).
|
||||
-- The starter is auth-only, so there's nothing to seed yet. Add your own
|
||||
-- INSERT … ON CONFLICT DO NOTHING rows here as you grow the schema.
|
||||
6
db/sqlite-test-seed.sql
Normal file
6
db/sqlite-test-seed.sql
Normal file
@@ -0,0 +1,6 @@
|
||||
-- Test-only seed: a minimal user row so getSession resolves against `user`.
|
||||
-- This file is only used by the function-tests harness (never in the sandbox
|
||||
-- or production).
|
||||
INSERT INTO "user" (id, name, email, email_verified, created_at, updated_at)
|
||||
VALUES ('user-001', 'Test User', 'test@example.com', 1, datetime('now'), datetime('now'))
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
67
db/sqlite/0001-init.sql
Normal file
67
db/sqlite/0001-init.sql
Normal file
@@ -0,0 +1,67 @@
|
||||
PRAGMA foreign_keys = ON;
|
||||
|
||||
-- Better Auth core schema. These four tables (user, session, account,
|
||||
-- verification) are owned and written entirely by Better Auth — the app never
|
||||
-- inserts into them directly; sign-up / sign-in / sessions all flow through the
|
||||
-- generated `/api/auth/**` routes (see src/auth.ts).
|
||||
--
|
||||
-- The schema is generated by Better Auth's own migration generator
|
||||
-- (`getMigrations(...).compileMigrations()`); the columns are spelled in
|
||||
-- snake_case to match the rest of the app's DB convention. Better Auth is handed
|
||||
-- the app's kysely (with CamelCasePlugin) in src/auth.ts, so its camelCase field
|
||||
-- names (emailVerified, userId, createdAt, ...) compile to exactly these
|
||||
-- snake_case columns at runtime. To change this schema (add a provider field, a
|
||||
-- plugin table, ...), re-run the generator rather than hand-editing.
|
||||
--
|
||||
-- Email + password is the default sign-in method. The credential hash is stored
|
||||
-- by Better Auth in account.password (provider_id = 'credential'), never on the
|
||||
-- user row.
|
||||
CREATE TABLE IF NOT EXISTS "user" (
|
||||
"id" text not null primary key,
|
||||
"name" text not null,
|
||||
"email" text not null unique,
|
||||
"email_verified" integer not null,
|
||||
"image" text,
|
||||
"created_at" date not null,
|
||||
"updated_at" date not null
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "session" (
|
||||
"id" text not null primary key,
|
||||
"expires_at" date not null,
|
||||
"token" text not null unique,
|
||||
"created_at" date not null,
|
||||
"updated_at" date not null,
|
||||
"ip_address" text,
|
||||
"user_agent" text,
|
||||
"user_id" text not null references "user" ("id") on delete cascade
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "account" (
|
||||
"id" text not null primary key,
|
||||
"account_id" text not null,
|
||||
"provider_id" text not null,
|
||||
"user_id" text not null references "user" ("id") on delete cascade,
|
||||
"access_token" text,
|
||||
"refresh_token" text,
|
||||
"id_token" text,
|
||||
"access_token_expires_at" date,
|
||||
"refresh_token_expires_at" date,
|
||||
"scope" text,
|
||||
"password" text,
|
||||
"created_at" date not null,
|
||||
"updated_at" date not null
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "verification" (
|
||||
"id" text not null primary key,
|
||||
"identifier" text not null,
|
||||
"value" text not null,
|
||||
"expires_at" date not null,
|
||||
"created_at" date not null,
|
||||
"updated_at" date not null
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "session_user_id_idx" ON "session" ("user_id");
|
||||
CREATE INDEX IF NOT EXISTS "account_user_id_idx" ON "account" ("user_id");
|
||||
CREATE INDEX IF NOT EXISTS "verification_identifier_idx" ON "verification" ("identifier");
|
||||
26
db/sqlite/0002-audit.sql
Normal file
26
db/sqlite/0002-audit.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
-- Audit log. Fabric's audit service flushes events here via a per-stage
|
||||
-- CF Queue. Add this table if you want audit capture; remove it (and skip
|
||||
-- this migration) if your project has no audit requirements.
|
||||
CREATE TABLE IF NOT EXISTS audit (
|
||||
audit_id TEXT NOT NULL PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
|
||||
occurred_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
type TEXT NOT NULL,
|
||||
source TEXT NOT NULL DEFAULT 'auto',
|
||||
outcome TEXT,
|
||||
function_id TEXT,
|
||||
wire_type TEXT,
|
||||
trace_id TEXT,
|
||||
transaction_id TEXT,
|
||||
query_id TEXT,
|
||||
actor_user_id TEXT,
|
||||
actor_org_id TEXT,
|
||||
tables TEXT, -- JSON array of table names touched
|
||||
changed_cols TEXT, -- JSON array of changed column names
|
||||
event TEXT, -- custom event label
|
||||
old TEXT, -- JSON: previous values
|
||||
data TEXT -- JSON: new values / event payload
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_occurred_at ON audit (occurred_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_actor ON audit (actor_user_id) WHERE actor_user_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_function ON audit (function_id) WHERE function_id IS NOT NULL;
|
||||
11
db/sqlite/0003-user-actor.sql
Normal file
11
db/sqlite/0003-user-actor.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
-- =============================================================================
|
||||
-- BETTER AUTH ACTOR PLUGIN — user-flow actors
|
||||
-- Adds the `actor` boolean the Better Auth `actor()` plugin declares so its
|
||||
-- schema is covered by an explicit migration and the db drift check passes.
|
||||
-- Actor rows are synthetic users signed in by pikkuUserFlow via
|
||||
-- POST /api/auth/sign-in/actor with the server-held USER_FLOW_ACTOR_SECRET;
|
||||
-- the flag rides into the pikku core session so audits/analytics can address
|
||||
-- synthetic traffic. A non-actor user can never be signed in this way.
|
||||
-- =============================================================================
|
||||
|
||||
ALTER TABLE "user" ADD COLUMN "actor" integer NOT NULL DEFAULT 0;
|
||||
16
db/sqlite/0004-admin.sql
Normal file
16
db/sqlite/0004-admin.sql
Normal file
@@ -0,0 +1,16 @@
|
||||
-- =============================================================================
|
||||
-- BETTER AUTH ADMIN PLUGIN — user administration
|
||||
-- Adds the columns the Better Auth `admin()` plugin declares so its schema is
|
||||
-- covered by an explicit migration and the db drift check passes. The plugin
|
||||
-- exposes /api/auth/admin/* (listUsers, setRole, ban, impersonateUser, …);
|
||||
-- `impersonated_by` marks a session created via "view as user" so it can be
|
||||
-- reverted with admin.stopImpersonating. All fields are optional — a user with
|
||||
-- `role` = 'admin' is an administrator; everyone else stays a normal user.
|
||||
-- =============================================================================
|
||||
|
||||
ALTER TABLE "user" ADD COLUMN "role" text;
|
||||
ALTER TABLE "user" ADD COLUMN "banned" integer DEFAULT 0;
|
||||
ALTER TABLE "user" ADD COLUMN "ban_reason" text;
|
||||
ALTER TABLE "user" ADD COLUMN "ban_expires" date;
|
||||
|
||||
ALTER TABLE "session" ADD COLUMN "impersonated_by" text;
|
||||
13
db/sqlite/0005-fabric.sql
Normal file
13
db/sqlite/0005-fabric.sql
Normal file
@@ -0,0 +1,13 @@
|
||||
-- =============================================================================
|
||||
-- BETTER AUTH FABRIC PLUGIN — Fabric operator admin sessions
|
||||
-- Adds the `fabric` boolean the Better Auth `fabric()` plugin declares so its
|
||||
-- schema is covered by an explicit migration and the db drift check passes.
|
||||
-- Fabric rows are synthetic operator users minted by POST /api/auth/sign-in/fabric
|
||||
-- after verifying a short-lived RS256 token the Fabric control plane signed
|
||||
-- (checked against FABRIC_AUTH_PUBLIC_KEY). They are created with role 'admin' so
|
||||
-- the console Users tab can list/impersonate real end-users WITHOUT the operator
|
||||
-- being one of them; these rows are filtered out of any end-user listing. A real
|
||||
-- user can never be signed in this way.
|
||||
-- =============================================================================
|
||||
|
||||
ALTER TABLE "user" ADD COLUMN "fabric" integer NOT NULL DEFAULT 0;
|
||||
Reference in New Issue
Block a user