Reapply "Add missing admin action files and navigation links"
This reverts commit 4d515bc400.
This commit is contained in:
1 parent
96ed768f14
commit
5b4228261a
338 files changed
+28143
-5948
No files matched your search
@@ -54,8 +54,7 @@ export async function recordRequest(ip: string): Promise<void> {
|
||||
if (!(await siteSettings.getBool("abuse_guard_enabled", false))) return;
|
||||
|
||||
const limit = Number(await siteSettings.get("abuse_guard_threshold", "200")) || 200;
|
||||
const windowMs =
|
||||
(Number(await siteSettings.get("abuse_guard_window_seconds", "10")) || 10) * 1000;
|
||||
const windowMs = (Number(await siteSettings.get("abuse_guard_window_seconds", "10")) || 10) * 1000;
|
||||
|
||||
const now = Date.now();
|
||||
if (buckets.size > 10_000) {
|
||||
|
||||
@@ -66,11 +66,7 @@ function alertEmail(): string | undefined {
|
||||
}
|
||||
|
||||
function escapeHtml(s: string): string {
|
||||
return s
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """);
|
||||
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,9 +140,7 @@ async function emailStaff(input: SendAlertInput): Promise<boolean> {
|
||||
const html =
|
||||
`<h2 style="margin:0 0 8px">${escapeHtml(input.type)} — ${escapeHtml(String(input.severity))}</h2>` +
|
||||
`<p style="margin:0 0 12px">${escapeHtml(input.message)}</p>` +
|
||||
(contextRows
|
||||
? `<table style="border-collapse:collapse;font-size:13px">${contextRows}</table>`
|
||||
: "") +
|
||||
(contextRows ? `<table style="border-collapse:collapse;font-size:13px">${contextRows}</table>` : "") +
|
||||
`<p style="margin-top:16px;color:#888;font-size:12px">` +
|
||||
`Sent by ${escapeHtml(env.HOTEL_NAME)} · <a href="${env.APP_URL}/admin/alerts">view alerts</a></p>`;
|
||||
|
||||
@@ -166,10 +160,7 @@ async function emailStaff(input: SendAlertInput): Promise<boolean> {
|
||||
*/
|
||||
export async function sendAlert(input: SendAlertInput): Promise<SendAlertResult> {
|
||||
// Fan out Discord + email first so we can record their outcome on the row.
|
||||
const [sentViaDiscord, sentViaEmail] = await Promise.all([
|
||||
postDiscord(input),
|
||||
emailStaff(input),
|
||||
]);
|
||||
const [sentViaDiscord, sentViaEmail] = await Promise.all([postDiscord(input), emailStaff(input)]);
|
||||
|
||||
let logged = false;
|
||||
try {
|
||||
|
||||
@@ -14,8 +14,7 @@ export interface IpVerdict {
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
const PRIVATE_RE =
|
||||
/^(127\.|10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.|::1|fc|fd|localhost$|0\.0\.0\.0$)/i;
|
||||
const PRIVATE_RE = /^(127\.|10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.|::1|fc|fd|localhost$|0\.0\.0\.0$)/i;
|
||||
|
||||
export async function checkVpn(ip: string): Promise<IpVerdict> {
|
||||
if (!ip || PRIVATE_RE.test(ip)) return { blocked: false };
|
||||
|
||||
@@ -39,9 +39,7 @@ async function loadWordFilter(): Promise<string[]> {
|
||||
const rows = await prisma.websiteWordfilter.findMany({
|
||||
select: { word: true },
|
||||
});
|
||||
wordFilterCache = rows
|
||||
.map((r) => r.word.trim().toLowerCase())
|
||||
.filter((w) => w.length > 0);
|
||||
wordFilterCache = rows.map((r) => r.word.trim().toLowerCase()).filter((w) => w.length > 0);
|
||||
wordFilterLoadedAt = now;
|
||||
} catch {
|
||||
// DB unavailable — return an empty filter WITHOUT caching, so the next
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
// carries `credits`, the in-game wallet), so a top-up credits the buyer's
|
||||
// `credits` wallet via sendCurrency(), exactly like the voucher flow.
|
||||
|
||||
export const PAYPAL_API =
|
||||
process.env.PAYPAL_API?.replace(/\/+$/, "") ?? "https://api-m.sandbox.paypal.com";
|
||||
export const PAYPAL_API = process.env.PAYPAL_API?.replace(/\/+$/, "") ?? "https://api-m.sandbox.paypal.com";
|
||||
|
||||
export const PAYPAL_CURRENCY = (process.env.PAYPAL_CURRENCY ?? "USD").toUpperCase();
|
||||
|
||||
@@ -28,9 +27,7 @@ function credentials(): { clientId: string; secret: string } {
|
||||
const clientId = process.env.PAYPAL_CLIENT_ID;
|
||||
const secret = process.env.PAYPAL_SECRET;
|
||||
if (!clientId || !secret) {
|
||||
throw new PayPalConfigError(
|
||||
"PayPal is not configured. Set PAYPAL_CLIENT_ID and PAYPAL_SECRET.",
|
||||
);
|
||||
throw new PayPalConfigError("PayPal is not configured. Set PAYPAL_CLIENT_ID and PAYPAL_SECRET.");
|
||||
}
|
||||
return { clientId, secret };
|
||||
}
|
||||
@@ -111,8 +108,7 @@ export async function createOrder(
|
||||
id: string;
|
||||
links?: { rel: string; href: string }[];
|
||||
};
|
||||
const approveUrl =
|
||||
json.links?.find((l) => l.rel === "approve" || l.rel === "payer-action")?.href ?? null;
|
||||
const approveUrl = json.links?.find((l) => l.rel === "approve" || l.rel === "payer-action")?.href ?? null;
|
||||
return { id: json.id, approveUrl };
|
||||
}
|
||||
|
||||
|
||||
@@ -11,10 +11,7 @@ const TYPE_VALUE: Record<Exclude<CurrencyName, "credits">, number> = {
|
||||
|
||||
export interface CurrencyDb {
|
||||
user: {
|
||||
update(args: {
|
||||
where: { id: number };
|
||||
data: { credits: { increment: number } };
|
||||
}): Promise<unknown>;
|
||||
update(args: { where: { id: number }; data: { credits: { increment: number } } }): Promise<unknown>;
|
||||
};
|
||||
usersCurrency: {
|
||||
upsert(args: {
|
||||
|
||||
Reference in new issue
Block a user