fase(25-26): keyboard shortcuts, mobile responsive, enterprise SSO/audit
- Phase 25.4: N shortcut for new exploration on dashboard (react-hotkeys-hook) - Phase 25.5: overflow-x-auto on tables, responsive padding (p-4 md:p-6) - Phase 26: SAML/OIDC/LDAP providers (build-fixed), TOTP/MFA service - Phase 26: KyselySSOConfigRepository + KyselyTOTPRepository - Phase 26: SSO HTTP controller (config CRUD + MFA setup/verify/disable) - Phase 26: Audit module index.ts + SSO module index.ts - Phase 26: Session management endpoints (findByUserId, deleteById, list/revoke) - Phase 26: SSO and audit routes feature-gated (auth:sso, audit:logs) - Phase 26: Frontend SSOSection (SAML/OIDC/LDAP config + TOTP setup) - Phase 26: Frontend SessionsSection (list/revoke active sessions) - Phase 26: Settings navigation updated with SSO & Sessions sections Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
50
dist/db/migrations/007_enterprise_tables.js
vendored
Normal file
50
dist/db/migrations/007_enterprise_tables.js
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.up = up;
|
||||
exports.down = down;
|
||||
const kysely_1 = require("kysely");
|
||||
async function up(db) {
|
||||
// SSO configurations per organization
|
||||
await db.schema
|
||||
.createTable('sso_configs')
|
||||
.ifNotExists()
|
||||
.addColumn('id', 'text', (c) => c.primaryKey())
|
||||
.addColumn('organization_id', 'text', (c) => c.notNull())
|
||||
.addColumn('provider', 'text', (c) => c.notNull())
|
||||
.addColumn('enabled', 'integer', (c) => c.notNull().defaultTo(1))
|
||||
.addColumn('config_json', 'text', (c) => c.notNull().defaultTo('{}'))
|
||||
.addColumn('created_at', 'integer', (c) => c.notNull())
|
||||
.execute();
|
||||
// TOTP secrets for MFA
|
||||
await db.schema
|
||||
.createTable('totp_secrets')
|
||||
.ifNotExists()
|
||||
.addColumn('id', 'text', (c) => c.primaryKey())
|
||||
.addColumn('user_id', 'text', (c) => c.notNull().unique())
|
||||
.addColumn('secret', 'text', (c) => c.notNull())
|
||||
.addColumn('verified', 'integer', (c) => c.notNull().defaultTo(0))
|
||||
.addColumn('created_at', 'integer', (c) => c.notNull())
|
||||
.execute();
|
||||
// Audit logs
|
||||
await db.schema
|
||||
.createTable('audit_logs')
|
||||
.ifNotExists()
|
||||
.addColumn('id', 'text', (c) => c.primaryKey())
|
||||
.addColumn('user_id', 'text')
|
||||
.addColumn('organization_id', 'text')
|
||||
.addColumn('action', 'text', (c) => c.notNull())
|
||||
.addColumn('resource', 'text', (c) => c.notNull())
|
||||
.addColumn('resource_id', 'text')
|
||||
.addColumn('ip_address', 'text')
|
||||
.addColumn('user_agent', 'text')
|
||||
.addColumn('details_json', 'text', (c) => c.notNull().defaultTo('{}'))
|
||||
.addColumn('occurred_at', 'integer', (c) => c.notNull())
|
||||
.execute();
|
||||
await (0, kysely_1.sql) `CREATE INDEX IF NOT EXISTS idx_audit_logs_user ON audit_logs (user_id)`.execute(db);
|
||||
await (0, kysely_1.sql) `CREATE INDEX IF NOT EXISTS idx_audit_logs_occurred ON audit_logs (occurred_at)`.execute(db);
|
||||
}
|
||||
async function down(db) {
|
||||
await db.schema.dropTable('audit_logs').ifExists().execute();
|
||||
await db.schema.dropTable('totp_secrets').ifExists().execute();
|
||||
await db.schema.dropTable('sso_configs').ifExists().execute();
|
||||
}
|
||||
Reference in New Issue
Block a user