mirror of
https://github.com/versia-pub/server.git
synced 2026-03-13 13:59:16 +01:00
Add media attachment functionality to posts
This commit is contained in:
parent
c66e1ac146
commit
28a16e95a4
5 changed files with 211 additions and 44 deletions
|
|
@ -78,26 +78,10 @@ export default async (
|
|||
}>(req);
|
||||
|
||||
// Validate status
|
||||
if (!status) {
|
||||
return errorResponse("Status is required", 422);
|
||||
}
|
||||
|
||||
let sanitizedStatus: string;
|
||||
|
||||
if (content_type === "text/markdown") {
|
||||
sanitizedStatus = await sanitizeHtml(parse(status));
|
||||
} else if (content_type === "text/x.misskeymarkdown") {
|
||||
// Parse as MFM
|
||||
// TODO: Parse as MFM
|
||||
sanitizedStatus = await sanitizeHtml(parse(status));
|
||||
} else {
|
||||
sanitizedStatus = await sanitizeHtml(status);
|
||||
}
|
||||
|
||||
if (sanitizedStatus.length > config.validation.max_note_size) {
|
||||
if (!status && !(media_ids && media_ids.length > 0)) {
|
||||
return errorResponse(
|
||||
`Status must be less than ${config.validation.max_note_size} characters`,
|
||||
400
|
||||
"Status is required unless media is attached",
|
||||
422
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -115,14 +99,74 @@ export default async (
|
|||
return errorResponse("Poll options must be less than 5", 422);
|
||||
}
|
||||
|
||||
// Validate poll expires_in
|
||||
if (expires_in && (expires_in < 60 || expires_in > 604800)) {
|
||||
if (media_ids && media_ids.length > 0) {
|
||||
// Disallow poll
|
||||
if (options) {
|
||||
return errorResponse("Cannot attach poll to media", 422);
|
||||
}
|
||||
if (media_ids.length > 4) {
|
||||
return errorResponse("Media IDs must be less than 5", 422);
|
||||
}
|
||||
}
|
||||
|
||||
if (options && options.length > config.validation.max_poll_options) {
|
||||
return errorResponse(
|
||||
"Poll expires_in must be between 60 and 604800",
|
||||
`Poll options must be less than ${config.validation.max_poll_options}`,
|
||||
422
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
options &&
|
||||
options.some(
|
||||
option => option.length > config.validation.max_poll_option_size
|
||||
)
|
||||
) {
|
||||
return errorResponse(
|
||||
`Poll options must be less than ${config.validation.max_poll_option_size} characters`,
|
||||
422
|
||||
);
|
||||
}
|
||||
|
||||
if (expires_in && expires_in < config.validation.min_poll_duration) {
|
||||
return errorResponse(
|
||||
`Poll duration must be greater than ${config.validation.min_poll_duration} seconds`,
|
||||
422
|
||||
);
|
||||
}
|
||||
|
||||
if (expires_in && expires_in > config.validation.max_poll_duration) {
|
||||
return errorResponse(
|
||||
`Poll duration must be less than ${config.validation.max_poll_duration} seconds`,
|
||||
422
|
||||
);
|
||||
}
|
||||
|
||||
if (scheduled_at) {
|
||||
if (new Date(scheduled_at).getTime() < Date.now()) {
|
||||
return errorResponse("Scheduled time must be in the future", 422);
|
||||
}
|
||||
}
|
||||
|
||||
let sanitizedStatus: string;
|
||||
|
||||
if (content_type === "text/markdown") {
|
||||
sanitizedStatus = await sanitizeHtml(parse(status ?? ""));
|
||||
} else if (content_type === "text/x.misskeymarkdown") {
|
||||
// Parse as MFM
|
||||
// TODO: Parse as MFM
|
||||
sanitizedStatus = await sanitizeHtml(parse(status ?? ""));
|
||||
} else {
|
||||
sanitizedStatus = await sanitizeHtml(status ?? "");
|
||||
}
|
||||
|
||||
if (sanitizedStatus.length > config.validation.max_note_size) {
|
||||
return errorResponse(
|
||||
`Status must be less than ${config.validation.max_note_size} characters`,
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
// Validate visibility
|
||||
if (
|
||||
visibility &&
|
||||
|
|
@ -145,10 +189,24 @@ export default async (
|
|||
}
|
||||
|
||||
// Check if status body doesnt match filters
|
||||
if (config.filters.note_filters.some(filter => status.match(filter))) {
|
||||
if (config.filters.note_filters.some(filter => status?.match(filter))) {
|
||||
return errorResponse("Status contains blocked words", 422);
|
||||
}
|
||||
|
||||
// Check if media attachments are all valid
|
||||
|
||||
const foundAttachments = await client.attachment.findMany({
|
||||
where: {
|
||||
id: {
|
||||
in: media_ids ?? [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (foundAttachments.length !== (media_ids ?? []).length) {
|
||||
return errorResponse("Invalid media IDs", 422);
|
||||
}
|
||||
|
||||
const newStatus = await createNewStatus({
|
||||
account: user,
|
||||
application,
|
||||
|
|
@ -163,6 +221,7 @@ export default async (
|
|||
sensitive: sensitive || false,
|
||||
spoiler_text: spoiler_text || "",
|
||||
emojis: [],
|
||||
media_attachments: media_ids,
|
||||
reply:
|
||||
replyStatus && replyUser
|
||||
? {
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export default async (req: Request): Promise<Response> => {
|
|||
|
||||
const file = form.get("file") as unknown as File | undefined;
|
||||
const thumbnail = form.get("thumbnail");
|
||||
const description = form.get("description");
|
||||
const description = form.get("description") as string | undefined;
|
||||
|
||||
// Floating point numbers from -1.0 to 1.0, comma delimited
|
||||
// const focus = form.get("focus");
|
||||
|
|
@ -45,6 +45,29 @@ export default async (req: Request): Promise<Response> => {
|
|||
return errorResponse("No file provided", 400);
|
||||
}
|
||||
|
||||
const config = getConfig();
|
||||
|
||||
if (file.size > config.validation.max_media_size) {
|
||||
return errorResponse(
|
||||
`File too large, max size is ${config.validation.max_media_size} bytes`,
|
||||
413
|
||||
);
|
||||
}
|
||||
|
||||
if (!config.validation.allowed_mime_types.includes(file.type)) {
|
||||
return errorResponse("Invalid file type", 415);
|
||||
}
|
||||
|
||||
if (
|
||||
description &&
|
||||
description.length > config.validation.max_media_description_size
|
||||
) {
|
||||
return errorResponse(
|
||||
`Description too long, max length is ${config.validation.max_media_description_size} characters`,
|
||||
413
|
||||
);
|
||||
}
|
||||
|
||||
const sha256 = new Bun.SHA256();
|
||||
|
||||
const isImage = file.type.startsWith("image/");
|
||||
|
|
@ -65,8 +88,6 @@ export default async (req: Request): Promise<Response> => {
|
|||
|
||||
let url = "";
|
||||
|
||||
const config = getConfig();
|
||||
|
||||
if (isImage) {
|
||||
const hash = await uploadFile(file, config);
|
||||
|
||||
|
|
@ -87,7 +108,7 @@ export default async (req: Request): Promise<Response> => {
|
|||
thumbnail_url: thumbnailUrl,
|
||||
sha256: sha256.update(await file.arrayBuffer()).digest("hex"),
|
||||
mime_type: file.type,
|
||||
description: (description as string | undefined) ?? "",
|
||||
description: description ?? "",
|
||||
size: file.size,
|
||||
blurhash: blurhash ?? undefined,
|
||||
width: metadata?.width ?? undefined,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue