import type { Application as APIApplication } from "@versia/client/types"; import { type InferInsertModel, type InferSelectModel, type SQL, desc, eq, inArray, } from "drizzle-orm"; import { z } from "zod"; import { db } from "~/drizzle/db"; import { Applications } from "~/drizzle/schema"; import { BaseInterface } from "./base.ts"; export type ApplicationType = InferSelectModel; export class Application extends BaseInterface { static schema: z.ZodType = z.object({ name: z.string(), website: z.string().url().optional().nullable(), vapid_key: z.string().optional().nullable(), redirect_uris: z.string().optional(), scopes: z.string().optional(), }); async reload(): Promise { const reloaded = await Application.fromId(this.data.id); if (!reloaded) { throw new Error("Failed to reload application"); } this.data = reloaded.data; } public static async fromId(id: string | null): Promise { if (!id) { return null; } return await Application.fromSql(eq(Applications.id, id)); } public static async fromIds(ids: string[]): Promise { return await Application.manyFromSql(inArray(Applications.id, ids)); } public static async fromSql( sql: SQL | undefined, orderBy: SQL | undefined = desc(Applications.id), ): Promise { const found = await db.query.Applications.findFirst({ where: sql, orderBy, }); if (!found) { return null; } return new Application(found); } public static async manyFromSql( sql: SQL | undefined, orderBy: SQL | undefined = desc(Applications.id), limit?: number, offset?: number, extra?: Parameters[0], ): Promise { const found = await db.query.Applications.findMany({ where: sql, orderBy, limit, offset, with: extra?.with, }); return found.map((s) => new Application(s)); } public static async getFromToken( token: string, ): Promise { const result = await db.query.Tokens.findFirst({ where: (tokens, { eq }) => eq(tokens.accessToken, token), with: { application: true, }, }); return result?.application ? new Application(result.application) : null; } public static fromClientId(clientId: string): Promise { return Application.fromSql(eq(Applications.clientId, clientId)); } async update( newApplication: Partial, ): Promise { await db .update(Applications) .set(newApplication) .where(eq(Applications.id, this.id)); const updated = await Application.fromId(this.data.id); if (!updated) { throw new Error("Failed to update application"); } this.data = updated.data; return updated.data; } save(): Promise { return this.update(this.data); } async delete(ids?: string[]): Promise { if (Array.isArray(ids)) { await db.delete(Applications).where(inArray(Applications.id, ids)); } else { await db.delete(Applications).where(eq(Applications.id, this.id)); } } public static async insert( data: InferInsertModel, ): Promise { const inserted = ( await db.insert(Applications).values(data).returning() )[0]; const application = await Application.fromId(inserted.id); if (!application) { throw new Error("Failed to insert application"); } return application; } get id() { return this.data.id; } public toApi(): APIApplication { return { name: this.data.name, website: this.data.website, vapid_key: this.data.vapidKey, }; } }