Add more API endpoints, better tests

This commit is contained in:
Jesse Wierzbinski 2023-09-21 12:42:59 -10:00
parent dacbc5a00b
commit 6d2f9072ac
15 changed files with 628 additions and 153 deletions

View file

@ -65,9 +65,10 @@ export default async (
return object;
}
const activity = await RawActivity.addIfNotExists(body);
const activity = await RawActivity.addIfNotExists(body, object);
if (activity instanceof Response) {
console.log(await activity.text());
return activity;
}
@ -78,7 +79,13 @@ export default async (
// Delete the object from database
// TODO: Add authentication
await RawActivity.deleteObjectIfExists(body.object as APObject);
const response = await RawActivity.deleteObjectIfExists(
body.object as APObject
);
if (response instanceof Response) {
return response;
}
// Store the Delete event in the database
const activity = await RawActivity.addIfNotExists(body);

View file

@ -0,0 +1,116 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { getUserByToken } from "@auth";
import { getConfig } from "@config";
import { parseRequest } from "@request";
import { errorResponse, jsonResponse } from "@response";
import { Application } from "~database/entities/Application";
import { Status } from "~database/entities/Status";
/**
* Post new status
*/
export default async (req: Request): Promise<Response> => {
// Check if request is a PATCH request
if (req.method !== "POST")
return errorResponse("This method requires a POST request", 405);
// Check auth token
const token = req.headers.get("Authorization")?.split(" ")[1] || null;
if (!token)
return errorResponse("This method requires an authenticated user", 422);
const user = await getUserByToken(token);
const application = await Application.getFromToken(token);
if (!user) return errorResponse("Unauthorized", 401);
const config = getConfig();
const {
status,
media_ids,
"poll[expires_in]": expires_in,
"poll[hide_totals]": hide_totals,
"poll[multiple]": multiple,
"poll[options]": options,
in_reply_to_id,
language,
scheduled_at,
sensitive,
spoiler_text,
visibility,
} = await parseRequest<{
status: string;
media_ids?: string[];
"poll[options]"?: string[];
"poll[expires_in]"?: number;
"poll[multiple]"?: boolean;
"poll[hide_totals]"?: boolean;
in_reply_to_id?: string;
sensitive?: boolean;
spoiler_text?: string;
visibility?: "public" | "unlisted" | "private" | "direct";
language?: string;
scheduled_at?: string;
}>(req);
// Validate status
if (!status) {
return errorResponse("Status is required", 422);
}
if (status.length > config.validation.max_note_size) {
return errorResponse(
`Status must be less than ${config.validation.max_note_size} characters`,
400
);
}
// Validate media_ids
if (media_ids && !Array.isArray(media_ids)) {
return errorResponse("Media IDs must be an array", 422);
}
// Validate poll options
if (options && !Array.isArray(options)) {
return errorResponse("Poll options must be an array", 422);
}
if (options && options.length > 4) {
return errorResponse("Poll options must be less than 5", 422);
}
// Validate poll expires_in
if (expires_in && (expires_in < 60 || expires_in > 604800)) {
return errorResponse(
"Poll expires_in must be between 60 and 604800",
422
);
}
// Validate visibility
if (
visibility &&
!["public", "unlisted", "private", "direct"].includes(visibility)
) {
return errorResponse("Invalid visibility", 422);
}
// Create status
const newStatus = new Status();
newStatus.account = user;
newStatus.application = application;
newStatus.content = status;
newStatus.spoiler_text = spoiler_text || "";
newStatus.sensitive = sensitive || false;
newStatus.visibility = visibility || "public";
newStatus.likes = [];
newStatus.isReblog = false;
// TODO: add database jobs to deliver the post
await newStatus.save();
return jsonResponse(newStatus);
};