refactor(database): 🚚 Rename Application to Client everywhere

This commit is contained in:
Jesse Wierzbinski 2025-08-21 01:21:32 +02:00
parent 6f97903f3b
commit 1a0a27bee1
No known key found for this signature in database
25 changed files with 2549 additions and 90 deletions

View file

@ -1,4 +1,4 @@
import { Application, Token } from "@versia-server/kit/db"; import { Client, Token } from "@versia-server/kit/db";
import { randomUUIDv7 } from "bun"; import { randomUUIDv7 } from "bun";
import chalk from "chalk"; import chalk from "chalk";
// @ts-expect-error - Root import is required or the Clec type definitions won't work // @ts-expect-error - Root import is required or the Clec type definitions won't work
@ -22,7 +22,7 @@ export const generateTokenCommand = defineCommand(
throw new Error(`User ${chalk.gray(username)} not found.`); throw new Error(`User ${chalk.gray(username)} not found.`);
} }
const application = await Application.insert({ const application = await Client.insert({
id: id:
user.id + user.id +
Buffer.from( Buffer.from(

View file

@ -1,4 +1,4 @@
//import { config } from "@versia-server/config"; import { config } from "@versia-server/config";
import type { Config } from "drizzle-kit"; import type { Config } from "drizzle-kit";
/** /**
@ -10,16 +10,16 @@ export default {
out: "./packages/kit/tables/migrations", out: "./packages/kit/tables/migrations",
schema: "./packages/kit/tables/schema.ts", schema: "./packages/kit/tables/schema.ts",
dbCredentials: { dbCredentials: {
host: "localhost", /* host: "localhost",
port: 40000, port: 40000,
user: "lysand", user: "lysand",
password: "lysand", password: "lysand",
database: "lysand", database: "lysand", */
/* host: config.postgres.host, host: config.postgres.host,
port: config.postgres.port, port: config.postgres.port,
user: config.postgres.username, user: config.postgres.username,
password: config.postgres.password, password: config.postgres.password,
database: config.postgres.database, */ database: config.postgres.database,
}, },
// Print all statements // Print all statements
verbose: true, verbose: true,

View file

@ -1,15 +1,15 @@
import { afterAll, describe, expect, test } from "bun:test"; import { afterAll, describe, expect, test } from "bun:test";
import { config } from "@versia-server/config"; import { config } from "@versia-server/config";
import { Application } from "@versia-server/kit/db"; import { Client } from "@versia-server/kit/db";
import { fakeRequest, getTestUsers } from "@versia-server/tests"; import { fakeRequest, getTestUsers } from "@versia-server/tests";
import { randomString } from "@/math"; import { randomString } from "@/math";
const { users, deleteUsers, passwords } = await getTestUsers(1); const { users, deleteUsers, passwords } = await getTestUsers(1);
// Create application // Create application
const application = await Application.insert({ const application = await Client.insert({
id: randomString(32, "hex"), id: randomString(32, "hex"),
name: "Test Application", name: "Test Client",
secret: "test", secret: "test",
redirectUris: ["https://example.com"], redirectUris: ["https://example.com"],
scopes: ["read", "write"], scopes: ["read", "write"],

View file

@ -1,7 +1,7 @@
import { config } from "@versia-server/config"; import { config } from "@versia-server/config";
import { ApiError } from "@versia-server/kit"; import { ApiError } from "@versia-server/kit";
import { apiRoute, handleZodError } from "@versia-server/kit/api"; import { apiRoute, handleZodError } from "@versia-server/kit/api";
import { Application, User } from "@versia-server/kit/db"; import { Client, User } from "@versia-server/kit/db";
import { Users } from "@versia-server/kit/tables"; import { Users } from "@versia-server/kit/tables";
import { password as bunPassword } from "bun"; import { password as bunPassword } from "bun";
import { eq, or } from "drizzle-orm"; import { eq, or } from "drizzle-orm";
@ -156,7 +156,7 @@ export default apiRoute((app) =>
config.authentication.key, config.authentication.key,
); );
const application = await Application.fromClientId(client_id); const application = await Client.fromClientId(client_id);
if (!application) { if (!application) {
throw new ApiError(400, "Invalid application"); throw new ApiError(400, "Invalid application");

View file

@ -4,7 +4,7 @@ import {
} from "@versia/client/schemas"; } from "@versia/client/schemas";
import { ApiError } from "@versia-server/kit"; import { ApiError } from "@versia-server/kit";
import { apiRoute, handleZodError, jsonOrForm } from "@versia-server/kit/api"; import { apiRoute, handleZodError, jsonOrForm } from "@versia-server/kit/api";
import { Application } from "@versia-server/kit/db"; import { Client } from "@versia-server/kit/db";
import { describeRoute, resolver, validator } from "hono-openapi"; import { describeRoute, resolver, validator } from "hono-openapi";
import { z } from "zod/v4"; import { z } from "zod/v4";
import { randomString } from "@/math"; import { randomString } from "@/math";
@ -62,7 +62,7 @@ export default apiRoute((app) =>
const { client_name, redirect_uris, scopes, website } = const { client_name, redirect_uris, scopes, website } =
context.req.valid("json"); context.req.valid("json");
const app = await Application.insert({ const app = await Client.insert({
id: randomString(32, "base64url"), id: randomString(32, "base64url"),
name: client_name, name: client_name,
redirectUris: Array.isArray(redirect_uris) redirectUris: Array.isArray(redirect_uris)

View file

@ -4,7 +4,7 @@ import {
} from "@versia/client/schemas"; } from "@versia/client/schemas";
import { ApiError } from "@versia-server/kit"; import { ApiError } from "@versia-server/kit";
import { apiRoute, auth } from "@versia-server/kit/api"; import { apiRoute, auth } from "@versia-server/kit/api";
import { Application } from "@versia-server/kit/db"; import { Client } from "@versia-server/kit/db";
import { describeRoute, resolver } from "hono-openapi"; import { describeRoute, resolver } from "hono-openapi";
export default apiRoute((app) => export default apiRoute((app) =>
@ -38,7 +38,7 @@ export default apiRoute((app) =>
async (context) => { async (context) => {
const { token } = context.get("auth"); const { token } = context.get("auth");
const application = await Application.getFromToken( const application = await Client.getFromToken(
token.data.accessToken, token.data.accessToken,
); );

View file

@ -2,7 +2,7 @@ import { RolePermission } from "@versia/client/schemas";
import { config } from "@versia-server/config"; import { config } from "@versia-server/config";
import { ApiError } from "@versia-server/kit"; import { ApiError } from "@versia-server/kit";
import { apiRoute, auth, handleZodError } from "@versia-server/kit/api"; import { apiRoute, auth, handleZodError } from "@versia-server/kit/api";
import { Application, db } from "@versia-server/kit/db"; import { Client, db } from "@versia-server/kit/db";
import { OpenIdLoginFlows } from "@versia-server/kit/tables"; import { OpenIdLoginFlows } from "@versia-server/kit/tables";
import { randomUUIDv7 } from "bun"; import { randomUUIDv7 } from "bun";
import { describeRoute, resolver, validator } from "hono-openapi"; import { describeRoute, resolver, validator } from "hono-openapi";
@ -123,7 +123,7 @@ export default apiRoute((app) => {
issuerId, issuerId,
); );
const application = await Application.insert({ const application = await Client.insert({
id: id:
user.id + user.id +
Buffer.from( Buffer.from(
@ -144,7 +144,7 @@ export default apiRoute((app) => {
codeVerifier, codeVerifier,
state: parameters.state, state: parameters.state,
issuerId, issuerId,
applicationId: application.id, clientId: application.id,
}) })
.returning() .returning()
)[0]; )[0];

View file

@ -249,7 +249,7 @@ export default apiRoute((app) =>
spoilerText: sanitizedSpoilerText, spoilerText: sanitizedSpoilerText,
replyId: in_reply_to_id ?? undefined, replyId: in_reply_to_id ?? undefined,
quotingId: quote_id ?? undefined, quotingId: quote_id ?? undefined,
applicationId: application?.id, clientId: application?.id,
contentSource: status, contentSource: status,
contentType: content_type, contentType: content_type,
}); });

View file

@ -13,10 +13,10 @@ afterAll(async () => {
}); });
describe("Login flow", () => { describe("Login flow", () => {
test("should create an application", async () => { test("should create a client", async () => {
const client = await generateClient(users[0]); const client = await generateClient(users[0]);
const { ok, data } = await client.createApp("Test Application", { const { ok, data } = await client.createApp("Test Client", {
redirect_uris: "https://example.com", redirect_uris: "https://example.com",
website: "https://example.com", website: "https://example.com",
scopes: ["read", "write"], scopes: ["read", "write"],
@ -24,7 +24,7 @@ describe("Login flow", () => {
expect(ok).toBe(true); expect(ok).toBe(true);
expect(data).toEqual({ expect(data).toEqual({
name: "Test Application", name: "Test Client",
website: "https://example.com", website: "https://example.com",
client_id: expect.any(String), client_id: expect.any(String),
client_secret: expect.any(String), client_secret: expect.any(String),

View file

@ -1,11 +1,11 @@
import { afterAll, describe, expect, test } from "bun:test"; import { afterAll, describe, expect, test } from "bun:test";
import { Application, Token } from "@versia-server/kit/db"; import { Client, Token } from "@versia-server/kit/db";
import { fakeRequest, getTestUsers } from "@versia-server/tests"; import { fakeRequest, getTestUsers } from "@versia-server/tests";
import { randomUUIDv7 } from "bun"; import { randomUUIDv7 } from "bun";
const { deleteUsers, users } = await getTestUsers(1); const { deleteUsers, users } = await getTestUsers(1);
const application = await Application.insert({ const application = await Client.insert({
id: randomUUIDv7(), id: randomUUIDv7(),
redirectUris: ["https://example.com/callback"], redirectUris: ["https://example.com/callback"],
scopes: ["openid", "profile", "email"], scopes: ["openid", "profile", "email"],

View file

@ -69,7 +69,7 @@ export default apiRoute((app) => {
const flow = await db.query.OpenIdLoginFlows.findFirst({ const flow = await db.query.OpenIdLoginFlows.findFirst({
where: (flow): SQL | undefined => eq(flow.id, flowId), where: (flow): SQL | undefined => eq(flow.id, flowId),
with: { with: {
application: true, client: true,
}, },
}); });
@ -129,11 +129,11 @@ export default apiRoute((app) => {
// If linking account // If linking account
if (link && user_id) { if (link && user_id) {
// Check if userId is equal to application.clientId // Check if userId is equal to application.clientId
if (!flow.application?.id.startsWith(user_id)) { if (!flow.client?.id.startsWith(user_id)) {
return redirectWithMessage( return redirectWithMessage(
{ {
oidc_account_linking_error: "Account linking error", oidc_account_linking_error: "Account linking error",
oidc_account_linking_error_message: `User ID does not match application client ID (${user_id} != ${flow.application?.id})`, oidc_account_linking_error_message: `User ID does not match application client ID (${user_id} != ${flow.client?.id})`,
}, },
config.frontend.routes.home, config.frontend.routes.home,
); );
@ -262,14 +262,14 @@ export default apiRoute((app) => {
}); });
} }
if (!flow.application) { if (!flow.client) {
throw new ApiError(500, "Application not found"); throw new ApiError(500, "Application not found");
} }
const code = randomString(32, "hex"); const code = randomString(32, "hex");
await db.insert(AuthorizationCodes).values({ await db.insert(AuthorizationCodes).values({
clientId: flow.application.id, clientId: flow.client.id,
code, code,
expiresAt: new Date(Date.now() + 60 * 1000).toISOString(), // 1 minute expiresAt: new Date(Date.now() + 60 * 1000).toISOString(), // 1 minute
redirectUri: flow.clientRedirectUri ?? undefined, redirectUri: flow.clientRedirectUri ?? undefined,
@ -281,7 +281,7 @@ export default apiRoute((app) => {
{ {
sub: user.id, sub: user.id,
iss: new URL(context.get("config").http.base_url).origin, iss: new URL(context.get("config").http.base_url).origin,
aud: flow.application.id, aud: flow.client.id,
exp: Math.floor(Date.now() / 1000) + 60 * 60, exp: Math.floor(Date.now() / 1000) + 60 * 60,
iat: Math.floor(Date.now() / 1000), iat: Math.floor(Date.now() / 1000),
nbf: Math.floor(Date.now() / 1000), nbf: Math.floor(Date.now() / 1000),
@ -303,9 +303,9 @@ export default apiRoute((app) => {
{ {
redirect_uri: flow.clientRedirectUri ?? undefined, redirect_uri: flow.clientRedirectUri ?? undefined,
code, code,
client_id: flow.application.id, client_id: flow.client.id,
application: flow.application.name, application: flow.client.name,
website: flow.application.website ?? "", website: flow.client.website ?? "",
scope: flow.clientScopes?.join(" "), scope: flow.clientScopes?.join(" "),
state: flow.clientState ?? undefined, state: flow.clientState ?? undefined,
}, },

View file

@ -1,7 +1,7 @@
import { config } from "@versia-server/config"; import { config } from "@versia-server/config";
import { ApiError } from "@versia-server/kit"; import { ApiError } from "@versia-server/kit";
import { apiRoute, handleZodError } from "@versia-server/kit/api"; import { apiRoute, handleZodError } from "@versia-server/kit/api";
import { Application, db } from "@versia-server/kit/db"; import { Client, db } from "@versia-server/kit/db";
import { OpenIdLoginFlows } from "@versia-server/kit/tables"; import { OpenIdLoginFlows } from "@versia-server/kit/tables";
import { randomUUIDv7 } from "bun"; import { randomUUIDv7 } from "bun";
import { describeRoute, validator } from "hono-openapi"; import { describeRoute, validator } from "hono-openapi";
@ -54,7 +54,7 @@ export default apiRoute((app) => {
throw new ApiError(422, "Unknown or invalid issuer"); throw new ApiError(422, "Unknown or invalid issuer");
} }
const application = await Application.fromClientId(client_id); const application = await Client.fromClientId(client_id);
if (!application) { if (!application) {
throw new ApiError(422, "Unknown or invalid client_id"); throw new ApiError(422, "Unknown or invalid client_id");
@ -98,7 +98,7 @@ export default apiRoute((app) => {
clientState: state, clientState: state,
clientRedirectUri: redirect_uri, clientRedirectUri: redirect_uri,
clientScopes: scopes, clientScopes: scopes,
applicationId: application.id, clientId: application.id,
issuerId, issuerId,
}) })
.returning() .returning()

View file

@ -1,5 +1,5 @@
import { afterAll, describe, expect, test } from "bun:test"; import { afterAll, describe, expect, test } from "bun:test";
import { Application, db } from "@versia-server/kit/db"; import { Client, db } from "@versia-server/kit/db";
import { fakeRequest, getTestUsers } from "@versia-server/tests"; import { fakeRequest, getTestUsers } from "@versia-server/tests";
import { randomUUIDv7 } from "bun"; import { randomUUIDv7 } from "bun";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
@ -8,7 +8,7 @@ import { AuthorizationCodes } from "~/packages/kit/tables/schema";
const { deleteUsers, users } = await getTestUsers(1); const { deleteUsers, users } = await getTestUsers(1);
const application = await Application.insert({ const application = await Client.insert({
id: randomUUIDv7(), id: randomUUIDv7(),
redirectUris: ["https://example.com/callback"], redirectUris: ["https://example.com/callback"],
scopes: ["openid", "profile", "email"], scopes: ["openid", "profile", "email"],

View file

@ -1,6 +1,6 @@
import { Token as TokenSchema } from "@versia/client/schemas"; import { Token as TokenSchema } from "@versia/client/schemas";
import { apiRoute, handleZodError, jsonOrForm } from "@versia-server/kit/api"; import { apiRoute, handleZodError, jsonOrForm } from "@versia-server/kit/api";
import { Application, db, Token } from "@versia-server/kit/db"; import { Client, db, Token } from "@versia-server/kit/db";
import { AuthorizationCodes } from "@versia-server/kit/tables"; import { AuthorizationCodes } from "@versia-server/kit/tables";
import { randomUUIDv7 } from "bun"; import { randomUUIDv7 } from "bun";
import { and, eq } from "drizzle-orm"; import { and, eq } from "drizzle-orm";
@ -77,7 +77,7 @@ export default apiRoute((app) => {
} }
// Verify the client_secret // Verify the client_secret
const client = await Application.fromClientId(client_id); const client = await Client.fromClientId(client_id);
if (!client || client.data.secret !== client_secret) { if (!client || client.data.secret !== client_secret) {
return context.json( return context.json(

View file

@ -14,7 +14,7 @@ import { type ZodAny, ZodError, z } from "zod/v4";
import { fromZodError } from "zod-validation-error"; import { fromZodError } from "zod-validation-error";
import type { AuthData, HonoEnv } from "~/types/api"; import type { AuthData, HonoEnv } from "~/types/api";
import { ApiError } from "./api-error.ts"; import { ApiError } from "./api-error.ts";
import { Application } from "./db/application.ts"; import { Client } from "./db/application.ts";
import { Emoji } from "./db/emoji.ts"; import { Emoji } from "./db/emoji.ts";
import { Note } from "./db/note.ts"; import { Note } from "./db/note.ts";
import { Token } from "./db/token.ts"; import { Token } from "./db/token.ts";
@ -170,7 +170,7 @@ export const auth = <AuthRequired extends boolean>(options: {
const auth: AuthData = { const auth: AuthData = {
token, token,
application: token?.data.client application: token?.data.client
? new Application(token?.data.client) ? new Client(token?.data.client)
: null, : null,
user: (await token?.getUser()) ?? null, user: (await token?.getUser()) ?? null,
}; };

View file

@ -16,37 +16,37 @@ import { Clients } from "../tables/schema.ts";
import { BaseInterface } from "./base.ts"; import { BaseInterface } from "./base.ts";
import { Token } from "./token.ts"; import { Token } from "./token.ts";
type ApplicationType = InferSelectModel<typeof Clients>; type ClientType = InferSelectModel<typeof Clients>;
export class Application extends BaseInterface<typeof Clients> { export class Client extends BaseInterface<typeof Clients> {
public static $type: ApplicationType; public static $type: ClientType;
public async reload(): Promise<void> { public async reload(): Promise<void> {
const reloaded = await Application.fromId(this.data.id); const reloaded = await Client.fromId(this.data.id);
if (!reloaded) { if (!reloaded) {
throw new Error("Failed to reload application"); throw new Error("Failed to reload client");
} }
this.data = reloaded.data; this.data = reloaded.data;
} }
public static async fromId(id: string | null): Promise<Application | null> { public static async fromId(id: string | null): Promise<Client | null> {
if (!id) { if (!id) {
return null; return null;
} }
return await Application.fromSql(eq(Clients.id, id)); return await Client.fromSql(eq(Clients.id, id));
} }
public static async fromIds(ids: string[]): Promise<Application[]> { public static async fromIds(ids: string[]): Promise<Client[]> {
return await Application.manyFromSql(inArray(Clients.id, ids)); return await Client.manyFromSql(inArray(Clients.id, ids));
} }
public static async fromSql( public static async fromSql(
sql: SQL<unknown> | undefined, sql: SQL<unknown> | undefined,
orderBy: SQL<unknown> | undefined = desc(Clients.id), orderBy: SQL<unknown> | undefined = desc(Clients.id),
): Promise<Application | null> { ): Promise<Client | null> {
const found = await db.query.Clients.findFirst({ const found = await db.query.Clients.findFirst({
where: sql, where: sql,
orderBy, orderBy,
@ -55,7 +55,7 @@ export class Application extends BaseInterface<typeof Clients> {
if (!found) { if (!found) {
return null; return null;
} }
return new Application(found); return new Client(found);
} }
public static async manyFromSql( public static async manyFromSql(
@ -64,7 +64,7 @@ export class Application extends BaseInterface<typeof Clients> {
limit?: number, limit?: number,
offset?: number, offset?: number,
extra?: Parameters<typeof db.query.Clients.findMany>[0], extra?: Parameters<typeof db.query.Clients.findMany>[0],
): Promise<Application[]> { ): Promise<Client[]> {
const found = await db.query.Clients.findMany({ const found = await db.query.Clients.findMany({
where: sql, where: sql,
orderBy, orderBy,
@ -73,30 +73,28 @@ export class Application extends BaseInterface<typeof Clients> {
with: extra?.with, with: extra?.with,
}); });
return found.map((s) => new Application(s)); return found.map((s) => new Client(s));
} }
public static async getFromToken( public static async getFromToken(token: string): Promise<Client | null> {
token: string,
): Promise<Application | null> {
const result = await Token.fromAccessToken(token); const result = await Token.fromAccessToken(token);
return result?.data.client ? new Application(result.data.client) : null; return result?.data.client ? new Client(result.data.client) : null;
} }
public static fromClientId(clientId: string): Promise<Application | null> { public static fromClientId(clientId: string): Promise<Client | null> {
return Application.fromSql(eq(Clients.id, clientId)); return Client.fromSql(eq(Clients.id, clientId));
} }
public async update( public async update(
newApplication: Partial<ApplicationType>, newApplication: Partial<ClientType>,
): Promise<ApplicationType> { ): Promise<ClientType> {
await db await db
.update(Clients) .update(Clients)
.set(newApplication) .set(newApplication)
.where(eq(Clients.id, this.id)); .where(eq(Clients.id, this.id));
const updated = await Application.fromId(this.data.id); const updated = await Client.fromId(this.data.id);
if (!updated) { if (!updated) {
throw new Error("Failed to update application"); throw new Error("Failed to update application");
@ -106,7 +104,7 @@ export class Application extends BaseInterface<typeof Clients> {
return updated.data; return updated.data;
} }
public save(): Promise<ApplicationType> { public save(): Promise<ClientType> {
return this.update(this.data); return this.update(this.data);
} }
@ -120,10 +118,10 @@ export class Application extends BaseInterface<typeof Clients> {
public static async insert( public static async insert(
data: InferInsertModel<typeof Clients>, data: InferInsertModel<typeof Clients>,
): Promise<Application> { ): Promise<Client> {
const inserted = (await db.insert(Clients).values(data).returning())[0]; const inserted = (await db.insert(Clients).values(data).returning())[0];
const application = await Application.fromId(inserted.id); const application = await Client.fromId(inserted.id);
if (!application) { if (!application) {
throw new Error("Failed to insert application"); throw new Error("Failed to insert application");

View file

@ -1,5 +1,5 @@
export { db, setupDatabase } from "../tables/db.ts"; export { db, setupDatabase } from "../tables/db.ts";
export { Application } from "./application.ts"; export { Client } from "./application.ts";
export { Emoji } from "./emoji.ts"; export { Emoji } from "./emoji.ts";
export { Instance } from "./instance.ts"; export { Instance } from "./instance.ts";
export { Like } from "./like.ts"; export { Like } from "./like.ts";

View file

@ -36,7 +36,7 @@ import {
Notifications, Notifications,
Users, Users,
} from "../tables/schema.ts"; } from "../tables/schema.ts";
import { Application } from "./application.ts"; import { Client } from "./application.ts";
import { BaseInterface } from "./base.ts"; import { BaseInterface } from "./base.ts";
import { Emoji } from "./emoji.ts"; import { Emoji } from "./emoji.ts";
import { Instance } from "./instance.ts"; import { Instance } from "./instance.ts";
@ -129,7 +129,7 @@ const findManyNotes = async (
}, },
}, },
likes: true, likes: true,
application: true, client: true,
mentions: { mentions: {
with: { with: {
user: { user: {
@ -238,7 +238,7 @@ type NoteTypeWithRelations = NoteType & {
emojis: (typeof Emoji.$type)[]; emojis: (typeof Emoji.$type)[];
reply: NoteType | null; reply: NoteType | null;
quote: NoteType | null; quote: NoteType | null;
application: typeof Application.$type | null; client: typeof Client.$type | null;
pinned: boolean; pinned: boolean;
reblogged: boolean; reblogged: boolean;
muted: boolean; muted: boolean;
@ -514,7 +514,7 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
visibility, visibility,
sensitive: false, sensitive: false,
updatedAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
applicationId: null, clientId: null,
uri: uri?.href, uri: uri?.href,
}); });
@ -1162,8 +1162,8 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
in_reply_to_account_id: data.reply?.authorId || null, in_reply_to_account_id: data.reply?.authorId || null,
account: this.author.toApi(userFetching?.id === data.authorId), account: this.author.toApi(userFetching?.id === data.authorId),
created_at: new Date(data.createdAt).toISOString(), created_at: new Date(data.createdAt).toISOString(),
application: data.application application: data.client
? new Application(data.application).toApi() ? new Client(data.client).toApi()
: undefined, : undefined,
card: null, card: null,
content: replacedContent, content: replacedContent,

View file

@ -10,12 +10,12 @@ import {
import type { z } from "zod/v4"; import type { z } from "zod/v4";
import { db } from "../tables/db.ts"; import { db } from "../tables/db.ts";
import { Tokens } from "../tables/schema.ts"; import { Tokens } from "../tables/schema.ts";
import type { Application } from "./application.ts"; import type { Client } from "./application.ts";
import { BaseInterface } from "./base.ts"; import { BaseInterface } from "./base.ts";
import { User } from "./user.ts"; import { User } from "./user.ts";
type TokenType = InferSelectModel<typeof Tokens> & { type TokenType = InferSelectModel<typeof Tokens> & {
client: typeof Application.$type; client: typeof Client.$type;
}; };
export class Token extends BaseInterface<typeof Tokens, TokenType> { export class Token extends BaseInterface<typeof Tokens, TokenType> {

View file

@ -0,0 +1,15 @@
ALTER TABLE "Applications" RENAME TO "Clients";--> statement-breakpoint
ALTER TABLE "Notes" RENAME COLUMN "applicationId" TO "clientId";--> statement-breakpoint
ALTER TABLE "OpenIdLoginFlows" RENAME COLUMN "applicationId" TO "clientId";--> statement-breakpoint
ALTER TABLE "AuthorizationCodes" DROP CONSTRAINT "AuthorizationCodes_clientId_Applications_client_id_fk";
--> statement-breakpoint
ALTER TABLE "Notes" DROP CONSTRAINT "Notes_applicationId_Applications_client_id_fk";
--> statement-breakpoint
ALTER TABLE "OpenIdLoginFlows" DROP CONSTRAINT "OpenIdLoginFlows_applicationId_Applications_client_id_fk";
--> statement-breakpoint
ALTER TABLE "Tokens" DROP CONSTRAINT "Tokens_clientId_Applications_client_id_fk";
--> statement-breakpoint
ALTER TABLE "AuthorizationCodes" ADD CONSTRAINT "AuthorizationCodes_clientId_Clients_client_id_fk" FOREIGN KEY ("clientId") REFERENCES "public"."Clients"("client_id") ON DELETE cascade ON UPDATE cascade;--> statement-breakpoint
ALTER TABLE "Notes" ADD CONSTRAINT "Notes_clientId_Clients_client_id_fk" FOREIGN KEY ("clientId") REFERENCES "public"."Clients"("client_id") ON DELETE set null ON UPDATE cascade;--> statement-breakpoint
ALTER TABLE "OpenIdLoginFlows" ADD CONSTRAINT "OpenIdLoginFlows_clientId_Clients_client_id_fk" FOREIGN KEY ("clientId") REFERENCES "public"."Clients"("client_id") ON DELETE cascade ON UPDATE cascade;--> statement-breakpoint
ALTER TABLE "Tokens" ADD CONSTRAINT "Tokens_clientId_Clients_client_id_fk" FOREIGN KEY ("clientId") REFERENCES "public"."Clients"("client_id") ON DELETE cascade ON UPDATE cascade;

File diff suppressed because it is too large Load diff

View file

@ -365,6 +365,13 @@
"when": 1755729662013, "when": 1755729662013,
"tag": "0051_stiff_morbius", "tag": "0051_stiff_morbius",
"breakpoints": true "breakpoints": true
},
{
"idx": 52,
"version": "7",
"when": 1755732000165,
"tag": "0052_complete_hellfire_club",
"breakpoints": true
} }
] ]
} }

View file

@ -309,7 +309,7 @@ export const RelationshipsRelations = relations(Relationships, ({ one }) => ({
}), }),
})); }));
export const Clients = pgTable("Applications", { export const Clients = pgTable("Clients", {
id: text("client_id").primaryKey(), id: text("client_id").primaryKey(),
secret: text("secret").notNull(), secret: text("secret").notNull(),
redirectUris: text("redirect_uris") redirectUris: text("redirect_uris")
@ -494,7 +494,7 @@ export const Notes = pgTable("Notes", {
}), }),
sensitive: boolean("sensitive").notNull().default(false), sensitive: boolean("sensitive").notNull().default(false),
spoilerText: text("spoiler_text").default("").notNull(), spoilerText: text("spoiler_text").default("").notNull(),
applicationId: text("applicationId").references(() => Clients.id, { clientId: text("clientId").references(() => Clients.id, {
onDelete: "set null", onDelete: "set null",
onUpdate: "cascade", onUpdate: "cascade",
}), }),
@ -528,8 +528,8 @@ export const NotesRelations = relations(Notes, ({ many, one }) => ({
references: [Notes.id], references: [Notes.id],
relationName: "NoteToQuotes", relationName: "NoteToQuotes",
}), }),
application: one(Clients, { client: one(Clients, {
fields: [Notes.applicationId], fields: [Notes.clientId],
references: [Clients.id], references: [Clients.id],
}), }),
quotes: many(Notes, { quotes: many(Notes, {
@ -703,7 +703,7 @@ export const OpenIdLoginFlows = pgTable("OpenIdLoginFlows", {
clientState: text("client_state"), clientState: text("client_state"),
clientRedirectUri: text("client_redirect_uri"), clientRedirectUri: text("client_redirect_uri"),
clientScopes: text("client_scopes").array(), clientScopes: text("client_scopes").array(),
applicationId: text("applicationId").references(() => Clients.id, { clientId: text("clientId").references(() => Clients.id, {
onDelete: "cascade", onDelete: "cascade",
onUpdate: "cascade", onUpdate: "cascade",
}), }),
@ -713,8 +713,8 @@ export const OpenIdLoginFlows = pgTable("OpenIdLoginFlows", {
export const OpenIdLoginFlowsRelations = relations( export const OpenIdLoginFlowsRelations = relations(
OpenIdLoginFlows, OpenIdLoginFlows,
({ one }) => ({ ({ one }) => ({
application: one(Clients, { client: one(Clients, {
fields: [OpenIdLoginFlows.applicationId], fields: [OpenIdLoginFlows.clientId],
references: [Clients.id], references: [Clients.id],
}), }),
}), }),

View file

@ -2,7 +2,7 @@ import { mock } from "bun:test";
import { Client as VersiaClient } from "@versia/client"; import { Client as VersiaClient } from "@versia/client";
import { config } from "@versia-server/config"; import { config } from "@versia-server/config";
import { import {
Application, Client,
db, db,
Note, Note,
setupDatabase, setupDatabase,
@ -50,7 +50,7 @@ export const generateClient = async (
dbToken: Token; dbToken: Token;
} }
> => { > => {
const application = await Application.insert({ const application = await Client.insert({
id: randomUUIDv7(), id: randomUUIDv7(),
name: "Versia", name: "Versia",
redirectUris: [], redirectUris: [],
@ -111,7 +111,7 @@ export const getTestUsers = async (
const users: User[] = []; const users: User[] = [];
const passwords: string[] = []; const passwords: string[] = [];
const application = await Application.insert({ const application = await Client.insert({
id: randomUUIDv7(), id: randomUUIDv7(),
name: "Versia", name: "Versia",
redirectUris: [], redirectUris: [],
@ -182,7 +182,7 @@ export const getTestStatuses = async (
sensitive: false, sensitive: false,
updatedAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
visibility: "public", visibility: "public",
applicationId: null, clientId: null,
...partial, ...partial,
}); });

View file

@ -1,6 +1,6 @@
import type * as VersiaEntities from "@versia/sdk/entities"; import type * as VersiaEntities from "@versia/sdk/entities";
import type { ConfigSchema } from "@versia-server/config"; import type { ConfigSchema } from "@versia-server/config";
import type { Application, Token, User } from "@versia-server/kit/db"; import type { Client, Token, User } from "@versia-server/kit/db";
import type { SocketAddress } from "bun"; import type { SocketAddress } from "bun";
import type { Hono } from "hono"; import type { Hono } from "hono";
import type { RouterRoute } from "hono/types"; import type { RouterRoute } from "hono/types";
@ -11,7 +11,7 @@ export type HttpVerb = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS";
export interface AuthData { export interface AuthData {
user: User | null; user: User | null;
token: Token | null; token: Token | null;
application: Application | null; application: Client | null;
} }
export type HonoEnv = { export type HonoEnv = {