diff --git a/migrations/0001_init.sql b/migrations/0001_init.sql index 20ffa9b..f904b6a 100644 --- a/migrations/0001_init.sql +++ b/migrations/0001_init.sql @@ -28,6 +28,7 @@ CREATE TABLE IF NOT EXISTS users ( verify_devices INTEGER NOT NULL DEFAULT 1, totp_secret TEXT, totp_recovery_code TEXT, + api_key TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); diff --git a/src/services/storage-schema.ts b/src/services/storage-schema.ts index 6a73b42..4cfee99 100644 --- a/src/services/storage-schema.ts +++ b/src/services/storage-schema.ts @@ -13,6 +13,7 @@ const SCHEMA_STATEMENTS: readonly string[] = [ 'ALTER TABLE users ADD COLUMN verify_devices INTEGER NOT NULL DEFAULT 1', 'ALTER TABLE users ADD COLUMN totp_secret TEXT', 'ALTER TABLE users ADD COLUMN totp_recovery_code TEXT', + 'ALTER TABLE users ADD COLUMN api_key TEXT', 'CREATE TABLE IF NOT EXISTS user_revisions (' + 'user_id TEXT PRIMARY KEY, revision_date TEXT NOT NULL, ' + diff --git a/src/services/storage-user-repo.ts b/src/services/storage-user-repo.ts index 54faff2..1bad39c 100644 --- a/src/services/storage-user-repo.ts +++ b/src/services/storage-user-repo.ts @@ -4,7 +4,7 @@ type SafeBind = (stmt: D1PreparedStatement, ...values: any[]) => D1PreparedState const USER_SELECT_COLUMNS = 'id, email, name, master_password_hint, master_password_hash, key, private_key, public_key, ' + 'kdf_type, kdf_iterations, kdf_memory, kdf_parallelism, security_stamp, role, status, verify_devices, ' + - 'totp_secret, totp_recovery_code, created_at, updated_at'; + 'totp_secret, totp_recovery_code, api_key, created_at, updated_at'; function mapUserRow(row: any): User { return { @@ -26,6 +26,7 @@ function mapUserRow(row: any): User { verifyDevices: row.verify_devices == null ? true : !!row.verify_devices, totpSecret: row.totp_secret ?? null, totpRecoveryCode: row.totp_recovery_code ?? null, + apiKey: row.api_key ?? null, createdAt: row.created_at, updatedAt: row.updated_at, }; @@ -64,11 +65,11 @@ export async function getAllUsers(db: D1Database): Promise { export async function saveUser(db: D1Database, safeBind: SafeBind, user: User): Promise { const email = user.email.toLowerCase(); const stmt = db.prepare( - 'INSERT INTO users(id, email, name, master_password_hint, master_password_hash, key, private_key, public_key, kdf_type, kdf_iterations, kdf_memory, kdf_parallelism, security_stamp, role, status, verify_devices, totp_secret, totp_recovery_code, created_at, updated_at) ' + - 'VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ' + + 'INSERT INTO users(id, email, name, master_password_hint, master_password_hash, key, private_key, public_key, kdf_type, kdf_iterations, kdf_memory, kdf_parallelism, security_stamp, role, status, verify_devices, totp_secret, totp_recovery_code, api_key, created_at, updated_at) ' + + 'VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ' + 'ON CONFLICT(id) DO UPDATE SET ' + 'email=excluded.email, name=excluded.name, master_password_hint=excluded.master_password_hint, master_password_hash=excluded.master_password_hash, key=excluded.key, private_key=excluded.private_key, public_key=excluded.public_key, ' + - 'kdf_type=excluded.kdf_type, kdf_iterations=excluded.kdf_iterations, kdf_memory=excluded.kdf_memory, kdf_parallelism=excluded.kdf_parallelism, security_stamp=excluded.security_stamp, role=excluded.role, status=excluded.status, verify_devices=excluded.verify_devices, totp_secret=excluded.totp_secret, totp_recovery_code=excluded.totp_recovery_code, updated_at=excluded.updated_at' + 'kdf_type=excluded.kdf_type, kdf_iterations=excluded.kdf_iterations, kdf_memory=excluded.kdf_memory, kdf_parallelism=excluded.kdf_parallelism, security_stamp=excluded.security_stamp, role=excluded.role, status=excluded.status, verify_devices=excluded.verify_devices, totp_secret=excluded.totp_secret, totp_recovery_code=excluded.totp_recovery_code, api_key=excluded.api_key, updated_at=excluded.updated_at' ); await safeBind( stmt, @@ -90,6 +91,7 @@ export async function saveUser(db: D1Database, safeBind: SafeBind, user: User): user.verifyDevices ? 1 : 0, user.totpSecret, user.totpRecoveryCode, + user.apiKey, user.createdAt, user.updatedAt ).run(); @@ -102,8 +104,8 @@ export async function createUser(db: D1Database, safeBind: SafeBind, user: User) export async function createFirstUser(db: D1Database, safeBind: SafeBind, user: User): Promise { const email = user.email.toLowerCase(); const stmt = db.prepare( - 'INSERT INTO users(id, email, name, master_password_hint, master_password_hash, key, private_key, public_key, kdf_type, kdf_iterations, kdf_memory, kdf_parallelism, security_stamp, role, status, verify_devices, totp_secret, totp_recovery_code, created_at, updated_at) ' + - 'SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ' + + 'INSERT INTO users(id, email, name, master_password_hint, master_password_hash, key, private_key, public_key, kdf_type, kdf_iterations, kdf_memory, kdf_parallelism, security_stamp, role, status, verify_devices, totp_secret, totp_recovery_code, api_key, created_at, updated_at) ' + + 'SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?' + 'WHERE NOT EXISTS (SELECT 1 FROM users LIMIT 1)' ); const result = await safeBind( @@ -126,6 +128,7 @@ export async function createFirstUser(db: D1Database, safeBind: SafeBind, user: user.verifyDevices ? 1 : 0, user.totpSecret, user.totpRecoveryCode, + user.apiKey, user.createdAt, user.updatedAt ).run(); diff --git a/src/types/index.ts b/src/types/index.ts index f188431..3803041 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -50,6 +50,7 @@ export interface User { verifyDevices?: boolean; totpSecret: string | null; totpRecoveryCode: string | null; + apiKey: string | null; createdAt: string; updatedAt: string; }