mirror of
https://github.com/versia-pub/server.git
synced 2026-03-13 13:59:16 +01:00
refactor(api): ♻️ Make SDK and client package only use resources in their own package
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 1s
Build Docker Images / lint (push) Failing after 7s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 6s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 1s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 1s
Build Docker Images / lint (push) Failing after 7s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 6s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 1s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
This commit is contained in:
parent
c0060f1baf
commit
5dfcfc548f
25 changed files with 256 additions and 260 deletions
|
|
@ -52,17 +52,56 @@ export default apiRoute((app) =>
|
|||
"json",
|
||||
z
|
||||
.object({
|
||||
display_name: AccountSchema.shape.display_name.openapi({
|
||||
description: "The display name to use for the profile.",
|
||||
example: "Lexi",
|
||||
}),
|
||||
username: AccountSchema.shape.username.openapi({
|
||||
description: "The username to use for the profile.",
|
||||
example: "lexi",
|
||||
}),
|
||||
note: AccountSchema.shape.note.openapi({
|
||||
description: "The account bio. Markdown is supported.",
|
||||
}),
|
||||
display_name: AccountSchema.shape.display_name
|
||||
.openapi({
|
||||
description:
|
||||
"The display name to use for the profile.",
|
||||
example: "Lexi",
|
||||
})
|
||||
.max(
|
||||
config.validation.accounts
|
||||
.max_displayname_characters,
|
||||
)
|
||||
.refine(
|
||||
(s) =>
|
||||
!config.validation.filters.displayname.some(
|
||||
(filter) => filter.test(s),
|
||||
),
|
||||
"Display name contains blocked words",
|
||||
),
|
||||
username: AccountSchema.shape.username
|
||||
.openapi({
|
||||
description: "The username to use for the profile.",
|
||||
example: "lexi",
|
||||
})
|
||||
.max(config.validation.accounts.max_username_characters)
|
||||
.refine(
|
||||
(s) =>
|
||||
!config.validation.filters.username.some(
|
||||
(filter) => filter.test(s),
|
||||
),
|
||||
"Username contains blocked words",
|
||||
)
|
||||
.refine(
|
||||
(s) =>
|
||||
!config.validation.accounts.disallowed_usernames.some(
|
||||
(u) => u.test(s),
|
||||
),
|
||||
"Username is disallowed",
|
||||
),
|
||||
note: AccountSchema.shape.note
|
||||
.openapi({
|
||||
description:
|
||||
"The account bio. Markdown is supported.",
|
||||
})
|
||||
.max(config.validation.accounts.max_bio_characters)
|
||||
.refine(
|
||||
(s) =>
|
||||
!config.validation.filters.bio.some((filter) =>
|
||||
filter.test(s),
|
||||
),
|
||||
"Bio contains blocked words",
|
||||
),
|
||||
avatar: z
|
||||
.string()
|
||||
.url()
|
||||
|
|
@ -150,10 +189,14 @@ export default apiRoute((app) =>
|
|||
fields_attributes: z
|
||||
.array(
|
||||
z.object({
|
||||
name: AccountSchema.shape.fields.element.shape
|
||||
.name,
|
||||
value: AccountSchema.shape.fields.element.shape
|
||||
.value,
|
||||
name: AccountSchema.shape.fields.element.shape.name.max(
|
||||
config.validation.accounts
|
||||
.max_field_name_characters,
|
||||
),
|
||||
value: AccountSchema.shape.fields.element.shape.value.max(
|
||||
config.validation.accounts
|
||||
.max_field_value_characters,
|
||||
),
|
||||
}),
|
||||
)
|
||||
.max(config.validation.accounts.max_field_count),
|
||||
|
|
|
|||
|
|
@ -113,7 +113,9 @@ export default apiRoute((app) => {
|
|||
"json",
|
||||
z
|
||||
.object({
|
||||
shortcode: CustomEmojiSchema.shape.shortcode,
|
||||
shortcode: CustomEmojiSchema.shape.shortcode.max(
|
||||
config.validation.emojis.max_shortcode_characters,
|
||||
),
|
||||
element: z
|
||||
.string()
|
||||
.url()
|
||||
|
|
@ -136,7 +138,12 @@ export default apiRoute((app) => {
|
|||
),
|
||||
),
|
||||
category: CustomEmojiSchema.shape.category.optional(),
|
||||
alt: CustomEmojiSchema.shape.description.optional(),
|
||||
alt: CustomEmojiSchema.shape.description
|
||||
.unwrap()
|
||||
.max(
|
||||
config.validation.emojis.max_description_characters,
|
||||
)
|
||||
.optional(),
|
||||
global: CustomEmojiSchema.shape.global.default(false),
|
||||
})
|
||||
.partial(),
|
||||
|
|
|
|||
|
|
@ -45,7 +45,9 @@ export default apiRoute((app) =>
|
|||
validator(
|
||||
"json",
|
||||
z.object({
|
||||
shortcode: CustomEmojiSchema.shape.shortcode,
|
||||
shortcode: CustomEmojiSchema.shape.shortcode.max(
|
||||
config.validation.emojis.max_shortcode_characters,
|
||||
),
|
||||
element: z
|
||||
.string()
|
||||
.url()
|
||||
|
|
@ -68,7 +70,10 @@ export default apiRoute((app) =>
|
|||
),
|
||||
),
|
||||
category: CustomEmojiSchema.shape.category.optional(),
|
||||
alt: CustomEmojiSchema.shape.description.optional(),
|
||||
alt: CustomEmojiSchema.shape.description
|
||||
.unwrap()
|
||||
.max(config.validation.emojis.max_description_characters)
|
||||
.optional(),
|
||||
global: CustomEmojiSchema.shape.global.default(false),
|
||||
}),
|
||||
handleZodError,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { resolver, validator } from "hono-openapi/zod";
|
|||
import { z } from "zod";
|
||||
import { apiRoute, auth, handleZodError } from "@/api";
|
||||
import { ApiError } from "~/classes/errors/api-error";
|
||||
import { config } from "~/config";
|
||||
|
||||
export default apiRoute((app) => {
|
||||
app.get(
|
||||
|
|
@ -109,7 +110,10 @@ export default apiRoute((app) => {
|
|||
description:
|
||||
"The custom thumbnail of the media to be attached, encoded using multipart form data.",
|
||||
}),
|
||||
description: AttachmentSchema.shape.description,
|
||||
description: AttachmentSchema.shape.description
|
||||
.unwrap()
|
||||
.max(config.validation.media.max_description_characters)
|
||||
.optional(),
|
||||
focus: z.string().openapi({
|
||||
description:
|
||||
"Two floating points (x,y), comma-delimited, ranging from -1.0 to 1.0. Used for media cropping on clients.",
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { resolver, validator } from "hono-openapi/zod";
|
|||
import { z } from "zod";
|
||||
import { apiRoute, auth, handleZodError } from "@/api";
|
||||
import { ApiError } from "~/classes/errors/api-error";
|
||||
import { config } from "~/config";
|
||||
|
||||
export default apiRoute((app) =>
|
||||
app.post(
|
||||
|
|
@ -67,7 +68,10 @@ export default apiRoute((app) =>
|
|||
description:
|
||||
"The custom thumbnail of the media to be attached, encoded using multipart form data.",
|
||||
}),
|
||||
description: AttachmentSchema.shape.description.optional(),
|
||||
description: AttachmentSchema.shape.description
|
||||
.unwrap()
|
||||
.max(config.validation.media.max_description_characters)
|
||||
.optional(),
|
||||
focus: z
|
||||
.string()
|
||||
.optional()
|
||||
|
|
|
|||
|
|
@ -25,10 +25,20 @@ import * as VersiaEntities from "~/packages/sdk/entities";
|
|||
|
||||
const schema = z
|
||||
.object({
|
||||
status: StatusSourceSchema.shape.text.optional().openapi({
|
||||
description:
|
||||
"The text content of the status. If media_ids is provided, this becomes optional. Attaching a poll is optional while status is provided.",
|
||||
}),
|
||||
status: StatusSourceSchema.shape.text
|
||||
.max(config.validation.notes.max_characters)
|
||||
.refine(
|
||||
(s) =>
|
||||
!config.validation.filters.note_content.some((filter) =>
|
||||
filter.test(s),
|
||||
),
|
||||
"Status contains blocked words",
|
||||
)
|
||||
.optional()
|
||||
.openapi({
|
||||
description:
|
||||
"The text content of the status. If media_ids is provided, this becomes optional. Attaching a poll is optional while status is provided.",
|
||||
}),
|
||||
/* Versia Server API Extension */
|
||||
content_type: z
|
||||
.enum(["text/plain", "text/html", "text/markdown"])
|
||||
|
|
@ -54,7 +64,11 @@ const schema = z
|
|||
}),
|
||||
language: StatusSchema.shape.language.optional(),
|
||||
"poll[options]": z
|
||||
.array(PollOption.shape.title)
|
||||
.array(
|
||||
PollOption.shape.title.max(
|
||||
config.validation.polls.max_option_characters,
|
||||
),
|
||||
)
|
||||
.max(config.validation.polls.max_options)
|
||||
.optional()
|
||||
.openapi({
|
||||
|
|
|
|||
|
|
@ -20,10 +20,20 @@ import * as VersiaEntities from "~/packages/sdk/entities";
|
|||
|
||||
const schema = z
|
||||
.object({
|
||||
status: StatusSourceSchema.shape.text.optional().openapi({
|
||||
description:
|
||||
"The text content of the status. If media_ids is provided, this becomes optional. Attaching a poll is optional while status is provided.",
|
||||
}),
|
||||
status: StatusSourceSchema.shape.text
|
||||
.max(config.validation.notes.max_characters)
|
||||
.refine(
|
||||
(s) =>
|
||||
!config.validation.filters.note_content.some((filter) =>
|
||||
filter.test(s),
|
||||
),
|
||||
"Status contains blocked words",
|
||||
)
|
||||
.optional()
|
||||
.openapi({
|
||||
description:
|
||||
"The text content of the status. If media_ids is provided, this becomes optional. Attaching a poll is optional while status is provided.",
|
||||
}),
|
||||
/* Versia Server API Extension */
|
||||
content_type: z
|
||||
.enum(["text/plain", "text/html", "text/markdown"])
|
||||
|
|
@ -49,7 +59,11 @@ const schema = z
|
|||
}),
|
||||
language: StatusSchema.shape.language.optional(),
|
||||
"poll[options]": z
|
||||
.array(PollOption.shape.title)
|
||||
.array(
|
||||
PollOption.shape.title.max(
|
||||
config.validation.polls.max_option_characters,
|
||||
),
|
||||
)
|
||||
.max(config.validation.polls.max_options)
|
||||
.optional()
|
||||
.openapi({
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { resolver, validator } from "hono-openapi/zod";
|
|||
import { z } from "zod";
|
||||
import { apiRoute, auth, handleZodError } from "@/api";
|
||||
import { ApiError } from "~/classes/errors/api-error";
|
||||
import { config } from "~/config";
|
||||
|
||||
export default apiRoute((app) =>
|
||||
app.post(
|
||||
|
|
@ -76,7 +77,10 @@ export default apiRoute((app) =>
|
|||
description:
|
||||
"The custom thumbnail of the media to be attached, encoded using multipart form data.",
|
||||
}),
|
||||
description: AttachmentSchema.shape.description.optional(),
|
||||
description: AttachmentSchema.shape.description
|
||||
.unwrap()
|
||||
.max(config.validation.media.max_description_characters)
|
||||
.optional(),
|
||||
focus: z
|
||||
.string()
|
||||
.optional()
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import {
|
|||
Id,
|
||||
RolePermission,
|
||||
Search as SearchSchema,
|
||||
userAddressRegex,
|
||||
zBoolean,
|
||||
} from "@versia/client/schemas";
|
||||
import { db, Note, User } from "@versia/kit/db";
|
||||
|
|
@ -11,13 +12,7 @@ import { and, eq, inArray, isNull, sql } from "drizzle-orm";
|
|||
import { describeRoute } from "hono-openapi";
|
||||
import { resolver, validator } from "hono-openapi/zod";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
apiRoute,
|
||||
auth,
|
||||
handleZodError,
|
||||
parseUserAddress,
|
||||
userAddressValidator,
|
||||
} from "@/api";
|
||||
import { apiRoute, auth, handleZodError, parseUserAddress } from "@/api";
|
||||
import { ApiError } from "~/classes/errors/api-error";
|
||||
import { searchManager } from "~/classes/search/search-manager";
|
||||
import { config } from "~/config.ts";
|
||||
|
|
@ -151,7 +146,7 @@ export default apiRoute((app) =>
|
|||
|
||||
if (!type || type === "accounts") {
|
||||
// Check if q is matching format username@domain.com or @username@domain.com
|
||||
const accountMatches = q?.trim().match(userAddressValidator);
|
||||
const accountMatches = q?.trim().match(userAddressRegex);
|
||||
if (accountMatches) {
|
||||
// Remove leading @ if it exists
|
||||
if (accountMatches[0].startsWith("@")) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue