| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113 |
- /**
- * Relay 数据库 Schema
- *
- * 表:
- * - Tenant:租户信息、API 凭证、RSA 公钥
- * - TenantDevice:租户下的设备 guid 映射、relaySecret
- * - WebhookRelayEvent:待投递事件队列
- * - UsageMetric:用量埋点
- * - AuditLog:租户自助注册/重置等审计日志
- */
- import { createTable, initDb, getDb } from './lib/db.js';
- export function initRelayDatabase(dbPath: string): void {
- initDb(dbPath);
- // 租户表
- createTable('Tenant', [
- { name: 'apiKey', type: 'TEXT', required: true },
- { name: 'apiSecretHash', type: 'TEXT', required: true },
- { name: 'publicKey', type: 'TEXT', required: true },
- { name: 'fmodeUserId', type: 'TEXT' },
- { name: 'name', type: 'TEXT' },
- { name: 'description', type: 'TEXT' },
- { name: 'plan', type: 'TEXT', defaultValue: 'free' },
- { name: 'status', type: 'TEXT', defaultValue: 'active' },
- { name: 'maxDevices', type: 'INTEGER', defaultValue: 10 },
- { name: 'dailyEventLimit', type: 'INTEGER', defaultValue: 100000 },
- { name: 'metadata', type: 'TEXT' },
- ]);
- // 租户设备表
- createTable('TenantDevice', [
- { name: 'tenantId', type: 'TEXT', required: true },
- { name: 'guid', type: 'TEXT', required: true },
- { name: 'relaySecret', type: 'TEXT', required: true },
- { name: 'deviceName', type: 'TEXT' },
- { name: 'lastOnlineAt', type: 'TEXT' },
- { name: 'status', type: 'TEXT', defaultValue: 'active' },
- ]);
- // Webhook 事件队列
- createTable('WebhookRelayEvent', [
- { name: 'tenantId', type: 'TEXT', required: true },
- { name: 'deviceGuid', type: 'TEXT', required: true },
- { name: 'eventId', type: 'TEXT', required: true },
- { name: 'status', type: 'TEXT', defaultValue: 'pending' }, // pending / delivered / failed / dropped
- { name: 'encryptedPayload', type: 'TEXT', required: true },
- { name: 'payloadHash', type: 'TEXT' },
- { name: 'retryCount', type: 'INTEGER', defaultValue: 0 },
- { name: 'deliveredAt', type: 'TEXT' },
- { name: 'expiresAt', type: 'TEXT' },
- { name: 'error', type: 'TEXT' },
- ]);
- // 用量埋点
- createTable('UsageMetric', [
- { name: 'tenantId', type: 'TEXT', required: true },
- { name: 'deviceGuid', type: 'TEXT' },
- { name: 'metricName', type: 'TEXT', required: true }, // webhook_event / message / group_created / device_online
- { name: 'metricValue', type: 'INTEGER', defaultValue: 1 },
- { name: 'metricDate', type: 'TEXT', required: true }, // YYYY-MM-DD
- { name: 'metadata', type: 'TEXT' },
- ]);
- // 审计日志
- createTable('AuditLog', [
- { name: 'action', type: 'TEXT', required: true }, // tenant.register / tenant.reset / tenant.delete
- { name: 'tenantId', type: 'TEXT' },
- { name: 'fmodeUserId', type: 'TEXT' },
- { name: 'ip', type: 'TEXT' },
- { name: 'details', type: 'TEXT' },
- ]);
- // 索引
- const db = getDb();
- db.exec(`CREATE INDEX IF NOT EXISTS idx_tenant_apiKey ON Tenant(apiKey)`);
- db.exec(`CREATE INDEX IF NOT EXISTS idx_device_tenant_guid ON TenantDevice(tenantId, guid)`);
- db.exec(`CREATE INDEX IF NOT EXISTS idx_device_guid ON TenantDevice(guid)`);
- db.exec(`CREATE INDEX IF NOT EXISTS idx_event_tenant_status ON WebhookRelayEvent(tenantId, status)`);
- db.exec(`CREATE INDEX IF NOT EXISTS idx_event_device_status ON WebhookRelayEvent(deviceGuid, status)`);
- db.exec(`CREATE INDEX IF NOT EXISTS idx_event_expires ON WebhookRelayEvent(expiresAt)`);
- db.exec(`CREATE INDEX IF NOT EXISTS idx_metric_tenant_date ON UsageMetric(tenantId, metricName, metricDate)`);
- db.exec(`CREATE INDEX IF NOT EXISTS idx_auditlog_tenant ON AuditLog(tenantId)`);
- db.exec(`CREATE INDEX IF NOT EXISTS idx_auditlog_action ON AuditLog(action, createdAt)`);
- migrateRelayDatabase();
- console.log('[Relay Schema] 数据库表初始化完成');
- }
- /** 对已有数据库追加新列/索引(幂等) */
- export function migrateRelayDatabase(): void {
- const db = getDb();
- const tableInfo = db.prepare(`PRAGMA table_info("Tenant")`).all() as Array<{ name: string }>;
- const hasColumn = (name: string) => tableInfo.some((col) => col.name === name);
- if (!hasColumn('fmodeUserId')) {
- db.exec(`ALTER TABLE "Tenant" ADD COLUMN "fmodeUserId" TEXT`);
- }
- if (!hasColumn('description')) {
- db.exec(`ALTER TABLE "Tenant" ADD COLUMN "description" TEXT`);
- }
- if (!hasColumn('maxDevices')) {
- db.exec(`ALTER TABLE "Tenant" ADD COLUMN "maxDevices" INTEGER DEFAULT 10`);
- }
- if (!hasColumn('dailyEventLimit')) {
- db.exec(`ALTER TABLE "Tenant" ADD COLUMN "dailyEventLimit" INTEGER DEFAULT 100000`);
- }
- db.exec(`CREATE INDEX IF NOT EXISTS idx_tenant_fmodeUserId ON Tenant(fmodeUserId)`);
- }
|