106 lines
3.4 KiB
TypeScript
106 lines
3.4 KiB
TypeScript
/**
|
|
* User credentials repository.
|
|
*
|
|
* Manages external service tokens (GitHub, GitLab, Bitbucket, etc.)
|
|
* stored per-user. Each credential has a type discriminator so multiple
|
|
* credential kinds can coexist in the same table.
|
|
*/
|
|
|
|
import { getConnection } from '@/modules/database/connection.js';
|
|
import type {
|
|
CreateCredentialResult,
|
|
CredentialPublicRow,
|
|
} from '@/shared/types.js';
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Queries
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export const credentialsDb = {
|
|
/** Stores a new credential and returns a safe (no raw value) result. */
|
|
createCredential(
|
|
userId: number,
|
|
credentialName: string,
|
|
credentialType: string,
|
|
credentialValue: string,
|
|
description: string | null = null
|
|
): CreateCredentialResult {
|
|
const db = getConnection();
|
|
const result = db
|
|
.prepare(
|
|
'INSERT INTO user_credentials (user_id, credential_name, credential_type, credential_value, description) VALUES (?, ?, ?, ?, ?)'
|
|
)
|
|
.run(userId, credentialName, credentialType, credentialValue, description);
|
|
return {
|
|
id: result.lastInsertRowid,
|
|
credentialName,
|
|
credentialType,
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Lists credentials for a user (excluding raw values).
|
|
* Optionally filters by credential type (e.g. 'github_token').
|
|
*/
|
|
getCredentials(
|
|
userId: number,
|
|
credentialType: string | null = null
|
|
): CredentialPublicRow[] {
|
|
const db = getConnection();
|
|
|
|
if (credentialType) {
|
|
return db
|
|
.prepare(
|
|
'SELECT id, credential_name, credential_type, description, created_at, is_active FROM user_credentials WHERE user_id = ? AND credential_type = ? ORDER BY created_at DESC'
|
|
)
|
|
.all(userId, credentialType) as CredentialPublicRow[];
|
|
}
|
|
|
|
return db
|
|
.prepare(
|
|
'SELECT id, credential_name, credential_type, description, created_at, is_active FROM user_credentials WHERE user_id = ? ORDER BY created_at DESC'
|
|
)
|
|
.all(userId) as CredentialPublicRow[];
|
|
},
|
|
|
|
/**
|
|
* Returns the raw credential value for the most recent active
|
|
* credential of the given type, or null if none exists.
|
|
*/
|
|
getActiveCredential(
|
|
userId: number,
|
|
credentialType: string
|
|
): string | null {
|
|
const db = getConnection();
|
|
const row = db
|
|
.prepare(
|
|
'SELECT credential_value FROM user_credentials WHERE user_id = ? AND credential_type = ? AND is_active = 1 ORDER BY created_at DESC LIMIT 1'
|
|
)
|
|
.get(userId, credentialType) as { credential_value: string } | undefined;
|
|
return row?.credential_value ?? null;
|
|
},
|
|
|
|
/** Permanently removes a credential. Returns true if a row was deleted. */
|
|
deleteCredential(userId: number, credentialId: number): boolean {
|
|
const db = getConnection();
|
|
const result = db
|
|
.prepare('DELETE FROM user_credentials WHERE id = ? AND user_id = ?')
|
|
.run(credentialId, userId);
|
|
return result.changes > 0;
|
|
},
|
|
|
|
/** Enables or disables a credential without deleting it. */
|
|
toggleCredential(
|
|
userId: number,
|
|
credentialId: number,
|
|
isActive: boolean
|
|
): boolean {
|
|
const db = getConnection();
|
|
const result = db
|
|
.prepare(
|
|
'UPDATE user_credentials SET is_active = ? WHERE id = ? AND user_id = ?'
|
|
)
|
|
.run(isActive ? 1 : 0, credentialId, userId);
|
|
return result.changes > 0;
|
|
},
|
|
};
|