mirror of
https://github.com/versia-pub/server.git
synced 2026-03-13 05:49:16 +01:00
feat(api): 🏷️ Port Status OpenAPI schemas from Mastodon API docs
This commit is contained in:
parent
2aeada4904
commit
7c622730dc
26 changed files with 920 additions and 148 deletions
|
|
@ -1,5 +1,4 @@
|
|||
import { z } from "@hono/zod-openapi";
|
||||
import type { Application as APIApplication } from "@versia/client/types";
|
||||
import type { z } from "@hono/zod-openapi";
|
||||
import { Token, db } from "@versia/kit/db";
|
||||
import { Applications } from "@versia/kit/tables";
|
||||
import {
|
||||
|
|
@ -10,19 +9,15 @@ import {
|
|||
eq,
|
||||
inArray,
|
||||
} from "drizzle-orm";
|
||||
import type {
|
||||
Application as ApplicationSchema,
|
||||
CredentialApplication,
|
||||
} from "../schemas/application.ts";
|
||||
import { BaseInterface } from "./base.ts";
|
||||
|
||||
type ApplicationType = InferSelectModel<typeof Applications>;
|
||||
|
||||
export class Application extends BaseInterface<typeof Applications> {
|
||||
public static schema: z.ZodType<APIApplication> = z.object({
|
||||
name: z.string(),
|
||||
website: z.string().url().optional().nullable(),
|
||||
vapid_key: z.string().optional().nullable(),
|
||||
redirect_uris: z.string().optional(),
|
||||
scopes: z.string().optional(),
|
||||
});
|
||||
|
||||
public static $type: ApplicationType;
|
||||
|
||||
public async reload(): Promise<void> {
|
||||
|
|
@ -144,11 +139,26 @@ export class Application extends BaseInterface<typeof Applications> {
|
|||
return this.data.id;
|
||||
}
|
||||
|
||||
public toApi(): APIApplication {
|
||||
public toApi(): z.infer<typeof ApplicationSchema> {
|
||||
return {
|
||||
name: this.data.name,
|
||||
website: this.data.website,
|
||||
vapid_key: this.data.vapidKey,
|
||||
scopes: this.data.scopes.split(" "),
|
||||
redirect_uri: this.data.redirectUri,
|
||||
redirect_uris: this.data.redirectUri.split("\n"),
|
||||
};
|
||||
}
|
||||
|
||||
public toApiCredential(): z.infer<typeof CredentialApplication> {
|
||||
return {
|
||||
name: this.data.name,
|
||||
website: this.data.website,
|
||||
client_id: this.data.clientId,
|
||||
client_secret: this.data.secret,
|
||||
client_secret_expires_at: "0",
|
||||
scopes: this.data.scopes.split(" "),
|
||||
redirect_uri: this.data.redirectUri,
|
||||
redirect_uris: this.data.redirectUri.split("\n"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { localObjectUri } from "@/constants";
|
|||
import { mergeAndDeduplicate } from "@/lib.ts";
|
||||
import { sanitizedHtmlStrip } from "@/sanitization";
|
||||
import { sentry } from "@/sentry";
|
||||
import { z } from "@hono/zod-openapi";
|
||||
import type { z } from "@hono/zod-openapi";
|
||||
import { getLogger } from "@logtape/logtape";
|
||||
import type {
|
||||
Attachment as ApiAttachment,
|
||||
|
|
@ -43,7 +43,7 @@ import {
|
|||
} from "~/classes/functions/status";
|
||||
import { config } from "~/packages/config-manager";
|
||||
import { DeliveryJobType, deliveryQueue } from "../queues/delivery.ts";
|
||||
import { Account } from "../schemas/account.ts";
|
||||
import type { Status } from "../schemas/status.ts";
|
||||
import { Application } from "./application.ts";
|
||||
import { BaseInterface } from "./base.ts";
|
||||
import { Emoji } from "./emoji.ts";
|
||||
|
|
@ -81,96 +81,6 @@ export type NoteTypeWithoutRecursiveRelations = Omit<
|
|||
* Gives helpers to fetch notes from database in a nice format
|
||||
*/
|
||||
export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
||||
public static schema: z.ZodType<ApiStatus> = z.object({
|
||||
id: z.string().uuid(),
|
||||
uri: z.string().url(),
|
||||
url: z.string().url(),
|
||||
account: Account,
|
||||
in_reply_to_id: z.string().uuid().nullable(),
|
||||
in_reply_to_account_id: z.string().uuid().nullable(),
|
||||
reblog: z.lazy(() => Note.schema).nullable(),
|
||||
content: z.string(),
|
||||
plain_content: z.string().nullable(),
|
||||
created_at: z.string(),
|
||||
edited_at: z.string().nullable(),
|
||||
emojis: z.array(Emoji.schema),
|
||||
replies_count: z.number().int().nonnegative(),
|
||||
reblogs_count: z.number().int().nonnegative(),
|
||||
favourites_count: z.number().int().nonnegative(),
|
||||
reblogged: z.boolean().nullable(),
|
||||
favourited: z.boolean().nullable(),
|
||||
muted: z.boolean().nullable(),
|
||||
sensitive: z.boolean(),
|
||||
spoiler_text: z.string(),
|
||||
visibility: z.enum(["public", "unlisted", "private", "direct"]),
|
||||
media_attachments: z.array(Media.schema),
|
||||
mentions: z.array(
|
||||
z.object({
|
||||
id: z.string().uuid(),
|
||||
username: z.string(),
|
||||
acct: z.string(),
|
||||
url: z.string().url(),
|
||||
}),
|
||||
),
|
||||
tags: z.array(z.object({ name: z.string(), url: z.string().url() })),
|
||||
card: z
|
||||
.object({
|
||||
url: z.string().url(),
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
type: z.enum(["link", "photo", "video", "rich"]),
|
||||
image: z.string().url().nullable(),
|
||||
author_name: z.string().nullable(),
|
||||
author_url: z.string().url().nullable(),
|
||||
provider_name: z.string().nullable(),
|
||||
provider_url: z.string().url().nullable(),
|
||||
html: z.string().nullable(),
|
||||
width: z.number().int().nonnegative().nullable(),
|
||||
height: z.number().int().nonnegative().nullable(),
|
||||
embed_url: z.string().url().nullable(),
|
||||
blurhash: z.string().nullable(),
|
||||
})
|
||||
.nullable(),
|
||||
poll: z
|
||||
.object({
|
||||
id: z.string().uuid(),
|
||||
expires_at: z.string(),
|
||||
expired: z.boolean(),
|
||||
multiple: z.boolean(),
|
||||
votes_count: z.number().int().nonnegative(),
|
||||
voted: z.boolean(),
|
||||
options: z.array(
|
||||
z.object({
|
||||
title: z.string(),
|
||||
votes_count: z.number().int().nonnegative().nullable(),
|
||||
}),
|
||||
),
|
||||
})
|
||||
.nullable(),
|
||||
application: z
|
||||
.object({
|
||||
name: z.string(),
|
||||
website: z.string().url().nullable().optional(),
|
||||
vapid_key: z.string().nullable().optional(),
|
||||
})
|
||||
.nullable(),
|
||||
language: z.string().nullable(),
|
||||
pinned: z.boolean().nullable(),
|
||||
emoji_reactions: z.array(
|
||||
z.object({
|
||||
count: z.number().int().nonnegative(),
|
||||
me: z.boolean(),
|
||||
name: z.string(),
|
||||
url: z.string().url().optional(),
|
||||
static_url: z.string().url().optional(),
|
||||
accounts: z.array(Account).optional(),
|
||||
account_ids: z.array(z.string().uuid()).optional(),
|
||||
}),
|
||||
),
|
||||
quote: z.lazy(() => Note.schema).nullable(),
|
||||
bookmarked: z.boolean(),
|
||||
});
|
||||
|
||||
public static $type: NoteTypeWithRelations;
|
||||
|
||||
public save(): Promise<NoteTypeWithRelations> {
|
||||
|
|
@ -861,7 +771,9 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
|||
* @param userFetching - The user fetching the note (used to check if the note is favourite and such)
|
||||
* @returns The note in the Mastodon API format
|
||||
*/
|
||||
public async toApi(userFetching?: User | null): Promise<ApiStatus> {
|
||||
public async toApi(
|
||||
userFetching?: User | null,
|
||||
): Promise<z.infer<typeof Status>> {
|
||||
const data = this.data;
|
||||
|
||||
// Convert mentions of local users from @username@host to @username
|
||||
|
|
@ -893,7 +805,7 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
|||
created_at: new Date(data.createdAt).toISOString(),
|
||||
application: data.application
|
||||
? new Application(data.application).toApi()
|
||||
: null,
|
||||
: undefined,
|
||||
card: null,
|
||||
content: replacedContent,
|
||||
emojis: data.emojis.map((emoji) => new Emoji(emoji).toApi()),
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import {
|
|||
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> & {
|
||||
|
|
@ -31,7 +32,7 @@ export class Notification extends BaseInterface<
|
|||
account: Account.nullable(),
|
||||
created_at: z.string(),
|
||||
id: z.string().uuid(),
|
||||
status: z.lazy(() => Note.schema).optional(),
|
||||
status: Status.optional(),
|
||||
// TODO: Add reactions
|
||||
type: z.enum([
|
||||
"mention",
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { getBestContentType, urlToContentFormat } from "@/content_types";
|
|||
import { randomString } from "@/math";
|
||||
import { proxyUrl } from "@/response";
|
||||
import { sentry } from "@/sentry";
|
||||
import type { z } from "@hono/zod-openapi";
|
||||
import { getLogger } from "@logtape/logtape";
|
||||
import type { Mention as ApiMention } from "@versia/client/types";
|
||||
import {
|
||||
|
|
@ -45,7 +46,6 @@ import {
|
|||
sql,
|
||||
} from "drizzle-orm";
|
||||
import { htmlToText } from "html-to-text";
|
||||
import type { z } from "zod";
|
||||
import { findManyUsers } from "~/classes/functions/user";
|
||||
import { searchManager } from "~/classes/search/search-manager";
|
||||
import { type Config, config } from "~/packages/config-manager";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue