Add docs navigation and tighten API request security
This commit is contained in:
30
lib/api.ts
30
lib/api.ts
@@ -14,6 +14,8 @@ import {
|
||||
import { mediaUrl, mediaUrls } from "./catalog";
|
||||
import { ServiceError, checkServiceError } from "./service-error";
|
||||
import { requestOrigin, verifyOrigin } from "./request-origin";
|
||||
import { browserAccount } from "./request-security";
|
||||
import { jsonBody } from "./json-body";
|
||||
import { accountApi } from "./account-api";
|
||||
import { multiplayerApi } from "./multiplayer-api";
|
||||
import { bugReportApi } from "./bug-report-api";
|
||||
@@ -45,10 +47,11 @@ const uuid = (value: string) => idSchema.parse(value);
|
||||
function cookieSecret(request: NextRequest) {
|
||||
return request.cookies.get("mg_guest")?.value ?? "";
|
||||
}
|
||||
async function contributor(request: NextRequest) {
|
||||
async function contributor(request: NextRequest, browserOnly = false) {
|
||||
const token = request.headers.get("authorization")?.replace(/^Bearer /, "");
|
||||
let owner: string | undefined;
|
||||
if (token) {
|
||||
if (browserOnly) owner = (await browserAccount(request, user)).id;
|
||||
else if (token) {
|
||||
const row = required(
|
||||
await admin()
|
||||
.from("contributor_tokens")
|
||||
@@ -67,7 +70,7 @@ async function contributor(request: NextRequest) {
|
||||
return owner;
|
||||
}
|
||||
async function moderator(request: NextRequest, adminOnly = false) {
|
||||
const owner = await contributor(request);
|
||||
const owner = await contributor(request, true);
|
||||
const profile = required(
|
||||
await admin().from("profiles").select("role").eq("id", owner).single(),
|
||||
);
|
||||
@@ -112,23 +115,6 @@ async function session(request: NextRequest, id: string) {
|
||||
if (account && record.owner_id === account.id) return record;
|
||||
throw new Error("Session not found");
|
||||
}
|
||||
async function jsonBody(request: NextRequest) {
|
||||
const reader = request.body?.getReader();
|
||||
if (!reader) throw new Error("JSON request required");
|
||||
const chunks: Uint8Array[] = [];
|
||||
let size = 0;
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
size += value.byteLength;
|
||||
if (size > 2 * 1024 * 1024) {
|
||||
await reader.cancel();
|
||||
throw new Error("Request too large");
|
||||
}
|
||||
chunks.push(value);
|
||||
}
|
||||
return JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
||||
}
|
||||
export async function handle(request: NextRequest, path: string[]) {
|
||||
try {
|
||||
verifyOrigin(request);
|
||||
@@ -802,7 +788,7 @@ export async function handle(request: NextRequest, path: string[]) {
|
||||
}
|
||||
}
|
||||
if (path[0] === "tokens") {
|
||||
const owner = await contributor(request);
|
||||
const owner = await contributor(request, true);
|
||||
if (method === "GET")
|
||||
return NextResponse.json({
|
||||
tokens: required(
|
||||
@@ -871,7 +857,7 @@ export async function handle(request: NextRequest, path: string[]) {
|
||||
});
|
||||
}
|
||||
if (method === "POST" && path[1] === "approve") {
|
||||
const owner = await contributor(request);
|
||||
const owner = await contributor(request, true);
|
||||
const code = z
|
||||
.string()
|
||||
.regex(/^[A-F0-9]{8}$/)
|
||||
|
||||
Reference in New Issue
Block a user