refactor(database): Remove dependency on pg_uuidv7 extension

This commit is contained in:
Jesse Wierzbinski 2025-03-30 22:10:33 +02:00
parent 2bb3731187
commit 37f68bbffd
No known key found for this signature in database
37 changed files with 2465 additions and 4 deletions

View file

@ -42,6 +42,7 @@ Versia Server now serves static files directly from a configurable path, and `ve
### Backend ### Backend
- [x] 🚀 Upgraded Bun to `1.2.7` - [x] 🚀 Upgraded Bun to `1.2.7`
- [x] 🔥 Removed dependency on the `pg_uuidv7` extension. Versia Server can now be used with "vanilla" PostgreSQL.
- [x] 🖼️ Simplified media pipeline: this will improve S3 performance - [x] 🖼️ Simplified media pipeline: this will improve S3 performance
- [ ] 📈 It is now possible to disable media proxying for your CDN (offloading considerable bandwidth to your more optimized CDN). - [ ] 📈 It is now possible to disable media proxying for your CDN (offloading considerable bandwidth to your more optimized CDN).
- [x] 👷 Outbound federation, inbox processing, data fetching and media processing are now handled by a queue system. - [x] 👷 Outbound federation, inbox processing, data fetching and media processing are now handled by a queue system.

View file

@ -1,6 +1,7 @@
import { afterAll, describe, expect, test } from "bun:test"; import { afterAll, describe, expect, test } from "bun:test";
import { randomString } from "@/math"; import { randomString } from "@/math";
import { Application } from "@versia/kit/db"; import { Application } from "@versia/kit/db";
import { randomUUIDv7 } from "bun";
import { config } from "~/config.ts"; import { config } from "~/config.ts";
import { fakeRequest, getTestUsers } from "~/tests/utils"; import { fakeRequest, getTestUsers } from "~/tests/utils";
@ -8,6 +9,7 @@ const { users, deleteUsers, passwords } = await getTestUsers(1);
// Create application // Create application
const application = await Application.insert({ const application = await Application.insert({
id: randomUUIDv7(),
name: "Test Application", name: "Test Application",
clientId: randomString(32, "hex"), clientId: randomString(32, "hex"),
secret: "test", secret: "test",

View file

@ -1,6 +1,7 @@
import { afterAll, describe, expect, test } from "bun:test"; import { afterAll, describe, expect, test } from "bun:test";
import { randomString } from "@/math"; import { randomString } from "@/math";
import { Application } from "@versia/kit/db"; import { Application } from "@versia/kit/db";
import { randomUUIDv7 } from "bun";
import { config } from "~/config.ts"; import { config } from "~/config.ts";
import { fakeRequest, getTestUsers } from "~/tests/utils"; import { fakeRequest, getTestUsers } from "~/tests/utils";
@ -10,6 +11,7 @@ const newPassword = randomString(16, "hex");
// Create application // Create application
const application = await Application.insert({ const application = await Application.insert({
id: randomUUIDv7(),
name: "Test Application", name: "Test Application",
clientId: randomString(32, "hex"), clientId: randomString(32, "hex"),
secret: "test", secret: "test",

View file

@ -1,8 +1,8 @@
import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import { RolePermission } from "@versia/client/schemas"; import { RolePermission } from "@versia/client/schemas";
import { Role } from "@versia/kit/db"; import { Role } from "@versia/kit/db";
import { randomUUIDv7 } from "bun";
import { generateClient, getTestUsers } from "~/tests/utils"; import { generateClient, getTestUsers } from "~/tests/utils";
const { users, deleteUsers } = await getTestUsers(2); const { users, deleteUsers } = await getTestUsers(2);
let role: Role; let role: Role;
let higherPriorityRole: Role; let higherPriorityRole: Role;
@ -10,6 +10,7 @@ let higherPriorityRole: Role;
beforeAll(async () => { beforeAll(async () => {
// Create new role // Create new role
role = await Role.insert({ role = await Role.insert({
id: randomUUIDv7(),
name: "test", name: "test",
permissions: [RolePermission.ManageRoles], permissions: [RolePermission.ManageRoles],
priority: 2, priority: 2,
@ -24,6 +25,7 @@ beforeAll(async () => {
// Create a role with higher priority than the user's role // Create a role with higher priority than the user's role
higherPriorityRole = await Role.insert({ higherPriorityRole = await Role.insert({
id: randomUUIDv7(),
name: "higherPriorityRole", name: "higherPriorityRole",
permissions: [RolePermission.ManageRoles], permissions: [RolePermission.ManageRoles],
priority: 3, // Higher priority than the user's role priority: 3, // Higher priority than the user's role

View file

@ -1,6 +1,7 @@
import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import { RolePermission } from "@versia/client/schemas"; import { RolePermission } from "@versia/client/schemas";
import { Role } from "@versia/kit/db"; import { Role } from "@versia/kit/db";
import { randomUUIDv7 } from "bun";
import { generateClient, getTestUsers } from "~/tests/utils"; import { generateClient, getTestUsers } from "~/tests/utils";
const { users, deleteUsers } = await getTestUsers(2); const { users, deleteUsers } = await getTestUsers(2);
@ -9,6 +10,7 @@ let role: Role;
beforeAll(async () => { beforeAll(async () => {
// Create new role // Create new role
role = await Role.insert({ role = await Role.insert({
id: randomUUIDv7(),
name: "test", name: "test",
permissions: [RolePermission.ManageRoles], permissions: [RolePermission.ManageRoles],
priority: 2, priority: 2,

View file

@ -5,6 +5,7 @@ import {
CredentialApplication as CredentialApplicationSchema, CredentialApplication as CredentialApplicationSchema,
} from "@versia/client/schemas"; } from "@versia/client/schemas";
import { Application } from "@versia/kit/db"; import { Application } from "@versia/kit/db";
import { randomUUIDv7 } from "bun";
import { describeRoute } from "hono-openapi"; import { describeRoute } from "hono-openapi";
import { resolver, validator } from "hono-openapi/zod"; import { resolver, validator } from "hono-openapi/zod";
import { z } from "zod"; import { z } from "zod";
@ -66,6 +67,7 @@ export default apiRoute((app) =>
context.req.valid("json"); context.req.valid("json");
const app = await Application.insert({ const app = await Application.insert({
id: randomUUIDv7(),
name: client_name, name: client_name,
redirectUri: redirect_uris.join("\n"), redirectUri: redirect_uris.join("\n"),
scopes: scopes.join(" "), scopes: scopes.join(" "),

View file

@ -4,6 +4,7 @@ import { CustomEmoji as CustomEmojiSchema } from "@versia/client/schemas";
import { RolePermission } from "@versia/client/schemas"; import { RolePermission } from "@versia/client/schemas";
import { Emoji, Media } from "@versia/kit/db"; import { Emoji, Media } from "@versia/kit/db";
import { Emojis } from "@versia/kit/tables"; import { Emojis } from "@versia/kit/tables";
import { randomUUIDv7 } from "bun";
import { and, eq, isNull, or } from "drizzle-orm"; import { and, eq, isNull, or } from "drizzle-orm";
import { describeRoute } from "hono-openapi"; import { describeRoute } from "hono-openapi";
import { resolver, validator } from "hono-openapi/zod"; import { resolver, validator } from "hono-openapi/zod";
@ -124,6 +125,7 @@ export default apiRoute((app) =>
}); });
const emoji = await Emoji.insert({ const emoji = await Emoji.insert({
id: randomUUIDv7(),
shortcode, shortcode,
mediaId: media.id, mediaId: media.id,
visibleInPicker: true, visibleInPicker: true,

View file

@ -7,6 +7,7 @@ import {
import { RolePermission } from "@versia/client/schemas"; import { RolePermission } from "@versia/client/schemas";
import { db } from "@versia/kit/db"; import { db } from "@versia/kit/db";
import { Markers } from "@versia/kit/tables"; import { Markers } from "@versia/kit/tables";
import { randomUUIDv7 } from "bun";
import { type SQL, and, eq } from "drizzle-orm"; import { type SQL, and, eq } from "drizzle-orm";
import { describeRoute } from "hono-openapi"; import { describeRoute } from "hono-openapi";
import { resolver, validator } from "hono-openapi/zod"; import { resolver, validator } from "hono-openapi/zod";
@ -189,6 +190,7 @@ export default apiRoute((app) => {
await db await db
.insert(Markers) .insert(Markers)
.values({ .values({
id: randomUUIDv7(),
userId: user.id, userId: user.id,
timeline: "home", timeline: "home",
noteId: homeId, noteId: homeId,
@ -218,6 +220,7 @@ export default apiRoute((app) => {
await db await db
.insert(Markers) .insert(Markers)
.values({ .values({
id: randomUUIDv7(),
userId: user.id, userId: user.id,
timeline: "notifications", timeline: "notifications",
notificationId: notificationsId, notificationId: notificationsId,

View file

@ -6,6 +6,7 @@ import {
} from "@versia/client/schemas"; } from "@versia/client/schemas";
import { RolePermission } from "@versia/client/schemas"; import { RolePermission } from "@versia/client/schemas";
import { PushSubscription } from "@versia/kit/db"; import { PushSubscription } from "@versia/kit/db";
import { randomUUIDv7 } from "bun";
import { describeRoute } from "hono-openapi"; import { describeRoute } from "hono-openapi";
import { resolver, validator } from "hono-openapi/zod"; import { resolver, validator } from "hono-openapi/zod";
import { ApiError } from "~/classes/errors/api-error"; import { ApiError } from "~/classes/errors/api-error";
@ -64,6 +65,7 @@ export default apiRoute((app) =>
await PushSubscription.clearAllOfToken(token); await PushSubscription.clearAllOfToken(token);
const ps = await PushSubscription.insert({ const ps = await PushSubscription.insert({
id: randomUUIDv7(),
alerts: data.alerts, alerts: data.alerts,
policy, policy,
endpoint: subscription.endpoint, endpoint: subscription.endpoint,

View file

@ -1,5 +1,6 @@
import { afterAll, beforeEach, describe, expect, test } from "bun:test"; import { afterAll, beforeEach, describe, expect, test } from "bun:test";
import { PushSubscription } from "@versia/kit/db"; import { PushSubscription } from "@versia/kit/db";
import { randomUUIDv7 } from "bun";
import { generateClient, getTestUsers } from "~/tests/utils"; import { generateClient, getTestUsers } from "~/tests/utils";
const { users, tokens, deleteUsers } = await getTestUsers(2); const { users, tokens, deleteUsers } = await getTestUsers(2);
@ -44,6 +45,7 @@ describe("/api/v1/push/subscriptions", () => {
await using client = await generateClient(users[1]); await using client = await generateClient(users[1]);
await PushSubscription.insert({ await PushSubscription.insert({
id: randomUUIDv7(),
endpoint: "https://example.com", endpoint: "https://example.com",
alerts: { alerts: {
update: true, update: true,
@ -76,6 +78,7 @@ describe("/api/v1/push/subscriptions", () => {
await using client = await generateClient(users[0]); await using client = await generateClient(users[0]);
await PushSubscription.insert({ await PushSubscription.insert({
id: randomUUIDv7(),
endpoint: "https://example.com", endpoint: "https://example.com",
alerts: { alerts: {
update: true, update: true,
@ -175,6 +178,7 @@ describe("/api/v1/push/subscriptions", () => {
await using client = await generateClient(users[0]); await using client = await generateClient(users[0]);
await PushSubscription.insert({ await PushSubscription.insert({
id: randomUUIDv7(),
endpoint: "https://example.com", endpoint: "https://example.com",
alerts: { alerts: {
update: true, update: true,
@ -217,6 +221,7 @@ describe("/api/v1/push/subscriptions", () => {
await using client = await generateClient(users[0]); await using client = await generateClient(users[0]);
await PushSubscription.insert({ await PushSubscription.insert({
id: randomUUIDv7(),
endpoint: "https://example.com", endpoint: "https://example.com",
alerts: { alerts: {
update: true, update: true,

View file

@ -1,6 +1,7 @@
import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import { RolePermission } from "@versia/client/schemas"; import { RolePermission } from "@versia/client/schemas";
import { Role } from "@versia/kit/db"; import { Role } from "@versia/kit/db";
import { randomUUIDv7 } from "bun";
import { generateClient, getTestUsers } from "~/tests/utils"; import { generateClient, getTestUsers } from "~/tests/utils";
const { users, deleteUsers } = await getTestUsers(2); const { users, deleteUsers } = await getTestUsers(2);
@ -10,6 +11,7 @@ let higherPriorityRole: Role;
beforeAll(async () => { beforeAll(async () => {
// Create new role // Create new role
role = await Role.insert({ role = await Role.insert({
id: randomUUIDv7(),
name: "test", name: "test",
permissions: [RolePermission.ManageRoles], permissions: [RolePermission.ManageRoles],
priority: 2, priority: 2,
@ -24,6 +26,7 @@ beforeAll(async () => {
// Create a role with higher priority than the user's role // Create a role with higher priority than the user's role
higherPriorityRole = await Role.insert({ higherPriorityRole = await Role.insert({
id: randomUUIDv7(),
name: "higherPriorityRole", name: "higherPriorityRole",
permissions: [RolePermission.ManageRoles], permissions: [RolePermission.ManageRoles],
priority: 3, // Higher priority than the user's role priority: 3, // Higher priority than the user's role
@ -173,6 +176,7 @@ describe("/api/v1/roles/:id", () => {
test("should delete role", async () => { test("should delete role", async () => {
const newRole = await Role.insert({ const newRole = await Role.insert({
id: randomUUIDv7(),
name: "test2", name: "test2",
permissions: [RolePermission.ManageRoles], permissions: [RolePermission.ManageRoles],
priority: 2, priority: 2,

View file

@ -1,6 +1,7 @@
import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import { RolePermission } from "@versia/client/schemas"; import { RolePermission } from "@versia/client/schemas";
import { Role } from "@versia/kit/db"; import { Role } from "@versia/kit/db";
import { randomUUIDv7 } from "bun";
import { config } from "~/config.ts"; import { config } from "~/config.ts";
import { generateClient, getTestUsers } from "~/tests/utils"; import { generateClient, getTestUsers } from "~/tests/utils";
@ -10,6 +11,7 @@ let role: Role;
beforeAll(async () => { beforeAll(async () => {
// Create new role // Create new role
role = await Role.insert({ role = await Role.insert({
id: randomUUIDv7(),
name: "test", name: "test",
permissions: [RolePermission.ManageRoles], permissions: [RolePermission.ManageRoles],
priority: 10, priority: 10,

View file

@ -2,6 +2,7 @@ import { apiRoute, auth, handleZodError } from "@/api";
import { Role as RoleSchema } from "@versia/client/schemas"; import { Role as RoleSchema } from "@versia/client/schemas";
import { RolePermission } from "@versia/client/schemas"; import { RolePermission } from "@versia/client/schemas";
import { Role } from "@versia/kit/db"; import { Role } from "@versia/kit/db";
import { randomUUIDv7 } from "bun";
import { describeRoute } from "hono-openapi"; import { describeRoute } from "hono-openapi";
import { resolver, validator } from "hono-openapi/zod"; import { resolver, validator } from "hono-openapi/zod";
import { z } from "zod"; import { z } from "zod";
@ -105,6 +106,7 @@ export default apiRoute((app) => {
} }
const newRole = await Role.insert({ const newRole = await Role.insert({
id: randomUUIDv7(),
description, description,
icon, icon,
name, name,

View file

@ -3,6 +3,7 @@ import { Status as StatusSchema } from "@versia/client/schemas";
import { RolePermission } from "@versia/client/schemas"; import { RolePermission } from "@versia/client/schemas";
import { Note } from "@versia/kit/db"; import { Note } from "@versia/kit/db";
import { Notes } from "@versia/kit/tables"; import { Notes } from "@versia/kit/tables";
import { randomUUIDv7 } from "bun";
import { and, eq } from "drizzle-orm"; import { and, eq } from "drizzle-orm";
import { describeRoute } from "hono-openapi"; import { describeRoute } from "hono-openapi";
import { resolver, validator } from "hono-openapi/zod"; import { resolver, validator } from "hono-openapi/zod";
@ -66,6 +67,7 @@ export default apiRoute((app) =>
} }
const newReblog = await Note.insert({ const newReblog = await Note.insert({
id: randomUUIDv7(),
authorId: user.id, authorId: user.id,
reblogId: note.data.id, reblogId: note.data.id,
visibility, visibility,

View file

@ -2,6 +2,7 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import type { Status } from "@versia/client/schemas"; import type { Status } from "@versia/client/schemas";
import { Media, db } from "@versia/kit/db"; import { Media, db } from "@versia/kit/db";
import { Emojis } from "@versia/kit/tables"; import { Emojis } from "@versia/kit/tables";
import { randomUUIDv7 } from "bun";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import type { z } from "zod"; import type { z } from "zod";
import { config } from "~/config.ts"; import { config } from "~/config.ts";
@ -17,6 +18,7 @@ afterAll(async () => {
beforeAll(async () => { beforeAll(async () => {
media = await Media.insert({ media = await Media.insert({
id: randomUUIDv7(),
content: { content: {
"image/png": { "image/png": {
content: "https://example.com/test.png", content: "https://example.com/test.png",
@ -24,7 +26,9 @@ beforeAll(async () => {
}, },
}, },
}); });
await db.insert(Emojis).values({ await db.insert(Emojis).values({
id: randomUUIDv7(),
shortcode: "test", shortcode: "test",
mediaId: media.id, mediaId: media.id,
visibleInPicker: true, visibleInPicker: true,

View file

@ -6,6 +6,7 @@ import {
import { RolePermission } from "@versia/client/schemas"; import { RolePermission } from "@versia/client/schemas";
import { db } from "@versia/kit/db"; import { db } from "@versia/kit/db";
import { FilterKeywords, Filters } from "@versia/kit/tables"; import { FilterKeywords, Filters } from "@versia/kit/tables";
import { randomUUIDv7 } from "bun";
import type { SQL } from "drizzle-orm"; import type { SQL } from "drizzle-orm";
import { describeRoute } from "hono-openapi"; import { describeRoute } from "hono-openapi";
import { resolver, validator } from "hono-openapi/zod"; import { resolver, validator } from "hono-openapi/zod";
@ -139,6 +140,7 @@ export default apiRoute((app) => {
await db await db
.insert(Filters) .insert(Filters)
.values({ .values({
id: randomUUIDv7(),
title, title,
context: ctx, context: ctx,
filterAction: filter_action, filterAction: filter_action,
@ -160,6 +162,7 @@ export default apiRoute((app) => {
.insert(FilterKeywords) .insert(FilterKeywords)
.values( .values(
keywords_attributes?.map((keyword) => ({ keywords_attributes?.map((keyword) => ({
id: randomUUIDv7(),
filterId: newFilter.id, filterId: newFilter.id,
keyword: keyword.keyword, keyword: keyword.keyword,
wholeWord: keyword.whole_word ?? false, wholeWord: keyword.whole_word ?? false,

View file

@ -4,6 +4,7 @@ import type { CustomEmoji } from "@versia/client/schemas";
import type { CustomEmojiExtension } from "@versia/federation/types"; import type { CustomEmojiExtension } from "@versia/federation/types";
import { type Instance, Media, db } from "@versia/kit/db"; import { type Instance, Media, db } from "@versia/kit/db";
import { Emojis, type Instances, type Medias } from "@versia/kit/tables"; import { Emojis, type Instances, type Medias } from "@versia/kit/tables";
import { randomUUIDv7 } from "bun";
import { import {
type InferInsertModel, type InferInsertModel,
type InferSelectModel, type InferSelectModel,
@ -212,6 +213,7 @@ export class Emoji extends BaseInterface<typeof Emojis, EmojiType> {
const media = await Media.fromVersia(emoji.url); const media = await Media.fromVersia(emoji.url);
return Emoji.insert({ return Emoji.insert({
id: randomUUIDv7(),
shortcode, shortcode,
mediaId: media.id, mediaId: media.id,
visibleInPicker: true, visibleInPicker: true,

View file

@ -3,6 +3,7 @@ import { EntityValidator, type ResponseError } from "@versia/federation";
import type { InstanceMetadata } from "@versia/federation/types"; import type { InstanceMetadata } from "@versia/federation/types";
import { db } from "@versia/kit/db"; import { db } from "@versia/kit/db";
import { Instances } from "@versia/kit/tables"; import { Instances } from "@versia/kit/tables";
import { randomUUIDv7 } from "bun";
import chalk from "chalk"; import chalk from "chalk";
import { import {
type InferInsertModel, type InferInsertModel,
@ -337,6 +338,7 @@ export class Instance extends BaseInterface<typeof Instances> {
const { metadata, protocol } = output; const { metadata, protocol } = output;
return Instance.insert({ return Instance.insert({
id: randomUUIDv7(),
baseUrl: host, baseUrl: host,
name: metadata.name, name: metadata.name,
version: metadata.software.version, version: metadata.software.version,

View file

@ -200,6 +200,7 @@ export class Media extends BaseInterface<typeof Medias> {
: undefined; : undefined;
const newAttachment = await Media.insert({ const newAttachment = await Media.insert({
id: randomUUIDv7(),
content, content,
thumbnail: thumbnailContent, thumbnail: thumbnailContent,
}); });
@ -242,6 +243,7 @@ export class Media extends BaseInterface<typeof Medias> {
}; };
const newAttachment = await Media.insert({ const newAttachment = await Media.insert({
id: randomUUIDv7(),
content, content,
}); });
@ -525,6 +527,7 @@ export class Media extends BaseInterface<typeof Medias> {
public static fromVersia(contentFormat: ContentFormat): Promise<Media> { public static fromVersia(contentFormat: ContentFormat): Promise<Media> {
return Media.insert({ return Media.insert({
id: randomUUIDv7(),
content: contentFormat, content: contentFormat,
originalContent: contentFormat, originalContent: contentFormat,
}); });

View file

@ -18,6 +18,7 @@ import {
Notes, Notes,
Users, Users,
} from "@versia/kit/tables"; } from "@versia/kit/tables";
import { randomUUIDv7 } from "bun";
import { import {
type InferInsertModel, type InferInsertModel,
type InferSelectModel, type InferSelectModel,
@ -369,6 +370,7 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
const htmlContent = await contentToHtml(data.content, parsedMentions); const htmlContent = await contentToHtml(data.content, parsedMentions);
const newNote = await Note.insert({ const newNote = await Note.insert({
id: randomUUIDv7(),
authorId: data.author.id, authorId: data.author.id,
content: htmlContent, content: htmlContent,
contentSource: contentSource:

View file

@ -1,6 +1,7 @@
import type { ReactionExtension } from "@versia/federation/types"; import type { ReactionExtension } from "@versia/federation/types";
import { Emoji, Instance, type Note, User, db } from "@versia/kit/db"; import { Emoji, Instance, type Note, User, db } from "@versia/kit/db";
import { type Notes, Reactions, type Users } from "@versia/kit/tables"; import { type Notes, Reactions, type Users } from "@versia/kit/tables";
import { randomUUIDv7 } from "bun";
import { import {
type InferInsertModel, type InferInsertModel,
type InferSelectModel, type InferSelectModel,
@ -231,6 +232,7 @@ export class Reaction extends BaseInterface<typeof Reactions, ReactionType> {
: null; : null;
return Reaction.insert({ return Reaction.insert({
id: randomUUIDv7(),
uri: reactionToConvert.uri, uri: reactionToConvert.uri,
authorId: author.id, authorId: author.id,
noteId: note.id, noteId: note.id,

View file

@ -1,6 +1,7 @@
import type { Relationship as RelationshipSchema } from "@versia/client/schemas"; import type { Relationship as RelationshipSchema } from "@versia/client/schemas";
import { db } from "@versia/kit/db"; import { db } from "@versia/kit/db";
import { Relationships } from "@versia/kit/tables"; import { Relationships } from "@versia/kit/tables";
import { randomUUIDv7 } from "bun";
import { import {
type InferInsertModel, type InferInsertModel,
type InferSelectModel, type InferSelectModel,
@ -83,6 +84,7 @@ export class Relationship extends BaseInterface<
if (!found) { if (!found) {
// Create a new relationship if one doesn't exist // Create a new relationship if one doesn't exist
return await Relationship.insert({ return await Relationship.insert({
id: randomUUIDv7(),
ownerId: owner.id, ownerId: owner.id,
subjectId: subject.id, subjectId: subject.id,
languages: [], languages: [],
@ -119,6 +121,7 @@ export class Relationship extends BaseInterface<
for (const subjectId of missingSubjectsIds) { for (const subjectId of missingSubjectsIds) {
await Relationship.insert({ await Relationship.insert({
id: randomUUIDv7(),
ownerId: owner.id, ownerId: owner.id,
subjectId, subjectId,
languages: [], languages: [],
@ -213,6 +216,7 @@ export class Relationship extends BaseInterface<
await db await db
.insert(Relationships) .insert(Relationships)
.values({ .values({
id: randomUUIDv7(),
ownerId: oppositeTo.subjectId, ownerId: oppositeTo.subjectId,
subjectId: oppositeTo.ownerId, subjectId: oppositeTo.ownerId,
languages: [], languages: [],

View file

@ -33,6 +33,7 @@ import {
UserToPinnedNotes, UserToPinnedNotes,
Users, Users,
} from "@versia/kit/tables"; } from "@versia/kit/tables";
import { randomUUIDv7 } from "bun";
import chalk from "chalk"; import chalk from "chalk";
import { import {
type InferInsertModel, type InferInsertModel,
@ -471,6 +472,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
} }
const newLike = await Like.insert({ const newLike = await Like.insert({
id: randomUUIDv7(),
likerId: this.id, likerId: this.id,
likedId: note.id, likedId: note.id,
uri, uri,
@ -520,6 +522,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
note?: Note, note?: Note,
): Promise<void> { ): Promise<void> {
const notification = await Notification.insert({ const notification = await Notification.insert({
id: randomUUIDv7(),
accountId: relatedUser.id, accountId: relatedUser.id,
type, type,
notifiedId: this.id, notifiedId: this.id,
@ -723,6 +726,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
); );
} else { } else {
avatar = await Media.insert({ avatar = await Media.insert({
id: randomUUIDv7(),
content: user.avatar, content: user.avatar,
}); });
} }
@ -737,6 +741,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
); );
} else { } else {
header = await Media.insert({ header = await Media.insert({
id: randomUUIDv7(),
content: user.header, content: user.header,
}); });
} }
@ -755,17 +760,20 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
// Else, create a new user // Else, create a new user
const avatar = user.avatar const avatar = user.avatar
? await Media.insert({ ? await Media.insert({
id: randomUUIDv7(),
content: user.avatar, content: user.avatar,
}) })
: null; : null;
const header = user.header const header = user.header
? await Media.insert({ ? await Media.insert({
id: randomUUIDv7(),
content: user.header, content: user.header,
}) })
: null; : null;
const newUser = await User.insert({ const newUser = await User.insert({
id: randomUUIDv7(),
...data, ...data,
avatarId: avatar?.id, avatarId: avatar?.id,
headerId: header?.id, headerId: header?.id,
@ -876,6 +884,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
await db await db
.insert(Users) .insert(Users)
.values({ .values({
id: randomUUIDv7(),
username: data.username, username: data.username,
displayName: data.display_name ?? data.username, displayName: data.display_name ?? data.username,
password: password:

View file

@ -1,4 +1,5 @@
import { randomString } from "@/math.ts"; import { randomString } from "@/math.ts";
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
// biome-ignore lint/correctness/noUnusedImports: Root import is required or the Clec type definitions won't work // biome-ignore lint/correctness/noUnusedImports: Root import is required or the Clec type definitions won't work
@ -22,6 +23,7 @@ export const generateTokenCommand = defineCommand(
} }
const token = await Token.insert({ const token = await Token.insert({
id: randomUUIDv7(),
accessToken: randomString(64, "base64url"), accessToken: randomString(64, "base64url"),
code: null, code: null,
scope: "read write follow", scope: "read write follow",

View file

@ -0,0 +1,22 @@
ALTER TABLE "Applications" ALTER COLUMN "id" DROP DEFAULT;--> statement-breakpoint
ALTER TABLE "Challenges" ALTER COLUMN "id" DROP DEFAULT;--> statement-breakpoint
ALTER TABLE "Emojis" ALTER COLUMN "id" DROP DEFAULT;--> statement-breakpoint
ALTER TABLE "FilterKeywords" ALTER COLUMN "id" DROP DEFAULT;--> statement-breakpoint
ALTER TABLE "Filters" ALTER COLUMN "id" DROP DEFAULT;--> statement-breakpoint
ALTER TABLE "Flags" ALTER COLUMN "id" DROP DEFAULT;--> statement-breakpoint
ALTER TABLE "Instances" ALTER COLUMN "id" DROP DEFAULT;--> statement-breakpoint
ALTER TABLE "Likes" ALTER COLUMN "id" DROP DEFAULT;--> statement-breakpoint
ALTER TABLE "Markers" ALTER COLUMN "id" DROP DEFAULT;--> statement-breakpoint
ALTER TABLE "Medias" ALTER COLUMN "id" DROP DEFAULT;--> statement-breakpoint
ALTER TABLE "ModNotes" ALTER COLUMN "id" DROP DEFAULT;--> statement-breakpoint
ALTER TABLE "ModTags" ALTER COLUMN "id" DROP DEFAULT;--> statement-breakpoint
ALTER TABLE "Notes" ALTER COLUMN "id" DROP DEFAULT;--> statement-breakpoint
ALTER TABLE "Notifications" ALTER COLUMN "id" DROP DEFAULT;--> statement-breakpoint
ALTER TABLE "OpenIdAccounts" ALTER COLUMN "id" DROP DEFAULT;--> statement-breakpoint
ALTER TABLE "OpenIdLoginFlows" ALTER COLUMN "id" DROP DEFAULT;--> statement-breakpoint
ALTER TABLE "PushSubscriptions" ALTER COLUMN "id" DROP DEFAULT;--> statement-breakpoint
ALTER TABLE "Reaction" ALTER COLUMN "id" DROP DEFAULT;--> statement-breakpoint
ALTER TABLE "Relationships" ALTER COLUMN "id" DROP DEFAULT;--> statement-breakpoint
ALTER TABLE "Roles" ALTER COLUMN "id" DROP DEFAULT;--> statement-breakpoint
ALTER TABLE "Tokens" ALTER COLUMN "id" DROP DEFAULT;--> statement-breakpoint
ALTER TABLE "Users" ALTER COLUMN "id" DROP DEFAULT;

File diff suppressed because it is too large Load diff

View file

@ -337,6 +337,13 @@
"when": 1743359397906, "when": 1743359397906,
"tag": "0047_black_vermin", "tag": "0047_black_vermin",
"breakpoints": true "breakpoints": true
},
{
"idx": 48,
"version": "7",
"when": 1743365238468,
"tag": "0048_chilly_vector",
"breakpoints": true
} }
] ]
} }

View file

@ -37,8 +37,7 @@ const updatedAt = () =>
const uri = () => text("uri").unique(); const uri = () => text("uri").unique();
// biome-ignore lint/nursery/useExplicitType: Type is too complex // biome-ignore lint/nursery/useExplicitType: Type is too complex
const id = () => const id = () => uuid("id").primaryKey().notNull();
uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull();
export const Challenges = pgTable("Challenges", { export const Challenges = pgTable("Challenges", {
id: id(), id: id(),

View file

@ -2,6 +2,7 @@ import { afterAll, describe, expect, test } from "bun:test";
import { randomString } from "@/math"; import { randomString } from "@/math";
import { RolePermission } from "@versia/client/schemas"; import { RolePermission } from "@versia/client/schemas";
import { Application } from "@versia/kit/db"; import { Application } from "@versia/kit/db";
import { randomUUIDv7 } from "bun";
import { SignJWT } from "jose"; import { SignJWT } from "jose";
import { config } from "~/config.ts"; import { config } from "~/config.ts";
import { fakeRequest, getTestUsers } from "~/tests/utils"; import { fakeRequest, getTestUsers } from "~/tests/utils";
@ -19,6 +20,7 @@ const privateKey = await crypto.subtle.importKey(
); );
const application = await Application.insert({ const application = await Application.insert({
id: randomUUIDv7(),
clientId: "test-client-id", clientId: "test-client-id",
redirectUri: "https://example.com/callback", redirectUri: "https://example.com/callback",
scopes: "openid profile email", scopes: "openid profile email",

View file

@ -2,6 +2,7 @@ import { auth, handleZodError, jsonOrForm } from "@/api";
import { randomString } from "@/math"; import { randomString } from "@/math";
import { RolePermission } from "@versia/client/schemas"; import { RolePermission } from "@versia/client/schemas";
import { Application, Token, User } from "@versia/kit/db"; import { Application, Token, User } from "@versia/kit/db";
import { randomUUIDv7 } from "bun";
import { describeRoute } from "hono-openapi"; import { describeRoute } from "hono-openapi";
import { validator } from "hono-openapi/zod"; import { validator } from "hono-openapi/zod";
import { type JWTPayload, SignJWT, jwtVerify } from "jose"; import { type JWTPayload, SignJWT, jwtVerify } from "jose";
@ -241,6 +242,7 @@ export default (plugin: PluginType): void =>
.sign(keys.private); .sign(keys.private);
await Token.insert({ await Token.insert({
id: randomUUIDv7(),
accessToken: randomString(64, "base64url"), accessToken: randomString(64, "base64url"),
code, code,
scope: scope ?? application.data.scopes, scope: scope ?? application.data.scopes,

View file

@ -1,8 +1,10 @@
import { afterAll, describe, expect, test } from "bun:test"; import { afterAll, describe, expect, test } from "bun:test";
import { Application } from "@versia/kit/db"; import { Application } from "@versia/kit/db";
import { randomUUIDv7 } from "bun";
import { fakeRequest } from "~/tests/utils"; import { fakeRequest } from "~/tests/utils";
const application = await Application.insert({ const application = await Application.insert({
id: randomUUIDv7(),
clientId: "test-client-id", clientId: "test-client-id",
redirectUri: "https://example.com/callback", redirectUri: "https://example.com/callback",
scopes: "openid profile email", scopes: "openid profile email",

View file

@ -5,6 +5,7 @@ import { RolePermission } from "@versia/client/schemas";
import { Media, Token, User, db } from "@versia/kit/db"; import { Media, Token, User, db } from "@versia/kit/db";
import { type SQL, and, eq, isNull } from "@versia/kit/drizzle"; import { type SQL, and, eq, isNull } from "@versia/kit/drizzle";
import { OpenIdAccounts, Users } from "@versia/kit/tables"; import { OpenIdAccounts, Users } from "@versia/kit/tables";
import { randomUUIDv7 } from "bun";
import { describeRoute } from "hono-openapi"; import { describeRoute } from "hono-openapi";
import { validator } from "hono-openapi/zod"; import { validator } from "hono-openapi/zod";
import { setCookie } from "hono/cookie"; import { setCookie } from "hono/cookie";
@ -175,6 +176,7 @@ export default (plugin: PluginType): void => {
// Link the account // Link the account
await db.insert(OpenIdAccounts).values({ await db.insert(OpenIdAccounts).values({
id: randomUUIDv7(),
serverId: sub, serverId: sub,
issuerId: issuer.id, issuerId: issuer.id,
userId: user_id, userId: user_id,
@ -242,6 +244,7 @@ export default (plugin: PluginType): void => {
// Link account // Link account
await db.insert(OpenIdAccounts).values({ await db.insert(OpenIdAccounts).values({
id: randomUUIDv7(),
serverId: sub, serverId: sub,
issuerId: issuer.id, issuerId: issuer.id,
userId: user.id, userId: user.id,
@ -294,6 +297,7 @@ export default (plugin: PluginType): void => {
const code = randomString(32, "hex"); const code = randomString(32, "hex");
await Token.insert({ await Token.insert({
id: randomUUIDv7(),
accessToken: randomString(64, "base64url"), accessToken: randomString(64, "base64url"),
code, code,
scope: flow.application.scopes, scope: flow.application.scopes,

View file

@ -1,17 +1,21 @@
import { afterAll, describe, expect, test } from "bun:test"; import { afterAll, describe, expect, test } from "bun:test";
import { Application, Token } from "@versia/kit/db"; import { Application, Token } from "@versia/kit/db";
import { randomUUIDv7 } from "bun";
import { fakeRequest, getTestUsers } from "~/tests/utils"; import { fakeRequest, getTestUsers } from "~/tests/utils";
const { deleteUsers, users } = await getTestUsers(1); const { deleteUsers, users } = await getTestUsers(1);
const application = await Application.insert({ const application = await Application.insert({
id: randomUUIDv7(),
clientId: "test-client-id", clientId: "test-client-id",
redirectUri: "https://example.com/callback", redirectUri: "https://example.com/callback",
scopes: "openid profile email", scopes: "openid profile email",
secret: "test-secret", secret: "test-secret",
name: "Test Application", name: "Test Application",
}); });
const token = await Token.insert({ const token = await Token.insert({
id: randomUUIDv7(),
code: "test-code", code: "test-code",
redirectUri: application.data.redirectUri, redirectUri: application.data.redirectUri,
clientId: application.data.clientId, clientId: application.data.clientId,

View file

@ -1,6 +1,7 @@
import { handleZodError } from "@/api.ts"; import { handleZodError } from "@/api.ts";
import { Application, db } from "@versia/kit/db"; import { Application, db } from "@versia/kit/db";
import { OpenIdLoginFlows } from "@versia/kit/tables"; import { OpenIdLoginFlows } from "@versia/kit/tables";
import { randomUUIDv7 } from "bun";
import { describeRoute } from "hono-openapi"; import { describeRoute } from "hono-openapi";
import { validator } from "hono-openapi/zod"; import { validator } from "hono-openapi/zod";
import { import {
@ -103,6 +104,7 @@ export default (plugin: PluginType): void => {
await db await db
.insert(OpenIdLoginFlows) .insert(OpenIdLoginFlows)
.values({ .values({
id: randomUUIDv7(),
codeVerifier, codeVerifier,
applicationId: application.id, applicationId: application.id,
issuerId, issuerId,

View file

@ -1,17 +1,21 @@
import { afterAll, describe, expect, test } from "bun:test"; import { afterAll, describe, expect, test } from "bun:test";
import { Application, Token } from "@versia/kit/db"; import { Application, Token } from "@versia/kit/db";
import { randomUUIDv7 } from "bun";
import { fakeRequest, getTestUsers } from "~/tests/utils"; import { fakeRequest, getTestUsers } from "~/tests/utils";
const { deleteUsers, users } = await getTestUsers(1); const { deleteUsers, users } = await getTestUsers(1);
const application = await Application.insert({ const application = await Application.insert({
id: randomUUIDv7(),
clientId: "test-client-id", clientId: "test-client-id",
redirectUri: "https://example.com/callback", redirectUri: "https://example.com/callback",
scopes: "openid profile email", scopes: "openid profile email",
secret: "test-secret", secret: "test-secret",
name: "Test Application", name: "Test Application",
}); });
const token = await Token.insert({ const token = await Token.insert({
id: randomUUIDv7(),
code: "test-code", code: "test-code",
redirectUri: application.data.redirectUri, redirectUri: application.data.redirectUri,
clientId: application.data.clientId, clientId: application.data.clientId,

View file

@ -2,6 +2,7 @@ import { auth, handleZodError } from "@/api";
import { RolePermission } from "@versia/client/schemas"; import { RolePermission } from "@versia/client/schemas";
import { Application, db } from "@versia/kit/db"; import { Application, db } from "@versia/kit/db";
import { OpenIdLoginFlows } from "@versia/kit/tables"; import { OpenIdLoginFlows } from "@versia/kit/tables";
import { randomUUIDv7 } from "bun";
import { describeRoute } from "hono-openapi"; import { describeRoute } from "hono-openapi";
import { resolver, validator } from "hono-openapi/zod"; import { resolver, validator } from "hono-openapi/zod";
import { import {
@ -117,6 +118,7 @@ export default (plugin: PluginType): void => {
); );
const application = await Application.insert({ const application = await Application.insert({
id: randomUUIDv7(),
clientId: clientId:
user.id + user.id +
Buffer.from( Buffer.from(
@ -133,6 +135,7 @@ export default (plugin: PluginType): void => {
await db await db
.insert(OpenIdLoginFlows) .insert(OpenIdLoginFlows)
.values({ .values({
id: randomUUIDv7(),
codeVerifier, codeVerifier,
issuerId, issuerId,
applicationId: application.id, applicationId: application.id,

View file

@ -5,7 +5,7 @@ import { Client as VersiaClient } from "@versia/client";
import { Note, Token, User, db } from "@versia/kit/db"; import { Note, Token, User, db } from "@versia/kit/db";
import { Notes, Users } from "@versia/kit/tables"; import { Notes, Users } from "@versia/kit/tables";
import { solveChallenge } from "altcha-lib"; import { solveChallenge } from "altcha-lib";
import { env } from "bun"; import { env, randomUUIDv7 } from "bun";
import { type InferSelectModel, asc, inArray, like } from "drizzle-orm"; import { type InferSelectModel, asc, inArray, like } from "drizzle-orm";
import { appFactory } from "~/app"; import { appFactory } from "~/app";
import { searchManager } from "~/classes/search/search-manager"; import { searchManager } from "~/classes/search/search-manager";
@ -46,6 +46,7 @@ export const generateClient = async (
> => { > => {
const token = user const token = user
? await Token.insert({ ? await Token.insert({
id: randomUUIDv7(),
accessToken: randomString(32, "hex"), accessToken: randomString(32, "hex"),
tokenType: "bearer", tokenType: "bearer",
userId: user.id, userId: user.id,
@ -118,6 +119,7 @@ export const getTestUsers = async (
const tokens = await Token.insertMany( const tokens = await Token.insertMany(
users.map((u) => ({ users.map((u) => ({
id: randomUUIDv7(),
accessToken: randomString(32, "hex"), accessToken: randomString(32, "hex"),
tokenType: "bearer", tokenType: "bearer",
userId: u.id, userId: u.id,
@ -155,6 +157,7 @@ export const getTestStatuses = async (
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
const newStatus = await Note.insert({ const newStatus = await Note.insert({
id: randomUUIDv7(),
content: `${i} ${randomString(32, "hex")}`, content: `${i} ${randomString(32, "hex")}`,
authorId: user.id, authorId: user.id,
sensitive: false, sensitive: false,