refactor(api): 🏷️ Begin porting all code over to new schemas

This commit is contained in:
Jesse Wierzbinski 2025-02-12 23:25:22 +01:00
parent fda1167234
commit bff1c5f734
No known key found for this signature in database
32 changed files with 171 additions and 264 deletions

View file

@ -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,
},
},
},

View file

@ -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,
},
},
},

View file

@ -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,
},
},
},

View file

@ -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,
},
},
},

View file

@ -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,
},
},
},

View file

@ -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,
},
},
},

View file

@ -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,
},
},
},

View file

@ -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,
},
},
},

View file

@ -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,
},
},
},

View file

@ -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,
},
},
},

View file

@ -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),
},
},
},

View file

@ -263,6 +263,7 @@ export default apiRoute((app) =>
self.source.fields.push({
name: field.name,
value: field.value,
verified_at: null,
});
}
}

View file

@ -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,
},
},
},

View file

@ -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,
},
},
},

View file

@ -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,
},
},
},

View file

@ -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,
},
},
},

View file

@ -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,
},
},
},

View file

@ -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),
},
},
},

View file

@ -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,
},
},
},

View file

@ -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,
},
},
},

View file

@ -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,
},
},
},

View file

@ -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,
},
},
},

View file

@ -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 {

View file

@ -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<typeof Medias>;
export class Media extends BaseInterface<typeof Medias> {
public static schema: z.ZodType<ApiAttachment> = 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<void> {
@ -446,7 +418,7 @@ export class Media extends BaseInterface<typeof Medias> {
*
* @returns
*/
public getMastodonType(): ApiAttachment["type"] {
public getMastodonType(): z.infer<typeof AttachmentSchema.shape.type> {
const type = this.getPreferredMimeType();
if (type.startsWith("image/")) {
@ -500,7 +472,7 @@ export class Media extends BaseInterface<typeof Medias> {
};
}
public toApiMeta(): ApiAttachment["meta"] {
public toApiMeta(): z.infer<typeof AttachmentSchema.shape.meta> {
const type = this.getPreferredMimeType();
const data = this.data.content[type];
const size =
@ -529,7 +501,7 @@ export class Media extends BaseInterface<typeof Medias> {
};
}
public toApi(): ApiAttachment {
public toApi(): z.infer<typeof AttachmentSchema> {
const type = this.getPreferredMimeType();
const data = this.data.content[type];
@ -545,7 +517,6 @@ export class Media extends BaseInterface<typeof Medias> {
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,

View file

@ -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<typeof Notes, NoteTypeWithRelations> {
public static async fromData(data: {
author: User;
content: ContentFormat;
visibility: ApiStatus["visibility"];
visibility: z.infer<typeof StatusSchema.shape.visibility>;
isSensitive: boolean;
spoilerText: string;
emojis?: Emoji[];
@ -420,7 +417,7 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
public async updateFromData(data: {
author: User;
content?: ContentFormat;
visibility?: ApiStatus["visibility"];
visibility?: z.infer<typeof StatusSchema.shape.visibility>;
isSensitive?: boolean;
spoilerText?: string;
emojis?: Emoji[];
@ -677,7 +674,7 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
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<typeof Notes, NoteTypeWithRelations> {
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<typeof Notes, NoteTypeWithRelations> {
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

View file

@ -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<typeof Notifications> & {
@ -28,37 +26,6 @@ export class Notification extends BaseInterface<
typeof Notifications,
NotificationType
> {
public static schema: z.ZodType<APINotification> = 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<void> {
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<APINotification> {
public async toApi(): Promise<z.infer<typeof NotificationSchema>> {
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}`,
};
}
}

View file

@ -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 servers VAPID key.",
}),
});
public static $type: PushSubscriptionType;
public async reload(): Promise<void> {

View file

@ -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<typeof Reactions> & {
@ -21,19 +18,7 @@ type ReactionType = InferSelectModel<typeof Reactions> & {
note: InferSelectModel<typeof Notes>;
};
export interface APIReaction {
id: string;
author_id: string;
emoji: APIEmoji | string;
}
export class Reaction extends BaseInterface<typeof Reactions, ReactionType> {
public static schema: z.ZodType<APIReaction> = z.object({
id: z.string().uuid(),
author_id: z.string().uuid(),
emoji: CustomEmoji,
});
public static $type: ReactionType;
public async reload(): Promise<void> {

View file

@ -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<typeof RelationshipSchema> {
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 ?? [],
};
}
}

View file

@ -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 servers 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({

View file

@ -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",

View file

@ -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<z.infer<typeof NotificationSchema.shape.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<z.infer<typeof StatusSchema.shape.visibility>>()
.notNull(),
replyId: uuid("replyId").references((): AnyPgColumn => Notes.id, {
onDelete: "cascade",
onUpdate: "cascade",