Fix admin permissions bounce, prefixes APIs, and Italian UI leftovers.
Local Build and Deploy / deploy (push) Successful in 56s

Gate permissions on ACL manage + resolve super-admin from live user ranks, restore prefixes API routes, and anglicize hardcoded admin copy with nav i18n.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
SimoandCursor committed 2026-07-15 20:33:00 +02:00
1 parent 0e89d03940
commit 3403d3b19f
38 files changed
+673 -208

No files matched your search

@@ -0,0 +1,48 @@
import { withAdmin } from "@/lib/api-handler";
import { apiError, apiOk } from "@/lib/api-response";
import { PERMS } from "@/lib/permissions";
import { prisma } from "@/lib/prisma";
interface BlacklistWord {
id: number;
word: string;
}
export const GET = withAdmin({ permission: PERMS.PREFIXES_VIEW }, async () => {
const words = await prisma.$queryRaw<BlacklistWord[]>`
SELECT id, word FROM custom_prefix_blacklist ORDER BY word ASC
`;
return apiOk({
words: words.map((w) => ({ ...w, created_at: "" })),
});
});
export const POST = withAdmin(
{ permission: PERMS.PREFIXES_EDIT },
async (request) => {
const body = await request.json();
const word = typeof body.word === "string" ? body.word.trim() : "";
if (!word) return apiError("Word is required");
await prisma.$executeRaw`INSERT INTO custom_prefix_blacklist (word) VALUES (${word})`;
return apiOk();
},
);
export const DELETE = withAdmin(
{ permission: PERMS.PREFIXES_EDIT },
async (request) => {
const body = await request.json();
const id = Number(body.id);
if (!Number.isInteger(id) || id <= 0)
return apiError("Missing or invalid word id");
await prisma.$executeRaw`DELETE FROM custom_prefix_blacklist WHERE id = ${id}`;
return apiOk({ deleted: id });
},
);
+124
View File
@@ -0,0 +1,124 @@
import { Prisma } from "@/generated/prisma/client";
import { withAdmin } from "@/lib/api-handler";
import { apiError, apiOk } from "@/lib/api-response";
import { PERMS } from "@/lib/permissions";
import { prisma } from "@/lib/prisma";
interface UserPrefix {
id: number;
user_id: number;
text: string;
color: string;
icon: string;
effect: string;
active: number;
username?: string | null;
}
export const GET = withAdmin(
{ permission: PERMS.PREFIXES_VIEW },
async (request) => {
const url = request.nextUrl;
const q = url.searchParams.get("q") || "";
const page = Math.max(1, Number(url.searchParams.get("page") || 1));
const limit = 50;
const offset = (page - 1) * limit;
const whereFragment = q
? Prisma.sql`WHERE up.text LIKE ${`%${q}%`} OR u.username LIKE ${`%${q}%`}`
: Prisma.empty;
const prefixes = await prisma.$queryRaw<UserPrefix[]>(
Prisma.sql`SELECT up.id, up.user_id, up.text, up.color, up.icon, up.effect, up.active, u.username
FROM custom_prefixes up
LEFT JOIN users u ON u.id = up.user_id
${whereFragment}
ORDER BY up.id DESC
LIMIT ${limit} OFFSET ${offset}`,
);
const countResult = await prisma.$queryRaw<[{ total: bigint }]>(
Prisma.sql`SELECT COUNT(*) as total FROM custom_prefixes up
LEFT JOIN users u ON u.id = up.user_id
${whereFragment}`,
);
const total = Number(countResult[0]?.total || 0);
return apiOk({
prefixes: prefixes.map((p) => ({
...p,
icon: p.icon || "",
effect: p.effect || "",
active: Boolean(p.active),
created_at: "",
})),
total,
page,
pages: Math.max(1, Math.ceil(total / limit)),
});
},
);
export const POST = withAdmin(
{ permission: PERMS.PREFIXES_EDIT },
async (request) => {
const body = await request.json();
const { username, text, color, icon, effect, active } = body;
if (!username || !text || !color) {
return apiError("Missing required fields: username, text, color");
}
const users = await prisma.$queryRaw<{ id: number }[]>(
Prisma.sql`SELECT id FROM users WHERE username = ${username} LIMIT 1`,
);
if (!users || users.length === 0) {
return apiError("User not found");
}
const userId = users[0].id;
await prisma.$executeRaw(
Prisma.sql`INSERT INTO custom_prefixes (user_id, text, color, icon, effect, active)
VALUES (${userId}, ${text}, ${color}, ${icon || ""}, ${effect || ""}, ${active ? 1 : 0})`,
);
return apiOk({ created: true });
},
);
export const PUT = withAdmin(
{ permission: PERMS.PREFIXES_EDIT },
async (request) => {
const body = await request.json();
const { id, text, color, icon, effect, active } = body;
if (!id) return apiError("Missing prefix id");
await prisma.$executeRaw(
Prisma.sql`UPDATE custom_prefixes
SET text = ${text}, color = ${color}, icon = ${icon || ""}, effect = ${effect || ""}, active = ${active ? 1 : 0}
WHERE id = ${id}`,
);
return apiOk({ updated: id });
},
);
export const DELETE = withAdmin(
{ permission: PERMS.PREFIXES_EDIT },
async (request) => {
const body = await request.json();
const id = body.id;
if (!id) return apiError("Missing prefix id");
await prisma.$executeRaw(
Prisma.sql`DELETE FROM custom_prefixes WHERE id = ${id}`,
);
return apiOk({ deleted: id });
},
);
@@ -0,0 +1,70 @@
import { withAdmin } from "@/lib/api-handler";
import { apiError, apiOk } from "@/lib/api-response";
import { PERMS } from "@/lib/permissions";
import { prisma } from "@/lib/prisma";
interface PrefixSetting {
key: string;
value: string;
}
const VALID_KEYS = new Set([
"enabled",
"max_length",
"min_rank",
"min_rank_to_buy",
"allow_colors",
"allow_bold",
"allow_italic",
"default_color",
"price_credits",
"price_points",
"points_type",
]);
const DEFAULT_SETTINGS: Record<string, string> = {
max_length: "15",
min_rank_to_buy: "1",
price_credits: "5",
price_points: "0",
points_type: "0",
};
export const GET = withAdmin({ permission: PERMS.PREFIXES_VIEW }, async () => {
const settings = await prisma.$queryRaw<PrefixSetting[]>`
SELECT \`key\`, \`value\` FROM custom_prefix_settings
`;
const result: Record<string, string> = { ...DEFAULT_SETTINGS };
for (const s of settings) {
result[s.key] = s.value;
}
return apiOk({ settings: result });
});
export const PUT = withAdmin(
{ permission: PERMS.PREFIXES_EDIT },
async (request) => {
const body = await request.json();
const settings = body.settings;
if (!settings || typeof settings !== "object") {
return apiError("Settings object is required");
}
for (const [key, value] of Object.entries(settings)) {
if (!VALID_KEYS.has(key)) {
return apiError(`Invalid setting key: ${key}`);
}
const strValue = String(value);
await prisma.$executeRaw`
INSERT INTO custom_prefix_settings (\`key\`, \`value\`)
VALUES (${key}, ${strValue})
ON DUPLICATE KEY UPDATE \`value\` = ${strValue}
`;
}
return apiOk();
},
);