Add account restrictions, appeals, and admin moderation tools
This commit is contained in:
32
lib/api.ts
32
lib/api.ts
@@ -2,7 +2,8 @@ import "server-only";
|
||||
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { z } from "zod";
|
||||
import { admin, required, user } from "./supabase";
|
||||
import { admin, required, user, assertAccountActive } from "./supabase";
|
||||
import { accountControlsApi } from "./account-controls-api";
|
||||
import {
|
||||
datasetSchema,
|
||||
faces,
|
||||
@@ -64,6 +65,7 @@ async function contributor(request: NextRequest, browserOnly = false) {
|
||||
owner = row.owner_id;
|
||||
} else owner = (await user())?.id;
|
||||
if (!owner) throw new Error("Sign in to continue");
|
||||
await assertAccountActive(owner);
|
||||
const { error } = await admin()
|
||||
.from("profiles")
|
||||
.upsert({ id: owner }, { onConflict: "id", ignoreDuplicates: true });
|
||||
@@ -97,7 +99,7 @@ async function limit(key: string, count: number, seconds: number) {
|
||||
}
|
||||
async function ownedDataset(request: NextRequest, id: string) {
|
||||
const owner = await contributor(request);
|
||||
return required(
|
||||
const dataset = required(
|
||||
await admin()
|
||||
.from("datasets")
|
||||
.select("*")
|
||||
@@ -105,6 +107,8 @@ async function ownedDataset(request: NextRequest, id: string) {
|
||||
.eq("owner_id", owner)
|
||||
.single(),
|
||||
);
|
||||
// The ownership predicate above proves this is a current account's map.
|
||||
return { ...dataset, owner_id: owner };
|
||||
}
|
||||
async function session(request: NextRequest, id: string) {
|
||||
const record = required(
|
||||
@@ -119,8 +123,16 @@ async function session(request: NextRequest, id: string) {
|
||||
export async function handle(request: NextRequest, path: string[]) {
|
||||
try {
|
||||
verifyOrigin(request);
|
||||
if (
|
||||
(path[0] === "ban" && path[1] === "appeal") ||
|
||||
(path[0] === "moderation" &&
|
||||
["accounts", "account-appeals"].includes(path[1]))
|
||||
)
|
||||
return await accountControlsApi(request, path, { jsonBody, limit });
|
||||
if (path[0] === "auth" || path[0] === "profile")
|
||||
return await accountApi(request, path, { jsonBody, limit });
|
||||
// Check signed-in identities before any guest-cookie fallback (games/rooms).
|
||||
if (path[0] !== "health") await user();
|
||||
if (path[0] === "rooms")
|
||||
return await multiplayerApi(request, path, { jsonBody, limit });
|
||||
if (path[0] === "bug-reports")
|
||||
@@ -733,7 +745,13 @@ export async function handle(request: NextRequest, path: string[]) {
|
||||
await client
|
||||
.from("profiles")
|
||||
.select("id,display_name")
|
||||
.in("id", [...new Set(rows.map((c) => c.owner_id))]),
|
||||
.in("id", [
|
||||
...new Set(
|
||||
rows
|
||||
.map((c) => c.owner_id)
|
||||
.filter((id): id is string => id !== null),
|
||||
),
|
||||
]),
|
||||
)
|
||||
: [];
|
||||
return NextResponse.json({
|
||||
@@ -1066,12 +1084,14 @@ export async function handle(request: NextRequest, path: string[]) {
|
||||
.is("parent_dataset_id", null)
|
||||
.in("state", ["review", "published", "unpublished"])
|
||||
.order("created_at");
|
||||
if (!isAdministrator(role))
|
||||
const adminView = request.nextUrl.searchParams.get("view") === "admin";
|
||||
if (adminView) requireAdministrator(role);
|
||||
if (!adminView)
|
||||
mapQuery = mapQuery.or(
|
||||
"state.eq.review,and(state.eq.published,update_state.eq.review)",
|
||||
);
|
||||
const maps = required(await mapQuery);
|
||||
const reports = isAdministrator(role)
|
||||
const reports = adminView
|
||||
? required(
|
||||
await client
|
||||
.from("reports")
|
||||
@@ -1080,7 +1100,7 @@ export async function handle(request: NextRequest, path: string[]) {
|
||||
.order("created_at"),
|
||||
)
|
||||
: [];
|
||||
const appeals = isAdministrator(role)
|
||||
const appeals = adminView
|
||||
? required(
|
||||
await client
|
||||
.from("map_appeals")
|
||||
|
||||
Reference in New Issue
Block a user