New API route format to make code cleaner

This commit is contained in:
Jesse Wierzbinski 2023-10-15 17:51:29 -10:00
parent c7b2f5b741
commit ca7d325cb1
36 changed files with 600 additions and 237 deletions

View file

@ -2,6 +2,19 @@ import { errorResponse, jsonResponse } from "@response";
import { MatchedRoute } from "bun";
import { Relationship } from "~database/entities/Relationship";
import { User } from "~database/entities/User";
import { applyConfig } from "@api";
export const meta = applyConfig({
allowedMethods: ["POST"],
ratelimits: {
max: 30,
duration: 60,
},
route: "/accounts/:id/block",
auth: {
required: true,
},
});
/**
* Blocks a user
@ -12,13 +25,7 @@ export default async (
): Promise<Response> => {
const id = matchedRoute.params.id;
// Check auth token
const token = req.headers.get("Authorization")?.split(" ")[1] || null;
if (!token)
return errorResponse("This method requires an authenticated user", 422);
const self = await User.retrieveFromToken(token);
const { user: self } = await User.getFromRequest(req);
if (!self) return errorResponse("Unauthorized", 401);

View file

@ -3,6 +3,19 @@ import { errorResponse, jsonResponse } from "@response";
import { MatchedRoute } from "bun";
import { Relationship } from "~database/entities/Relationship";
import { User } from "~database/entities/User";
import { applyConfig } from "@api";
export const meta = applyConfig({
allowedMethods: ["POST"],
ratelimits: {
max: 30,
duration: 60,
},
route: "/accounts/:id/follow",
auth: {
required: true,
},
});
/**
* Follow a user
@ -13,13 +26,7 @@ export default async (
): Promise<Response> => {
const id = matchedRoute.params.id;
// Check auth token
const token = req.headers.get("Authorization")?.split(" ")[1] || null;
if (!token)
return errorResponse("This method requires an authenticated user", 422);
const self = await User.retrieveFromToken(token);
const { user: self } = await User.getFromRequest(req);
if (!self) return errorResponse("Unauthorized", 401);

View file

@ -1,6 +1,19 @@
import { errorResponse, jsonResponse } from "@response";
import { MatchedRoute } from "bun";
import { User } from "~database/entities/User";
import { applyConfig } from "@api";
export const meta = applyConfig({
allowedMethods: ["POST"],
ratelimits: {
max: 30,
duration: 60,
},
route: "/accounts/:id",
auth: {
required: true,
},
});
/**
* Fetch a user
@ -11,13 +24,7 @@ export default async (
): Promise<Response> => {
const id = matchedRoute.params.id;
// Check auth token
const token = req.headers.get("Authorization")?.split(" ")[1];
if (!token)
return errorResponse("This method requires an authenticated user", 422);
const user = await User.retrieveFromToken(token);
const { user } = await User.getFromRequest(req);
let foundUser: User | null;
try {

View file

@ -3,6 +3,19 @@ import { errorResponse, jsonResponse } from "@response";
import { MatchedRoute } from "bun";
import { Relationship } from "~database/entities/Relationship";
import { User } from "~database/entities/User";
import { applyConfig } from "@api";
export const meta = applyConfig({
allowedMethods: ["POST"],
ratelimits: {
max: 30,
duration: 60,
},
route: "/accounts/:id/mute",
auth: {
required: true,
},
});
/**
* Mute a user
@ -13,13 +26,7 @@ export default async (
): Promise<Response> => {
const id = matchedRoute.params.id;
// Check auth token
const token = req.headers.get("Authorization")?.split(" ")[1] || null;
if (!token)
return errorResponse("This method requires an authenticated user", 422);
const self = await User.retrieveFromToken(token);
const { user: self } = await User.getFromRequest(req);
if (!self) return errorResponse("Unauthorized", 401);

View file

@ -3,6 +3,19 @@ import { errorResponse, jsonResponse } from "@response";
import { MatchedRoute } from "bun";
import { Relationship } from "~database/entities/Relationship";
import { User } from "~database/entities/User";
import { applyConfig } from "@api";
export const meta = applyConfig({
allowedMethods: ["POST"],
ratelimits: {
max: 30,
duration: 60,
},
route: "/accounts/:id/note",
auth: {
required: true,
},
});
/**
* Sets a user note
@ -13,13 +26,7 @@ export default async (
): Promise<Response> => {
const id = matchedRoute.params.id;
// Check auth token
const token = req.headers.get("Authorization")?.split(" ")[1] || null;
if (!token)
return errorResponse("This method requires an authenticated user", 422);
const self = await User.retrieveFromToken(token);
const { user: self } = await User.getFromRequest(req);
if (!self) return errorResponse("Unauthorized", 401);

View file

@ -2,6 +2,19 @@ import { errorResponse, jsonResponse } from "@response";
import { MatchedRoute } from "bun";
import { Relationship } from "~database/entities/Relationship";
import { User } from "~database/entities/User";
import { applyConfig } from "@api";
export const meta = applyConfig({
allowedMethods: ["POST"],
ratelimits: {
max: 30,
duration: 60,
},
route: "/accounts/:id/pin",
auth: {
required: true,
},
});
/**
* Pin a user
@ -12,13 +25,7 @@ export default async (
): Promise<Response> => {
const id = matchedRoute.params.id;
// Check auth token
const token = req.headers.get("Authorization")?.split(" ")[1] || null;
if (!token)
return errorResponse("This method requires an authenticated user", 422);
const self = await User.retrieveFromToken(token);
const { user: self } = await User.getFromRequest(req);
if (!self) return errorResponse("Unauthorized", 401);

View file

@ -2,6 +2,19 @@ import { errorResponse, jsonResponse } from "@response";
import { MatchedRoute } from "bun";
import { Relationship } from "~database/entities/Relationship";
import { User } from "~database/entities/User";
import { applyConfig } from "@api";
export const meta = applyConfig({
allowedMethods: ["POST"],
ratelimits: {
max: 30,
duration: 60,
},
route: "/accounts/:id/remove_from_followers",
auth: {
required: true,
},
});
/**
* Removes an account from your followers list
@ -12,13 +25,7 @@ export default async (
): Promise<Response> => {
const id = matchedRoute.params.id;
// Check auth token
const token = req.headers.get("Authorization")?.split(" ")[1] || null;
if (!token)
return errorResponse("This method requires an authenticated user", 422);
const self = await User.retrieveFromToken(token);
const { user: self } = await User.getFromRequest(req);
if (!self) return errorResponse("Unauthorized", 401);

View file

@ -2,6 +2,19 @@ import { errorResponse, jsonResponse } from "@response";
import { MatchedRoute } from "bun";
import { Status } from "~database/entities/Status";
import { User } from "~database/entities/User";
import { applyConfig } from "@api";
export const meta = applyConfig({
allowedMethods: ["GET"],
ratelimits: {
max: 30,
duration: 60,
},
route: "/accounts/:id/statuses",
auth: {
required: false,
},
});
/**
* Fetch all statuses for a user

View file

@ -2,6 +2,19 @@ import { errorResponse, jsonResponse } from "@response";
import { MatchedRoute } from "bun";
import { Relationship } from "~database/entities/Relationship";
import { User } from "~database/entities/User";
import { applyConfig } from "@api";
export const meta = applyConfig({
allowedMethods: ["POST"],
ratelimits: {
max: 30,
duration: 60,
},
route: "/accounts/:id/unblock",
auth: {
required: true,
},
});
/**
* Blocks a user
@ -12,13 +25,7 @@ export default async (
): Promise<Response> => {
const id = matchedRoute.params.id;
// Check auth token
const token = req.headers.get("Authorization")?.split(" ")[1] || null;
if (!token)
return errorResponse("This method requires an authenticated user", 422);
const self = await User.retrieveFromToken(token);
const { user: self } = await User.getFromRequest(req);
if (!self) return errorResponse("Unauthorized", 401);

View file

@ -2,6 +2,19 @@ import { errorResponse, jsonResponse } from "@response";
import { MatchedRoute } from "bun";
import { Relationship } from "~database/entities/Relationship";
import { User } from "~database/entities/User";
import { applyConfig } from "@api";
export const meta = applyConfig({
allowedMethods: ["POST"],
ratelimits: {
max: 30,
duration: 60,
},
route: "/accounts/:id/unfollow",
auth: {
required: true,
},
});
/**
* Unfollows a user
@ -12,13 +25,7 @@ export default async (
): Promise<Response> => {
const id = matchedRoute.params.id;
// Check auth token
const token = req.headers.get("Authorization")?.split(" ")[1] || null;
if (!token)
return errorResponse("This method requires an authenticated user", 422);
const self = await User.retrieveFromToken(token);
const { user: self } = await User.getFromRequest(req);
if (!self) return errorResponse("Unauthorized", 401);

View file

@ -2,6 +2,19 @@ import { errorResponse, jsonResponse } from "@response";
import { MatchedRoute } from "bun";
import { Relationship } from "~database/entities/Relationship";
import { User } from "~database/entities/User";
import { applyConfig } from "@api";
export const meta = applyConfig({
allowedMethods: ["POST"],
ratelimits: {
max: 30,
duration: 60,
},
route: "/accounts/:id/unmute",
auth: {
required: true,
},
});
/**
* Unmute a user
@ -12,13 +25,7 @@ export default async (
): Promise<Response> => {
const id = matchedRoute.params.id;
// Check auth token
const token = req.headers.get("Authorization")?.split(" ")[1] || null;
if (!token)
return errorResponse("This method requires an authenticated user", 422);
const self = await User.retrieveFromToken(token);
const { user: self } = await User.getFromRequest(req);
if (!self) return errorResponse("Unauthorized", 401);

View file

@ -2,6 +2,19 @@ import { errorResponse, jsonResponse } from "@response";
import { MatchedRoute } from "bun";
import { Relationship } from "~database/entities/Relationship";
import { User } from "~database/entities/User";
import { applyConfig } from "@api";
export const meta = applyConfig({
allowedMethods: ["POST"],
ratelimits: {
max: 30,
duration: 60,
},
route: "/accounts/:id/unpin",
auth: {
required: true,
},
});
/**
* Unpin a user
@ -12,13 +25,7 @@ export default async (
): Promise<Response> => {
const id = matchedRoute.params.id;
// Check auth token
const token = req.headers.get("Authorization")?.split(" ")[1] || null;
if (!token)
return errorResponse("This method requires an authenticated user", 422);
const self = await User.retrieveFromToken(token);
const { user: self } = await User.getFromRequest(req);
if (!self) return errorResponse("Unauthorized", 401);

View file

@ -2,18 +2,25 @@ import { parseRequest } from "@request";
import { errorResponse, jsonResponse } from "@response";
import { User } from "~database/entities/User";
import { APIAccount } from "~types/entities/account";
import { applyConfig } from "@api";
export const meta = applyConfig({
allowedMethods: ["GET"],
route: "/api/v1/accounts/familiar_followers",
ratelimits: {
max: 30,
duration: 60,
},
auth: {
required: true,
},
});
/**
* Find familiar followers (followers of a user that you also follow)
*/
export default async (req: Request): Promise<Response> => {
// Check auth token
const token = req.headers.get("Authorization")?.split(" ")[1] || null;
if (!token)
return errorResponse("This method requires an authenticated user", 422);
const self = await User.retrieveFromToken(token);
const { user: self } = await User.getFromRequest(req);
if (!self) return errorResponse("Unauthorized", 401);

View file

@ -3,6 +3,19 @@ import { parseRequest } from "@request";
import { jsonResponse } from "@response";
import { tempmailDomains } from "@tempmail";
import { User } from "~database/entities/User";
import { applyConfig } from "@api";
export const meta = applyConfig({
allowedMethods: ["POST"],
route: "/api/v1/accounts",
ratelimits: {
max: 2,
duration: 60,
},
auth: {
required: true,
},
});
/**
* Creates a new user

View file

@ -2,18 +2,25 @@ import { parseRequest } from "@request";
import { errorResponse, jsonResponse } from "@response";
import { Relationship } from "~database/entities/Relationship";
import { User } from "~database/entities/User";
import { applyConfig } from "@api";
export const meta = applyConfig({
allowedMethods: ["GET"],
route: "/api/v1/accounts/relationships",
ratelimits: {
max: 30,
duration: 60,
},
auth: {
required: true,
},
});
/**
* Find relationships
*/
export default async (req: Request): Promise<Response> => {
// Check auth token
const token = req.headers.get("Authorization")?.split(" ")[1] || null;
if (!token)
return errorResponse("This method requires an authenticated user", 422);
const self = await User.retrieveFromToken(token);
const { user: self } = await User.getFromRequest(req);
if (!self) return errorResponse("Unauthorized", 401);

View file

@ -2,22 +2,25 @@ import { getConfig } from "@config";
import { parseRequest } from "@request";
import { errorResponse, jsonResponse } from "@response";
import { User } from "~database/entities/User";
import { applyConfig } from "@api";
export const meta = applyConfig({
allowedMethods: ["PATCH"],
route: "/api/v1/accounts/update_credentials",
ratelimits: {
max: 2,
duration: 60,
},
auth: {
required: true,
},
});
/**
* Patches a user
*/
export default async (req: Request): Promise<Response> => {
// Check if request is a PATCH request
if (req.method !== "PATCH")
return errorResponse("This method requires a PATCH 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 User.retrieveFromToken(token);
const { user } = await User.getFromRequest(req);
if (!user) return errorResponse("Unauthorized", 401);

View file

@ -1,22 +1,23 @@
import { errorResponse, jsonResponse } from "@response";
import { User } from "~database/entities/User";
import { applyConfig } from "@api";
export const meta = applyConfig({
allowedMethods: ["GET"],
route: "/api/v1/accounts/verify_credentials",
ratelimits: {
max: 100,
duration: 60,
},
auth: {
required: true,
},
});
/**
* Patches a user
*/
export default async (req: Request): Promise<Response> => {
// TODO: Add checks for disabled or not email verified accounts
// Check if request is a PATCH request
if (req.method !== "GET")
return errorResponse("This method requires a GET 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 User.retrieveFromToken(token);
const { user } = await User.getFromRequest(req);
if (!user) return errorResponse("Unauthorized", 401);

View file

@ -1,8 +1,21 @@
import { applyConfig } from "@api";
import { parseRequest } from "@request";
import { errorResponse, jsonResponse } from "@response";
import { randomBytes } from "crypto";
import { Application } from "~database/entities/Application";
export const meta = applyConfig({
allowedMethods: ["POST"],
route: "/api/v1/apps",
ratelimits: {
max: 2,
duration: 60,
},
auth: {
required: false,
},
});
/**
* Creates a new application to obtain OAuth 2 credentials
*/

View file

@ -1,18 +1,25 @@
import { applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { Application } from "~database/entities/Application";
import { User } from "~database/entities/User";
export const meta = applyConfig({
allowedMethods: ["GET"],
route: "/api/v1/apps/verify_credentials",
ratelimits: {
max: 100,
duration: 60,
},
auth: {
required: true,
},
});
/**
* Returns OAuth2 credentials
*/
export default async (req: Request): Promise<Response> => {
// 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 User.retrieveFromToken(token);
const { user, token } = await User.getFromRequest(req);
const application = await Application.getFromToken(token);
if (!user) return errorResponse("Unauthorized", 401);

View file

@ -1,9 +1,22 @@
import { applyConfig } from "@api";
import { jsonResponse } from "@response";
import { IsNull } from "typeorm";
import { Emoji } from "~database/entities/Emoji";
export const meta = applyConfig({
allowedMethods: ["GET"],
route: "/api/v1/custom_emojis",
ratelimits: {
max: 100,
duration: 60,
},
auth: {
required: false,
},
});
/**
* Creates a new user
* S
*/
// eslint-disable-next-line @typescript-eslint/require-await
export default async (): Promise<Response> => {

View file

@ -1,8 +1,21 @@
import { applyConfig } from "@api";
import { getConfig } from "@config";
import { jsonResponse } from "@response";
import { Status } from "~database/entities/Status";
import { User } from "~database/entities/User";
export const meta = applyConfig({
allowedMethods: ["GET"],
route: "/api/v1/instance",
ratelimits: {
max: 300,
duration: 60,
},
auth: {
required: false,
},
});
/**
* Creates a new user
*/

View file

@ -1,8 +1,23 @@
import { applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response";
import { MatchedRoute } from "bun";
import { RawObject } from "~database/entities/RawObject";
import { Status } from "~database/entities/Status";
import { User } from "~database/entities/User";
import { APIRouteMeta } from "~types/api";
export const meta: APIRouteMeta = applyConfig({
allowedMethods: ["GET", "DELETE"],
ratelimits: {
max: 100,
duration: 60,
},
route: "/api/v1/statuses/:id",
auth: {
required: false,
requiredOnMethods: ["DELETE"],
},
});
/**
* Fetch a user
@ -13,13 +28,7 @@ export default async (
): Promise<Response> => {
const id = matchedRoute.params.id;
// 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 User.retrieveFromToken(token);
const { user } = await User.getFromRequest(req);
// TODO: Add checks for user's permissions to view this status

View file

@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unused-vars */
import { applyConfig } from "@api";
import { getConfig } from "@config";
import { parseRequest } from "@request";
import { errorResponse, jsonResponse } from "@response";
@ -9,22 +10,25 @@ import { Application } from "~database/entities/Application";
import { RawObject } from "~database/entities/RawObject";
import { Status } from "~database/entities/Status";
import { User } from "~database/entities/User";
import { APIRouteMeta } from "~types/api";
export const meta: APIRouteMeta = applyConfig({
allowedMethods: ["POST"],
ratelimits: {
max: 300,
duration: 60,
},
route: "/api/v1/statuses",
auth: {
required: true,
},
});
/**
* 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 User.retrieveFromToken(token);
const { user, token } = await User.getFromRequest(req);
const application = await Application.getFromToken(token);
if (!user) return errorResponse("Unauthorized", 401);

View file

@ -1,6 +1,20 @@
import { applyConfig } from "@api";
import { parseRequest } from "@request";
import { errorResponse, jsonResponse } from "@response";
import { RawObject } from "~database/entities/RawObject";
import { APIRouteMeta } from "~types/api";
export const meta: APIRouteMeta = applyConfig({
allowedMethods: ["GET"],
ratelimits: {
max: 200,
duration: 60,
},
route: "/api/v1/timelines/home",
auth: {
required: true,
},
});
/**
* Fetch home timeline statuses

View file

@ -1,6 +1,20 @@
import { applyConfig } from "@api";
import { parseRequest } from "@request";
import { errorResponse, jsonResponse } from "@response";
import { RawObject } from "~database/entities/RawObject";
import { APIRouteMeta } from "~types/api";
export const meta: APIRouteMeta = applyConfig({
allowedMethods: ["GET"],
ratelimits: {
max: 200,
duration: 60,
},
route: "/api/v1/timelines/public",
auth: {
required: false,
},
});
/**
* Fetch public timeline statuses

View file

@ -1,9 +1,23 @@
import { applyConfig } from "@api";
import { errorResponse } from "@response";
import { MatchedRoute } from "bun";
import { randomBytes } from "crypto";
import { Application } from "~database/entities/Application";
import { Token } from "~database/entities/Token";
import { User } from "~database/entities/User";
import { APIRouteMeta } from "~types/api";
export const meta: APIRouteMeta = applyConfig({
allowedMethods: ["POST"],
ratelimits: {
max: 4,
duration: 60,
},
route: "/auth/login",
auth: {
required: false,
},
});
/**
* OAuth Code flow