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
This commit is contained in:
45
api/src/db/migrations/026_api_keys.ts
Normal file
45
api/src/db/migrations/026_api_keys.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
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`)
|
||||
}
|
||||
Reference in New Issue
Block a user