all repos — stealth-developers @ 3420181c4dcd5388990032bbfd2808fdaec0516d

api,bot: impl sync endpoint
vi did:web:vt3e.cat
Sun, 05 Jul 2026 22:38:25 +0100
commit

3420181c4dcd5388990032bbfd2808fdaec0516d

parent

850ad778d793cddde0eb43ab1444dc59aed8a59b

M apps/api/src/app.tsapps/api/src/app.ts

@@ -28,6 +28,7 @@ .use(routes.authRoutes)

.group("/api", (app) => app .get("/health", () => "ok") + .use(routes.syncRoutes) .resolve(({ session, status }) => { if (!session) return status(401, { message: "unauthorized" }); return { session };
M apps/api/src/routes/index.tsapps/api/src/routes/index.ts

@@ -1,2 +1,3 @@

export { authRoutes } from "./auth"; export { guildRoutes } from "./guilds"; +export { syncRoutes } from "./sync";
A apps/api/src/routes/sync.ts

@@ -0,0 +1,33 @@

+import { editActor, getActorByPin } from "@stealth-developers/db"; +import Elysia, { t } from "elysia"; + +export const syncRoutes = new Elysia({ prefix: "/sync" }).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 }; + }, + { + body: t.Object({ + pin: t.String(), + roblox_user_id: t.String(), + }), + }, +);
M apps/bot/src/feats/index.tsapps/bot/src/feats/index.ts

@@ -1,5 +1,5 @@

import { bugCommands, bugComponents } from "./bugs"; -// import { syncCommand } from "./link"; +import { syncCommand } from "./link"; import { miscCommands } from "./misc"; import { robloxCommands } from "./roblox"; import { ticketCommands, ticketComponents } from "./tickets";

@@ -9,6 +9,6 @@ ...ticketCommands,

...miscCommands, ...bugCommands, ...robloxCommands, - // syncCommand, + syncCommand, ]; export const components = [...ticketComponents, ...bugComponents];
M apps/bot/src/feats/link/index.tsapps/bot/src/feats/link/index.ts

@@ -1,10 +1,11 @@

+import { Result } from "@sapphire/result"; import { editActor, getActor } from "@stealth-developers/db"; import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; import { logger } from "@/lib"; import { useGetData } from "@/middleware"; -import { getUser } from "../roblox/lib"; +import { client } from "../roblox/client"; export const syncCommand = useGetData.command("sync", { description: "Link your Roblox account to your Discord account",

@@ -103,12 +104,15 @@ content: "You are not linked to a Roblox account.",

flags: ["Ephemeral"], }); - const [account, accountError] = await getUser(actor.robloxId); - if (accountError) + const accountRes = await Result.fromAsync(client.getUserProfile(actor.robloxId)); + + if (accountRes.isErr()) return interaction.reply({ content: "Failed to fetch your Roblox account.", flags: ["Ephemeral"], }); + + const account = accountRes.unwrap(); interaction.reply({ content: `You are linked to Roblox account **@${account.name}**.`,