Rewrite every route to use the new framework

This commit is contained in:
Jesse Wierzbinski 2024-03-10 13:25:44 -10:00
parent 05140f0d6f
commit 852efaea50
No known key found for this signature in database
51 changed files with 413 additions and 707 deletions

View file

@ -1,14 +1,10 @@
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { import {
createNewRelationship, createNewRelationship,
relationshipToAPI, relationshipToAPI,
} from "~database/entities/Relationship"; } from "~database/entities/Relationship";
import { import { getRelationshipToOtherUser } from "~database/entities/User";
getFromRequest, import { apiRoute, applyConfig } from "@api";
getRelationshipToOtherUser,
} from "~database/entities/User";
import { applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
export const meta = applyConfig({ export const meta = applyConfig({
@ -26,13 +22,10 @@ export const meta = applyConfig({
/** /**
* Blocks a user * Blocks a user
*/ */
export default async ( export default apiRoute(async (req, matchedRoute, extraData) => {
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const id = matchedRoute.params.id; const id = matchedRoute.params.id;
const { user: self } = await getFromRequest(req); const { user: self } = extraData.auth;
if (!self) return errorResponse("Unauthorized", 401); if (!self) return errorResponse("Unauthorized", 401);
@ -84,4 +77,4 @@ export default async (
}); });
return jsonResponse(relationshipToAPI(relationship)); return jsonResponse(relationshipToAPI(relationship));
}; });

View file

@ -1,15 +1,10 @@
import { parseRequest } from "@request";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { import {
createNewRelationship, createNewRelationship,
relationshipToAPI, relationshipToAPI,
} from "~database/entities/Relationship"; } from "~database/entities/Relationship";
import { import { getRelationshipToOtherUser } from "~database/entities/User";
getFromRequest, import { apiRoute, applyConfig } from "@api";
getRelationshipToOtherUser,
} from "~database/entities/User";
import { applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
export const meta = applyConfig({ export const meta = applyConfig({
@ -27,21 +22,18 @@ export const meta = applyConfig({
/** /**
* Follow a user * Follow a user
*/ */
export default async ( export default apiRoute<{
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const id = matchedRoute.params.id;
const { user: self } = await getFromRequest(req);
if (!self) return errorResponse("Unauthorized", 401);
const { languages, notify, reblogs } = await parseRequest<{
reblogs?: boolean; reblogs?: boolean;
notify?: boolean; notify?: boolean;
languages?: string[]; languages?: string[];
}>(req); }>(async (req, matchedRoute, extraData) => {
const id = matchedRoute.params.id;
const { user: self } = extraData.auth;
if (!self) return errorResponse("Unauthorized", 401);
const { languages, notify, reblogs } = extraData.parsedRequest;
const user = await client.user.findUnique({ const user = await client.user.findUnique({
where: { id }, where: { id },
@ -103,4 +95,4 @@ export default async (
}); });
return jsonResponse(relationshipToAPI(relationship)); return jsonResponse(relationshipToAPI(relationship));
}; });

View file

@ -1,8 +1,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { userRelations, userToAPI } from "~database/entities/User"; import { userRelations, userToAPI } from "~database/entities/User";
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
export const meta = applyConfig({ export const meta = applyConfig({
@ -20,24 +19,16 @@ export const meta = applyConfig({
/** /**
* Fetch all statuses for a user * Fetch all statuses for a user
*/ */
export default async ( export default apiRoute<{
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const id = matchedRoute.params.id;
// TODO: Add pinned
const {
max_id,
min_id,
since_id,
limit = 20,
}: {
max_id?: string; max_id?: string;
since_id?: string; since_id?: string;
min_id?: string; min_id?: string;
limit?: number; limit?: number;
} = matchedRoute.query; }>(async (req, matchedRoute, extraData) => {
const id = matchedRoute.params.id;
// TODO: Add pinned
const { max_id, min_id, since_id, limit = 20 } = extraData.parsedRequest;
const user = await client.user.findUnique({ const user = await client.user.findUnique({
where: { id }, where: { id },
@ -86,4 +77,4 @@ export default async (
Link: linkHeader.join(", "), Link: linkHeader.join(", "),
} }
); );
}; });

View file

@ -1,8 +1,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { userRelations, userToAPI } from "~database/entities/User"; import { userRelations, userToAPI } from "~database/entities/User";
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
export const meta = applyConfig({ export const meta = applyConfig({
@ -20,24 +19,16 @@ export const meta = applyConfig({
/** /**
* Fetch all statuses for a user * Fetch all statuses for a user
*/ */
export default async ( export default apiRoute<{
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const id = matchedRoute.params.id;
// TODO: Add pinned
const {
max_id,
min_id,
since_id,
limit = 20,
}: {
max_id?: string; max_id?: string;
since_id?: string; since_id?: string;
min_id?: string; min_id?: string;
limit?: number; limit?: number;
} = matchedRoute.query; }>(async (req, matchedRoute, extraData) => {
const id = matchedRoute.params.id;
// TODO: Add pinned
const { max_id, min_id, since_id, limit = 20 } = extraData.parsedRequest;
const user = await client.user.findUnique({ const user = await client.user.findUnique({
where: { id }, where: { id },
@ -86,4 +77,4 @@ export default async (
Link: linkHeader.join(", "), Link: linkHeader.join(", "),
} }
); );
}; });

View file

@ -1,12 +1,7 @@
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import type { UserWithRelations } from "~database/entities/User"; import type { UserWithRelations } from "~database/entities/User";
import { import { userRelations, userToAPI } from "~database/entities/User";
getFromRequest, import { apiRoute, applyConfig } from "@api";
userRelations,
userToAPI,
} from "~database/entities/User";
import { applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
export const meta = applyConfig({ export const meta = applyConfig({
@ -24,17 +19,14 @@ export const meta = applyConfig({
/** /**
* Fetch a user * Fetch a user
*/ */
export default async ( export default apiRoute(async (req, matchedRoute, extraData) => {
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const id = matchedRoute.params.id; const id = matchedRoute.params.id;
// Check if ID is valid UUID // Check if ID is valid UUID
if (!id.match(/^[0-9a-fA-F]{24}$/)) { if (!id.match(/^[0-9a-fA-F]{24}$/)) {
return errorResponse("Invalid ID", 404); return errorResponse("Invalid ID", 404);
} }
const { user } = await getFromRequest(req); const { user } = extraData.auth;
let foundUser: UserWithRelations | null; let foundUser: UserWithRelations | null;
try { try {
@ -49,4 +41,4 @@ export default async (
if (!foundUser) return errorResponse("User not found", 404); if (!foundUser) return errorResponse("User not found", 404);
return jsonResponse(userToAPI(foundUser, user?.id === foundUser.id)); return jsonResponse(userToAPI(foundUser, user?.id === foundUser.id));
}; });

View file

@ -1,15 +1,10 @@
import { parseRequest } from "@request";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { import {
createNewRelationship, createNewRelationship,
relationshipToAPI, relationshipToAPI,
} from "~database/entities/Relationship"; } from "~database/entities/Relationship";
import { import { getRelationshipToOtherUser } from "~database/entities/User";
getFromRequest, import { apiRoute, applyConfig } from "@api";
getRelationshipToOtherUser,
} from "~database/entities/User";
import { applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
export const meta = applyConfig({ export const meta = applyConfig({
@ -27,21 +22,18 @@ export const meta = applyConfig({
/** /**
* Mute a user * Mute a user
*/ */
export default async ( export default apiRoute<{
req: Request, notifications: boolean;
matchedRoute: MatchedRoute duration: number;
): Promise<Response> => { }>(async (req, matchedRoute, extraData) => {
const id = matchedRoute.params.id; const id = matchedRoute.params.id;
const { user: self } = await getFromRequest(req); const { user: self } = extraData.auth;
if (!self) return errorResponse("Unauthorized", 401); if (!self) return errorResponse("Unauthorized", 401);
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
const { notifications, duration } = await parseRequest<{ const { notifications, duration } = extraData.parsedRequest;
notifications: boolean;
duration: number;
}>(req);
const user = await client.user.findUnique({ const user = await client.user.findUnique({
where: { id }, where: { id },
@ -97,4 +89,4 @@ export default async (
// TODO: Implement duration // TODO: Implement duration
return jsonResponse(relationshipToAPI(relationship)); return jsonResponse(relationshipToAPI(relationship));
}; });

View file

@ -1,15 +1,10 @@
import { parseRequest } from "@request";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { import {
createNewRelationship, createNewRelationship,
relationshipToAPI, relationshipToAPI,
} from "~database/entities/Relationship"; } from "~database/entities/Relationship";
import { import { getRelationshipToOtherUser } from "~database/entities/User";
getFromRequest, import { apiRoute, applyConfig } from "@api";
getRelationshipToOtherUser,
} from "~database/entities/User";
import { applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
export const meta = applyConfig({ export const meta = applyConfig({
@ -27,19 +22,16 @@ export const meta = applyConfig({
/** /**
* Sets a user note * Sets a user note
*/ */
export default async ( export default apiRoute<{
req: Request, comment: string;
matchedRoute: MatchedRoute }>(async (req, matchedRoute, extraData) => {
): Promise<Response> => {
const id = matchedRoute.params.id; const id = matchedRoute.params.id;
const { user: self } = await getFromRequest(req); const { user: self } = extraData.auth;
if (!self) return errorResponse("Unauthorized", 401); if (!self) return errorResponse("Unauthorized", 401);
const { comment } = await parseRequest<{ const { comment } = extraData.parsedRequest;
comment: string;
}>(req);
const user = await client.user.findUnique({ const user = await client.user.findUnique({
where: { id }, where: { id },
@ -87,4 +79,4 @@ export default async (
}); });
return jsonResponse(relationshipToAPI(relationship)); return jsonResponse(relationshipToAPI(relationship));
}; });

View file

@ -1,14 +1,10 @@
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { import {
createNewRelationship, createNewRelationship,
relationshipToAPI, relationshipToAPI,
} from "~database/entities/Relationship"; } from "~database/entities/Relationship";
import { import { getRelationshipToOtherUser } from "~database/entities/User";
getFromRequest, import { apiRoute, applyConfig } from "@api";
getRelationshipToOtherUser,
} from "~database/entities/User";
import { applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
export const meta = applyConfig({ export const meta = applyConfig({
@ -26,13 +22,10 @@ export const meta = applyConfig({
/** /**
* Pin a user * Pin a user
*/ */
export default async ( export default apiRoute(async (req, matchedRoute, extraData) => {
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const id = matchedRoute.params.id; const id = matchedRoute.params.id;
const { user: self } = await getFromRequest(req); const { user: self } = extraData.auth;
if (!self) return errorResponse("Unauthorized", 401); if (!self) return errorResponse("Unauthorized", 401);
@ -84,4 +77,4 @@ export default async (
}); });
return jsonResponse(relationshipToAPI(relationship)); return jsonResponse(relationshipToAPI(relationship));
}; });

View file

@ -1,14 +1,10 @@
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { import {
createNewRelationship, createNewRelationship,
relationshipToAPI, relationshipToAPI,
} from "~database/entities/Relationship"; } from "~database/entities/Relationship";
import { import { getRelationshipToOtherUser } from "~database/entities/User";
getFromRequest, import { apiRoute, applyConfig } from "@api";
getRelationshipToOtherUser,
} from "~database/entities/User";
import { applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
export const meta = applyConfig({ export const meta = applyConfig({
@ -26,13 +22,10 @@ export const meta = applyConfig({
/** /**
* Removes an account from your followers list * Removes an account from your followers list
*/ */
export default async ( export default apiRoute(async (req, matchedRoute, extraData) => {
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const id = matchedRoute.params.id; const id = matchedRoute.params.id;
const { user: self } = await getFromRequest(req); const { user: self } = extraData.auth;
if (!self) return errorResponse("Unauthorized", 401); if (!self) return errorResponse("Unauthorized", 401);
@ -98,4 +91,4 @@ export default async (
} }
return jsonResponse(relationshipToAPI(relationship)); return jsonResponse(relationshipToAPI(relationship));
}; });

View file

@ -1,9 +1,8 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { statusAndUserRelations, statusToAPI } from "~database/entities/Status"; import { statusAndUserRelations, statusToAPI } from "~database/entities/Status";
import { userRelations } from "~database/entities/User"; import { userRelations } from "~database/entities/User";
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
export const meta = applyConfig({ export const meta = applyConfig({
@ -21,21 +20,7 @@ export const meta = applyConfig({
/** /**
* Fetch all statuses for a user * Fetch all statuses for a user
*/ */
export default async ( export default apiRoute<{
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const id = matchedRoute.params.id;
// TODO: Add pinned
const {
max_id,
min_id,
since_id,
limit = "20",
exclude_reblogs,
pinned,
}: {
max_id?: string; max_id?: string;
since_id?: string; since_id?: string;
min_id?: string; min_id?: string;
@ -46,7 +31,18 @@ export default async (
// TODO: Add with_muted // TODO: Add with_muted
pinned?: boolean; pinned?: boolean;
tagged?: string; tagged?: string;
} = matchedRoute.query; }>(async (req, matchedRoute, extraData) => {
const id = matchedRoute.params.id;
// TODO: Add pinned
const {
max_id,
min_id,
since_id,
limit = "20",
exclude_reblogs,
pinned,
} = extraData.parsedRequest;
const user = await client.user.findUnique({ const user = await client.user.findUnique({
where: { id }, where: { id },
@ -131,4 +127,4 @@ export default async (
Link: linkHeader.join(", "), Link: linkHeader.join(", "),
} }
); );
}; });

View file

@ -1,14 +1,10 @@
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { import {
createNewRelationship, createNewRelationship,
relationshipToAPI, relationshipToAPI,
} from "~database/entities/Relationship"; } from "~database/entities/Relationship";
import { import { getRelationshipToOtherUser } from "~database/entities/User";
getFromRequest, import { apiRoute, applyConfig } from "@api";
getRelationshipToOtherUser,
} from "~database/entities/User";
import { applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
export const meta = applyConfig({ export const meta = applyConfig({
@ -26,13 +22,10 @@ export const meta = applyConfig({
/** /**
* Blocks a user * Blocks a user
*/ */
export default async ( export default apiRoute(async (req, matchedRoute, extraData) => {
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const id = matchedRoute.params.id; const id = matchedRoute.params.id;
const { user: self } = await getFromRequest(req); const { user: self } = extraData.auth;
if (!self) return errorResponse("Unauthorized", 401); if (!self) return errorResponse("Unauthorized", 401);
@ -84,4 +77,4 @@ export default async (
}); });
return jsonResponse(relationshipToAPI(relationship)); return jsonResponse(relationshipToAPI(relationship));
}; });

View file

@ -1,14 +1,10 @@
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { import {
createNewRelationship, createNewRelationship,
relationshipToAPI, relationshipToAPI,
} from "~database/entities/Relationship"; } from "~database/entities/Relationship";
import { import { getRelationshipToOtherUser } from "~database/entities/User";
getFromRequest, import { apiRoute, applyConfig } from "@api";
getRelationshipToOtherUser,
} from "~database/entities/User";
import { applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
export const meta = applyConfig({ export const meta = applyConfig({
@ -26,13 +22,10 @@ export const meta = applyConfig({
/** /**
* Unfollows a user * Unfollows a user
*/ */
export default async ( export default apiRoute(async (req, matchedRoute, extraData) => {
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const id = matchedRoute.params.id; const id = matchedRoute.params.id;
const { user: self } = await getFromRequest(req); const { user: self } = extraData.auth;
if (!self) return errorResponse("Unauthorized", 401); if (!self) return errorResponse("Unauthorized", 401);
@ -84,4 +77,4 @@ export default async (
}); });
return jsonResponse(relationshipToAPI(relationship)); return jsonResponse(relationshipToAPI(relationship));
}; });

View file

@ -1,14 +1,10 @@
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { import {
createNewRelationship, createNewRelationship,
relationshipToAPI, relationshipToAPI,
} from "~database/entities/Relationship"; } from "~database/entities/Relationship";
import { import { getRelationshipToOtherUser } from "~database/entities/User";
getFromRequest, import { apiRoute, applyConfig } from "@api";
getRelationshipToOtherUser,
} from "~database/entities/User";
import { applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
export const meta = applyConfig({ export const meta = applyConfig({
@ -26,13 +22,10 @@ export const meta = applyConfig({
/** /**
* Unmute a user * Unmute a user
*/ */
export default async ( export default apiRoute(async (req, matchedRoute, extraData) => {
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const id = matchedRoute.params.id; const id = matchedRoute.params.id;
const { user: self } = await getFromRequest(req); const { user: self } = extraData.auth;
if (!self) return errorResponse("Unauthorized", 401); if (!self) return errorResponse("Unauthorized", 401);
@ -86,4 +79,4 @@ export default async (
}); });
return jsonResponse(relationshipToAPI(relationship)); return jsonResponse(relationshipToAPI(relationship));
}; });

View file

@ -1,14 +1,10 @@
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { import {
createNewRelationship, createNewRelationship,
relationshipToAPI, relationshipToAPI,
} from "~database/entities/Relationship"; } from "~database/entities/Relationship";
import { import { getRelationshipToOtherUser } from "~database/entities/User";
getFromRequest, import { apiRoute, applyConfig } from "@api";
getRelationshipToOtherUser,
} from "~database/entities/User";
import { applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
export const meta = applyConfig({ export const meta = applyConfig({
@ -26,13 +22,10 @@ export const meta = applyConfig({
/** /**
* Unpin a user * Unpin a user
*/ */
export default async ( export default apiRoute(async (req, matchedRoute, extraData) => {
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const id = matchedRoute.params.id; const id = matchedRoute.params.id;
const { user: self } = await getFromRequest(req); const { user: self } = extraData.auth;
if (!self) return errorResponse("Unauthorized", 401); if (!self) return errorResponse("Unauthorized", 401);
@ -84,4 +77,4 @@ export default async (
}); });
return jsonResponse(relationshipToAPI(relationship)); return jsonResponse(relationshipToAPI(relationship));
}; });

View file

@ -1,11 +1,6 @@
import { parseRequest } from "@request";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { import { userRelations, userToAPI } from "~database/entities/User";
getFromRequest, import { apiRoute, applyConfig } from "@api";
userRelations,
userToAPI,
} from "~database/entities/User";
import { applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
export const meta = applyConfig({ export const meta = applyConfig({
@ -23,14 +18,14 @@ export const meta = applyConfig({
/** /**
* Find familiar followers (followers of a user that you also follow) * Find familiar followers (followers of a user that you also follow)
*/ */
export default async (req: Request): Promise<Response> => { export default apiRoute<{
const { user: self } = await getFromRequest(req); "id[]": string[];
}>(async (req, matchedRoute, extraData) => {
const { user: self } = extraData.auth;
if (!self) return errorResponse("Unauthorized", 401); if (!self) return errorResponse("Unauthorized", 401);
const { "id[]": ids } = await parseRequest<{ const { "id[]": ids } = extraData.parsedRequest;
"id[]": string[];
}>(req);
// Minimum id count 1, maximum 10 // Minimum id count 1, maximum 10
if (!ids || ids.length < 1 || ids.length > 10) { if (!ids || ids.length < 1 || ids.length > 10) {
@ -67,4 +62,4 @@ export default async (req: Request): Promise<Response> => {
}); });
return jsonResponse(output.map(o => userToAPI(o))); return jsonResponse(output.map(o => userToAPI(o)));
}; });

View file

@ -1,11 +1,9 @@
import { getConfig } from "~classes/configmanager";
import { jsonResponse } from "@response"; import { jsonResponse } from "@response";
import { tempmailDomains } from "@tempmail"; import { tempmailDomains } from "@tempmail";
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { createNewLocalUser } from "~database/entities/User"; import { createNewLocalUser } from "~database/entities/User";
import ISO6391 from "iso-639-1"; import ISO6391 from "iso-639-1";
import type { RouteHandler } from "~server/api/routes.type";
export const meta = applyConfig({ export const meta = applyConfig({
allowedMethods: ["POST"], allowedMethods: ["POST"],
@ -19,19 +17,19 @@ export const meta = applyConfig({
}, },
}); });
const handler: RouteHandler<{ export default apiRoute<{
username: string; username: string;
email: string; email: string;
password: string; password: string;
agreement: boolean; agreement: boolean;
locale: string; locale: string;
reason: string; reason: string;
}> = async (req, matchedRoute, extraData) => { }>(async (req, matchedRoute, extraData) => {
// TODO: Add Authorization check // TODO: Add Authorization check
const body = extraData.parsedRequest; const body = extraData.parsedRequest;
const config = getConfig(); const config = await extraData.configManager.getConfig();
if (!config.signups.registration) { if (!config.signups.registration) {
return jsonResponse( return jsonResponse(
@ -200,9 +198,4 @@ const handler: RouteHandler<{
return new Response("", { return new Response("", {
status: 200, status: 200,
}); });
}; });
/**
* Creates a new user
*/
export default handler;

View file

@ -1,11 +1,9 @@
import { parseRequest } from "@request";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { import {
createNewRelationship, createNewRelationship,
relationshipToAPI, relationshipToAPI,
} from "~database/entities/Relationship"; } from "~database/entities/Relationship";
import { getFromRequest } from "~database/entities/User"; import { apiRoute, applyConfig } from "@api";
import { applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
export const meta = applyConfig({ export const meta = applyConfig({
@ -23,14 +21,14 @@ export const meta = applyConfig({
/** /**
* Find relationships * Find relationships
*/ */
export default async (req: Request): Promise<Response> => { export default apiRoute<{
const { user: self } = await getFromRequest(req); "id[]": string[];
}>(async (req, matchedRoute, extraData) => {
const { user: self } = extraData.auth;
if (!self) return errorResponse("Unauthorized", 401); if (!self) return errorResponse("Unauthorized", 401);
const { "id[]": ids } = await parseRequest<{ const { "id[]": ids } = extraData.parsedRequest;
"id[]": string[];
}>(req);
// Minimum id count 1, maximum 10 // Minimum id count 1, maximum 10
if (!ids || ids.length < 1 || ids.length > 10) { if (!ids || ids.length < 1 || ids.length > 10) {
@ -64,4 +62,4 @@ export default async (req: Request): Promise<Response> => {
); );
return jsonResponse(relationships.map(r => relationshipToAPI(r))); return jsonResponse(relationships.map(r => relationshipToAPI(r)));
}; });

View file

@ -1,8 +1,7 @@
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { userRelations, userToAPI } from "~database/entities/User"; import { userRelations, userToAPI } from "~database/entities/User";
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import type { RouteHandler } from "~server/api/routes.type";
export const meta = applyConfig({ export const meta = applyConfig({
allowedMethods: ["GET"], allowedMethods: ["GET"],
@ -16,13 +15,13 @@ export const meta = applyConfig({
}, },
}); });
const handler: RouteHandler<{ export default apiRoute<{
q?: string; q?: string;
limit?: number; limit?: number;
offset?: number; offset?: number;
resolve?: boolean; resolve?: boolean;
following?: boolean; following?: boolean;
}> = async (req, matchedRoute, extraData) => { }>(async (req, matchedRoute, extraData) => {
// TODO: Add checks for disabled or not email verified accounts // TODO: Add checks for disabled or not email verified accounts
const { user } = extraData.auth; const { user } = extraData.auth;
@ -71,6 +70,4 @@ const handler: RouteHandler<{
}); });
return jsonResponse(accounts.map(acct => userToAPI(acct))); return jsonResponse(accounts.map(acct => userToAPI(acct)));
}; });
export default handler;

View file

@ -1,7 +1,6 @@
import { getConfig } from "~classes/configmanager";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { userRelations, userToAPI } from "~database/entities/User"; import { userRelations, userToAPI } from "~database/entities/User";
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { sanitize } from "isomorphic-dompurify"; import { sanitize } from "isomorphic-dompurify";
import { sanitizeHtml } from "@sanitization"; import { sanitizeHtml } from "@sanitization";
import { uploadFile } from "~classes/media"; import { uploadFile } from "~classes/media";
@ -10,7 +9,6 @@ import { parseEmojis } from "~database/entities/Emoji";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import type { APISource } from "~types/entities/source"; import type { APISource } from "~types/entities/source";
import { convertTextToHtml } from "@formatting"; import { convertTextToHtml } from "@formatting";
import type { RouteHandler } from "~server/api/routes.type";
export const meta = applyConfig({ export const meta = applyConfig({
allowedMethods: ["PATCH"], allowedMethods: ["PATCH"],
@ -24,7 +22,7 @@ export const meta = applyConfig({
}, },
}); });
const handler: RouteHandler<{ export default apiRoute<{
display_name: string; display_name: string;
note: string; note: string;
avatar: File; avatar: File;
@ -35,12 +33,12 @@ const handler: RouteHandler<{
"source[privacy]": string; "source[privacy]": string;
"source[sensitive]": string; "source[sensitive]": string;
"source[language]": string; "source[language]": string;
}> = async (req, matchedRoute, extraData) => { }>(async (req, matchedRoute, extraData) => {
const { user } = extraData.auth; const { user } = extraData.auth;
if (!user) return errorResponse("Unauthorized", 401); if (!user) return errorResponse("Unauthorized", 401);
const config = getConfig(); const config = await extraData.configManager.getConfig();
const { const {
display_name, display_name,
@ -134,7 +132,7 @@ const handler: RouteHandler<{
); );
} }
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access // @ts-expect-error Prisma Typescript doesn't include relations
user.source.privacy = source_privacy; user.source.privacy = source_privacy;
} }
@ -144,7 +142,7 @@ const handler: RouteHandler<{
return errorResponse("Sensitive must be a boolean", 422); return errorResponse("Sensitive must be a boolean", 422);
} }
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access // @ts-expect-error Prisma Typescript doesn't include relations
user.source.sensitive = source_sensitive === "true"; user.source.sensitive = source_sensitive === "true";
} }
@ -156,7 +154,7 @@ const handler: RouteHandler<{
); );
} }
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access // @ts-expect-error Prisma Typescript doesn't include relations
user.source.language = source_language; user.source.language = source_language;
} }
@ -251,6 +249,4 @@ const handler: RouteHandler<{
}); });
return jsonResponse(userToAPI(output)); return jsonResponse(userToAPI(output));
}; });
export default handler;

View file

@ -1,7 +1,6 @@
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { userToAPI } from "~database/entities/User"; import { userToAPI } from "~database/entities/User";
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import type { RouteHandler } from "~server/api/routes.type";
export const meta = applyConfig({ export const meta = applyConfig({
allowedMethods: ["GET"], allowedMethods: ["GET"],
@ -15,9 +14,7 @@ export const meta = applyConfig({
}, },
}); });
const handler: RouteHandler<> = (req, matchedRoute, extraData) => {}; export default apiRoute((req, matchedRoute, extraData) => {
const handler: RouteHandler<""> = (req, matchedRoute, extraData) => {
// TODO: Add checks for disabled or not email verified accounts // TODO: Add checks for disabled or not email verified accounts
const { user } = extraData.auth; const { user } = extraData.auth;
@ -27,6 +24,4 @@ const handler: RouteHandler<""> = (req, matchedRoute, extraData) => {
return jsonResponse({ return jsonResponse({
...userToAPI(user, true), ...userToAPI(user, true),
}); });
}; });
export default handler;

View file

@ -1,5 +1,4 @@
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { parseRequest } from "@request";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { randomBytes } from "crypto"; import { randomBytes } from "crypto";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
@ -19,13 +18,14 @@ export const meta = applyConfig({
/** /**
* Creates a new application to obtain OAuth 2 credentials * Creates a new application to obtain OAuth 2 credentials
*/ */
export default async (req: Request): Promise<Response> => { export default apiRoute<{
const { client_name, redirect_uris, scopes, website } = await parseRequest<{
client_name: string; client_name: string;
redirect_uris: string; redirect_uris: string;
scopes: string; scopes: string;
website: string; website: string;
}>(req); }>(async (req, matchedRoute, extraData) => {
const { client_name, redirect_uris, scopes, website } =
extraData.parsedRequest;
// Check if redirect URI is a valid URI, and also an absolute URI // Check if redirect URI is a valid URI, and also an absolute URI
if (redirect_uris) { if (redirect_uris) {
@ -62,4 +62,4 @@ export default async (req: Request): Promise<Response> => {
redirect_uri: application.redirect_uris, redirect_uri: application.redirect_uris,
vapid_link: application.vapid_key, vapid_link: application.vapid_key,
}); });
}; });

View file

@ -1,7 +1,6 @@
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { getFromToken } from "~database/entities/Application"; import { getFromToken } from "~database/entities/Application";
import { getFromRequest } from "~database/entities/User";
export const meta = applyConfig({ export const meta = applyConfig({
allowedMethods: ["GET"], allowedMethods: ["GET"],
@ -18,8 +17,8 @@ export const meta = applyConfig({
/** /**
* Returns OAuth2 credentials * Returns OAuth2 credentials
*/ */
export default async (req: Request): Promise<Response> => { export default apiRoute(async (req, matchedRoute, extraData) => {
const { user, token } = await getFromRequest(req); const { user, token } = extraData.auth;
const application = await getFromToken(token); const application = await getFromToken(token);
if (!user) return errorResponse("Unauthorized", 401); if (!user) return errorResponse("Unauthorized", 401);
@ -32,4 +31,4 @@ export default async (req: Request): Promise<Response> => {
redirect_uris: application.redirect_uris, redirect_uris: application.redirect_uris,
scopes: application.scopes, scopes: application.scopes,
}); });
}; });

View file

@ -1,10 +1,6 @@
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { import { userRelations, userToAPI } from "~database/entities/User";
getFromRequest, import { apiRoute, applyConfig } from "@api";
userRelations,
userToAPI,
} from "~database/entities/User";
import { applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
export const meta = applyConfig({ export const meta = applyConfig({
@ -19,8 +15,8 @@ export const meta = applyConfig({
}, },
}); });
export default async (req: Request): Promise<Response> => { export default apiRoute(async (req, matchedRoute, extraData) => {
const { user } = await getFromRequest(req); const { user } = extraData.auth;
if (!user) return errorResponse("Unauthorized", 401); if (!user) return errorResponse("Unauthorized", 401);
@ -37,4 +33,4 @@ export default async (req: Request): Promise<Response> => {
}); });
return jsonResponse(blocks.map(u => userToAPI(u))); return jsonResponse(blocks.map(u => userToAPI(u)));
}; });

View file

@ -1,4 +1,4 @@
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { jsonResponse } from "@response"; import { jsonResponse } from "@response";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { emojiToAPI } from "~database/entities/Emoji"; import { emojiToAPI } from "~database/entities/Emoji";
@ -15,11 +15,7 @@ export const meta = applyConfig({
}, },
}); });
/** export default apiRoute(async () => {
* S
*/
// eslint-disable-next-line @typescript-eslint/require-await
export default async (): Promise<Response> => {
const emojis = await client.emoji.findMany({ const emojis = await client.emoji.findMany({
where: { where: {
instanceId: null, instanceId: null,
@ -29,4 +25,4 @@ export default async (): Promise<Response> => {
return jsonResponse( return jsonResponse(
await Promise.all(emojis.map(emoji => emojiToAPI(emoji))) await Promise.all(emojis.map(emoji => emojiToAPI(emoji)))
); );
}; });

View file

@ -1,8 +1,6 @@
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { getFromRequest } from "~database/entities/User"; import { apiRoute, applyConfig } from "@api";
import { applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { parseRequest } from "@request";
import { statusAndUserRelations, statusToAPI } from "~database/entities/Status"; import { statusAndUserRelations, statusToAPI } from "~database/entities/Status";
export const meta = applyConfig({ export const meta = applyConfig({
@ -17,20 +15,15 @@ export const meta = applyConfig({
}, },
}); });
export default async (req: Request): Promise<Response> => { export default apiRoute<{
const { user } = await getFromRequest(req);
const {
limit = 20,
max_id,
min_id,
since_id,
} = await parseRequest<{
max_id?: string; max_id?: string;
since_id?: string; since_id?: string;
min_id?: string; min_id?: string;
limit?: number; limit?: number;
}>(req); }>(async (req, matchedRoute, extraData) => {
const { user } = extraData.auth;
const { limit = 20, max_id, min_id, since_id } = extraData.parsedRequest;
if (limit < 1 || limit > 40) { if (limit < 1 || limit > 40) {
return errorResponse("Limit must be between 1 and 40", 400); return errorResponse("Limit must be between 1 and 40", 400);
@ -77,4 +70,4 @@ export default async (req: Request): Promise<Response> => {
Link: linkHeader.join(", "), Link: linkHeader.join(", "),
} }
); );
}; });

View file

@ -1,8 +1,7 @@
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { getFromRequest, userRelations } from "~database/entities/User"; import { userRelations } from "~database/entities/User";
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import type { MatchedRoute } from "bun";
import { import {
checkForBidirectionalRelationships, checkForBidirectionalRelationships,
relationshipToAPI, relationshipToAPI,
@ -20,11 +19,8 @@ export const meta = applyConfig({
}, },
}); });
export default async ( export default apiRoute(async (req, matchedRoute, extraData) => {
req: Request, const { user } = extraData.auth;
matchedRoute: MatchedRoute
): Promise<Response> => {
const { user } = await getFromRequest(req);
if (!user) return errorResponse("Unauthorized", 401); if (!user) return errorResponse("Unauthorized", 401);
@ -76,4 +72,4 @@ export default async (
if (!relationship) return errorResponse("Relationship not found", 404); if (!relationship) return errorResponse("Relationship not found", 404);
return jsonResponse(relationshipToAPI(relationship)); return jsonResponse(relationshipToAPI(relationship));
}; });

View file

@ -1,8 +1,7 @@
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { getFromRequest, userRelations } from "~database/entities/User"; import { userRelations } from "~database/entities/User";
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import type { MatchedRoute } from "bun";
import { import {
checkForBidirectionalRelationships, checkForBidirectionalRelationships,
relationshipToAPI, relationshipToAPI,
@ -20,11 +19,8 @@ export const meta = applyConfig({
}, },
}); });
export default async ( export default apiRoute(async (req, matchedRoute, extraData) => {
req: Request, const { user } = extraData.auth;
matchedRoute: MatchedRoute
): Promise<Response> => {
const { user } = await getFromRequest(req);
if (!user) return errorResponse("Unauthorized", 401); if (!user) return errorResponse("Unauthorized", 401);
@ -64,4 +60,4 @@ export default async (
if (!relationship) return errorResponse("Relationship not found", 404); if (!relationship) return errorResponse("Relationship not found", 404);
return jsonResponse(relationshipToAPI(relationship)); return jsonResponse(relationshipToAPI(relationship));
}; });

View file

@ -1,12 +1,7 @@
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { import { userRelations, userToAPI } from "~database/entities/User";
getFromRequest, import { apiRoute, applyConfig } from "@api";
userRelations,
userToAPI,
} from "~database/entities/User";
import { applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { parseRequest } from "@request";
export const meta = applyConfig({ export const meta = applyConfig({
allowedMethods: ["GET"], allowedMethods: ["GET"],
@ -20,20 +15,15 @@ export const meta = applyConfig({
}, },
}); });
export default async (req: Request): Promise<Response> => { export default apiRoute<{
const { user } = await getFromRequest(req);
const {
limit = 20,
max_id,
min_id,
since_id,
} = await parseRequest<{
max_id?: string; max_id?: string;
since_id?: string; since_id?: string;
min_id?: string; min_id?: string;
limit?: number; limit?: number;
}>(req); }>(async (req, matchedRoute, extraData) => {
const { user } = extraData.auth;
const { limit = 20, max_id, min_id, since_id } = extraData.parsedRequest;
if (limit < 1 || limit > 40) { if (limit < 1 || limit > 40) {
return errorResponse("Limit must be between 1 and 40", 400); return errorResponse("Limit must be between 1 and 40", 400);
@ -79,4 +69,4 @@ export default async (req: Request): Promise<Response> => {
Link: linkHeader.join(", "), Link: linkHeader.join(", "),
} }
); );
}; });

View file

@ -1,5 +1,4 @@
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { getConfig } from "~classes/configmanager";
import { jsonResponse } from "@response"; import { jsonResponse } from "@response";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { userRelations, userToAPI } from "~database/entities/User"; import { userRelations, userToAPI } from "~database/entities/User";
@ -18,12 +17,8 @@ export const meta = applyConfig({
}, },
}); });
/** export default apiRoute(async (req, matchedRoute, extraData) => {
* Creates a new user const config = await extraData.configManager.getConfig();
*/
// eslint-disable-next-line @typescript-eslint/require-await
export default async (): Promise<Response> => {
const config = getConfig();
// Get software version from package.json // Get software version from package.json
const version = manifest.version; const version = manifest.version;
@ -159,4 +154,4 @@ export default async (): Promise<Response> => {
}, },
contact_account: contactAccount ? userToAPI(contactAccount) : null, contact_account: contactAccount ? userToAPI(contactAccount) : null,
} as APIInstance); } as APIInstance);
}; });

View file

@ -1,13 +1,9 @@
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { getFromRequest } from "~database/entities/User";
import type { APIRouteMeta } from "~types/api"; import type { APIRouteMeta } from "~types/api";
import { uploadFile } from "~classes/media"; import { uploadFile } from "~classes/media";
import { getConfig } from "~classes/configmanager";
import { attachmentToAPI, getUrl } from "~database/entities/Attachment"; import { attachmentToAPI, getUrl } from "~database/entities/Attachment";
import type { MatchedRoute } from "bun";
import { parseRequest } from "@request";
export const meta: APIRouteMeta = applyConfig({ export const meta: APIRouteMeta = applyConfig({
allowedMethods: ["GET", "PUT"], allowedMethods: ["GET", "PUT"],
@ -25,11 +21,12 @@ export const meta: APIRouteMeta = applyConfig({
/** /**
* Get media information * Get media information
*/ */
export default async ( export default apiRoute<{
req: Request, thumbnail?: File;
matchedRoute: MatchedRoute description?: string;
): Promise<Response> => { focus?: string;
const { user } = await getFromRequest(req); }>(async (req, matchedRoute, extraData) => {
const { user } = extraData.auth;
if (!user) { if (!user) {
return errorResponse("Unauthorized", 401); return errorResponse("Unauthorized", 401);
@ -47,7 +44,7 @@ export default async (
return errorResponse("Media not found", 404); return errorResponse("Media not found", 404);
} }
const config = getConfig(); const config = await extraData.configManager.getConfig();
switch (req.method) { switch (req.method) {
case "GET": { case "GET": {
@ -60,11 +57,7 @@ export default async (
} }
} }
case "PUT": { case "PUT": {
const { description, thumbnail } = await parseRequest<{ const { description, thumbnail } = extraData.parsedRequest;
thumbnail?: File;
description?: string;
focus?: string;
}>(req);
let thumbnailUrl = attachment.thumbnail_url; let thumbnailUrl = attachment.thumbnail_url;
@ -101,4 +94,4 @@ export default async (
} }
return errorResponse("Method not allowed", 405); return errorResponse("Method not allowed", 405);
}; });

View file

@ -1,12 +1,10 @@
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { encode } from "blurhash"; import { encode } from "blurhash";
import { getFromRequest } from "~database/entities/User";
import type { APIRouteMeta } from "~types/api"; import type { APIRouteMeta } from "~types/api";
import sharp from "sharp"; import sharp from "sharp";
import { uploadFile } from "~classes/media"; import { uploadFile } from "~classes/media";
import { getConfig } from "~classes/configmanager";
import { attachmentToAPI, getUrl } from "~database/entities/Attachment"; import { attachmentToAPI, getUrl } from "~database/entities/Attachment";
export const meta: APIRouteMeta = applyConfig({ export const meta: APIRouteMeta = applyConfig({
@ -25,27 +23,26 @@ export const meta: APIRouteMeta = applyConfig({
/** /**
* Upload new media * Upload new media
*/ */
export default async (req: Request): Promise<Response> => { export default apiRoute<{
const { user } = await getFromRequest(req); file: File;
thumbnail?: File;
description?: string;
// TODO: Add focus
focus?: string;
}>(async (req, matchedRoute, extraData) => {
const { user } = extraData.auth;
if (!user) { if (!user) {
return errorResponse("Unauthorized", 401); return errorResponse("Unauthorized", 401);
} }
const form = await req.formData(); const { file, thumbnail, description } = extraData.parsedRequest;
const file = form.get("file") as unknown as File | undefined;
const thumbnail = form.get("thumbnail");
const description = form.get("description") as string | undefined;
// Floating point numbers from -1.0 to 1.0, comma delimited
// const focus = form.get("focus");
if (!file) { if (!file) {
return errorResponse("No file provided", 400); return errorResponse("No file provided", 400);
} }
const config = getConfig(); const config = await extraData.configManager.getConfig();
if (file.size > config.validation.max_media_size) { if (file.size > config.validation.max_media_size) {
return errorResponse( return errorResponse(
@ -120,4 +117,4 @@ export default async (req: Request): Promise<Response> => {
// TODO: Add job to process videos and other media // TODO: Add job to process videos and other media
return jsonResponse(attachmentToAPI(newAttachment)); return jsonResponse(attachmentToAPI(newAttachment));
}; });

View file

@ -1,10 +1,6 @@
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { import { userRelations, userToAPI } from "~database/entities/User";
getFromRequest, import { apiRoute, applyConfig } from "@api";
userRelations,
userToAPI,
} from "~database/entities/User";
import { applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
export const meta = applyConfig({ export const meta = applyConfig({
@ -19,8 +15,8 @@ export const meta = applyConfig({
}, },
}); });
export default async (req: Request): Promise<Response> => { export default apiRoute(async (req, matchedRoute, extraData) => {
const { user } = await getFromRequest(req); const { user } = extraData.auth;
if (!user) return errorResponse("Unauthorized", 401); if (!user) return errorResponse("Unauthorized", 401);
@ -37,4 +33,4 @@ export default async (req: Request): Promise<Response> => {
}); });
return jsonResponse(blocks.map(u => userToAPI(u))); return jsonResponse(blocks.map(u => userToAPI(u)));
}; });

View file

@ -1,9 +1,8 @@
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { getFromRequest, userRelations } from "~database/entities/User"; import { userRelations } from "~database/entities/User";
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { statusAndUserRelations } from "~database/entities/Status"; import { statusAndUserRelations } from "~database/entities/Status";
import { parseRequest } from "@request";
import { notificationToAPI } from "~database/entities/Notification"; import { notificationToAPI } from "~database/entities/Notification";
export const meta = applyConfig({ export const meta = applyConfig({
@ -18,8 +17,16 @@ export const meta = applyConfig({
}, },
}); });
export default async (req: Request): Promise<Response> => { export default apiRoute<{
const { user } = await getFromRequest(req); max_id?: string;
since_id?: string;
min_id?: string;
limit?: number;
exclude_types?: string[];
types?: string[];
account_id?: string;
}>(async (req, matchedRoute, extraData) => {
const { user } = extraData.auth;
if (!user) return errorResponse("Unauthorized", 401); if (!user) return errorResponse("Unauthorized", 401);
@ -31,15 +38,7 @@ export default async (req: Request): Promise<Response> => {
min_id, min_id,
since_id, since_id,
types, types,
} = await parseRequest<{ } = extraData.parsedRequest;
max_id?: string;
since_id?: string;
min_id?: string;
limit?: number;
exclude_types?: string[];
types?: string[];
account_id?: string;
}>(req);
if (limit > 30) return errorResponse("Limit too high", 400); if (limit > 30) return errorResponse("Limit too high", 400);
@ -85,8 +84,9 @@ export default async (req: Request): Promise<Response> => {
`<${urlWithoutQuery}?max_id=${objects[0].id}&limit=${limit}>; rel="next"` `<${urlWithoutQuery}?max_id=${objects[0].id}&limit=${limit}>; rel="next"`
); );
linkHeader.push( linkHeader.push(
`<${urlWithoutQuery}?since_id=${objects.at(-1) `<${urlWithoutQuery}?since_id=${
?.id}&limit=${limit}>; rel="prev"` objects.at(-1)?.id
}&limit=${limit}>; rel="prev"`
); );
} }
@ -97,4 +97,4 @@ export default async (req: Request): Promise<Response> => {
Link: linkHeader.join(", "), Link: linkHeader.join(", "),
} }
); );
}; });

View file

@ -1,11 +1,7 @@
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { import { userRelations, userToAPI } from "~database/entities/User";
getFromRequest,
userRelations,
userToAPI,
} from "~database/entities/User";
import type { APIRouteMeta } from "~types/api"; import type { APIRouteMeta } from "~types/api";
export const meta: APIRouteMeta = applyConfig({ export const meta: APIRouteMeta = applyConfig({
@ -23,8 +19,8 @@ export const meta: APIRouteMeta = applyConfig({
/** /**
* Deletes a user avatar * Deletes a user avatar
*/ */
export default async (req: Request): Promise<Response> => { export default apiRoute(async (req, matchedRoute, extraData) => {
const { user } = await getFromRequest(req); const { user } = extraData.auth;
if (!user) return errorResponse("Unauthorized", 401); if (!user) return errorResponse("Unauthorized", 401);
@ -40,4 +36,4 @@ export default async (req: Request): Promise<Response> => {
}); });
return jsonResponse(userToAPI(newUser)); return jsonResponse(userToAPI(newUser));
}; });

View file

@ -1,11 +1,7 @@
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { import { userRelations, userToAPI } from "~database/entities/User";
getFromRequest,
userRelations,
userToAPI,
} from "~database/entities/User";
import type { APIRouteMeta } from "~types/api"; import type { APIRouteMeta } from "~types/api";
export const meta: APIRouteMeta = applyConfig({ export const meta: APIRouteMeta = applyConfig({
@ -23,8 +19,8 @@ export const meta: APIRouteMeta = applyConfig({
/** /**
* Deletes a user header * Deletes a user header
*/ */
export default async (req: Request): Promise<Response> => { export default apiRoute(async (req, matchedRoute, extraData) => {
const { user } = await getFromRequest(req); const { user } = extraData.auth;
if (!user) return errorResponse("Unauthorized", 401); if (!user) return errorResponse("Unauthorized", 401);
@ -40,4 +36,4 @@ export default async (req: Request): Promise<Response> => {
}); });
return jsonResponse(userToAPI(newUser)); return jsonResponse(userToAPI(newUser));
}; });

View file

@ -1,6 +1,5 @@
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { import {
getAncestors, getAncestors,
@ -8,7 +7,6 @@ import {
statusAndUserRelations, statusAndUserRelations,
statusToAPI, statusToAPI,
} from "~database/entities/Status"; } from "~database/entities/Status";
import { getFromRequest } from "~database/entities/User";
import type { APIRouteMeta } from "~types/api"; import type { APIRouteMeta } from "~types/api";
export const meta: APIRouteMeta = applyConfig({ export const meta: APIRouteMeta = applyConfig({
@ -26,15 +24,12 @@ export const meta: APIRouteMeta = applyConfig({
/** /**
* Fetch a user * Fetch a user
*/ */
export default async ( export default apiRoute(async (req, matchedRoute, extraData) => {
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
// Public for public statuses limited to 40 ancestors and 60 descendants with a maximum depth of 20. // Public for public statuses limited to 40 ancestors and 60 descendants with a maximum depth of 20.
// User token + read:statuses for up to 4,096 ancestors, 4,096 descendants, unlimited depth, and private statuses. // User token + read:statuses for up to 4,096 ancestors, 4,096 descendants, unlimited depth, and private statuses.
const id = matchedRoute.params.id; const id = matchedRoute.params.id;
const { user } = await getFromRequest(req); const { user } = extraData.auth;
const foundStatus = await client.status.findUnique({ const foundStatus = await client.status.findUnique({
where: { id }, where: { id },
@ -55,4 +50,4 @@ export default async (
descendants.map(status => statusToAPI(status, user || undefined)) descendants.map(status => statusToAPI(status, user || undefined))
), ),
}); });
}; });

View file

@ -1,7 +1,6 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { createLike } from "~database/entities/Like"; import { createLike } from "~database/entities/Like";
import { import {
@ -9,7 +8,6 @@ import {
statusAndUserRelations, statusAndUserRelations,
statusToAPI, statusToAPI,
} from "~database/entities/Status"; } from "~database/entities/Status";
import { getFromRequest } from "~database/entities/User";
import type { APIRouteMeta } from "~types/api"; import type { APIRouteMeta } from "~types/api";
import type { APIStatus } from "~types/entities/status"; import type { APIStatus } from "~types/entities/status";
@ -28,13 +26,10 @@ export const meta: APIRouteMeta = applyConfig({
/** /**
* Favourite a post * Favourite a post
*/ */
export default async ( export default apiRoute(async (req, matchedRoute, extraData) => {
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const id = matchedRoute.params.id; const id = matchedRoute.params.id;
const { user } = await getFromRequest(req); const { user } = extraData.auth;
if (!user) return errorResponse("Unauthorized", 401); if (!user) return errorResponse("Unauthorized", 401);
@ -63,4 +58,4 @@ export default async (
favourited: true, favourited: true,
favourites_count: status._count.likes + 1, favourites_count: status._count.likes + 1,
} as APIStatus); } as APIStatus);
}; });

View file

@ -1,18 +1,11 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { apiRoute, applyConfig } from "@api";
import { applyConfig } from "@api";
import { parseRequest } from "@request";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { import {
isViewableByUser, isViewableByUser,
statusAndUserRelations, statusAndUserRelations,
} from "~database/entities/Status"; } from "~database/entities/Status";
import { import { userRelations, userToAPI } from "~database/entities/User";
getFromRequest,
userRelations,
userToAPI,
} from "~database/entities/User";
import type { APIRouteMeta } from "~types/api"; import type { APIRouteMeta } from "~types/api";
export const meta: APIRouteMeta = applyConfig({ export const meta: APIRouteMeta = applyConfig({
@ -30,13 +23,15 @@ export const meta: APIRouteMeta = applyConfig({
/** /**
* Fetch users who favourited the post * Fetch users who favourited the post
*/ */
export default async ( export default apiRoute<{
req: Request, max_id?: string;
matchedRoute: MatchedRoute min_id?: string;
): Promise<Response> => { since_id?: string;
limit?: number;
}>(async (req, matchedRoute, extraData) => {
const id = matchedRoute.params.id; const id = matchedRoute.params.id;
const { user } = await getFromRequest(req); const { user } = extraData.auth;
const status = await client.status.findUnique({ const status = await client.status.findUnique({
where: { id }, where: { id },
@ -52,12 +47,7 @@ export default async (
min_id = null, min_id = null,
since_id = null, since_id = null,
limit = 40, limit = 40,
} = await parseRequest<{ } = extraData.parsedRequest;
max_id?: string;
min_id?: string;
since_id?: string;
limit?: number;
}>(req);
// Check for limit limits // Check for limit limits
if (limit > 80) return errorResponse("Invalid limit (maximum is 80)", 400); if (limit > 80) return errorResponse("Invalid limit (maximum is 80)", 400);
@ -111,4 +101,4 @@ export default async (
Link: linkHeader.join(", "), Link: linkHeader.join(", "),
} }
); );
}; });

View file

@ -1,9 +1,6 @@
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { getConfig } from "~classes/configmanager";
import { parseRequest } from "@request";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { sanitizeHtml } from "@sanitization"; import { sanitizeHtml } from "@sanitization";
import type { MatchedRoute } from "bun";
import { parse } from "marked"; import { parse } from "marked";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { import {
@ -12,7 +9,6 @@ import {
statusAndUserRelations, statusAndUserRelations,
statusToAPI, statusToAPI,
} from "~database/entities/Status"; } from "~database/entities/Status";
import { getFromRequest } from "~database/entities/User";
import type { APIRouteMeta } from "~types/api"; import type { APIRouteMeta } from "~types/api";
export const meta: APIRouteMeta = applyConfig({ export const meta: APIRouteMeta = applyConfig({
@ -31,20 +27,28 @@ export const meta: APIRouteMeta = applyConfig({
/** /**
* Fetch a user * Fetch a user
*/ */
export default async ( export default apiRoute<{
req: Request, status?: string;
matchedRoute: MatchedRoute spoiler_text?: string;
): Promise<Response> => { sensitive?: boolean;
language?: string;
content_type?: string;
"media_ids[]"?: string[];
"poll[options][]"?: string[];
"poll[expires_in]"?: number;
"poll[multiple]"?: boolean;
"poll[hide_totals]"?: boolean;
}>(async (req, matchedRoute, extraData) => {
const id = matchedRoute.params.id; const id = matchedRoute.params.id;
const { user } = await getFromRequest(req); const { user } = extraData.auth;
const status = await client.status.findUnique({ const status = await client.status.findUnique({
where: { id }, where: { id },
include: statusAndUserRelations, include: statusAndUserRelations,
}); });
const config = getConfig(); const config = await extraData.configManager.getConfig();
// Check if user is authorized to view this status (if it's private) // Check if user is authorized to view this status (if it's private)
if (!status || !isViewableByUser(status, user)) if (!status || !isViewableByUser(status, user))
@ -89,18 +93,7 @@ export default async (
"media_ids[]": media_ids, "media_ids[]": media_ids,
spoiler_text, spoiler_text,
sensitive, sensitive,
} = await parseRequest<{ } = extraData.parsedRequest;
status?: string;
spoiler_text?: string;
sensitive?: boolean;
language?: string;
content_type?: string;
"media_ids[]"?: string[];
"poll[options][]"?: string[];
"poll[expires_in]"?: number;
"poll[multiple]"?: boolean;
"poll[hide_totals]"?: boolean;
}>(req);
// TODO: Add Poll support // TODO: Add Poll support
// Validate status // Validate status
@ -171,11 +164,11 @@ export default async (
let sanitizedStatus: string; let sanitizedStatus: string;
if (content_type === "text/markdown") { if (content_type === "text/markdown") {
sanitizedStatus = await sanitizeHtml(parse(statusText ?? "")); sanitizedStatus = await sanitizeHtml(await parse(statusText ?? ""));
} else if (content_type === "text/x.misskeymarkdown") { } else if (content_type === "text/x.misskeymarkdown") {
// Parse as MFM // Parse as MFM
// TODO: Parse as MFM // TODO: Parse as MFM
sanitizedStatus = await sanitizeHtml(parse(statusText ?? "")); sanitizedStatus = await sanitizeHtml(await parse(statusText ?? ""));
} else { } else {
sanitizedStatus = await sanitizeHtml(statusText ?? ""); sanitizedStatus = await sanitizeHtml(statusText ?? "");
} }
@ -189,8 +182,8 @@ export default async (
// Check if status body doesnt match filters // Check if status body doesnt match filters
if ( if (
config.filters.note_filters.some( config.filters.note_filters.some(filter =>
filter => statusText?.match(filter) statusText?.match(filter)
) )
) { ) {
return errorResponse("Status contains blocked words", 422); return errorResponse("Status contains blocked words", 422);
@ -223,4 +216,4 @@ export default async (
} }
return jsonResponse({}); return jsonResponse({});
}; });

View file

@ -1,10 +1,8 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { statusAndUserRelations, statusToAPI } from "~database/entities/Status"; import { statusAndUserRelations, statusToAPI } from "~database/entities/Status";
import { getFromRequest } from "~database/entities/User";
import type { APIRouteMeta } from "~types/api"; import type { APIRouteMeta } from "~types/api";
export const meta: APIRouteMeta = applyConfig({ export const meta: APIRouteMeta = applyConfig({
@ -22,13 +20,10 @@ export const meta: APIRouteMeta = applyConfig({
/** /**
* Pin a post * Pin a post
*/ */
export default async ( export default apiRoute(async (req, matchedRoute, extraData) => {
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const id = matchedRoute.params.id; const id = matchedRoute.params.id;
const { user } = await getFromRequest(req); const { user } = extraData.auth;
if (!user) return errorResponse("Unauthorized", 401); if (!user) return errorResponse("Unauthorized", 401);
@ -62,4 +57,4 @@ export default async (
if (!status) return errorResponse("Record not found", 404); if (!status) return errorResponse("Record not found", 404);
return jsonResponse(statusToAPI(status, user)); return jsonResponse(statusToAPI(status, user));
}; });

View file

@ -1,19 +1,13 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { getConfig } from "~classes/configmanager";
import { parseRequest } from "@request";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { import {
isViewableByUser, isViewableByUser,
statusAndUserRelations, statusAndUserRelations,
statusToAPI, statusToAPI,
} from "~database/entities/Status"; } from "~database/entities/Status";
import { import { type UserWithRelations } from "~database/entities/User";
getFromRequest,
type UserWithRelations,
} from "~database/entities/User";
import type { APIRouteMeta } from "~types/api"; import type { APIRouteMeta } from "~types/api";
export const meta: APIRouteMeta = applyConfig({ export const meta: APIRouteMeta = applyConfig({
@ -31,20 +25,17 @@ export const meta: APIRouteMeta = applyConfig({
/** /**
* Reblogs a post * Reblogs a post
*/ */
export default async ( export default apiRoute<{
req: Request, visibility: "public" | "unlisted" | "private";
matchedRoute: MatchedRoute }>(async (req, matchedRoute, extraData) => {
): Promise<Response> => {
const id = matchedRoute.params.id; const id = matchedRoute.params.id;
const config = getConfig(); const config = await extraData.configManager.getConfig();
const { user } = await getFromRequest(req); const { user } = extraData.auth;
if (!user) return errorResponse("Unauthorized", 401); if (!user) return errorResponse("Unauthorized", 401);
const { visibility = "public" } = await parseRequest<{ const { visibility = "public" } = extraData.parsedRequest;
visibility: "public" | "unlisted" | "private";
}>(req);
const status = await client.status.findUnique({ const status = await client.status.findUnique({
where: { id }, where: { id },
@ -107,4 +98,4 @@ export default async (
user user
) )
); );
}; });

View file

@ -1,18 +1,11 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { apiRoute, applyConfig } from "@api";
import { applyConfig } from "@api";
import { parseRequest } from "@request";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { import {
isViewableByUser, isViewableByUser,
statusAndUserRelations, statusAndUserRelations,
} from "~database/entities/Status"; } from "~database/entities/Status";
import { import { userRelations, userToAPI } from "~database/entities/User";
getFromRequest,
userRelations,
userToAPI,
} from "~database/entities/User";
import type { APIRouteMeta } from "~types/api"; import type { APIRouteMeta } from "~types/api";
export const meta: APIRouteMeta = applyConfig({ export const meta: APIRouteMeta = applyConfig({
@ -30,13 +23,15 @@ export const meta: APIRouteMeta = applyConfig({
/** /**
* Fetch users who reblogged the post * Fetch users who reblogged the post
*/ */
export default async ( export default apiRoute<{
req: Request, max_id?: string;
matchedRoute: MatchedRoute min_id?: string;
): Promise<Response> => { since_id?: string;
limit?: number;
}>(async (req, matchedRoute, extraData) => {
const id = matchedRoute.params.id; const id = matchedRoute.params.id;
const { user } = await getFromRequest(req); const { user } = extraData.auth;
const status = await client.status.findUnique({ const status = await client.status.findUnique({
where: { id }, where: { id },
@ -52,12 +47,7 @@ export default async (
min_id = null, min_id = null,
since_id = null, since_id = null,
limit = 40, limit = 40,
} = await parseRequest<{ } = extraData.parsedRequest;
max_id?: string;
min_id?: string;
since_id?: string;
limit?: number;
}>(req);
// Check for limit limits // Check for limit limits
if (limit > 80) return errorResponse("Invalid limit (maximum is 80)", 400); if (limit > 80) return errorResponse("Invalid limit (maximum is 80)", 400);
@ -112,4 +102,4 @@ export default async (
Link: linkHeader.join(", "), Link: linkHeader.join(", "),
} }
); );
}; });

View file

@ -1,17 +1,11 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { apiRoute, applyConfig } from "@api";
import { applyConfig } from "@api"; import { errorResponse } from "@response";
import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { createLike } from "~database/entities/Like";
import { import {
isViewableByUser, isViewableByUser,
statusAndUserRelations, statusAndUserRelations,
statusToAPI,
} from "~database/entities/Status"; } from "~database/entities/Status";
import { getFromRequest } from "~database/entities/User";
import type { APIRouteMeta } from "~types/api"; import type { APIRouteMeta } from "~types/api";
import type { APIStatus } from "~types/entities/status";
export const meta: APIRouteMeta = applyConfig({ export const meta: APIRouteMeta = applyConfig({
allowedMethods: ["GET"], allowedMethods: ["GET"],
@ -28,13 +22,10 @@ export const meta: APIRouteMeta = applyConfig({
/** /**
* Favourite a post * Favourite a post
*/ */
export default async ( export default apiRoute(async (req, matchedRoute, extraData) => {
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const id = matchedRoute.params.id; const id = matchedRoute.params.id;
const { user } = await getFromRequest(req); const { user } = extraData.auth;
if (!user) return errorResponse("Unauthorized", 401); if (!user) return errorResponse("Unauthorized", 401);
@ -46,4 +37,6 @@ export default async (
// Check if user is authorized to view this status (if it's private) // Check if user is authorized to view this status (if it's private)
if (!status || !isViewableByUser(status, user)) if (!status || !isViewableByUser(status, user))
return errorResponse("Record not found", 404); return errorResponse("Record not found", 404);
};
return errorResponse("Not implemented yet");
});

View file

@ -1,7 +1,6 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { deleteLike } from "~database/entities/Like"; import { deleteLike } from "~database/entities/Like";
import { import {
@ -9,7 +8,6 @@ import {
statusAndUserRelations, statusAndUserRelations,
statusToAPI, statusToAPI,
} from "~database/entities/Status"; } from "~database/entities/Status";
import { getFromRequest } from "~database/entities/User";
import type { APIRouteMeta } from "~types/api"; import type { APIRouteMeta } from "~types/api";
import type { APIStatus } from "~types/entities/status"; import type { APIStatus } from "~types/entities/status";
@ -28,13 +26,10 @@ export const meta: APIRouteMeta = applyConfig({
/** /**
* Unfavourite a post * Unfavourite a post
*/ */
export default async ( export default apiRoute(async (req, matchedRoute, extraData) => {
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const id = matchedRoute.params.id; const id = matchedRoute.params.id;
const { user } = await getFromRequest(req); const { user } = extraData.auth;
if (!user) return errorResponse("Unauthorized", 401); if (!user) return errorResponse("Unauthorized", 401);
@ -54,4 +49,4 @@ export default async (
favourited: false, favourited: false,
favourites_count: status._count.likes - 1, favourites_count: status._count.likes - 1,
} as APIStatus); } as APIStatus);
}; });

View file

@ -1,10 +1,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { apiRoute, applyConfig } from "@api";
import { applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { statusAndUserRelations, statusToAPI } from "~database/entities/Status"; import { statusAndUserRelations, statusToAPI } from "~database/entities/Status";
import { getFromRequest } from "~database/entities/User";
import type { APIRouteMeta } from "~types/api"; import type { APIRouteMeta } from "~types/api";
export const meta: APIRouteMeta = applyConfig({ export const meta: APIRouteMeta = applyConfig({
@ -22,13 +19,10 @@ export const meta: APIRouteMeta = applyConfig({
/** /**
* Unpins a post * Unpins a post
*/ */
export default async ( export default apiRoute(async (req, matchedRoute, extraData) => {
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const id = matchedRoute.params.id; const id = matchedRoute.params.id;
const { user } = await getFromRequest(req); const { user } = extraData.auth;
if (!user) return errorResponse("Unauthorized", 401); if (!user) return errorResponse("Unauthorized", 401);
@ -62,4 +56,4 @@ export default async (
if (!status) return errorResponse("Record not found", 404); if (!status) return errorResponse("Record not found", 404);
return jsonResponse(statusToAPI(status, user)); return jsonResponse(statusToAPI(status, user));
}; });

View file

@ -1,14 +1,11 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { apiRoute, applyConfig } from "@api";
import { applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import type { MatchedRoute } from "bun";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { import {
isViewableByUser, isViewableByUser,
statusAndUserRelations, statusAndUserRelations,
statusToAPI, statusToAPI,
} from "~database/entities/Status"; } from "~database/entities/Status";
import { getFromRequest } from "~database/entities/User";
import type { APIRouteMeta } from "~types/api"; import type { APIRouteMeta } from "~types/api";
import type { APIStatus } from "~types/entities/status"; import type { APIStatus } from "~types/entities/status";
@ -27,13 +24,10 @@ export const meta: APIRouteMeta = applyConfig({
/** /**
* Unreblogs a post * Unreblogs a post
*/ */
export default async ( export default apiRoute(async (req, matchedRoute, extraData) => {
req: Request,
matchedRoute: MatchedRoute
): Promise<Response> => {
const id = matchedRoute.params.id; const id = matchedRoute.params.id;
const { user } = await getFromRequest(req); const { user } = extraData.auth;
if (!user) return errorResponse("Unauthorized", 401); if (!user) return errorResponse("Unauthorized", 401);
@ -66,4 +60,4 @@ export default async (
reblogged: false, reblogged: false,
reblogs_count: status._count.reblogs - 1, reblogs_count: status._count.reblogs - 1,
} as APIStatus); } as APIStatus);
}; });

View file

@ -1,12 +1,6 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { apiRoute, applyConfig } from "@api";
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unused-vars */
import { applyConfig } from "@api";
import { getConfig } from "~classes/configmanager";
import { parseRequest } from "@request";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { sanitizeHtml } from "@sanitization"; import { sanitizeHtml } from "@sanitization";
import type { MatchedRoute } from "bun";
import { parse } from "marked"; import { parse } from "marked";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { getFromToken } from "~database/entities/Application"; import { getFromToken } from "~database/entities/Application";
@ -16,7 +10,7 @@ import {
statusAndUserRelations, statusAndUserRelations,
statusToAPI, statusToAPI,
} from "~database/entities/Status"; } from "~database/entities/Status";
import type { AuthData, UserWithRelations } from "~database/entities/User"; import type { UserWithRelations } from "~database/entities/User";
import type { APIRouteMeta } from "~types/api"; import type { APIRouteMeta } from "~types/api";
export const meta: APIRouteMeta = applyConfig({ export const meta: APIRouteMeta = applyConfig({
@ -34,34 +28,7 @@ export const meta: APIRouteMeta = applyConfig({
/** /**
* Post new status * Post new status
*/ */
export default async ( export default apiRoute<{
req: Request,
matchedRoute: MatchedRoute,
authData: AuthData
): Promise<Response> => {
const { user, token } = authData;
const application = await 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,
quote_id,
language,
scheduled_at,
sensitive,
spoiler_text,
visibility,
content_type,
} = await parseRequest<{
status: string; status: string;
media_ids?: string[]; media_ids?: string[];
"poll[options]"?: string[]; "poll[options]"?: string[];
@ -77,7 +44,30 @@ export default async (
scheduled_at?: string; scheduled_at?: string;
local_only?: boolean; local_only?: boolean;
content_type?: string; content_type?: string;
}>(req); }>(async (req, matchedRoute, extraData) => {
const { user, token } = extraData.auth;
const application = await getFromToken(token);
if (!user) return errorResponse("Unauthorized", 401);
const config = await extraData.configManager.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,
quote_id,
// language,
scheduled_at,
sensitive,
spoiler_text,
visibility,
content_type,
} = extraData.parsedRequest;
// Validate status // Validate status
if (!status && !(media_ids && media_ids.length > 0)) { if (!status && !(media_ids && media_ids.length > 0)) {
@ -254,4 +244,4 @@ export default async (
// TODO: add database jobs to deliver the post // TODO: add database jobs to deliver the post
return jsonResponse(await statusToAPI(newStatus, user)); return jsonResponse(await statusToAPI(newStatus, user));
}; });

View file

@ -1,10 +1,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { apiRoute, applyConfig } from "@api";
import { applyConfig } from "@api";
import { parseRequest } from "@request";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { statusAndUserRelations, statusToAPI } from "~database/entities/Status"; import { statusAndUserRelations, statusToAPI } from "~database/entities/Status";
import { getFromRequest } from "~database/entities/User";
import type { APIRouteMeta } from "~types/api"; import type { APIRouteMeta } from "~types/api";
export const meta: APIRouteMeta = applyConfig({ export const meta: APIRouteMeta = applyConfig({
@ -22,20 +19,15 @@ export const meta: APIRouteMeta = applyConfig({
/** /**
* Fetch home timeline statuses * Fetch home timeline statuses
*/ */
export default async (req: Request): Promise<Response> => { export default apiRoute<{
const { user } = await getFromRequest(req);
const {
limit = 20,
max_id,
min_id,
since_id,
} = await parseRequest<{
max_id?: string; max_id?: string;
since_id?: string; since_id?: string;
min_id?: string; min_id?: string;
limit?: number; limit?: number;
}>(req); }>(async (req, matchedRoute, extraData) => {
const { user } = extraData.auth;
const { limit = 20, max_id, min_id, since_id } = extraData.parsedRequest;
if (limit < 1 || limit > 40) { if (limit < 1 || limit > 40) {
return errorResponse("Limit must be between 1 and 40", 400); return errorResponse("Limit must be between 1 and 40", 400);
@ -104,4 +96,4 @@ export default async (req: Request): Promise<Response> => {
Link: linkHeader.join(", "), Link: linkHeader.join(", "),
} }
); );
}; });

View file

@ -1,9 +1,7 @@
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { parseRequest } from "@request";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { statusAndUserRelations, statusToAPI } from "~database/entities/Status"; import { statusAndUserRelations, statusToAPI } from "~database/entities/Status";
import { getFromRequest } from "~database/entities/User";
import type { APIRouteMeta } from "~types/api"; import type { APIRouteMeta } from "~types/api";
export const meta: APIRouteMeta = applyConfig({ export const meta: APIRouteMeta = applyConfig({
@ -18,8 +16,16 @@ export const meta: APIRouteMeta = applyConfig({
}, },
}); });
export default async (req: Request): Promise<Response> => { export default apiRoute<{
const { user } = await getFromRequest(req); local?: boolean;
only_media?: boolean;
remote?: boolean;
max_id?: string;
since_id?: string;
min_id?: string;
limit?: number;
}>(async (req, matchedRoute, extraData) => {
const { user } = extraData.auth;
const { const {
local, local,
limit = 20, limit = 20,
@ -28,15 +34,7 @@ export default async (req: Request): Promise<Response> => {
// only_media, // only_media,
remote, remote,
since_id, since_id,
} = await parseRequest<{ } = extraData.parsedRequest;
local?: boolean;
only_media?: boolean;
remote?: boolean;
max_id?: string;
since_id?: string;
min_id?: string;
limit?: number;
}>(req);
if (limit < 1 || limit > 40) { if (limit < 1 || limit > 40) {
return errorResponse("Limit must be between 1 and 40", 400); return errorResponse("Limit must be between 1 and 40", 400);
@ -87,4 +85,4 @@ export default async (req: Request): Promise<Response> => {
Link: linkHeader.join(", "), Link: linkHeader.join(", "),
} }
); );
}; });

View file

@ -1,12 +1,10 @@
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { encode } from "blurhash"; import { encode } from "blurhash";
import { getFromRequest } from "~database/entities/User";
import type { APIRouteMeta } from "~types/api"; import type { APIRouteMeta } from "~types/api";
import sharp from "sharp"; import sharp from "sharp";
import { uploadFile } from "~classes/media"; import { uploadFile } from "~classes/media";
import { getConfig } from "~classes/configmanager";
import { attachmentToAPI, getUrl } from "~database/entities/Attachment"; import { attachmentToAPI, getUrl } from "~database/entities/Attachment";
export const meta: APIRouteMeta = applyConfig({ export const meta: APIRouteMeta = applyConfig({
@ -25,27 +23,26 @@ export const meta: APIRouteMeta = applyConfig({
/** /**
* Upload new media * Upload new media
*/ */
export default async (req: Request): Promise<Response> => { export default apiRoute<{
const { user } = await getFromRequest(req); file: File;
thumbnail: File;
description: string;
// TODO: Implement focus storage
focus: string;
}>(async (req, matchedRoute, extraData) => {
const { user } = extraData.auth;
if (!user) { if (!user) {
return errorResponse("Unauthorized", 401); return errorResponse("Unauthorized", 401);
} }
const form = await req.formData(); const { file, thumbnail, description } = extraData.parsedRequest;
const file = form.get("file") as unknown as File | undefined;
const thumbnail = form.get("thumbnail");
const description = form.get("description") as string | undefined;
// Floating point numbers from -1.0 to 1.0, comma delimited
// const focus = form.get("focus");
if (!file) { if (!file) {
return errorResponse("No file provided", 400); return errorResponse("No file provided", 400);
} }
const config = getConfig(); const config = await extraData.configManager.getConfig();
if (file.size > config.validation.max_media_size) { if (file.size > config.validation.max_media_size) {
return errorResponse( return errorResponse(
@ -132,4 +129,4 @@ export default async (req: Request): Promise<Response> => {
202 202
); );
} }
}; });

View file

@ -1,15 +1,9 @@
import { applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { getConfig } from "~classes/configmanager";
import { MeiliIndexType, meilisearch } from "@meilisearch"; import { MeiliIndexType, meilisearch } from "@meilisearch";
import { parseRequest } from "@request";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { statusAndUserRelations, statusToAPI } from "~database/entities/Status"; import { statusAndUserRelations, statusToAPI } from "~database/entities/Status";
import { import { userRelations, userToAPI } from "~database/entities/User";
getFromRequest,
userRelations,
userToAPI,
} from "~database/entities/User";
import type { APIRouteMeta } from "~types/api"; import type { APIRouteMeta } from "~types/api";
export const meta: APIRouteMeta = applyConfig({ export const meta: APIRouteMeta = applyConfig({
@ -28,8 +22,18 @@ export const meta: APIRouteMeta = applyConfig({
/** /**
* Upload new media * Upload new media
*/ */
export default async (req: Request): Promise<Response> => { export default apiRoute<{
const { user } = await getFromRequest(req); q?: string;
type?: string;
resolve?: boolean;
following?: boolean;
account_id?: string;
max_id?: string;
min_id?: string;
limit?: number;
offset?: number;
}>(async (req, matchedRoute, extraData) => {
const { user } = extraData.auth;
const { const {
q, q,
@ -41,19 +45,9 @@ export default async (req: Request): Promise<Response> => {
// min_id, // min_id,
limit = 20, limit = 20,
offset, offset,
} = await parseRequest<{ } = extraData.parsedRequest;
q?: string;
type?: string;
resolve?: boolean;
following?: boolean;
account_id?: string;
max_id?: string;
min_id?: string;
limit?: number;
offset?: number;
}>(req);
const config = getConfig(); const config = await extraData.configManager.getConfig();
if (!config.meilisearch.enabled) { if (!config.meilisearch.enabled) {
return errorResponse("Meilisearch is not enabled", 501); return errorResponse("Meilisearch is not enabled", 501);
@ -143,4 +137,4 @@ export default async (req: Request): Promise<Response> => {
), ),
hashtags: [], hashtags: [],
}); });
}; });