Files
Sankofa/api/src/db/migrations/026_api_keys.ts
defiQUG 8436e22f4c API: Phoenix railing proxy, API key auth for /api/v1/*, schema export, docs, migrations, tests
- Phoenix API Railing: proxy to PHOENIX_RAILING_URL, tenant me routes
- Tenant-auth: X-API-Key support for /api/v1/* (api_keys table)
- Migration 026: api_keys table; 025 sovereign stack marketplace
- GET /graphql/schema, GET /graphql-playground, api/docs OpenAPI
- Integration tests: phoenix-railing.test.ts
- docs/api/API_VERSIONING: /api/v1/ railing alignment
- docs/phoenix/PORTAL_RAILING_WIRING

Made-with: Cursor
2026-03-11 12:57:41 -07:00

46 lines
2.0 KiB
TypeScript

import { Migration } from '../migrate.js'
/**
* API keys table for client/partner API access (key hash, tenant_id, scopes).
* Used by X-API-Key auth for /api/v1/* and Phoenix API Railing.
*/
export const up: Migration['up'] = async (db) => {
await db.query(`
CREATE TABLE IF NOT EXISTS api_keys (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name VARCHAR(255) NOT NULL,
key_prefix VARCHAR(20) NOT NULL,
key_hash VARCHAR(255) NOT NULL UNIQUE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
tenant_id UUID REFERENCES tenants(id) ON DELETE SET NULL,
permissions JSONB DEFAULT '["read", "write"]'::jsonb,
last_used_at TIMESTAMP WITH TIME ZONE,
expires_at TIMESTAMP WITH TIME ZONE,
revoked BOOLEAN NOT NULL DEFAULT false,
revoked_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
)
`)
await db.query(`CREATE INDEX IF NOT EXISTS idx_api_keys_user_id ON api_keys(user_id)`)
await db.query(`CREATE INDEX IF NOT EXISTS idx_api_keys_tenant_id ON api_keys(tenant_id)`)
await db.query(`CREATE INDEX IF NOT EXISTS idx_api_keys_key_hash ON api_keys(key_hash)`)
await db.query(`CREATE INDEX IF NOT EXISTS idx_api_keys_revoked ON api_keys(revoked) WHERE revoked = false`)
await db.query(`
DROP TRIGGER IF EXISTS update_api_keys_updated_at ON api_keys
`)
await db.query(`
CREATE TRIGGER update_api_keys_updated_at BEFORE UPDATE ON api_keys
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column()
`)
}
export const down: Migration['down'] = async (db) => {
await db.query(`DROP TRIGGER IF EXISTS update_api_keys_updated_at ON api_keys`)
await db.query(`DROP INDEX IF EXISTS idx_api_keys_revoked`)
await db.query(`DROP INDEX IF EXISTS idx_api_keys_key_hash`)
await db.query(`DROP INDEX IF EXISTS idx_api_keys_tenant_id`)
await db.query(`DROP INDEX IF EXISTS idx_api_keys_user_id`)
await db.query(`DROP TABLE IF EXISTS api_keys`)
}