mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 16:38:19 +01:00
New API route format to make code cleaner
This commit is contained in:
parent
c7b2f5b741
commit
ca7d325cb1
15
.dockerignore
Normal file
15
.dockerignore
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
node_modules
|
||||||
|
Dockerfile*
|
||||||
|
docker-compose*
|
||||||
|
.dockerignore
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
README.md
|
||||||
|
LICENSE
|
||||||
|
.vscode
|
||||||
|
Makefile
|
||||||
|
helm-charts
|
||||||
|
.env
|
||||||
|
.editorconfig
|
||||||
|
.idea
|
||||||
|
coverage*
|
||||||
|
|
@ -14,5 +14,7 @@ module.exports = {
|
||||||
root: true,
|
root: true,
|
||||||
rules: {
|
rules: {
|
||||||
"@typescript-eslint/no-unsafe-assignment": "off",
|
"@typescript-eslint/no-unsafe-assignment": "off",
|
||||||
|
"@typescript-eslint/no-unsafe-argument": "off",
|
||||||
|
"@typescript-eslint/no-explicit-any": "off"
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -193,7 +193,7 @@ export class RawActivity extends BaseEntity {
|
||||||
* Returns the ActivityPub representation of the activity.
|
* Returns the ActivityPub representation of the activity.
|
||||||
* @returns The ActivityPub representation of the activity.
|
* @returns The ActivityPub representation of the activity.
|
||||||
*/
|
*/
|
||||||
makeActivityPubRepresentation() {
|
getActivityPubRepresentation() {
|
||||||
return {
|
return {
|
||||||
...this.data,
|
...this.data,
|
||||||
object: this.objects[0].data,
|
object: this.objects[0].data,
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,13 @@ export class User extends BaseEntity {
|
||||||
@JoinTable()
|
@JoinTable()
|
||||||
pinned_notes!: RawObject[];
|
pinned_notes!: RawObject[];
|
||||||
|
|
||||||
|
static async getFromRequest(req: Request) {
|
||||||
|
// Check auth token
|
||||||
|
const token = req.headers.get("Authorization")?.split(" ")[1] || "";
|
||||||
|
|
||||||
|
return { user: await User.retrieveFromToken(token), token };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update this user data from its actor
|
* Update this user data from its actor
|
||||||
* @returns The updated user.
|
* @returns The updated user.
|
||||||
|
|
|
||||||
108
index.ts
108
index.ts
|
|
@ -1,8 +1,12 @@
|
||||||
import { getConfig } from "@config";
|
import { getConfig } from "@config";
|
||||||
import { jsonResponse } from "@response";
|
import { jsonResponse } from "@response";
|
||||||
|
import { MatchedRoute } from "bun";
|
||||||
import { appendFile } from "fs/promises";
|
import { appendFile } from "fs/promises";
|
||||||
|
import { matches } from "ip-matching";
|
||||||
import "reflect-metadata";
|
import "reflect-metadata";
|
||||||
import { AppDataSource } from "~database/datasource";
|
import { AppDataSource } from "~database/datasource";
|
||||||
|
import { User } from "~database/entities/User";
|
||||||
|
import { APIRouteMeta } from "~types/api";
|
||||||
|
|
||||||
const router = new Bun.FileSystemRouter({
|
const router = new Bun.FileSystemRouter({
|
||||||
style: "nextjs",
|
style: "nextjs",
|
||||||
|
|
@ -25,6 +29,88 @@ Bun.serve({
|
||||||
port: config.http.bind_port,
|
port: config.http.bind_port,
|
||||||
hostname: config.http.bind || "0.0.0.0", // defaults to "0.0.0.0"
|
hostname: config.http.bind || "0.0.0.0", // defaults to "0.0.0.0"
|
||||||
async fetch(req) {
|
async fetch(req) {
|
||||||
|
/* Check for banned IPs */
|
||||||
|
const request_ip = this.requestIP(req)?.address ?? "";
|
||||||
|
|
||||||
|
for (const ip of config.http.banned_ips) {
|
||||||
|
try {
|
||||||
|
if (matches(ip, request_ip)) {
|
||||||
|
return new Response(undefined, {
|
||||||
|
status: 403,
|
||||||
|
statusText: "Forbidden",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`[-] Error while parsing banned IP "${ip}" `);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await logRequest(req);
|
||||||
|
|
||||||
|
if (req.method === "OPTIONS") {
|
||||||
|
return jsonResponse({});
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchedRoute = router.match(req);
|
||||||
|
|
||||||
|
if (matchedRoute) {
|
||||||
|
const file: {
|
||||||
|
meta: APIRouteMeta;
|
||||||
|
default: (
|
||||||
|
req: Request,
|
||||||
|
matchedRoute: MatchedRoute
|
||||||
|
) => Response | Promise<Response>;
|
||||||
|
} = await import(matchedRoute.filePath);
|
||||||
|
|
||||||
|
const meta = file.meta;
|
||||||
|
|
||||||
|
// Check for allowed requests
|
||||||
|
if (!meta.allowedMethods.includes(req.method as any)) {
|
||||||
|
return new Response(undefined, {
|
||||||
|
status: 405,
|
||||||
|
statusText: `Method not allowed: allowed methods are: ${meta.allowedMethods.join(
|
||||||
|
", "
|
||||||
|
)}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Check for ratelimits
|
||||||
|
|
||||||
|
// Check for authentication if required
|
||||||
|
if (meta.auth.required) {
|
||||||
|
const { user } = await User.getFromRequest(req);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return new Response(undefined, {
|
||||||
|
status: 401,
|
||||||
|
statusText: "Unauthorized",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
(meta.auth.requiredOnMethods ?? []).includes(req.method as any)
|
||||||
|
) {
|
||||||
|
const { user } = await User.getFromRequest(req);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return new Response(undefined, {
|
||||||
|
status: 401,
|
||||||
|
statusText: "Unauthorized",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return file.default(req, matchedRoute);
|
||||||
|
} else {
|
||||||
|
return new Response(undefined, {
|
||||||
|
status: 404,
|
||||||
|
statusText: "Route not found",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const logRequest = async (req: Request) => {
|
||||||
if (config.logging.log_requests_verbose) {
|
if (config.logging.log_requests_verbose) {
|
||||||
await appendFile(
|
await appendFile(
|
||||||
`${process.cwd()}/logs/requests.log`,
|
`${process.cwd()}/logs/requests.log`,
|
||||||
|
|
@ -56,26 +142,6 @@ Bun.serve({
|
||||||
`[${new Date().toISOString()}] ${req.method} ${req.url}\n`
|
`[${new Date().toISOString()}] ${req.method} ${req.url}\n`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
if (req.method === "OPTIONS") {
|
|
||||||
return jsonResponse({});
|
|
||||||
}
|
|
||||||
|
|
||||||
const matchedRoute = router.match(req);
|
|
||||||
|
|
||||||
if (matchedRoute) {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
|
||||||
return (await import(matchedRoute.filePath)).default(
|
|
||||||
req,
|
|
||||||
matchedRoute
|
|
||||||
) as Response | Promise<Response>;
|
|
||||||
} else {
|
|
||||||
return new Response(undefined, {
|
|
||||||
status: 404,
|
|
||||||
statusText: "Route not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("[+] Lysand started!");
|
console.log("[+] Lysand started!");
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@
|
||||||
"typescript": "^5.0.0"
|
"typescript": "^5.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"ip-matching": "^2.1.2",
|
||||||
"jsonld": "^8.3.1",
|
"jsonld": "^8.3.1",
|
||||||
"pg": "^8.11.3",
|
"pg": "^8.11.3",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,19 @@ import { errorResponse, jsonResponse } from "@response";
|
||||||
import { MatchedRoute } from "bun";
|
import { MatchedRoute } from "bun";
|
||||||
import { Relationship } from "~database/entities/Relationship";
|
import { Relationship } from "~database/entities/Relationship";
|
||||||
import { User } from "~database/entities/User";
|
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
|
* Blocks a user
|
||||||
|
|
@ -12,13 +25,7 @@ export default async (
|
||||||
): Promise<Response> => {
|
): Promise<Response> => {
|
||||||
const id = matchedRoute.params.id;
|
const id = matchedRoute.params.id;
|
||||||
|
|
||||||
// Check auth token
|
const { user: self } = await User.getFromRequest(req);
|
||||||
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);
|
|
||||||
|
|
||||||
if (!self) return errorResponse("Unauthorized", 401);
|
if (!self) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,19 @@ import { errorResponse, jsonResponse } from "@response";
|
||||||
import { MatchedRoute } from "bun";
|
import { MatchedRoute } from "bun";
|
||||||
import { Relationship } from "~database/entities/Relationship";
|
import { Relationship } from "~database/entities/Relationship";
|
||||||
import { User } from "~database/entities/User";
|
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
|
* Follow a user
|
||||||
|
|
@ -13,13 +26,7 @@ export default async (
|
||||||
): Promise<Response> => {
|
): Promise<Response> => {
|
||||||
const id = matchedRoute.params.id;
|
const id = matchedRoute.params.id;
|
||||||
|
|
||||||
// Check auth token
|
const { user: self } = await User.getFromRequest(req);
|
||||||
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);
|
|
||||||
|
|
||||||
if (!self) return errorResponse("Unauthorized", 401);
|
if (!self) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,19 @@
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { MatchedRoute } from "bun";
|
import { MatchedRoute } from "bun";
|
||||||
import { User } from "~database/entities/User";
|
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
|
* Fetch a user
|
||||||
|
|
@ -11,13 +24,7 @@ export default async (
|
||||||
): Promise<Response> => {
|
): Promise<Response> => {
|
||||||
const id = matchedRoute.params.id;
|
const id = matchedRoute.params.id;
|
||||||
|
|
||||||
// Check auth token
|
const { user } = await User.getFromRequest(req);
|
||||||
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);
|
|
||||||
|
|
||||||
let foundUser: User | null;
|
let foundUser: User | null;
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,19 @@ import { errorResponse, jsonResponse } from "@response";
|
||||||
import { MatchedRoute } from "bun";
|
import { MatchedRoute } from "bun";
|
||||||
import { Relationship } from "~database/entities/Relationship";
|
import { Relationship } from "~database/entities/Relationship";
|
||||||
import { User } from "~database/entities/User";
|
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
|
* Mute a user
|
||||||
|
|
@ -13,13 +26,7 @@ export default async (
|
||||||
): Promise<Response> => {
|
): Promise<Response> => {
|
||||||
const id = matchedRoute.params.id;
|
const id = matchedRoute.params.id;
|
||||||
|
|
||||||
// Check auth token
|
const { user: self } = await User.getFromRequest(req);
|
||||||
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);
|
|
||||||
|
|
||||||
if (!self) return errorResponse("Unauthorized", 401);
|
if (!self) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,19 @@ import { errorResponse, jsonResponse } from "@response";
|
||||||
import { MatchedRoute } from "bun";
|
import { MatchedRoute } from "bun";
|
||||||
import { Relationship } from "~database/entities/Relationship";
|
import { Relationship } from "~database/entities/Relationship";
|
||||||
import { User } from "~database/entities/User";
|
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
|
* Sets a user note
|
||||||
|
|
@ -13,13 +26,7 @@ export default async (
|
||||||
): Promise<Response> => {
|
): Promise<Response> => {
|
||||||
const id = matchedRoute.params.id;
|
const id = matchedRoute.params.id;
|
||||||
|
|
||||||
// Check auth token
|
const { user: self } = await User.getFromRequest(req);
|
||||||
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);
|
|
||||||
|
|
||||||
if (!self) return errorResponse("Unauthorized", 401);
|
if (!self) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,19 @@ import { errorResponse, jsonResponse } from "@response";
|
||||||
import { MatchedRoute } from "bun";
|
import { MatchedRoute } from "bun";
|
||||||
import { Relationship } from "~database/entities/Relationship";
|
import { Relationship } from "~database/entities/Relationship";
|
||||||
import { User } from "~database/entities/User";
|
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
|
* Pin a user
|
||||||
|
|
@ -12,13 +25,7 @@ export default async (
|
||||||
): Promise<Response> => {
|
): Promise<Response> => {
|
||||||
const id = matchedRoute.params.id;
|
const id = matchedRoute.params.id;
|
||||||
|
|
||||||
// Check auth token
|
const { user: self } = await User.getFromRequest(req);
|
||||||
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);
|
|
||||||
|
|
||||||
if (!self) return errorResponse("Unauthorized", 401);
|
if (!self) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,19 @@ import { errorResponse, jsonResponse } from "@response";
|
||||||
import { MatchedRoute } from "bun";
|
import { MatchedRoute } from "bun";
|
||||||
import { Relationship } from "~database/entities/Relationship";
|
import { Relationship } from "~database/entities/Relationship";
|
||||||
import { User } from "~database/entities/User";
|
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
|
* Removes an account from your followers list
|
||||||
|
|
@ -12,13 +25,7 @@ export default async (
|
||||||
): Promise<Response> => {
|
): Promise<Response> => {
|
||||||
const id = matchedRoute.params.id;
|
const id = matchedRoute.params.id;
|
||||||
|
|
||||||
// Check auth token
|
const { user: self } = await User.getFromRequest(req);
|
||||||
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);
|
|
||||||
|
|
||||||
if (!self) return errorResponse("Unauthorized", 401);
|
if (!self) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,19 @@ import { errorResponse, jsonResponse } from "@response";
|
||||||
import { MatchedRoute } from "bun";
|
import { MatchedRoute } from "bun";
|
||||||
import { Status } from "~database/entities/Status";
|
import { Status } from "~database/entities/Status";
|
||||||
import { User } from "~database/entities/User";
|
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
|
* Fetch all statuses for a user
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,19 @@ import { errorResponse, jsonResponse } from "@response";
|
||||||
import { MatchedRoute } from "bun";
|
import { MatchedRoute } from "bun";
|
||||||
import { Relationship } from "~database/entities/Relationship";
|
import { Relationship } from "~database/entities/Relationship";
|
||||||
import { User } from "~database/entities/User";
|
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
|
* Blocks a user
|
||||||
|
|
@ -12,13 +25,7 @@ export default async (
|
||||||
): Promise<Response> => {
|
): Promise<Response> => {
|
||||||
const id = matchedRoute.params.id;
|
const id = matchedRoute.params.id;
|
||||||
|
|
||||||
// Check auth token
|
const { user: self } = await User.getFromRequest(req);
|
||||||
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);
|
|
||||||
|
|
||||||
if (!self) return errorResponse("Unauthorized", 401);
|
if (!self) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,19 @@ import { errorResponse, jsonResponse } from "@response";
|
||||||
import { MatchedRoute } from "bun";
|
import { MatchedRoute } from "bun";
|
||||||
import { Relationship } from "~database/entities/Relationship";
|
import { Relationship } from "~database/entities/Relationship";
|
||||||
import { User } from "~database/entities/User";
|
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
|
* Unfollows a user
|
||||||
|
|
@ -12,13 +25,7 @@ export default async (
|
||||||
): Promise<Response> => {
|
): Promise<Response> => {
|
||||||
const id = matchedRoute.params.id;
|
const id = matchedRoute.params.id;
|
||||||
|
|
||||||
// Check auth token
|
const { user: self } = await User.getFromRequest(req);
|
||||||
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);
|
|
||||||
|
|
||||||
if (!self) return errorResponse("Unauthorized", 401);
|
if (!self) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,19 @@ import { errorResponse, jsonResponse } from "@response";
|
||||||
import { MatchedRoute } from "bun";
|
import { MatchedRoute } from "bun";
|
||||||
import { Relationship } from "~database/entities/Relationship";
|
import { Relationship } from "~database/entities/Relationship";
|
||||||
import { User } from "~database/entities/User";
|
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
|
* Unmute a user
|
||||||
|
|
@ -12,13 +25,7 @@ export default async (
|
||||||
): Promise<Response> => {
|
): Promise<Response> => {
|
||||||
const id = matchedRoute.params.id;
|
const id = matchedRoute.params.id;
|
||||||
|
|
||||||
// Check auth token
|
const { user: self } = await User.getFromRequest(req);
|
||||||
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);
|
|
||||||
|
|
||||||
if (!self) return errorResponse("Unauthorized", 401);
|
if (!self) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,19 @@ import { errorResponse, jsonResponse } from "@response";
|
||||||
import { MatchedRoute } from "bun";
|
import { MatchedRoute } from "bun";
|
||||||
import { Relationship } from "~database/entities/Relationship";
|
import { Relationship } from "~database/entities/Relationship";
|
||||||
import { User } from "~database/entities/User";
|
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
|
* Unpin a user
|
||||||
|
|
@ -12,13 +25,7 @@ export default async (
|
||||||
): Promise<Response> => {
|
): Promise<Response> => {
|
||||||
const id = matchedRoute.params.id;
|
const id = matchedRoute.params.id;
|
||||||
|
|
||||||
// Check auth token
|
const { user: self } = await User.getFromRequest(req);
|
||||||
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);
|
|
||||||
|
|
||||||
if (!self) return errorResponse("Unauthorized", 401);
|
if (!self) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,25 @@ import { parseRequest } from "@request";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { User } from "~database/entities/User";
|
import { User } from "~database/entities/User";
|
||||||
import { APIAccount } from "~types/entities/account";
|
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)
|
* Find familiar followers (followers of a user that you also follow)
|
||||||
*/
|
*/
|
||||||
export default async (req: Request): Promise<Response> => {
|
export default async (req: Request): Promise<Response> => {
|
||||||
// Check auth token
|
const { user: self } = await User.getFromRequest(req);
|
||||||
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);
|
|
||||||
|
|
||||||
if (!self) return errorResponse("Unauthorized", 401);
|
if (!self) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,19 @@ import { parseRequest } from "@request";
|
||||||
import { jsonResponse } from "@response";
|
import { jsonResponse } from "@response";
|
||||||
import { tempmailDomains } from "@tempmail";
|
import { tempmailDomains } from "@tempmail";
|
||||||
import { User } from "~database/entities/User";
|
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
|
* Creates a new user
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,25 @@ import { parseRequest } from "@request";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { Relationship } from "~database/entities/Relationship";
|
import { Relationship } from "~database/entities/Relationship";
|
||||||
import { User } from "~database/entities/User";
|
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
|
* Find relationships
|
||||||
*/
|
*/
|
||||||
export default async (req: Request): Promise<Response> => {
|
export default async (req: Request): Promise<Response> => {
|
||||||
// Check auth token
|
const { user: self } = await User.getFromRequest(req);
|
||||||
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);
|
|
||||||
|
|
||||||
if (!self) return errorResponse("Unauthorized", 401);
|
if (!self) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,25 @@ import { getConfig } from "@config";
|
||||||
import { parseRequest } from "@request";
|
import { parseRequest } from "@request";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { User } from "~database/entities/User";
|
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
|
* Patches a user
|
||||||
*/
|
*/
|
||||||
export default async (req: Request): Promise<Response> => {
|
export default async (req: Request): Promise<Response> => {
|
||||||
// Check if request is a PATCH request
|
const { user } = await User.getFromRequest(req);
|
||||||
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);
|
|
||||||
|
|
||||||
if (!user) return errorResponse("Unauthorized", 401);
|
if (!user) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,23 @@
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { User } from "~database/entities/User";
|
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> => {
|
export default async (req: Request): Promise<Response> => {
|
||||||
// TODO: Add checks for disabled or not email verified accounts
|
// 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 { user } = await User.getFromRequest(req);
|
||||||
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);
|
|
||||||
|
|
||||||
if (!user) return errorResponse("Unauthorized", 401);
|
if (!user) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,21 @@
|
||||||
|
import { applyConfig } from "@api";
|
||||||
import { parseRequest } from "@request";
|
import { parseRequest } from "@request";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { randomBytes } from "crypto";
|
import { randomBytes } from "crypto";
|
||||||
import { Application } from "~database/entities/Application";
|
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
|
* Creates a new application to obtain OAuth 2 credentials
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,25 @@
|
||||||
|
import { applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { Application } from "~database/entities/Application";
|
import { Application } from "~database/entities/Application";
|
||||||
import { User } from "~database/entities/User";
|
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
|
* Returns OAuth2 credentials
|
||||||
*/
|
*/
|
||||||
export default async (req: Request): Promise<Response> => {
|
export default async (req: Request): Promise<Response> => {
|
||||||
// Check auth token
|
const { user, token } = await User.getFromRequest(req);
|
||||||
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 application = await Application.getFromToken(token);
|
const application = await Application.getFromToken(token);
|
||||||
|
|
||||||
if (!user) return errorResponse("Unauthorized", 401);
|
if (!user) return errorResponse("Unauthorized", 401);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,22 @@
|
||||||
|
import { applyConfig } from "@api";
|
||||||
import { jsonResponse } from "@response";
|
import { jsonResponse } from "@response";
|
||||||
import { IsNull } from "typeorm";
|
import { IsNull } from "typeorm";
|
||||||
import { Emoji } from "~database/entities/Emoji";
|
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
|
// eslint-disable-next-line @typescript-eslint/require-await
|
||||||
export default async (): Promise<Response> => {
|
export default async (): Promise<Response> => {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,21 @@
|
||||||
|
import { applyConfig } from "@api";
|
||||||
import { getConfig } from "@config";
|
import { getConfig } from "@config";
|
||||||
import { jsonResponse } from "@response";
|
import { jsonResponse } from "@response";
|
||||||
import { Status } from "~database/entities/Status";
|
import { Status } from "~database/entities/Status";
|
||||||
import { User } from "~database/entities/User";
|
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
|
* Creates a new user
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,23 @@
|
||||||
|
import { applyConfig } from "@api";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { MatchedRoute } from "bun";
|
import { MatchedRoute } from "bun";
|
||||||
import { RawObject } from "~database/entities/RawObject";
|
import { RawObject } from "~database/entities/RawObject";
|
||||||
import { Status } from "~database/entities/Status";
|
import { Status } from "~database/entities/Status";
|
||||||
import { User } from "~database/entities/User";
|
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
|
* Fetch a user
|
||||||
|
|
@ -13,13 +28,7 @@ export default async (
|
||||||
): Promise<Response> => {
|
): Promise<Response> => {
|
||||||
const id = matchedRoute.params.id;
|
const id = matchedRoute.params.id;
|
||||||
|
|
||||||
// Check auth token
|
const { user } = await User.getFromRequest(req);
|
||||||
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);
|
|
||||||
|
|
||||||
// TODO: Add checks for user's permissions to view this status
|
// TODO: Add checks for user's permissions to view this status
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
import { applyConfig } from "@api";
|
||||||
import { getConfig } from "@config";
|
import { getConfig } from "@config";
|
||||||
import { parseRequest } from "@request";
|
import { parseRequest } from "@request";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
|
|
@ -9,22 +10,25 @@ import { Application } from "~database/entities/Application";
|
||||||
import { RawObject } from "~database/entities/RawObject";
|
import { RawObject } from "~database/entities/RawObject";
|
||||||
import { Status } from "~database/entities/Status";
|
import { Status } from "~database/entities/Status";
|
||||||
import { User } from "~database/entities/User";
|
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
|
* Post new status
|
||||||
*/
|
*/
|
||||||
export default async (req: Request): Promise<Response> => {
|
export default async (req: Request): Promise<Response> => {
|
||||||
// Check if request is a PATCH request
|
const { user, token } = await User.getFromRequest(req);
|
||||||
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 application = await Application.getFromToken(token);
|
const application = await Application.getFromToken(token);
|
||||||
|
|
||||||
if (!user) return errorResponse("Unauthorized", 401);
|
if (!user) return errorResponse("Unauthorized", 401);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,20 @@
|
||||||
|
import { applyConfig } from "@api";
|
||||||
import { parseRequest } from "@request";
|
import { parseRequest } from "@request";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { RawObject } from "~database/entities/RawObject";
|
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
|
* Fetch home timeline statuses
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,20 @@
|
||||||
|
import { applyConfig } from "@api";
|
||||||
import { parseRequest } from "@request";
|
import { parseRequest } from "@request";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
import { RawObject } from "~database/entities/RawObject";
|
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
|
* Fetch public timeline statuses
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,23 @@
|
||||||
|
import { applyConfig } from "@api";
|
||||||
import { errorResponse } from "@response";
|
import { errorResponse } from "@response";
|
||||||
import { MatchedRoute } from "bun";
|
import { MatchedRoute } from "bun";
|
||||||
import { randomBytes } from "crypto";
|
import { randomBytes } from "crypto";
|
||||||
import { Application } from "~database/entities/Application";
|
import { Application } from "~database/entities/Application";
|
||||||
import { Token } from "~database/entities/Token";
|
import { Token } from "~database/entities/Token";
|
||||||
import { User } from "~database/entities/User";
|
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
|
* OAuth Code flow
|
||||||
|
|
|
||||||
12
types/api.ts
Normal file
12
types/api.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
export interface APIRouteMeta {
|
||||||
|
allowedMethods: ("GET" | "POST" | "PUT" | "DELETE" | "PATCH")[];
|
||||||
|
ratelimits: {
|
||||||
|
max: number;
|
||||||
|
duration: number;
|
||||||
|
};
|
||||||
|
route: string;
|
||||||
|
auth: {
|
||||||
|
required: boolean;
|
||||||
|
requiredOnMethods?: ("GET" | "POST" | "PUT" | "DELETE" | "PATCH")[];
|
||||||
|
};
|
||||||
|
}
|
||||||
18
utils/api.ts
Normal file
18
utils/api.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { getConfig } from "@config";
|
||||||
|
import { APIRouteMeta } from "~types/api";
|
||||||
|
|
||||||
|
export const applyConfig = (routeMeta: APIRouteMeta) => {
|
||||||
|
const config = getConfig();
|
||||||
|
const newMeta = routeMeta;
|
||||||
|
|
||||||
|
// Apply ratelimits from config
|
||||||
|
newMeta.ratelimits.duration *= config.ratelimits.duration_coeff;
|
||||||
|
newMeta.ratelimits.max *= config.ratelimits.max_coeff;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
|
if (config.custom_ratelimits[routeMeta.route]) {
|
||||||
|
newMeta.ratelimits = config.custom_ratelimits[routeMeta.route];
|
||||||
|
}
|
||||||
|
|
||||||
|
return newMeta;
|
||||||
|
};
|
||||||
|
|
@ -13,6 +13,7 @@ export interface ConfigType {
|
||||||
base_url: string;
|
base_url: string;
|
||||||
bind: string;
|
bind: string;
|
||||||
bind_port: string;
|
bind_port: string;
|
||||||
|
banned_ips: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
validation: {
|
validation: {
|
||||||
|
|
@ -67,6 +68,19 @@ export interface ConfigType {
|
||||||
log_requests_verbose: boolean;
|
log_requests_verbose: boolean;
|
||||||
log_filters: boolean;
|
log_filters: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ratelimits: {
|
||||||
|
duration_coeff: number;
|
||||||
|
max_coeff: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
custom_ratelimits: Record<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
duration: number;
|
||||||
|
max: number;
|
||||||
|
}
|
||||||
|
>;
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,6 +89,7 @@ export const configDefaults: ConfigType = {
|
||||||
bind: "http://0.0.0.0",
|
bind: "http://0.0.0.0",
|
||||||
bind_port: "8000",
|
bind_port: "8000",
|
||||||
base_url: "http://fediproject.localhost:8000",
|
base_url: "http://fediproject.localhost:8000",
|
||||||
|
banned_ips: [],
|
||||||
},
|
},
|
||||||
database: {
|
database: {
|
||||||
host: "localhost",
|
host: "localhost",
|
||||||
|
|
@ -178,6 +193,11 @@ export const configDefaults: ConfigType = {
|
||||||
log_requests_verbose: false,
|
log_requests_verbose: false,
|
||||||
log_filters: true,
|
log_filters: true,
|
||||||
},
|
},
|
||||||
|
ratelimits: {
|
||||||
|
duration_coeff: 1,
|
||||||
|
max_coeff: 1,
|
||||||
|
},
|
||||||
|
custom_ratelimits: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getConfig = () => {
|
export const getConfig = () => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue