/** * The unified async DB interface. Routes depend only on this — never on * `better-sqlite3` or `pg` directly — so the backend can switch between SQLite * (`deployMode=local`) and Postgres (`deployMode=k8s`) without touching callers. * @module dshs/db/adapter */ import type { BusinessPlugin, CredentialKey, CreateSessionInput, CreateUserInput, Domain, DshInstance, DshInstanceRole, DshInstanceStatus, PublicUser, SessionRow, SessionUser, UpsertBusinessPluginInput, UpsertDshInstanceInput, User, UserRole, Workspace, } from './types.js' export interface DbAdapter { // users createUser(input: CreateUserInput): Promise findUserByUsername(username: string): Promise findUserBySlug(slug: string): Promise findUserById(id: string): Promise listPublicUsers(): Promise countAdmins(): Promise setUserRole(id: string, role: UserRole, approvedBy?: string): Promise /** Assign a Linux uid to a user (used by the legacy backfill). */ setUserUid(userId: string, uid: number): Promise /** Ids of users whose uid column is still null (legacy rows awaiting backfill). */ listUsersWithoutUid(): Promise /** * Permanently delete a user and every owned row (credential_vault / domains / * sessions / dsh_instances / audit_log / folder_plugins / workspaces), inside * one transaction. Returns false when no such user exists. */ deleteUser(userId: string): Promise // sessions createSession(input: CreateSessionInput): Promise findSession(tokenHash: string): Promise deleteSession(tokenHash: string): Promise deleteUserSessions(userId: string): Promise findSessionWithUser(tokenHash: string): Promise /** Whether the user has any session that has not yet expired (idle reap). */ hasActiveSession(userId: string): Promise // audit audit(actor: string | null, action: string, detail?: string | null): Promise // workspaces / plugins findWorkspaceByPath(userId: string, relPath: string): Promise getOrCreateWorkspace(userId: string, relPath: string): Promise setFolderPlugins(workspaceId: string, selections: ReadonlyArray<{ id: string; enabled: boolean }>): Promise getEnabledPluginIds(workspaceId: string): Promise // business plugins (系统外插件候选池) listBusinessPlugins(): Promise findBusinessPlugin(id: string): Promise upsertBusinessPlugin(input: UpsertBusinessPluginInput): Promise deleteBusinessPlugin(id: string): Promise // domains findDomainByUser(userId: string): Promise findDomainById(id: string): Promise listDomains(): Promise upsertDomain(userId: string, domain: string, nginxConfig: string): Promise setDomainVerified(id: string, verified: boolean): Promise // credential vault listCredentialKeys(userId: string): Promise getEnabledCredentialKeyRef(userId: string): Promise setCredentialKey(userId: string, name: string, encryptedRef: string): Promise selectCredentialKey(userId: string, id: string): Promise deleteCredentialKey(userId: string, id: string): Promise // instances (desired state the k8s controller reconciles against — docs/k8s.md §5.7) upsertInstance(input: UpsertDshInstanceInput): Promise findInstance(id: string): Promise findUserInstance(userId: string, role: DshInstanceRole): Promise listInstancesByRole(role: DshInstanceRole): Promise /** Record a state transition; `exitCode`/`lastError` also stamp `last_exit`. */ setInstanceStatus( id: string, status: DshInstanceStatus, outcome?: { exitCode?: number; lastError?: string }, ): Promise deleteInstance(id: string): Promise deleteUserInstances(userId: string): Promise // lifecycle close(): Promise }