import config from "@stealth-developers/config"; import { editActor, getActorByPin, getActorByRobloxId, getToken } from "@stealth-developers/db"; import Elysia, { type Static, t } from "elysia"; import { generateWidget } from "@/lib/widgets/meow"; const syncPushResponseItem = t.Union([ t.Object( { user_id: t.String(), success: t.Literal(true), }, { title: "SyncPushResponseItemSuccess" }, ), t.Object( { user_id: t.String(), success: t.Literal(false), error: t.String(), }, { title: "SyncPushResponseItemError" }, ), ]); const syncPushResponse = t.Array(syncPushResponseItem); export type SyncPushResponseItem = Static; export type SyncPushResponse = Static; export const syncRoutes = new Elysia({ prefix: "/sync" }) .resolve(async ({ request }) => { const auth = request.headers.get("Authorization"); const tokenString = auth?.split(" ")[1]; if (!tokenString) throw new Error("Found no token, note that it must be prefixed with `Bearer`"); const token = await getToken(tokenString); if (!token) throw new Error("Invalid token"); return { token }; }) .post( "/verify", async ({ body, set }) => { const user = await getActorByPin(body.pin); if (!user) { set.status = 404; return { error: "Pin not found" }; } const expiryDate = new Date(user.pinExpiresIn!); if (expiryDate < new Date()) { set.status = 401; return { error: "Pin expired" }; } await editActor(user.id, { robloxId: body.roblox_user_id, pin: null, pinExpiresIn: null, }); return { success: true }; }, { tags: ["widgets"], body: t.Object({ pin: t.String(), roblox_user_id: t.String(), }), response: { 200: t.Object({ success: t.Boolean(), }), 401: t.Object({ error: t.String(), }), 404: t.Object({ error: t.String(), }), }, }, ) .post( "/push", async ({ body, set }) => { const results: SyncPushResponse = []; for (const user of body) { try { const actor = await getActorByRobloxId(user.user_id); if (!actor) { results.push({ user_id: user.user_id, success: false, error: "User not found", }); continue; } const discordId = actor.discordId; if (!discordId) { results.push({ user_id: user.user_id, success: false, error: "Discord ID not found", }); continue; } const widget = generateWidget({ kills: String(user.kills), deaths: String(user.deaths), matchesWon: String(user.wins), level: String(user.level), }); const response = await fetch( `https://discord.com/api/v10/applications/${config.discord.app_id}/users/${discordId}/identities/${user.user_id}/profile`, { method: "PATCH", headers: { Authorization: `Bot ${config.discord.token}`, "Content-Type": "application/json", }, body: JSON.stringify(widget), }, ); if (!response.ok) { const errorText = await response.text(); console.error( `Discord API Error for ${user.user_id}: ${response.status} - ${errorText}`, ); results.push({ user_id: user.user_id, success: false, error: `Discord API Error: ${response.status}`, }); continue; } results.push({ user_id: user.user_id, success: true, }); } catch (error) { console.error(`Unexpected error processing user ${user.user_id}:`, error); results.push({ user_id: user.user_id, success: false, error: error instanceof Error ? error.message : "Internal Server Error", }); } } const hasFailures = results.some((r) => !r.success); const hasSuccesses = results.some((r) => r.success); if (!hasFailures) set.status = 200; else if (hasSuccesses) set.status = 207; else set.status = 400; return results; }, { tags: ["widgets"], body: t.Array( t.Object({ user_id: t.String(), kills: t.Number(), deaths: t.Number(), wins: t.Number(), level: t.Number(), }), ), response: { 200: syncPushResponse, 207: syncPushResponse, 400: syncPushResponse, }, }, );