mirror of
https://github.com/trafficlunar/tomodachi-share.git
synced 2026-06-28 06:34:15 +00:00
feat: astro test
This commit is contained in:
parent
df6e31ba89
commit
84144c383c
262 changed files with 18993 additions and 2655 deletions
29
backend/src/app/api/admin/accept-mii/route.ts
Normal file
29
backend/src/app/api/admin/accept-mii/route.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
import { auth } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { idSchema } from "@tomodachi-share/shared/schemas";
|
||||
|
||||
export async function PATCH(request: NextRequest) {
|
||||
const session = await auth();
|
||||
if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
|
||||
if (Number(session.user?.id) !== Number(process.env.NEXT_PUBLIC_ADMIN_USER_ID)) return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
||||
|
||||
const searchParams = request.nextUrl.searchParams;
|
||||
const parsedMiiId = idSchema.safeParse(searchParams.get("id"));
|
||||
|
||||
if (!parsedMiiId.success) return NextResponse.json({ error: parsedMiiId.error.issues[0].message }, { status: 400 });
|
||||
const miiId = parsedMiiId.data;
|
||||
|
||||
await prisma.mii.update({
|
||||
where: {
|
||||
id: miiId,
|
||||
},
|
||||
data: {
|
||||
in_queue: false,
|
||||
},
|
||||
});
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
}
|
||||
30
backend/src/app/api/admin/banner/route.ts
Normal file
30
backend/src/app/api/admin/banner/route.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { auth } from "@/lib/auth";
|
||||
|
||||
let bannerText: string | null = null;
|
||||
|
||||
export async function GET() {
|
||||
return NextResponse.json({ success: true, message: bannerText });
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const session = await auth();
|
||||
if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
|
||||
if (Number(session.user?.id) !== Number(process.env.NEXT_PUBLIC_ADMIN_USER_ID)) return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
||||
|
||||
const body = await request.text();
|
||||
bannerText = body;
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
}
|
||||
|
||||
export async function DELETE() {
|
||||
const session = await auth();
|
||||
if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
|
||||
if (Number(session.user?.id) !== Number(process.env.NEXT_PUBLIC_ADMIN_USER_ID)) return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
||||
|
||||
bannerText = null;
|
||||
return NextResponse.json({ success: true });
|
||||
}
|
||||
22
backend/src/app/api/admin/can-submit/route.ts
Normal file
22
backend/src/app/api/admin/can-submit/route.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { z } from "zod";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { settings } from "@/lib/settings";
|
||||
|
||||
export async function GET() {
|
||||
return NextResponse.json({ success: true, value: settings.canSubmit });
|
||||
}
|
||||
|
||||
export async function PATCH(request: NextRequest) {
|
||||
const session = await auth();
|
||||
if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
|
||||
if (Number(session.user?.id) !== Number(process.env.NEXT_PUBLIC_ADMIN_USER_ID)) return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
||||
|
||||
const body = await request.json();
|
||||
const validated = z.boolean().safeParse(body);
|
||||
if (!validated.success) return NextResponse.json({ error: "Failed to validate body" }, { status: 400 });
|
||||
|
||||
settings.canSubmit = validated.data;
|
||||
return NextResponse.json({ success: true });
|
||||
}
|
||||
58
backend/src/app/api/admin/lookup/route.ts
Normal file
58
backend/src/app/api/admin/lookup/route.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
import { auth } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { idSchema } from "@tomodachi-share/shared/schemas";
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const session = await auth();
|
||||
if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
|
||||
if (Number(session.user?.id) !== Number(process.env.NEXT_PUBLIC_ADMIN_USER_ID)) return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
||||
|
||||
const searchParams = request.nextUrl.searchParams;
|
||||
const parsed = idSchema.safeParse(searchParams.get("id"));
|
||||
|
||||
if (!parsed.success) return NextResponse.json({ error: parsed.error.issues[0].message }, { status: 400 });
|
||||
const userId = parsed.data;
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {
|
||||
id: userId,
|
||||
},
|
||||
include: {
|
||||
punishments: {
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
type: true,
|
||||
returned: true,
|
||||
|
||||
notes: true,
|
||||
reasons: true,
|
||||
violatingMiis: {
|
||||
select: {
|
||||
miiId: true,
|
||||
reason: true,
|
||||
},
|
||||
},
|
||||
|
||||
expiresAt: true,
|
||||
createdAt: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) return NextResponse.json({ error: "No user found" }, { status: 404 });
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
name: user.name,
|
||||
image: user.image,
|
||||
createdAt: user.createdAt,
|
||||
punishments: user.punishments,
|
||||
});
|
||||
}
|
||||
87
backend/src/app/api/admin/punish/route.ts
Normal file
87
backend/src/app/api/admin/punish/route.ts
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
import { z } from "zod";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
import { auth } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { idSchema } from "@tomodachi-share/shared/schemas";
|
||||
import { PunishmentType } from "@prisma/client";
|
||||
|
||||
const punishSchema = z.object({
|
||||
type: z.enum([PunishmentType.WARNING, PunishmentType.TEMP_EXILE, PunishmentType.PERM_EXILE]),
|
||||
duration: z
|
||||
.number({ error: "Duration (days) must be a number" })
|
||||
.int({ error: "Duration (days) must be an integer" })
|
||||
.positive({ error: "Duration (days) must be valid" }),
|
||||
notes: z.string(),
|
||||
reasons: z.array(z.string()).optional(),
|
||||
miiReasons: z
|
||||
.array(
|
||||
z.object({
|
||||
id: z.number({ error: "Mii ID must be a number" }).int({ error: "Mii ID must be an integer" }).positive({ error: "Mii ID must be valid" }),
|
||||
reason: z.string(),
|
||||
}),
|
||||
)
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const session = await auth();
|
||||
if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
|
||||
if (Number(session.user?.id) !== Number(process.env.NEXT_PUBLIC_ADMIN_USER_ID)) return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
||||
|
||||
const searchParams = request.nextUrl.searchParams;
|
||||
const parsedUserId = idSchema.safeParse(searchParams.get("id"));
|
||||
|
||||
if (!parsedUserId.success) return NextResponse.json({ error: parsedUserId.error.issues[0].message }, { status: 400 });
|
||||
const userId = parsedUserId.data;
|
||||
|
||||
const body = await request.json();
|
||||
const parsed = punishSchema.safeParse(body);
|
||||
|
||||
if (!parsed.success) return NextResponse.json({ error: parsed.error.issues[0].message }, { status: 400 });
|
||||
const { type, duration, notes, reasons, miiReasons } = parsed.data;
|
||||
|
||||
const expiresAt = type === "TEMP_EXILE" ? dayjs().add(duration, "days").toDate() : null;
|
||||
|
||||
await prisma.punishment.create({
|
||||
data: {
|
||||
userId,
|
||||
type: type as PunishmentType,
|
||||
expiresAt,
|
||||
notes,
|
||||
reasons: reasons?.length !== 0 ? reasons : [],
|
||||
violatingMiis: {
|
||||
create: miiReasons?.map((mii) => ({
|
||||
miiId: mii.id,
|
||||
reason: mii.reason,
|
||||
})),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
}
|
||||
|
||||
export async function DELETE(request: NextRequest) {
|
||||
const session = await auth();
|
||||
if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
|
||||
if (Number(session.user?.id) !== Number(process.env.NEXT_PUBLIC_ADMIN_USER_ID)) return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
||||
|
||||
const searchParams = request.nextUrl.searchParams;
|
||||
const parsedPunishmentId = idSchema.safeParse(searchParams.get("id"));
|
||||
|
||||
if (!parsedPunishmentId.success) return NextResponse.json({ error: parsedPunishmentId.error.issues[0].message }, { status: 400 });
|
||||
const punishmentId = parsedPunishmentId.data;
|
||||
|
||||
await prisma.punishment.delete({
|
||||
where: {
|
||||
id: punishmentId,
|
||||
},
|
||||
});
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
}
|
||||
22
backend/src/app/api/admin/queue/route.ts
Normal file
22
backend/src/app/api/admin/queue/route.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { z } from "zod";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { settings } from "@/lib/settings";
|
||||
|
||||
export async function GET() {
|
||||
return NextResponse.json({ success: true, value: settings.queueEnabled });
|
||||
}
|
||||
|
||||
export async function PATCH(request: NextRequest) {
|
||||
const session = await auth();
|
||||
if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
|
||||
if (Number(session.user?.id) !== Number(process.env.NEXT_PUBLIC_ADMIN_USER_ID)) return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
||||
|
||||
const body = await request.json();
|
||||
const validated = z.boolean().safeParse(body);
|
||||
if (!validated.success) return NextResponse.json({ error: "Failed to validate body" }, { status: 400 });
|
||||
|
||||
settings.queueEnabled = validated.data;
|
||||
return NextResponse.json({ success: true });
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import { NextResponse } from "next/server";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { generateMetadataImage } from "@/lib/images";
|
||||
|
||||
export async function PATCH() {
|
||||
const session = await auth();
|
||||
if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
|
||||
if (Number(session.user?.id) !== Number(process.env.NEXT_PUBLIC_ADMIN_USER_ID)) return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
||||
|
||||
// Start processing in background
|
||||
regenerateImages().catch(console.error);
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
}
|
||||
|
||||
async function regenerateImages() {
|
||||
// Get miis in batches to reduce memory usage
|
||||
const BATCH_SIZE = 10;
|
||||
const totalMiis = await prisma.mii.count();
|
||||
let processed = 0;
|
||||
|
||||
for (let skip = 0; skip < totalMiis; skip += BATCH_SIZE) {
|
||||
const miis = await prisma.mii.findMany({
|
||||
skip,
|
||||
take: BATCH_SIZE,
|
||||
include: { user: { select: { name: true } } },
|
||||
});
|
||||
|
||||
// Process each batch sequentially to avoid overwhelming the server
|
||||
for (const mii of miis) {
|
||||
try {
|
||||
await generateMetadataImage(mii, mii.user.name);
|
||||
processed++;
|
||||
} catch (error) {
|
||||
console.error(`Failed to generate image for mii ${mii.id}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue