mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-11-06 18:45:25 +00:00
106 lines
2.8 KiB
TypeScript
106 lines
2.8 KiB
TypeScript
import { eq, and } from "drizzle-orm";
|
|
import { apiKeys, users } from "../db/schema.js";
|
|
import type { DatabaseContext } from "./database-context.js";
|
|
import { rowsAffected } from "./mutation-result.js";
|
|
import { deleteReturning, insertReturning } from "./returning.js";
|
|
|
|
export type ApiKeyRecord = typeof apiKeys.$inferSelect;
|
|
export type NewApiKeyRecord = typeof apiKeys.$inferInsert;
|
|
|
|
export interface ApiKeyListRecord {
|
|
id: string;
|
|
name: string;
|
|
userId: string;
|
|
username: string | null;
|
|
tokenPrefix: string;
|
|
createdAt: string;
|
|
expiresAt: string | null;
|
|
lastUsedAt: string | null;
|
|
isActive: boolean;
|
|
/** JSON list, or null for full access. */
|
|
scopes: string | null;
|
|
}
|
|
|
|
export class ApiKeyRepository {
|
|
constructor(
|
|
private readonly context: DatabaseContext,
|
|
private readonly onWrite?: () => void | Promise<void>,
|
|
) {}
|
|
|
|
async create(apiKey: NewApiKeyRecord): Promise<ApiKeyRecord> {
|
|
const rows = await insertReturning(this.context, apiKeys, apiKey);
|
|
await this.afterWrite();
|
|
return rows[0];
|
|
}
|
|
|
|
async listAllWithUsers(): Promise<ApiKeyListRecord[]> {
|
|
return this.context.drizzle
|
|
.select({
|
|
id: apiKeys.id,
|
|
name: apiKeys.name,
|
|
userId: apiKeys.userId,
|
|
username: users.username,
|
|
tokenPrefix: apiKeys.tokenPrefix,
|
|
createdAt: apiKeys.createdAt,
|
|
expiresAt: apiKeys.expiresAt,
|
|
lastUsedAt: apiKeys.lastUsedAt,
|
|
isActive: apiKeys.isActive,
|
|
scopes: apiKeys.scopes,
|
|
})
|
|
.from(apiKeys)
|
|
.leftJoin(users, eq(apiKeys.userId, users.id))
|
|
.orderBy(apiKeys.createdAt);
|
|
}
|
|
|
|
async findById(id: string): Promise<ApiKeyRecord | null> {
|
|
const rows = await this.context.drizzle
|
|
.select()
|
|
.from(apiKeys)
|
|
.where(eq(apiKeys.id, id))
|
|
.limit(1);
|
|
|
|
return rows[0] ?? null;
|
|
}
|
|
|
|
async listActiveByTokenPrefix(tokenPrefix: string): Promise<ApiKeyRecord[]> {
|
|
return this.context.drizzle
|
|
.select()
|
|
.from(apiKeys)
|
|
.where(
|
|
and(eq(apiKeys.tokenPrefix, tokenPrefix), eq(apiKeys.isActive, true)),
|
|
);
|
|
}
|
|
|
|
async updateLastUsedAt(id: string, lastUsedAt: string): Promise<void> {
|
|
await this.context.drizzle
|
|
.update(apiKeys)
|
|
.set({ lastUsedAt })
|
|
.where(eq(apiKeys.id, id));
|
|
await this.afterWrite();
|
|
}
|
|
|
|
async delete(id: string): Promise<ApiKeyRecord | null> {
|
|
const rows = await deleteReturning(
|
|
this.context,
|
|
apiKeys,
|
|
eq(apiKeys.id, id),
|
|
);
|
|
|
|
await this.afterWrite();
|
|
return rows[0] ?? null;
|
|
}
|
|
|
|
async deleteByUserId(userId: string): Promise<number> {
|
|
const result = await this.context.drizzle
|
|
.delete(apiKeys)
|
|
.where(eq(apiKeys.userId, userId));
|
|
|
|
await this.afterWrite();
|
|
return rowsAffected(result);
|
|
}
|
|
|
|
private async afterWrite(): Promise<void> {
|
|
await this.onWrite?.();
|
|
}
|
|
}
|