mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 08:28:19 +01:00
feat(api): 🏷️ Port Status OpenAPI schemas from Mastodon API docs
This commit is contained in:
parent
2aeada4904
commit
7c622730dc
|
|
@ -1,8 +1,9 @@
|
||||||
import { apiRoute, auth, withUserParam } from "@/api";
|
import { apiRoute, auth, withUserParam } from "@/api";
|
||||||
import { createRoute, z } from "@hono/zod-openapi";
|
import { createRoute, z } from "@hono/zod-openapi";
|
||||||
import { Note, Timeline } from "@versia/kit/db";
|
import { Timeline } from "@versia/kit/db";
|
||||||
import { Notes, RolePermissions } from "@versia/kit/tables";
|
import { Notes, RolePermissions } from "@versia/kit/tables";
|
||||||
import { and, eq, gt, gte, inArray, isNull, lt, or, sql } from "drizzle-orm";
|
import { and, eq, gt, gte, inArray, isNull, lt, or, sql } from "drizzle-orm";
|
||||||
|
import { Status } from "~/classes/schemas/status";
|
||||||
|
|
||||||
const schemas = {
|
const schemas = {
|
||||||
param: z.object({
|
param: z.object({
|
||||||
|
|
@ -61,7 +62,7 @@ const route = createRoute({
|
||||||
description: "A list of statuses by the specified account",
|
description: "A list of statuses by the specified account",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: z.array(Note.schema),
|
schema: z.array(Status),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { createRoute } from "@hono/zod-openapi";
|
||||||
import { Application } from "@versia/kit/db";
|
import { Application } from "@versia/kit/db";
|
||||||
import { RolePermissions } from "@versia/kit/tables";
|
import { RolePermissions } from "@versia/kit/tables";
|
||||||
import { ApiError } from "~/classes/errors/api-error";
|
import { ApiError } from "~/classes/errors/api-error";
|
||||||
|
import { Application as ApplicationSchema } from "~/classes/schemas/application";
|
||||||
import { ErrorSchema } from "~/types/api";
|
import { ErrorSchema } from "~/types/api";
|
||||||
|
|
||||||
const route = createRoute({
|
const route = createRoute({
|
||||||
|
|
@ -21,7 +22,7 @@ const route = createRoute({
|
||||||
description: "Application",
|
description: "Application",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: Application.schema,
|
schema: ApplicationSchema,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -52,13 +53,6 @@ export default apiRoute((app) =>
|
||||||
throw new ApiError(401, "Application not found");
|
throw new ApiError(401, "Application not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
return context.json(
|
return context.json(application.toApi(), 200);
|
||||||
{
|
|
||||||
...application.toApi(),
|
|
||||||
redirect_uris: application.data.redirectUri,
|
|
||||||
scopes: application.data.scopes,
|
|
||||||
},
|
|
||||||
200,
|
|
||||||
);
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import { apiRoute, auth } from "@/api";
|
import { apiRoute, auth } from "@/api";
|
||||||
import { createRoute, z } from "@hono/zod-openapi";
|
import { createRoute, z } from "@hono/zod-openapi";
|
||||||
import { Note, Timeline } from "@versia/kit/db";
|
import { Timeline } from "@versia/kit/db";
|
||||||
import { Notes, RolePermissions } from "@versia/kit/tables";
|
import { Notes, RolePermissions } from "@versia/kit/tables";
|
||||||
import { and, gt, gte, lt, sql } from "drizzle-orm";
|
import { and, gt, gte, lt, sql } from "drizzle-orm";
|
||||||
|
import { Status } from "~/classes/schemas/status";
|
||||||
|
|
||||||
const schemas = {
|
const schemas = {
|
||||||
query: z.object({
|
query: z.object({
|
||||||
|
|
@ -31,7 +32,7 @@ const route = createRoute({
|
||||||
description: "Favourites",
|
description: "Favourites",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: z.array(Note.schema),
|
schema: z.array(Status),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { apiRoute, auth, withNoteParam } from "@/api";
|
import { apiRoute, auth, withNoteParam } from "@/api";
|
||||||
import { createRoute, z } from "@hono/zod-openapi";
|
import { createRoute, z } from "@hono/zod-openapi";
|
||||||
import { Note } from "@versia/kit/db";
|
|
||||||
import { RolePermissions } from "@versia/kit/tables";
|
import { RolePermissions } from "@versia/kit/tables";
|
||||||
|
import { Status } from "~/classes/schemas/status";
|
||||||
import { ErrorSchema } from "~/types/api";
|
import { ErrorSchema } from "~/types/api";
|
||||||
|
|
||||||
const route = createRoute({
|
const route = createRoute({
|
||||||
|
|
@ -26,8 +26,8 @@ const route = createRoute({
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: z.object({
|
schema: z.object({
|
||||||
ancestors: z.array(Note.schema),
|
ancestors: z.array(Status),
|
||||||
descendants: z.array(Note.schema),
|
descendants: z.array(Status),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { apiRoute, auth, withNoteParam } from "@/api";
|
import { apiRoute, auth, withNoteParam } from "@/api";
|
||||||
import { createRoute, z } from "@hono/zod-openapi";
|
import { createRoute, z } from "@hono/zod-openapi";
|
||||||
import { Note } from "@versia/kit/db";
|
|
||||||
import { RolePermissions } from "@versia/kit/tables";
|
import { RolePermissions } from "@versia/kit/tables";
|
||||||
|
import { Status } from "~/classes/schemas/status";
|
||||||
|
|
||||||
const route = createRoute({
|
const route = createRoute({
|
||||||
method: "post",
|
method: "post",
|
||||||
|
|
@ -27,7 +27,7 @@ const route = createRoute({
|
||||||
description: "Favourited status",
|
description: "Favourited status",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: Note.schema,
|
schema: Status,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import { apiRoute, auth, jsonOrForm, withNoteParam } from "@/api";
|
import { apiRoute, auth, jsonOrForm, withNoteParam } from "@/api";
|
||||||
import { createRoute, z } from "@hono/zod-openapi";
|
import { createRoute, z } from "@hono/zod-openapi";
|
||||||
import { Media, Note } from "@versia/kit/db";
|
import { Media } from "@versia/kit/db";
|
||||||
import { RolePermissions } from "@versia/kit/tables";
|
import { RolePermissions } from "@versia/kit/tables";
|
||||||
import ISO6391 from "iso-639-1";
|
import ISO6391 from "iso-639-1";
|
||||||
import { ApiError } from "~/classes/errors/api-error";
|
import { ApiError } from "~/classes/errors/api-error";
|
||||||
|
import { Status } from "~/classes/schemas/status";
|
||||||
import { config } from "~/packages/config-manager/index.ts";
|
import { config } from "~/packages/config-manager/index.ts";
|
||||||
import { ErrorSchema } from "~/types/api";
|
import { ErrorSchema } from "~/types/api";
|
||||||
|
|
||||||
|
|
@ -84,7 +85,7 @@ const routeGet = createRoute({
|
||||||
description: "Status",
|
description: "Status",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: Note.schema,
|
schema: Status,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -121,7 +122,7 @@ const routeDelete = createRoute({
|
||||||
description: "Deleted status",
|
description: "Deleted status",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: Note.schema,
|
schema: Status,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -180,7 +181,7 @@ const routePut = createRoute({
|
||||||
description: "Updated status",
|
description: "Updated status",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: Note.schema,
|
schema: Status,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import { apiRoute, auth, withNoteParam } from "@/api";
|
import { apiRoute, auth, withNoteParam } from "@/api";
|
||||||
import { createRoute, z } from "@hono/zod-openapi";
|
import { createRoute, z } from "@hono/zod-openapi";
|
||||||
import { Note, db } from "@versia/kit/db";
|
import { db } from "@versia/kit/db";
|
||||||
import { RolePermissions } from "@versia/kit/tables";
|
import { RolePermissions } from "@versia/kit/tables";
|
||||||
import type { SQL } from "drizzle-orm";
|
import type { SQL } from "drizzle-orm";
|
||||||
import { ApiError } from "~/classes/errors/api-error";
|
import { ApiError } from "~/classes/errors/api-error";
|
||||||
|
import { Status } from "~/classes/schemas/status";
|
||||||
import { ErrorSchema } from "~/types/api";
|
import { ErrorSchema } from "~/types/api";
|
||||||
|
|
||||||
const route = createRoute({
|
const route = createRoute({
|
||||||
|
|
@ -30,7 +31,7 @@ const route = createRoute({
|
||||||
description: "Pinned status",
|
description: "Pinned status",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: Note.schema,
|
schema: Status,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { Note } from "@versia/kit/db";
|
||||||
import { Notes, RolePermissions } from "@versia/kit/tables";
|
import { Notes, RolePermissions } from "@versia/kit/tables";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import { ApiError } from "~/classes/errors/api-error";
|
import { ApiError } from "~/classes/errors/api-error";
|
||||||
|
import { Status } from "~/classes/schemas/status";
|
||||||
import { ErrorSchema } from "~/types/api";
|
import { ErrorSchema } from "~/types/api";
|
||||||
|
|
||||||
const schemas = {
|
const schemas = {
|
||||||
|
|
@ -51,7 +52,7 @@ const route = createRoute({
|
||||||
description: "Reblogged status",
|
description: "Reblogged status",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: Note.schema,
|
schema: Status,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { apiRoute, auth, withNoteParam } from "@/api";
|
import { apiRoute, auth, withNoteParam } from "@/api";
|
||||||
import { createRoute, z } from "@hono/zod-openapi";
|
import { createRoute, z } from "@hono/zod-openapi";
|
||||||
import { Note } from "@versia/kit/db";
|
|
||||||
import { RolePermissions } from "@versia/kit/tables";
|
import { RolePermissions } from "@versia/kit/tables";
|
||||||
|
import { Status } from "~/classes/schemas/status";
|
||||||
|
|
||||||
const route = createRoute({
|
const route = createRoute({
|
||||||
method: "post",
|
method: "post",
|
||||||
|
|
@ -27,7 +27,7 @@ const route = createRoute({
|
||||||
description: "Unfavourited status",
|
description: "Unfavourited status",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: Note.schema,
|
schema: Status,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { apiRoute, auth, withNoteParam } from "@/api";
|
import { apiRoute, auth, withNoteParam } from "@/api";
|
||||||
import { createRoute, z } from "@hono/zod-openapi";
|
import { createRoute, z } from "@hono/zod-openapi";
|
||||||
import { Note } from "@versia/kit/db";
|
|
||||||
import { RolePermissions } from "@versia/kit/tables";
|
import { RolePermissions } from "@versia/kit/tables";
|
||||||
import { ApiError } from "~/classes/errors/api-error";
|
import { ApiError } from "~/classes/errors/api-error";
|
||||||
|
import { Status } from "~/classes/schemas/status";
|
||||||
import { ErrorSchema } from "~/types/api";
|
import { ErrorSchema } from "~/types/api";
|
||||||
|
|
||||||
const route = createRoute({
|
const route = createRoute({
|
||||||
|
|
@ -29,7 +29,7 @@ const route = createRoute({
|
||||||
description: "Unpinned status",
|
description: "Unpinned status",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: Note.schema,
|
schema: Status,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { Note } from "@versia/kit/db";
|
||||||
import { Notes, RolePermissions } from "@versia/kit/tables";
|
import { Notes, RolePermissions } from "@versia/kit/tables";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import { ApiError } from "~/classes/errors/api-error";
|
import { ApiError } from "~/classes/errors/api-error";
|
||||||
|
import { Status } from "~/classes/schemas/status";
|
||||||
import { ErrorSchema } from "~/types/api";
|
import { ErrorSchema } from "~/types/api";
|
||||||
|
|
||||||
const route = createRoute({
|
const route = createRoute({
|
||||||
|
|
@ -30,7 +31,7 @@ const route = createRoute({
|
||||||
description: "Unreblogged status",
|
description: "Unreblogged status",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: Note.schema,
|
schema: Status,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { Media, Note } from "@versia/kit/db";
|
||||||
import { RolePermissions } from "@versia/kit/tables";
|
import { RolePermissions } from "@versia/kit/tables";
|
||||||
import ISO6391 from "iso-639-1";
|
import ISO6391 from "iso-639-1";
|
||||||
import { ApiError } from "~/classes/errors/api-error";
|
import { ApiError } from "~/classes/errors/api-error";
|
||||||
|
import { Status } from "~/classes/schemas/status";
|
||||||
import { config } from "~/packages/config-manager/index.ts";
|
import { config } from "~/packages/config-manager/index.ts";
|
||||||
import { ErrorSchema } from "~/types/api";
|
import { ErrorSchema } from "~/types/api";
|
||||||
|
|
||||||
|
|
@ -116,7 +117,7 @@ const route = createRoute({
|
||||||
description: "The new status",
|
description: "The new status",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: Note.schema,
|
schema: Status,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import { apiRoute, auth } from "@/api";
|
import { apiRoute, auth } from "@/api";
|
||||||
import { createRoute, z } from "@hono/zod-openapi";
|
import { createRoute, z } from "@hono/zod-openapi";
|
||||||
import { Note, Timeline } from "@versia/kit/db";
|
import { Timeline } from "@versia/kit/db";
|
||||||
import { Notes, RolePermissions } from "@versia/kit/tables";
|
import { Notes, RolePermissions } from "@versia/kit/tables";
|
||||||
import { and, eq, gt, gte, inArray, lt, or, sql } from "drizzle-orm";
|
import { and, eq, gt, gte, inArray, lt, or, sql } from "drizzle-orm";
|
||||||
|
import { Status } from "~/classes/schemas/status";
|
||||||
|
|
||||||
const schemas = {
|
const schemas = {
|
||||||
query: z.object({
|
query: z.object({
|
||||||
|
|
@ -36,7 +37,7 @@ const route = createRoute({
|
||||||
description: "Home timeline",
|
description: "Home timeline",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: z.array(Note.schema),
|
schema: z.array(Status),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import { apiRoute, auth } from "@/api";
|
import { apiRoute, auth } from "@/api";
|
||||||
import { createRoute, z } from "@hono/zod-openapi";
|
import { createRoute, z } from "@hono/zod-openapi";
|
||||||
import { Note, Timeline } from "@versia/kit/db";
|
import { Timeline } from "@versia/kit/db";
|
||||||
import { Notes, RolePermissions } from "@versia/kit/tables";
|
import { Notes, RolePermissions } from "@versia/kit/tables";
|
||||||
import { and, eq, gt, gte, inArray, lt, or, sql } from "drizzle-orm";
|
import { and, eq, gt, gte, inArray, lt, or, sql } from "drizzle-orm";
|
||||||
|
import { Status } from "~/classes/schemas/status";
|
||||||
|
|
||||||
const schemas = {
|
const schemas = {
|
||||||
query: z.object({
|
query: z.object({
|
||||||
|
|
@ -47,7 +48,7 @@ const route = createRoute({
|
||||||
description: "Public timeline",
|
description: "Public timeline",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: z.array(Note.schema),
|
schema: z.array(Status),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { Instances, Notes, RolePermissions, Users } from "@versia/kit/tables";
|
||||||
import { and, eq, inArray, isNull, sql } from "drizzle-orm";
|
import { and, eq, inArray, isNull, sql } from "drizzle-orm";
|
||||||
import { ApiError } from "~/classes/errors/api-error";
|
import { ApiError } from "~/classes/errors/api-error";
|
||||||
import { Account } from "~/classes/schemas/account";
|
import { Account } from "~/classes/schemas/account";
|
||||||
|
import { Status } from "~/classes/schemas/status";
|
||||||
import { searchManager } from "~/classes/search/search-manager";
|
import { searchManager } from "~/classes/search/search-manager";
|
||||||
import { config } from "~/packages/config-manager";
|
import { config } from "~/packages/config-manager";
|
||||||
import { ErrorSchema } from "~/types/api";
|
import { ErrorSchema } from "~/types/api";
|
||||||
|
|
@ -48,7 +49,7 @@ const route = createRoute({
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: z.object({
|
schema: z.object({
|
||||||
accounts: z.array(Account),
|
accounts: z.array(Account),
|
||||||
statuses: z.array(Note.schema),
|
statuses: z.array(Status),
|
||||||
hashtags: z.array(z.string()),
|
hashtags: z.array(z.string()),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { z } from "@hono/zod-openapi";
|
import type { z } from "@hono/zod-openapi";
|
||||||
import type { Application as APIApplication } from "@versia/client/types";
|
|
||||||
import { Token, db } from "@versia/kit/db";
|
import { Token, db } from "@versia/kit/db";
|
||||||
import { Applications } from "@versia/kit/tables";
|
import { Applications } from "@versia/kit/tables";
|
||||||
import {
|
import {
|
||||||
|
|
@ -10,19 +9,15 @@ import {
|
||||||
eq,
|
eq,
|
||||||
inArray,
|
inArray,
|
||||||
} from "drizzle-orm";
|
} from "drizzle-orm";
|
||||||
|
import type {
|
||||||
|
Application as ApplicationSchema,
|
||||||
|
CredentialApplication,
|
||||||
|
} from "../schemas/application.ts";
|
||||||
import { BaseInterface } from "./base.ts";
|
import { BaseInterface } from "./base.ts";
|
||||||
|
|
||||||
type ApplicationType = InferSelectModel<typeof Applications>;
|
type ApplicationType = InferSelectModel<typeof Applications>;
|
||||||
|
|
||||||
export class Application extends BaseInterface<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 static $type: ApplicationType;
|
||||||
|
|
||||||
public async reload(): Promise<void> {
|
public async reload(): Promise<void> {
|
||||||
|
|
@ -144,11 +139,26 @@ export class Application extends BaseInterface<typeof Applications> {
|
||||||
return this.data.id;
|
return this.data.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public toApi(): APIApplication {
|
public toApi(): z.infer<typeof ApplicationSchema> {
|
||||||
return {
|
return {
|
||||||
name: this.data.name,
|
name: this.data.name,
|
||||||
website: this.data.website,
|
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 { mergeAndDeduplicate } from "@/lib.ts";
|
||||||
import { sanitizedHtmlStrip } from "@/sanitization";
|
import { sanitizedHtmlStrip } from "@/sanitization";
|
||||||
import { sentry } from "@/sentry";
|
import { sentry } from "@/sentry";
|
||||||
import { z } from "@hono/zod-openapi";
|
import type { z } from "@hono/zod-openapi";
|
||||||
import { getLogger } from "@logtape/logtape";
|
import { getLogger } from "@logtape/logtape";
|
||||||
import type {
|
import type {
|
||||||
Attachment as ApiAttachment,
|
Attachment as ApiAttachment,
|
||||||
|
|
@ -43,7 +43,7 @@ import {
|
||||||
} from "~/classes/functions/status";
|
} from "~/classes/functions/status";
|
||||||
import { config } from "~/packages/config-manager";
|
import { config } from "~/packages/config-manager";
|
||||||
import { DeliveryJobType, deliveryQueue } from "../queues/delivery.ts";
|
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 { Application } from "./application.ts";
|
||||||
import { BaseInterface } from "./base.ts";
|
import { BaseInterface } from "./base.ts";
|
||||||
import { Emoji } from "./emoji.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
|
* Gives helpers to fetch notes from database in a nice format
|
||||||
*/
|
*/
|
||||||
export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
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 static $type: NoteTypeWithRelations;
|
||||||
|
|
||||||
public save(): Promise<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)
|
* @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
|
* @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;
|
const data = this.data;
|
||||||
|
|
||||||
// Convert mentions of local users from @username@host to @username
|
// 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(),
|
created_at: new Date(data.createdAt).toISOString(),
|
||||||
application: data.application
|
application: data.application
|
||||||
? new Application(data.application).toApi()
|
? new Application(data.application).toApi()
|
||||||
: null,
|
: undefined,
|
||||||
card: null,
|
card: null,
|
||||||
content: replacedContent,
|
content: replacedContent,
|
||||||
emojis: data.emojis.map((emoji) => new Emoji(emoji).toApi()),
|
emojis: data.emojis.map((emoji) => new Emoji(emoji).toApi()),
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import {
|
||||||
userRelations,
|
userRelations,
|
||||||
} from "../functions/user.ts";
|
} from "../functions/user.ts";
|
||||||
import { Account } from "../schemas/account.ts";
|
import { Account } from "../schemas/account.ts";
|
||||||
|
import { Status } from "../schemas/status.ts";
|
||||||
import { BaseInterface } from "./base.ts";
|
import { BaseInterface } from "./base.ts";
|
||||||
|
|
||||||
export type NotificationType = InferSelectModel<typeof Notifications> & {
|
export type NotificationType = InferSelectModel<typeof Notifications> & {
|
||||||
|
|
@ -31,7 +32,7 @@ export class Notification extends BaseInterface<
|
||||||
account: Account.nullable(),
|
account: Account.nullable(),
|
||||||
created_at: z.string(),
|
created_at: z.string(),
|
||||||
id: z.string().uuid(),
|
id: z.string().uuid(),
|
||||||
status: z.lazy(() => Note.schema).optional(),
|
status: Status.optional(),
|
||||||
// TODO: Add reactions
|
// TODO: Add reactions
|
||||||
type: z.enum([
|
type: z.enum([
|
||||||
"mention",
|
"mention",
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { getBestContentType, urlToContentFormat } from "@/content_types";
|
||||||
import { randomString } from "@/math";
|
import { randomString } from "@/math";
|
||||||
import { proxyUrl } from "@/response";
|
import { proxyUrl } from "@/response";
|
||||||
import { sentry } from "@/sentry";
|
import { sentry } from "@/sentry";
|
||||||
|
import type { z } from "@hono/zod-openapi";
|
||||||
import { getLogger } from "@logtape/logtape";
|
import { getLogger } from "@logtape/logtape";
|
||||||
import type { Mention as ApiMention } from "@versia/client/types";
|
import type { Mention as ApiMention } from "@versia/client/types";
|
||||||
import {
|
import {
|
||||||
|
|
@ -45,7 +46,6 @@ import {
|
||||||
sql,
|
sql,
|
||||||
} from "drizzle-orm";
|
} from "drizzle-orm";
|
||||||
import { htmlToText } from "html-to-text";
|
import { htmlToText } from "html-to-text";
|
||||||
import type { z } from "zod";
|
|
||||||
import { findManyUsers } from "~/classes/functions/user";
|
import { findManyUsers } from "~/classes/functions/user";
|
||||||
import { searchManager } from "~/classes/search/search-manager";
|
import { searchManager } from "~/classes/search/search-manager";
|
||||||
import { type Config, config } from "~/packages/config-manager";
|
import { type Config, config } from "~/packages/config-manager";
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ export const Source = z
|
||||||
follow_requests_count: z
|
follow_requests_count: z
|
||||||
.number()
|
.number()
|
||||||
.int()
|
.int()
|
||||||
|
.nonnegative()
|
||||||
.optional()
|
.optional()
|
||||||
.openapi({
|
.openapi({
|
||||||
description: "The number of pending follow requests.",
|
description: "The number of pending follow requests.",
|
||||||
|
|
@ -352,8 +353,8 @@ export const Account = z.object({
|
||||||
.nullable(),
|
.nullable(),
|
||||||
statuses_count: z
|
statuses_count: z
|
||||||
.number()
|
.number()
|
||||||
.min(0)
|
|
||||||
.int()
|
.int()
|
||||||
|
.nonnegative()
|
||||||
.openapi({
|
.openapi({
|
||||||
description: "How many statuses are attached to this account.",
|
description: "How many statuses are attached to this account.",
|
||||||
example: 42,
|
example: 42,
|
||||||
|
|
@ -363,8 +364,8 @@ export const Account = z.object({
|
||||||
}),
|
}),
|
||||||
followers_count: z
|
followers_count: z
|
||||||
.number()
|
.number()
|
||||||
.min(0)
|
|
||||||
.int()
|
.int()
|
||||||
|
.nonnegative()
|
||||||
.openapi({
|
.openapi({
|
||||||
description: "The reported followers of this profile.",
|
description: "The reported followers of this profile.",
|
||||||
example: 6,
|
example: 6,
|
||||||
|
|
@ -374,8 +375,8 @@ export const Account = z.object({
|
||||||
}),
|
}),
|
||||||
following_count: z
|
following_count: z
|
||||||
.number()
|
.number()
|
||||||
.min(0)
|
|
||||||
.int()
|
.int()
|
||||||
|
.nonnegative()
|
||||||
.openapi({
|
.openapi({
|
||||||
description: "The reported follows of this profile.",
|
description: "The reported follows of this profile.",
|
||||||
example: 23,
|
example: 23,
|
||||||
|
|
|
||||||
71
classes/schemas/application.ts
Normal file
71
classes/schemas/application.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
import { z } from "@hono/zod-openapi";
|
||||||
|
|
||||||
|
export const Application = z.object({
|
||||||
|
name: z.string().openapi({
|
||||||
|
description: "The name of your application.",
|
||||||
|
example: "Test Application",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Application/#name",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
website: z
|
||||||
|
.string()
|
||||||
|
.nullable()
|
||||||
|
.openapi({
|
||||||
|
description: "The website associated with your application.",
|
||||||
|
example: "https://app.example",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Application/#website",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
scopes: z.array(z.string()).openapi({
|
||||||
|
description:
|
||||||
|
"The scopes for your application. This is the registered scopes string split on whitespace.",
|
||||||
|
example: ["read", "write", "push"],
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Application/#scopes",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
redirect_uris: z
|
||||||
|
.array(
|
||||||
|
z.string().url().openapi({
|
||||||
|
description: "URL or 'urn:ietf:wg:oauth:2.0:oob'",
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.openapi({
|
||||||
|
description:
|
||||||
|
"The registered redirection URI(s) for your application.",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Application/#redirect_uris",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
redirect_uri: z.string().openapi({
|
||||||
|
description:
|
||||||
|
"The registered redirection URI(s) for your application. May contain \\n characters when multiple redirect URIs are registered.",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Application/#redirect_uri",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const CredentialApplication = Application.extend({
|
||||||
|
client_id: z.string().openapi({
|
||||||
|
description: "Client ID key, to be used for obtaining OAuth tokens",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/CredentialApplication/#client_id",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
client_secret: z.string().openapi({
|
||||||
|
description: "Client secret key, to be used for obtaining OAuth tokens",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/CredentialApplication/#client_secret",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
client_secret_expires_at: z.string().openapi({
|
||||||
|
description:
|
||||||
|
"When the client secret key will expire at, presently this always returns 0 indicating that OAuth Clients do not expire",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/CredentialApplication/#client_secret_expires_at",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
151
classes/schemas/card.ts
Normal file
151
classes/schemas/card.ts
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
import { z } from "@hono/zod-openapi";
|
||||||
|
import { Account } from "./account.ts";
|
||||||
|
|
||||||
|
export const PreviewCardAuthor = z.object({
|
||||||
|
name: z.string().openapi({
|
||||||
|
description: "The original resource author’s name.",
|
||||||
|
example: "The Doubleclicks",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/PreviewCardAuthor/#name",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
url: z
|
||||||
|
.string()
|
||||||
|
.url()
|
||||||
|
.openapi({
|
||||||
|
description: "A link to the author of the original resource.",
|
||||||
|
example: "https://www.youtube.com/user/thedoubleclicks",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/PreviewCardAuthor/#url",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
account: Account.nullable().openapi({
|
||||||
|
description: "The fediverse account of the author.",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/PreviewCardAuthor/#account",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const PreviewCard = z.object({
|
||||||
|
url: z
|
||||||
|
.string()
|
||||||
|
.url()
|
||||||
|
.openapi({
|
||||||
|
description: "Location of linked resource.",
|
||||||
|
example: "https://www.youtube.com/watch?v=OMv_EPMED8Y",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/PreviewCard/#url",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
title: z
|
||||||
|
.string()
|
||||||
|
.min(1)
|
||||||
|
.openapi({
|
||||||
|
description: "Title of linked resource.",
|
||||||
|
example: "♪ Brand New Friend (Christmas Song!)",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/PreviewCard/#title",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
description: z.string().openapi({
|
||||||
|
description: "Description of preview.",
|
||||||
|
example: "",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/PreviewCard/#description",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
type: z.enum(["link", "photo", "video"]).openapi({
|
||||||
|
description: "The type of the preview card.",
|
||||||
|
example: "video",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/PreviewCard/#type",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
authors: z.array(PreviewCardAuthor).openapi({
|
||||||
|
description:
|
||||||
|
"Fediverse account of the authors of the original resource.",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/PreviewCard/#authors",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
provider_name: z.string().openapi({
|
||||||
|
description: "The provider of the original resource.",
|
||||||
|
example: "YouTube",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/PreviewCard/#provider_name",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
provider_url: z
|
||||||
|
.string()
|
||||||
|
.url()
|
||||||
|
.openapi({
|
||||||
|
description: "A link to the provider of the original resource.",
|
||||||
|
example: "https://www.youtube.com/",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/PreviewCard/#provider_url",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
html: z.string().openapi({
|
||||||
|
description: "HTML to be used for generating the preview card.",
|
||||||
|
example:
|
||||||
|
'<iframe width="480" height="270" src="https://www.youtube.com/embed/OMv_EPMED8Y?feature=oembed" frameborder="0" allowfullscreen=""></iframe>',
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/PreviewCard/#html",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
width: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.openapi({
|
||||||
|
description: "Width of preview, in pixels.",
|
||||||
|
example: 480,
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/PreviewCard/#width",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
height: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.openapi({
|
||||||
|
description: "Height of preview, in pixels.",
|
||||||
|
example: 270,
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/PreviewCard/#height",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
image: z
|
||||||
|
.string()
|
||||||
|
.url()
|
||||||
|
.nullable()
|
||||||
|
.openapi({
|
||||||
|
description: "Preview thumbnail.",
|
||||||
|
example:
|
||||||
|
"https://cdn.versia.social/preview_cards/images/014/179/145/original/9cf4b7cf5567b569.jpeg",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/PreviewCard/#image",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
embed_url: z
|
||||||
|
.string()
|
||||||
|
.url()
|
||||||
|
.openapi({
|
||||||
|
description: "Used for photo embeds, instead of custom html.",
|
||||||
|
example:
|
||||||
|
"https://live.staticflickr.com/65535/49088768431_6a4322b3bb_b.jpg",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/PreviewCard/#embed_url",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
blurhash: z
|
||||||
|
.string()
|
||||||
|
.nullable()
|
||||||
|
.openapi({
|
||||||
|
description:
|
||||||
|
"A hash computed by the BlurHash algorithm, for generating colorful preview thumbnails when media has not been downloaded yet.",
|
||||||
|
example: "UvK0HNkV,:s9xBR%njog0fo2W=WBS5ozofV@",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/PreviewCard/#blurhash",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
3
classes/schemas/common.ts
Normal file
3
classes/schemas/common.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { z } from "@hono/zod-openapi";
|
||||||
|
|
||||||
|
export const Id = z.string().uuid();
|
||||||
129
classes/schemas/filters.ts
Normal file
129
classes/schemas/filters.ts
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
import { z } from "@hono/zod-openapi";
|
||||||
|
import { Id } from "./common.ts";
|
||||||
|
|
||||||
|
export const FilterStatus = z.object({
|
||||||
|
id: Id.openapi({
|
||||||
|
description: "The ID of the FilterStatus in the database.",
|
||||||
|
example: "3b19ed7c-0c4b-45e1-8c75-e21dfc8e86c3",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/FilterStatus/#id",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
status_id: Id.openapi({
|
||||||
|
description: "The ID of the Status that will be filtered.",
|
||||||
|
example: "4f941ac8-295c-4c2d-9300-82c162ac8028",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/FilterStatus/#status_id",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const FilterKeyword = z.object({
|
||||||
|
id: Id.openapi({
|
||||||
|
description: "The ID of the FilterKeyword in the database.",
|
||||||
|
example: "ca921e60-5b96-4686-90f3-d7cc420d7391",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/FilterKeyword/#id",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
keyword: z.string().openapi({
|
||||||
|
description: "The phrase to be matched against.",
|
||||||
|
example: "badword",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/FilterKeyword/#keyword",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
whole_word: z.boolean().openapi({
|
||||||
|
description:
|
||||||
|
"Should the filter consider word boundaries? See implementation guidelines for filters.",
|
||||||
|
example: false,
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/FilterKeyword/#whole_word",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Filter = z.object({
|
||||||
|
id: Id.openapi({
|
||||||
|
description: "The ID of the Filter in the database.",
|
||||||
|
example: "6b8fa22f-b128-43c2-9a1f-3c0499ef3a51",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Filter/#id",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
title: z.string().openapi({
|
||||||
|
description: "A title given by the user to name the filter.",
|
||||||
|
example: "Test filter",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Filter/#title",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
context: z
|
||||||
|
.array(z.enum(["home", "notifications", "public", "thread", "account"]))
|
||||||
|
.openapi({
|
||||||
|
description: "The contexts in which the filter should be applied.",
|
||||||
|
example: ["home"],
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Filter/#context",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
expires_at: z
|
||||||
|
.string()
|
||||||
|
.nullable()
|
||||||
|
.openapi({
|
||||||
|
description: "When the filter should no longer be applied.",
|
||||||
|
example: "2026-09-20T17:27:39.296Z",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Filter/#expires_at",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
filter_action: z.enum(["warn", "hide"]).openapi({
|
||||||
|
description:
|
||||||
|
"The action to be taken when a status matches this filter.",
|
||||||
|
example: "warn",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Filter/#filter_action",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
keywords: z.array(FilterKeyword).openapi({
|
||||||
|
description: "The keywords grouped under this filter.",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Filter/#keywords",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
statuses: z.array(FilterStatus).openapi({
|
||||||
|
description: "The statuses grouped under this filter.",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Filter/#statuses",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const FilterResult = z.object({
|
||||||
|
filter: Filter.openapi({
|
||||||
|
description: "The filter that was matched.",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/FilterResult/#filter",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
keyword_matches: z
|
||||||
|
.array(z.string())
|
||||||
|
.nullable()
|
||||||
|
.openapi({
|
||||||
|
description: "The keyword within the filter that was matched.",
|
||||||
|
example: ["badword"],
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/FilterResult/#keyword_matches",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
status_matches: z
|
||||||
|
.array(Id)
|
||||||
|
.nullable()
|
||||||
|
.openapi({
|
||||||
|
description: "The status ID within the filter that was matched.",
|
||||||
|
example: ["3819515a-5ceb-4078-8524-c939e38dcf8f"],
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/FilterResult/#status_matches",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
490
classes/schemas/status.ts
Normal file
490
classes/schemas/status.ts
Normal file
|
|
@ -0,0 +1,490 @@
|
||||||
|
import { z } from "@hono/zod-openapi";
|
||||||
|
import type { Status as ApiNote } from "@versia/client/types";
|
||||||
|
import { Emoji, Media } from "@versia/kit/db";
|
||||||
|
import ISO6391 from "iso-639-1";
|
||||||
|
import { zBoolean } from "~/packages/config-manager/config.type.ts";
|
||||||
|
import { Account } from "./account.ts";
|
||||||
|
import { PreviewCard } from "./card.ts";
|
||||||
|
import { Id } from "./common.ts";
|
||||||
|
import { FilterResult } from "./filters.ts";
|
||||||
|
|
||||||
|
export const Mention = z
|
||||||
|
.object({
|
||||||
|
id: Account.shape.id.openapi({
|
||||||
|
description: "The account ID of the mentioned user.",
|
||||||
|
example: "b9dcb548-bd4d-42af-8b48-3693e6d298e6",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#Mention-id",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
username: Account.shape.username.openapi({
|
||||||
|
description: "The username of the mentioned user.",
|
||||||
|
example: "lexi",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#Mention-username",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
url: Account.shape.url.openapi({
|
||||||
|
description: "The location of the mentioned user’s profile.",
|
||||||
|
example: "https://beta.versia.social/@lexi",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#Mention-url",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
acct: Account.shape.acct.openapi({
|
||||||
|
description:
|
||||||
|
"The webfinger acct: URI of the mentioned user. Equivalent to username for local users, or username@domain for remote users.",
|
||||||
|
example: "lexi@beta.versia.social",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#Mention-acct",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.openapi({
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#Mention",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Tag = z
|
||||||
|
.object({
|
||||||
|
name: z
|
||||||
|
.string()
|
||||||
|
.min(1)
|
||||||
|
.max(128)
|
||||||
|
.openapi({
|
||||||
|
description: "The value of the hashtag after the # sign.",
|
||||||
|
example: "versia",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#Tag-name",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
url: z
|
||||||
|
.string()
|
||||||
|
.url()
|
||||||
|
.openapi({
|
||||||
|
description: "A link to the hashtag on the instance.",
|
||||||
|
example: "https://beta.versia.social/tags/versia",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#Tag-url",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.openapi({
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#Tag",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const PollOption = z
|
||||||
|
.object({
|
||||||
|
title: z.string().openapi({
|
||||||
|
description: "The text value of the poll option.",
|
||||||
|
example: "yes",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Poll/#Option-title",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
votes_count: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.nonnegative()
|
||||||
|
.nullable()
|
||||||
|
.openapi({
|
||||||
|
description:
|
||||||
|
"The total number of received votes for this option.",
|
||||||
|
example: 6,
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Poll/#Option-votes_count",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.openapi({
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Poll/#Option",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Poll = z.object({
|
||||||
|
id: Id.openapi({
|
||||||
|
description: "ID of the poll in the database.",
|
||||||
|
example: "d87d230f-e401-4282-80c7-2044ab989662",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Poll/#id",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
expires_at: z
|
||||||
|
.string()
|
||||||
|
.datetime()
|
||||||
|
.nullable()
|
||||||
|
.openapi({
|
||||||
|
description: "When the poll ends.",
|
||||||
|
example: "2025-01-07T14:11:00.000Z",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Poll/#expires_at",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
expired: zBoolean.openapi({
|
||||||
|
description: "Is the poll currently expired?",
|
||||||
|
example: false,
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Poll/#expired",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
multiple: zBoolean.openapi({
|
||||||
|
description: "Does the poll allow multiple-choice answers?",
|
||||||
|
example: false,
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Poll/#multiple",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
votes_count: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.nonnegative()
|
||||||
|
.openapi({
|
||||||
|
description: "How many votes have been received.",
|
||||||
|
example: 6,
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Poll/#votes_count",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
voters_count: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.nonnegative()
|
||||||
|
.nullable()
|
||||||
|
.openapi({
|
||||||
|
description:
|
||||||
|
"How many unique accounts have voted on a multiple-choice poll.",
|
||||||
|
example: 3,
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Poll/#voters_count",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
options: z.array(PollOption).openapi({
|
||||||
|
description: "Possible answers for the poll.",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Poll/#options",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
emojis: z.array(Emoji.schema).openapi({
|
||||||
|
description: "Custom emoji to be used for rendering poll options.",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Poll/#emojis",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
voted: zBoolean.optional().openapi({
|
||||||
|
description:
|
||||||
|
"When called with a user token, has the authorized user voted?",
|
||||||
|
example: true,
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Poll/#voted",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
own_votes: z
|
||||||
|
.array(z.number().int())
|
||||||
|
.optional()
|
||||||
|
.openapi({
|
||||||
|
description:
|
||||||
|
"When called with a user token, which options has the authorized user chosen? Contains an array of index values for options.",
|
||||||
|
example: [0],
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Poll/#own_votes",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Status = z.object({
|
||||||
|
id: Id.openapi({
|
||||||
|
description: "ID of the status in the database.",
|
||||||
|
example: "2de861d3-a3dd-42ee-ba38-2c7d3f4af588",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#id",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
uri: z
|
||||||
|
.string()
|
||||||
|
.url()
|
||||||
|
.openapi({
|
||||||
|
description: "URI of the status used for federation.",
|
||||||
|
example:
|
||||||
|
"https://beta.versia.social/@lexi/2de861d3-a3dd-42ee-ba38-2c7d3f4af588",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#uri",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
url: z
|
||||||
|
.string()
|
||||||
|
.url()
|
||||||
|
.nullable()
|
||||||
|
.openapi({
|
||||||
|
description: "A link to the status’s HTML representation.",
|
||||||
|
example:
|
||||||
|
"https://beta.versia.social/@lexi/2de861d3-a3dd-42ee-ba38-2c7d3f4af588",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#url",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
account: Account.openapi({
|
||||||
|
description: "The account that authored this status.",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#account",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
in_reply_to_id: Id.nullable().openapi({
|
||||||
|
description: "ID of the status being replied to.",
|
||||||
|
example: "c41c9fe9-919a-4d35-a921-d3e79a5c95f8",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#in_reply_to_id",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
in_reply_to_account_id: Account.shape.id.nullable().openapi({
|
||||||
|
description:
|
||||||
|
"ID of the account that authored the status being replied to.",
|
||||||
|
example: "7b9b3ec6-1013-4cc6-8902-94ad00cf2ccc",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#in_reply_to_account_id",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
reblog: z
|
||||||
|
.lazy((): z.ZodType<ApiNote> => Status as z.ZodType<ApiNote>)
|
||||||
|
.nullable()
|
||||||
|
.openapi({
|
||||||
|
description: "The status being reblogged.",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#reblog",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
content: z.string().openapi({
|
||||||
|
description: "HTML-encoded status content.",
|
||||||
|
example: "<p>hello world</p>",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#content",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
created_at: z
|
||||||
|
.string()
|
||||||
|
.datetime()
|
||||||
|
.openapi({
|
||||||
|
description: "The date when this status was created.",
|
||||||
|
example: "2025-01-07T14:11:00.000Z",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#created_at",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
edited_at: z
|
||||||
|
.string()
|
||||||
|
.datetime()
|
||||||
|
.nullable()
|
||||||
|
.openapi({
|
||||||
|
description: "Timestamp of when the status was last edited.",
|
||||||
|
example: "2025-01-07T14:11:00.000Z",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#edited_at",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
emojis: z.array(Emoji.schema).openapi({
|
||||||
|
description: "Custom emoji to be used when rendering status content.",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#emojis",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
replies_count: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.nonnegative()
|
||||||
|
.openapi({
|
||||||
|
description: "How many replies this status has received.",
|
||||||
|
example: 1,
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#replies_count",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
reblogs_count: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.nonnegative()
|
||||||
|
.openapi({
|
||||||
|
description: "How many boosts this status has received.",
|
||||||
|
example: 6,
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#reblogs_count",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
favourites_count: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.nonnegative()
|
||||||
|
.openapi({
|
||||||
|
description: "How many favourites this status has received.",
|
||||||
|
example: 11,
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#favourites_count",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
reblogged: zBoolean.optional().openapi({
|
||||||
|
description:
|
||||||
|
"If the current token has an authorized user: Have you boosted this status?",
|
||||||
|
example: false,
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#reblogged",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
favourited: zBoolean.optional().openapi({
|
||||||
|
description:
|
||||||
|
"If the current token has an authorized user: Have you favourited this status?",
|
||||||
|
example: true,
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#favourited",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
muted: zBoolean.optional().openapi({
|
||||||
|
description:
|
||||||
|
"If the current token has an authorized user: Have you muted notifications for this status’s conversation?",
|
||||||
|
example: false,
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#muted",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
sensitive: zBoolean.openapi({
|
||||||
|
description: "Is this status marked as sensitive content?",
|
||||||
|
example: false,
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#sensitive",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
spoiler_text: z.string().openapi({
|
||||||
|
description:
|
||||||
|
"Subject or summary line, below which status content is collapsed until expanded.",
|
||||||
|
example: "lewd text",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#spoiler_text",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
visibility: z.enum(["public", "unlisted", "private", "direct"]).openapi({
|
||||||
|
description: "Visibility of this status.",
|
||||||
|
example: "public",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#visibility",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
media_attachments: z.array(Media.schema).openapi({
|
||||||
|
description: "Media that is attached to this status.",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#media_attachments",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
mentions: z.array(Mention).openapi({
|
||||||
|
description: "Mentions of users within the status content.",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#mentions",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
tags: z.array(Tag).openapi({
|
||||||
|
description: "Hashtags used within the status content.",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#tags",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
card: PreviewCard.nullable().openapi({
|
||||||
|
description: "Preview card for links included within status content.",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#card",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
poll: Poll.nullable().openapi({
|
||||||
|
description: "The poll attached to the status.",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#poll",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
application: z
|
||||||
|
.object({
|
||||||
|
name: z.string().openapi({
|
||||||
|
description:
|
||||||
|
"The name of the application that posted this status.",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#application-name",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
website: z
|
||||||
|
.string()
|
||||||
|
.url()
|
||||||
|
.nullable()
|
||||||
|
.openapi({
|
||||||
|
description:
|
||||||
|
"The website associated with the application that posted this status.",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#application-website",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
.openapi({
|
||||||
|
description: "The application used to post this status.",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#application",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
language: z
|
||||||
|
.enum(ISO6391.getAllCodes() as [string, ...string[]])
|
||||||
|
.nullable()
|
||||||
|
.openapi({
|
||||||
|
description: "Primary language of this status.",
|
||||||
|
example: "en",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#language",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
text: z
|
||||||
|
.string()
|
||||||
|
.nullable()
|
||||||
|
.openapi({
|
||||||
|
description:
|
||||||
|
"Plain-text source of a status. Returned instead of content when status is deleted, so the user may redraft from the source text without the client having to reverse-engineer the original text from the HTML content.",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#text",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
pinned: zBoolean.optional().openapi({
|
||||||
|
description:
|
||||||
|
"If the current token has an authorized user: Have you pinned this status? Only appears if the status is pinnable.",
|
||||||
|
example: true,
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#pinned",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
emoji_reactions: z.array(
|
||||||
|
z.object({
|
||||||
|
count: z.number().int().nonnegative(),
|
||||||
|
me: zBoolean,
|
||||||
|
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((): z.ZodType<ApiNote> => Status as z.ZodType<ApiNote>)
|
||||||
|
.nullable(),
|
||||||
|
bookmarked: zBoolean.optional().openapi({
|
||||||
|
description:
|
||||||
|
"If the current token has an authorized user: Have you bookmarked this status?",
|
||||||
|
example: false,
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#bookmarked",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
filtered: z
|
||||||
|
.array(FilterResult)
|
||||||
|
.optional()
|
||||||
|
.openapi({
|
||||||
|
description:
|
||||||
|
"If the current token has an authorized user: The filter and keywords that matched this status.",
|
||||||
|
externalDocs: {
|
||||||
|
url: "https://docs.joinmastodon.org/entities/Status/#filtered",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
@ -156,7 +156,7 @@ describe("API Tests", () => {
|
||||||
expect(statusJson.created_at).toBeDefined();
|
expect(statusJson.created_at).toBeDefined();
|
||||||
expect(statusJson.account).toBeDefined();
|
expect(statusJson.account).toBeDefined();
|
||||||
expect(statusJson.reblog).toBeDefined();
|
expect(statusJson.reblog).toBeDefined();
|
||||||
expect(statusJson.application).toBeDefined();
|
expect(statusJson.application).toBeUndefined();
|
||||||
expect(statusJson.emojis).toBeDefined();
|
expect(statusJson.emojis).toBeDefined();
|
||||||
expect(statusJson.media_attachments).toBeDefined();
|
expect(statusJson.media_attachments).toBeDefined();
|
||||||
expect(statusJson.poll).toBeDefined();
|
expect(statusJson.poll).toBeDefined();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue