From bff1c5f734cd4674ac9cc4fc0f1f5efa3c0e1fa7 Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Wed, 12 Feb 2025 23:25:22 +0100 Subject: [PATCH] refactor(api): :label: Begin porting all code over to new schemas --- api/api/v1/accounts/:id/block.ts | 3 +- api/api/v1/accounts/:id/follow.ts | 3 +- api/api/v1/accounts/:id/mute.ts | 3 +- api/api/v1/accounts/:id/note.ts | 3 +- api/api/v1/accounts/:id/pin.ts | 3 +- .../v1/accounts/:id/remove_from_followers.ts | 3 +- api/api/v1/accounts/:id/unblock.ts | 3 +- api/api/v1/accounts/:id/unfollow.ts | 3 +- api/api/v1/accounts/:id/unmute.ts | 3 +- api/api/v1/accounts/:id/unpin.ts | 3 +- api/api/v1/accounts/relationships/index.ts | 3 +- .../v1/accounts/update_credentials/index.ts | 1 + .../follow_requests/:account_id/authorize.ts | 3 +- .../v1/follow_requests/:account_id/reject.ts | 3 +- api/api/v1/media/:id/index.ts | 5 +- api/api/v1/media/index.ts | 3 +- api/api/v1/notifications/:id/index.ts | 3 +- api/api/v1/notifications/index.ts | 68 +++------------ api/api/v1/push/subscription/index.get.ts | 3 +- api/api/v1/push/subscription/index.post.ts | 3 +- api/api/v1/push/subscription/index.put.ts | 7 +- api/api/v2/media/index.ts | 3 +- api/well-known/webfinger/index.ts | 5 +- classes/database/media.ts | 39 ++------- classes/database/note.ts | 17 ++-- classes/database/notification.ts | 40 +-------- classes/database/pushsubscription.ts | 80 ----------------- classes/database/reaction.ts | 15 ---- classes/database/relationship.ts | 5 +- classes/schemas/pushsubscription.ts | 85 ++++++++++++++++++- classes/schemas/status.ts | 4 +- drizzle/schema.ts | 10 ++- 32 files changed, 171 insertions(+), 264 deletions(-) diff --git a/api/api/v1/accounts/:id/block.ts b/api/api/v1/accounts/:id/block.ts index 58b7f8fb..b82ef2c6 100644 --- a/api/api/v1/accounts/:id/block.ts +++ b/api/api/v1/accounts/:id/block.ts @@ -2,6 +2,7 @@ import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; +import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; const route = createRoute({ method: "post", @@ -24,7 +25,7 @@ const route = createRoute({ description: "Updated relationship", content: { "application/json": { - schema: Relationship.schema, + schema: RelationshipSchema, }, }, }, diff --git a/api/api/v1/accounts/:id/follow.ts b/api/api/v1/accounts/:id/follow.ts index 3b3edc07..ed50a53c 100644 --- a/api/api/v1/accounts/:id/follow.ts +++ b/api/api/v1/accounts/:id/follow.ts @@ -3,6 +3,7 @@ import { createRoute, z } from "@hono/zod-openapi"; import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { iso631 } from "~/classes/schemas/common"; +import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; const schemas = { param: z.object({ @@ -39,7 +40,7 @@ const route = createRoute({ description: "Updated relationship", content: { "application/json": { - schema: Relationship.schema, + schema: RelationshipSchema, }, }, }, diff --git a/api/api/v1/accounts/:id/mute.ts b/api/api/v1/accounts/:id/mute.ts index 45b49541..c896987a 100644 --- a/api/api/v1/accounts/:id/mute.ts +++ b/api/api/v1/accounts/:id/mute.ts @@ -2,6 +2,7 @@ import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; +import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; const schemas = { param: z.object({ @@ -49,7 +50,7 @@ const route = createRoute({ description: "Updated relationship", content: { "application/json": { - schema: Relationship.schema, + schema: RelationshipSchema, }, }, }, diff --git a/api/api/v1/accounts/:id/note.ts b/api/api/v1/accounts/:id/note.ts index c0f04822..673d99f9 100644 --- a/api/api/v1/accounts/:id/note.ts +++ b/api/api/v1/accounts/:id/note.ts @@ -2,6 +2,7 @@ import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; +import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; const schemas = { param: z.object({ @@ -43,7 +44,7 @@ const route = createRoute({ description: "Updated relationship", content: { "application/json": { - schema: Relationship.schema, + schema: RelationshipSchema, }, }, }, diff --git a/api/api/v1/accounts/:id/pin.ts b/api/api/v1/accounts/:id/pin.ts index 72a13771..6eefbabb 100644 --- a/api/api/v1/accounts/:id/pin.ts +++ b/api/api/v1/accounts/:id/pin.ts @@ -2,6 +2,7 @@ import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; +import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; const route = createRoute({ method: "post", @@ -29,7 +30,7 @@ const route = createRoute({ description: "Updated relationship", content: { "application/json": { - schema: Relationship.schema, + schema: RelationshipSchema, }, }, }, diff --git a/api/api/v1/accounts/:id/remove_from_followers.ts b/api/api/v1/accounts/:id/remove_from_followers.ts index aee745c1..3c3f6bef 100644 --- a/api/api/v1/accounts/:id/remove_from_followers.ts +++ b/api/api/v1/accounts/:id/remove_from_followers.ts @@ -2,6 +2,7 @@ import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; +import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; const route = createRoute({ method: "post", @@ -29,7 +30,7 @@ const route = createRoute({ description: "Updated relationship", content: { "application/json": { - schema: Relationship.schema, + schema: RelationshipSchema, }, }, }, diff --git a/api/api/v1/accounts/:id/unblock.ts b/api/api/v1/accounts/:id/unblock.ts index f480b575..b1c1e56a 100644 --- a/api/api/v1/accounts/:id/unblock.ts +++ b/api/api/v1/accounts/:id/unblock.ts @@ -2,6 +2,7 @@ import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; +import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; const route = createRoute({ method: "post", @@ -29,7 +30,7 @@ const route = createRoute({ description: "Updated relationship", content: { "application/json": { - schema: Relationship.schema, + schema: RelationshipSchema, }, }, }, diff --git a/api/api/v1/accounts/:id/unfollow.ts b/api/api/v1/accounts/:id/unfollow.ts index d38ecef8..57eb749e 100644 --- a/api/api/v1/accounts/:id/unfollow.ts +++ b/api/api/v1/accounts/:id/unfollow.ts @@ -2,6 +2,7 @@ import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; +import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; import { ErrorSchema } from "~/types/api"; const route = createRoute({ @@ -30,7 +31,7 @@ const route = createRoute({ description: "Updated relationship", content: { "application/json": { - schema: Relationship.schema, + schema: RelationshipSchema, }, }, }, diff --git a/api/api/v1/accounts/:id/unmute.ts b/api/api/v1/accounts/:id/unmute.ts index 4bbd9cec..0b2e0f46 100644 --- a/api/api/v1/accounts/:id/unmute.ts +++ b/api/api/v1/accounts/:id/unmute.ts @@ -2,6 +2,7 @@ import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; +import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; const route = createRoute({ method: "post", @@ -29,7 +30,7 @@ const route = createRoute({ description: "Updated relationship", content: { "application/json": { - schema: Relationship.schema, + schema: RelationshipSchema, }, }, }, diff --git a/api/api/v1/accounts/:id/unpin.ts b/api/api/v1/accounts/:id/unpin.ts index 59aa469b..57925ceb 100644 --- a/api/api/v1/accounts/:id/unpin.ts +++ b/api/api/v1/accounts/:id/unpin.ts @@ -2,6 +2,7 @@ import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; +import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; const route = createRoute({ method: "post", @@ -29,7 +30,7 @@ const route = createRoute({ description: "Updated relationship", content: { "application/json": { - schema: Relationship.schema, + schema: RelationshipSchema, }, }, }, diff --git a/api/api/v1/accounts/relationships/index.ts b/api/api/v1/accounts/relationships/index.ts index f3a81c56..f76f3d99 100644 --- a/api/api/v1/accounts/relationships/index.ts +++ b/api/api/v1/accounts/relationships/index.ts @@ -2,6 +2,7 @@ import { apiRoute, auth, qsQuery } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; +import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; const schemas = { query: z.object({ @@ -30,7 +31,7 @@ const route = createRoute({ description: "Relationships", content: { "application/json": { - schema: z.array(Relationship.schema), + schema: z.array(RelationshipSchema), }, }, }, diff --git a/api/api/v1/accounts/update_credentials/index.ts b/api/api/v1/accounts/update_credentials/index.ts index 868886dc..fca71f64 100644 --- a/api/api/v1/accounts/update_credentials/index.ts +++ b/api/api/v1/accounts/update_credentials/index.ts @@ -263,6 +263,7 @@ export default apiRoute((app) => self.source.fields.push({ name: field.name, value: field.value, + verified_at: null, }); } } diff --git a/api/api/v1/follow_requests/:account_id/authorize.ts b/api/api/v1/follow_requests/:account_id/authorize.ts index 2cb28c6d..1e253b78 100644 --- a/api/api/v1/follow_requests/:account_id/authorize.ts +++ b/api/api/v1/follow_requests/:account_id/authorize.ts @@ -3,6 +3,7 @@ import { createRoute, z } from "@hono/zod-openapi"; import { Relationship, User } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { ApiError } from "~/classes/errors/api-error"; +import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; import { ErrorSchema } from "~/types/api"; const schemas = { @@ -29,7 +30,7 @@ const route = createRoute({ description: "Relationship", content: { "application/json": { - schema: Relationship.schema, + schema: RelationshipSchema, }, }, }, diff --git a/api/api/v1/follow_requests/:account_id/reject.ts b/api/api/v1/follow_requests/:account_id/reject.ts index e487cc88..00d5fc02 100644 --- a/api/api/v1/follow_requests/:account_id/reject.ts +++ b/api/api/v1/follow_requests/:account_id/reject.ts @@ -3,6 +3,7 @@ import { createRoute, z } from "@hono/zod-openapi"; import { Relationship, User } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { ApiError } from "~/classes/errors/api-error"; +import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; import { ErrorSchema } from "~/types/api"; const schemas = { @@ -29,7 +30,7 @@ const route = createRoute({ description: "Relationship", content: { "application/json": { - schema: Relationship.schema, + schema: RelationshipSchema, }, }, }, diff --git a/api/api/v1/media/:id/index.ts b/api/api/v1/media/:id/index.ts index abf63bda..713cc971 100644 --- a/api/api/v1/media/:id/index.ts +++ b/api/api/v1/media/:id/index.ts @@ -3,6 +3,7 @@ import { createRoute, z } from "@hono/zod-openapi"; import { Media } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { ApiError } from "~/classes/errors/api-error"; +import { Attachment as AttachmentSchema } from "~/classes/schemas/attachment"; import { config } from "~/packages/config-manager/index.ts"; import { ErrorSchema } from "~/types/api"; @@ -46,7 +47,7 @@ const routePut = createRoute({ description: "Media updated", content: { "application/json": { - schema: Media.schema, + schema: AttachmentSchema, }, }, }, @@ -80,7 +81,7 @@ const routeGet = createRoute({ description: "Media", content: { "application/json": { - schema: Media.schema, + schema: AttachmentSchema, }, }, }, diff --git a/api/api/v1/media/index.ts b/api/api/v1/media/index.ts index 25b8b349..98485aca 100644 --- a/api/api/v1/media/index.ts +++ b/api/api/v1/media/index.ts @@ -2,6 +2,7 @@ import { apiRoute, auth } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; import { Media } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; +import { Attachment as AttachmentSchema } from "~/classes/schemas/attachment"; import { config } from "~/packages/config-manager/index.ts"; import { ErrorSchema } from "~/types/api"; @@ -42,7 +43,7 @@ const route = createRoute({ description: "Attachment", content: { "application/json": { - schema: Media.schema, + schema: AttachmentSchema, }, }, }, diff --git a/api/api/v1/notifications/:id/index.ts b/api/api/v1/notifications/:id/index.ts index 5a1166ba..7ae0a4e6 100644 --- a/api/api/v1/notifications/:id/index.ts +++ b/api/api/v1/notifications/:id/index.ts @@ -3,6 +3,7 @@ import { createRoute, z } from "@hono/zod-openapi"; import { Notification } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { ApiError } from "~/classes/errors/api-error"; +import { Notification as NotificationSchema } from "~/classes/schemas/notification.ts"; import { ErrorSchema } from "~/types/api"; const route = createRoute({ @@ -26,7 +27,7 @@ const route = createRoute({ description: "Notification", content: { "application/json": { - schema: Notification.schema, + schema: NotificationSchema, }, }, }, diff --git a/api/api/v1/notifications/index.ts b/api/api/v1/notifications/index.ts index b2bfd987..88d766f4 100644 --- a/api/api/v1/notifications/index.ts +++ b/api/api/v1/notifications/index.ts @@ -1,72 +1,26 @@ import { apiRoute, auth } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; -import { Notification, Timeline } from "@versia/kit/db"; +import { Timeline } from "@versia/kit/db"; import { Notifications, RolePermissions } from "@versia/kit/tables"; import { and, eq, gt, gte, inArray, lt, not, sql } from "drizzle-orm"; +import { Account as AccountSchema } from "~/classes/schemas/account"; +import { Notification as NotificationSchema } from "~/classes/schemas/notification.ts"; const schemas = { query: z .object({ - max_id: z.string().uuid().optional(), - since_id: z.string().uuid().optional(), - min_id: z.string().uuid().optional(), + max_id: NotificationSchema.shape.id.optional(), + since_id: NotificationSchema.shape.id.optional(), + min_id: NotificationSchema.shape.id.optional(), limit: z.coerce.number().int().min(1).max(80).default(15), - exclude_types: z - .enum([ - "mention", - "status", - "follow", - "follow_request", - "reblog", - "poll", - "favourite", - "update", - "admin.sign_up", - "admin.report", - "chat", - "pleroma:chat_mention", - "pleroma:emoji_reaction", - "pleroma:event_reminder", - "pleroma:participation_request", - "pleroma:participation_accepted", - "move", - "group_reblog", - "group_favourite", - "user_approved", - ]) - .array() - .optional(), - types: z - .enum([ - "mention", - "status", - "follow", - "follow_request", - "reblog", - "poll", - "favourite", - "update", - "admin.sign_up", - "admin.report", - "chat", - "pleroma:chat_mention", - "pleroma:emoji_reaction", - "pleroma:event_reminder", - "pleroma:participation_request", - "pleroma:participation_accepted", - "move", - "group_reblog", - "group_favourite", - "user_approved", - ]) - .array() - .optional(), - account_id: z.string().uuid().optional(), + exclude_types: z.array(NotificationSchema.shape.type).optional(), + types: z.array(NotificationSchema.shape.type).optional(), + account_id: AccountSchema.shape.id.optional(), }) .refine((val) => { // Can't use both exclude_types and types return !(val.exclude_types && val.types); - }), + }, "Can't use both exclude_types and types"), }; const route = createRoute({ @@ -90,7 +44,7 @@ const route = createRoute({ description: "Notifications", content: { "application/json": { - schema: z.array(Notification.schema), + schema: z.array(NotificationSchema), }, }, }, diff --git a/api/api/v1/push/subscription/index.get.ts b/api/api/v1/push/subscription/index.get.ts index 33c8bd2b..157e36b5 100644 --- a/api/api/v1/push/subscription/index.get.ts +++ b/api/api/v1/push/subscription/index.get.ts @@ -2,6 +2,7 @@ import { apiRoute, auth } from "@/api"; import { createRoute } from "@hono/zod-openapi"; import { PushSubscription } from "@versia/kit/db"; import { ApiError } from "~/classes/errors/api-error"; +import { WebPushSubscription as WebPushSubscriptionSchema } from "~/classes/schemas/pushsubscription"; import { RolePermissions } from "~/drizzle/schema"; export default apiRoute((app) => @@ -27,7 +28,7 @@ export default apiRoute((app) => description: "WebPushSubscription", content: { "application/json": { - schema: PushSubscription.schema, + schema: WebPushSubscriptionSchema, }, }, }, diff --git a/api/api/v1/push/subscription/index.post.ts b/api/api/v1/push/subscription/index.post.ts index 926aae32..2f76301f 100644 --- a/api/api/v1/push/subscription/index.post.ts +++ b/api/api/v1/push/subscription/index.post.ts @@ -3,6 +3,7 @@ import { auth, jsonOrForm } from "@/api"; import { createRoute } from "@hono/zod-openapi"; import { PushSubscription } from "@versia/kit/db"; import { WebPushSubscriptionInput } from "~/classes/schemas/pushsubscription"; +import { WebPushSubscription as WebPushSubscriptionSchema } from "~/classes/schemas/pushsubscription"; import { RolePermissions } from "~/drizzle/schema"; export default apiRoute((app) => @@ -39,7 +40,7 @@ export default apiRoute((app) => "A new PushSubscription has been generated, which will send the requested alerts to your endpoint.", content: { "application/json": { - schema: PushSubscription.schema, + schema: WebPushSubscriptionSchema, }, }, }, diff --git a/api/api/v1/push/subscription/index.put.ts b/api/api/v1/push/subscription/index.put.ts index c9736c95..3cb454e4 100644 --- a/api/api/v1/push/subscription/index.put.ts +++ b/api/api/v1/push/subscription/index.put.ts @@ -2,7 +2,10 @@ import { apiRoute, auth, jsonOrForm } from "@/api"; import { createRoute } from "@hono/zod-openapi"; import { PushSubscription } from "@versia/kit/db"; import { ApiError } from "~/classes/errors/api-error"; -import { WebPushSubscriptionInput } from "~/classes/schemas/pushsubscription"; +import { + WebPushSubscriptionInput, + WebPushSubscription as WebPushSubscriptionSchema, +} from "~/classes/schemas/pushsubscription"; import { RolePermissions } from "~/drizzle/schema"; export default apiRoute((app) => @@ -41,7 +44,7 @@ export default apiRoute((app) => description: "The WebPushSubscription has been updated.", content: { "application/json": { - schema: PushSubscription.schema, + schema: WebPushSubscriptionSchema, }, }, }, diff --git a/api/api/v2/media/index.ts b/api/api/v2/media/index.ts index 48b0f838..44f06e05 100644 --- a/api/api/v2/media/index.ts +++ b/api/api/v2/media/index.ts @@ -2,6 +2,7 @@ import { apiRoute, auth } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; import { Media } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; +import { Attachment as AttachmentSchema } from "~/classes/schemas/attachment"; import { config } from "~/packages/config-manager/index.ts"; import { ErrorSchema } from "~/types/api"; @@ -42,7 +43,7 @@ const route = createRoute({ description: "Uploaded media", content: { "application/json": { - schema: Media.schema, + schema: AttachmentSchema, }, }, }, diff --git a/api/well-known/webfinger/index.ts b/api/well-known/webfinger/index.ts index 8cab7a37..c749c032 100644 --- a/api/well-known/webfinger/index.ts +++ b/api/well-known/webfinger/index.ts @@ -132,9 +132,10 @@ export default apiRoute((app) => }, { rel: "avatar", + // Default avatars are SVGs type: - user.data.source.avatar?.content_type || - "application/octet-stream", + user.avatar?.getPreferredMimeType() ?? + "image/svg+xml", href: user.getAvatarUrl(config), }, ].filter(Boolean) as { diff --git a/classes/database/media.ts b/classes/database/media.ts index 06a85903..715e51c8 100644 --- a/classes/database/media.ts +++ b/classes/database/media.ts @@ -1,8 +1,7 @@ import { join } from "node:path"; import { mimeLookup } from "@/content_types.ts"; import { proxyUrl } from "@/response"; -import { z } from "@hono/zod-openapi"; -import type { Attachment as ApiAttachment } from "@versia/client/types"; +import type { z } from "@hono/zod-openapi"; import type { ContentFormat } from "@versia/federation/types"; import { db } from "@versia/kit/db"; import { Medias } from "@versia/kit/tables"; @@ -16,6 +15,7 @@ import { inArray, } from "drizzle-orm"; import sharp from "sharp"; +import type { Attachment as AttachmentSchema } from "~/classes/schemas/attachment.ts"; import { MediaBackendType } from "~/packages/config-manager/config.type"; import { config } from "~/packages/config-manager/index.ts"; import { ApiError } from "../errors/api-error.ts"; @@ -26,34 +26,6 @@ import { BaseInterface } from "./base.ts"; type MediaType = InferSelectModel; export class Media extends BaseInterface { - public static schema: z.ZodType = z.object({ - id: z.string().uuid(), - type: z.enum(["unknown", "image", "gifv", "video", "audio"]), - url: z.string().url(), - remote_url: z.string().url().nullable(), - preview_url: z.string().url().nullable(), - text_url: z.string().url().nullable(), - meta: z - .object({ - width: z.number().optional(), - height: z.number().optional(), - fps: z.number().optional(), - size: z.string().optional(), - duration: z.number().optional(), - length: z.string().optional(), - aspect: z.number().optional(), - original: z.object({ - width: z.number().optional(), - height: z.number().optional(), - size: z.string().optional(), - aspect: z.number().optional(), - }), - }) - .nullable(), - description: z.string().nullable(), - blurhash: z.string().nullable(), - }); - public static $type: MediaType; public async reload(): Promise { @@ -446,7 +418,7 @@ export class Media extends BaseInterface { * * @returns */ - public getMastodonType(): ApiAttachment["type"] { + public getMastodonType(): z.infer { const type = this.getPreferredMimeType(); if (type.startsWith("image/")) { @@ -500,7 +472,7 @@ export class Media extends BaseInterface { }; } - public toApiMeta(): ApiAttachment["meta"] { + public toApiMeta(): z.infer { const type = this.getPreferredMimeType(); const data = this.data.content[type]; const size = @@ -529,7 +501,7 @@ export class Media extends BaseInterface { }; } - public toApi(): ApiAttachment { + public toApi(): z.infer { const type = this.getPreferredMimeType(); const data = this.data.content[type]; @@ -545,7 +517,6 @@ export class Media extends BaseInterface { preview_url: thumbnailData?.content ? proxyUrl(new URL(thumbnailData.content)).toString() : null, - text_url: null, meta: this.toApiMeta(), description: data.description || null, blurhash: this.data.blurhash, diff --git a/classes/database/note.ts b/classes/database/note.ts index ac9710ed..1edabc7d 100644 --- a/classes/database/note.ts +++ b/classes/database/note.ts @@ -5,10 +5,6 @@ import { sanitizedHtmlStrip } from "@/sanitization"; import { sentry } from "@/sentry"; import type { z } from "@hono/zod-openapi"; import { getLogger } from "@logtape/logtape"; -import type { - Attachment as ApiAttachment, - Status as ApiStatus, -} from "@versia/client/types"; import { EntityValidator } from "@versia/federation"; import type { ContentFormat, @@ -41,6 +37,7 @@ import { findManyNotes, parseTextMentions, } from "~/classes/functions/status"; +import type { Status as StatusSchema } from "~/classes/schemas/status.ts"; import { config } from "~/packages/config-manager"; import { DeliveryJobType, deliveryQueue } from "../queues/delivery.ts"; import type { Status } from "../schemas/status.ts"; @@ -346,7 +343,7 @@ export class Note extends BaseInterface { public static async fromData(data: { author: User; content: ContentFormat; - visibility: ApiStatus["visibility"]; + visibility: z.infer; isSensitive: boolean; spoilerText: string; emojis?: Emoji[]; @@ -420,7 +417,7 @@ export class Note extends BaseInterface { public async updateFromData(data: { author: User; content?: ContentFormat; - visibility?: ApiStatus["visibility"]; + visibility?: z.infer; isSensitive?: boolean; spoilerText?: string; emojis?: Emoji[]; @@ -677,7 +674,7 @@ export class Note extends BaseInterface { remote: false, }, }, - visibility: visibility as ApiStatus["visibility"], + visibility, isSensitive: note.is_sensitive ?? false, spoilerText: note.subject ?? "", emojis, @@ -811,8 +808,8 @@ export class Note extends BaseInterface { emojis: data.emojis.map((emoji) => new Emoji(emoji).toApi()), favourited: data.liked, favourites_count: data.likeCount, - media_attachments: (data.attachments ?? []).map( - (a) => new Media(a).toApi() as ApiAttachment, + media_attachments: (data.attachments ?? []).map((a) => + new Media(a).toApi(), ), mentions: data.mentions.map((mention) => ({ id: mention.id, @@ -844,7 +841,7 @@ export class Note extends BaseInterface { spoiler_text: data.spoilerText, tags: [], uri: data.uri || this.getUri().toString(), - visibility: data.visibility as ApiStatus["visibility"], + visibility: data.visibility, url: data.uri || this.getMastoUri().toString(), bookmarked: false, quote: data.quotingId diff --git a/classes/database/notification.ts b/classes/database/notification.ts index 8e9b8c21..8a706ef3 100644 --- a/classes/database/notification.ts +++ b/classes/database/notification.ts @@ -1,5 +1,4 @@ -import { z } from "@hono/zod-openapi"; -import type { Notification as APINotification } from "@versia/client/types"; +import type { z } from "@hono/zod-openapi"; import { Note, User, db } from "@versia/kit/db"; import { Notifications } from "@versia/kit/tables"; import { @@ -10,13 +9,12 @@ import { eq, inArray, } from "drizzle-orm"; +import type { Notification as NotificationSchema } from "~/classes/schemas/notification.ts"; import { transformOutputToUserWithRelations, userExtrasTemplate, userRelations, } from "../functions/user.ts"; -import { Account } from "../schemas/account.ts"; -import { Status } from "../schemas/status.ts"; import { BaseInterface } from "./base.ts"; export type NotificationType = InferSelectModel & { @@ -28,37 +26,6 @@ export class Notification extends BaseInterface< typeof Notifications, NotificationType > { - public static schema: z.ZodType = z.object({ - account: Account.nullable(), - created_at: z.string(), - id: z.string().uuid(), - status: Status.optional(), - // TODO: Add reactions - type: z.enum([ - "mention", - "status", - "follow", - "follow_request", - "reblog", - "poll", - "favourite", - "update", - "admin.sign_up", - "admin.report", - "chat", - "pleroma:chat_mention", - "pleroma:emoji_reaction", - "pleroma:event_reminder", - "pleroma:participation_request", - "pleroma:participation_accepted", - "move", - "group_reblog", - "group_favourite", - "user_approved", - ]), - target: Account.optional(), - }); - public async reload(): Promise { const reloaded = await Notification.fromId(this.data.id); @@ -215,7 +182,7 @@ export class Notification extends BaseInterface< return this.data.id; } - public async toApi(): Promise { + public async toApi(): Promise> { const account = new User(this.data.account); return { @@ -226,6 +193,7 @@ export class Notification extends BaseInterface< status: this.data.status ? await new Note(this.data.status).toApi(account) : undefined, + group_key: `ungrouped-${this.data.id}`, }; } } diff --git a/classes/database/pushsubscription.ts b/classes/database/pushsubscription.ts index 336b39f0..b8cf4833 100644 --- a/classes/database/pushsubscription.ts +++ b/classes/database/pushsubscription.ts @@ -1,4 +1,3 @@ -import { z } from "@hono/zod-openapi"; import type { Alerts, PushSubscription as ApiPushSubscription, @@ -21,85 +20,6 @@ export class PushSubscription extends BaseInterface< typeof PushSubscriptions, PushSubscriptionType > { - public static schema = z.object({ - id: z.string().uuid().openapi({ - example: "24eb1891-accc-43b4-b213-478e37d525b4", - description: "The ID of the Web Push subscription in the database.", - }), - endpoint: z.string().url().openapi({ - example: "https://yourdomain.example/listener", - description: "Where push alerts will be sent to.", - }), - alerts: z - .object({ - mention: z.boolean().optional().openapi({ - example: true, - description: "Receive mention notifications?", - }), - favourite: z.boolean().optional().openapi({ - example: true, - description: "Receive favourite notifications?", - }), - reblog: z.boolean().optional().openapi({ - example: true, - description: "Receive reblog notifications?", - }), - follow: z.boolean().optional().openapi({ - example: true, - description: "Receive follow notifications?", - }), - poll: z.boolean().optional().openapi({ - example: false, - description: "Receive poll notifications?", - }), - follow_request: z.boolean().optional().openapi({ - example: false, - description: "Receive follow request notifications?", - }), - status: z.boolean().optional().openapi({ - example: false, - description: - "Receive new subscribed account notifications?", - }), - update: z.boolean().optional().openapi({ - example: false, - description: "Receive status edited notifications?", - }), - "admin.sign_up": z.boolean().optional().openapi({ - example: false, - description: - "Receive new user signup notifications? Must have a role with the appropriate permissions.", - }), - "admin.report": z.boolean().optional().openapi({ - example: false, - description: - "Receive new report notifications? Must have a role with the appropriate permissions.", - }), - }) - .default({}) - .openapi({ - example: { - mention: true, - favourite: true, - reblog: true, - follow: true, - poll: false, - follow_request: false, - status: false, - update: false, - "admin.sign_up": false, - "admin.report": false, - }, - description: - "Which alerts should be delivered to the endpoint.", - }), - server_key: z.string().openapi({ - example: - "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=", - description: "The streaming server’s VAPID key.", - }), - }); - public static $type: PushSubscriptionType; public async reload(): Promise { diff --git a/classes/database/reaction.ts b/classes/database/reaction.ts index 66226651..41e07acf 100644 --- a/classes/database/reaction.ts +++ b/classes/database/reaction.ts @@ -1,5 +1,3 @@ -import { z } from "@hono/zod-openapi"; -import type { Emoji as APIEmoji } from "@versia/client/types"; import type { ReactionExtension } from "@versia/federation/types"; import { Emoji, Instance, Note, User, db } from "@versia/kit/db"; import { type Notes, Reactions, type Users } from "@versia/kit/tables"; @@ -12,7 +10,6 @@ import { inArray, } from "drizzle-orm"; import { config } from "~/packages/config-manager/index.ts"; -import { CustomEmoji } from "../schemas/emoji.ts"; import { BaseInterface } from "./base.ts"; type ReactionType = InferSelectModel & { @@ -21,19 +18,7 @@ type ReactionType = InferSelectModel & { note: InferSelectModel; }; -export interface APIReaction { - id: string; - author_id: string; - emoji: APIEmoji | string; -} - export class Reaction extends BaseInterface { - public static schema: z.ZodType = z.object({ - id: z.string().uuid(), - author_id: z.string().uuid(), - emoji: CustomEmoji, - }); - public static $type: ReactionType; public async reload(): Promise { diff --git a/classes/database/relationship.ts b/classes/database/relationship.ts index 1a431346..48827df1 100644 --- a/classes/database/relationship.ts +++ b/classes/database/relationship.ts @@ -1,5 +1,4 @@ import { z } from "@hono/zod-openapi"; -import type { Relationship as APIRelationship } from "@versia/client/types"; import { db } from "@versia/kit/db"; import { Relationships } from "@versia/kit/tables"; import { @@ -11,6 +10,7 @@ import { eq, inArray, } from "drizzle-orm"; +import type { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; import { BaseInterface } from "./base.ts"; import type { User } from "./user.ts"; @@ -292,7 +292,7 @@ export class Relationship extends BaseInterface< return this.data.id; } - public toApi(): APIRelationship { + public toApi(): z.infer { return { id: this.data.subjectId, blocked_by: this.data.blockedBy, @@ -308,6 +308,7 @@ export class Relationship extends BaseInterface< requested_by: this.data.requestedBy, requested: this.data.requested, showing_reblogs: this.data.showingReblogs, + languages: this.data.languages ?? [], }; } } diff --git a/classes/schemas/pushsubscription.ts b/classes/schemas/pushsubscription.ts index 0d8b565c..0e58643d 100644 --- a/classes/schemas/pushsubscription.ts +++ b/classes/schemas/pushsubscription.ts @@ -1,5 +1,86 @@ import { z } from "@hono/zod-openapi"; -import { PushSubscription } from "@versia/kit/db"; +import { Id } from "./common.ts"; + +export const WebPushSubscription = z + .object({ + id: Id.openapi({ + example: "24eb1891-accc-43b4-b213-478e37d525b4", + description: "The ID of the Web Push subscription in the database.", + }), + endpoint: z.string().url().openapi({ + example: "https://yourdomain.example/listener", + description: "Where push alerts will be sent to.", + }), + alerts: z + .object({ + mention: z.boolean().optional().openapi({ + example: true, + description: "Receive mention notifications?", + }), + favourite: z.boolean().optional().openapi({ + example: true, + description: "Receive favourite notifications?", + }), + reblog: z.boolean().optional().openapi({ + example: true, + description: "Receive reblog notifications?", + }), + follow: z.boolean().optional().openapi({ + example: true, + description: "Receive follow notifications?", + }), + poll: z.boolean().optional().openapi({ + example: false, + description: "Receive poll notifications?", + }), + follow_request: z.boolean().optional().openapi({ + example: false, + description: "Receive follow request notifications?", + }), + status: z.boolean().optional().openapi({ + example: false, + description: + "Receive new subscribed account notifications?", + }), + update: z.boolean().optional().openapi({ + example: false, + description: "Receive status edited notifications?", + }), + "admin.sign_up": z.boolean().optional().openapi({ + example: false, + description: + "Receive new user signup notifications? Must have a role with the appropriate permissions.", + }), + "admin.report": z.boolean().optional().openapi({ + example: false, + description: + "Receive new report notifications? Must have a role with the appropriate permissions.", + }), + }) + .default({}) + .openapi({ + example: { + mention: true, + favourite: true, + reblog: true, + follow: true, + poll: false, + follow_request: false, + status: false, + update: false, + "admin.sign_up": false, + "admin.report": false, + }, + description: + "Which alerts should be delivered to the endpoint.", + }), + server_key: z.string().openapi({ + example: + "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=", + description: "The streaming server’s VAPID key.", + }), + }) + .openapi({}); export const WebPushSubscriptionInput = z .object({ @@ -33,7 +114,7 @@ export const WebPushSubscriptionInput = z }), data: z .object({ - alerts: PushSubscription.schema.shape.alerts, + alerts: WebPushSubscription.shape.alerts, }) .strict() .default({ diff --git a/classes/schemas/status.ts b/classes/schemas/status.ts index 88ff73cd..9b3275ed 100644 --- a/classes/schemas/status.ts +++ b/classes/schemas/status.ts @@ -1,8 +1,8 @@ import { z } from "@hono/zod-openapi"; import type { Status as ApiNote } from "@versia/client/types"; -import { Media } from "@versia/kit/db"; import { zBoolean } from "~/packages/config-manager/config.type.ts"; import { Account } from "./account.ts"; +import { Attachment } from "./attachment.ts"; import { PreviewCard } from "./card.ts"; import { Id, iso631 } from "./common.ts"; import { CustomEmoji } from "./emoji.ts"; @@ -223,7 +223,7 @@ export const Status = z.object({ url: "https://docs.joinmastodon.org/entities/Status/#visibility", }, }), - media_attachments: z.array(Media.schema).openapi({ + media_attachments: z.array(Attachment).openapi({ description: "Media that is attached to this status.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Status/#media_attachments", diff --git a/drizzle/schema.ts b/drizzle/schema.ts index 8847905d..14e548cf 100644 --- a/drizzle/schema.ts +++ b/drizzle/schema.ts @@ -15,6 +15,8 @@ import { uuid, } from "drizzle-orm/pg-core"; import type { Source } from "~/classes/schemas/account"; +import type { Notification as NotificationSchema } from "~/classes/schemas/notification.ts"; +import type { Status as StatusSchema } from "~/classes/schemas/status.ts"; // biome-ignore lint/nursery/useExplicitType: Type is too complex const createdAt = () => @@ -376,7 +378,9 @@ export const MediasRelations = relations(Medias, ({ many }) => ({ export const Notifications = pgTable("Notifications", { id: id(), - type: text("type").notNull(), + type: text("type") + .$type>() + .notNull(), createdAt: createdAt(), notifiedId: uuid("notifiedId") .notNull() @@ -431,7 +435,9 @@ export const Notes = pgTable("Notes", { }), content: text("content").default("").notNull(), contentType: text("content_type").default("text/plain").notNull(), - visibility: text("visibility").notNull(), + visibility: text("visibility") + .$type>() + .notNull(), replyId: uuid("replyId").references((): AnyPgColumn => Notes.id, { onDelete: "cascade", onUpdate: "cascade",