Files
MineGeo/lib/moderator-applications.ts
perfect_python500 3afad02686
Some checks failed
Deploy MineGeo to Dokploy / deploy (push) Failing after 1m41s
Add Telegram moderator application mini app
2026-11-01 10:21:05 +00:00

81 lines
2.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import "server-only";
import { createHash, randomBytes } from "node:crypto";
import { admin } from "@/lib/supabase";
const clean = (value: unknown) =>
String(value ?? "")
.replace(/[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/gu, " ")
.slice(0, 80);
export type ModeratorApplicationResult =
| { ok: true; url: string }
| { ok: false; message: string };
/**
* Creates a moderator application for a confirmed platform identity (Telegram
* or Discord user verified by the caller). The applicant still has to open the
* returned link while signed in to the matching MineGeo account.
*/
export async function createModeratorApplication(input: {
platform: "telegram" | "discord";
platformUserId: string;
platformUsername?: string | undefined;
requestedUsername: string;
origin: string;
}): Promise<ModeratorApplicationResult> {
const username = input.requestedUsername.trim().toLowerCase();
if (!/^[a-z0-9_]{3,32}$/.test(username))
return {
ok: false,
message:
"Use your MineGeo username (3–32 letters, numbers or underscores). Set it in MineGeo settings first.",
};
const database = admin();
const { data: allowed, error: rateError } = await database.rpc("check_rate", {
p_key: `mod-apply:${input.platform}:${input.platformUserId}`,
p_limit: 5,
p_seconds: 3600,
});
if (rateError || !allowed)
return { ok: false, message: "Too many attempts. Try again later." };
const { data: profile, error } = await database
.from("profiles")
.select("id,role,account_status")
.eq("username", username)
.maybeSingle();
if (error) return { ok: false, message: "MineGeo is unavailable. Try again later." };
if (!profile || profile.account_status !== "active")
return {
ok: false,
message: "MineGeo username not found. Check the username in your account settings.",
};
if (profile.role !== "contributor")
return { ok: false, message: "This account already has staff permissions." };
const { data: existing } = await database
.from("moderator_applications")
.select("id")
.eq("platform", input.platform)
.eq("platform_user_id", input.platformUserId)
.eq("status", "pending")
.limit(1);
if (existing?.length)
return { ok: false, message: "You already have an application waiting for review." };
const token = randomBytes(32).toString("hex");
const hash = createHash("sha256").update(token).digest("hex");
const insert = await database.from("moderator_applications").insert({
platform: input.platform,
platform_user_id: input.platformUserId,
platform_username: clean(input.platformUsername),
requested_username: username,
confirmation_hash: hash,
});
if (insert.error) {
console.error("Moderator application insert failed", insert.error);
return { ok: false, message: "MineGeo is unavailable. Try again later." };
}
return {
ok: true,
url: `${new URL(input.origin).origin}/moderator-application/${token}`,
};
}