all repos — stealth-developers @ 3cf61e27a0383113ae6ea9547a7d27d692158402

api: remove discord.js dependency
vi did:web:vt3e.cat
Mon, 29 Jun 2026 04:17:09 +0100
commit

3cf61e27a0383113ae6ea9547a7d27d692158402

parent

53c15c459294561011bea319dfc5eb4f4f4523c3

M apps/api/package.jsonapps/api/package.json

@@ -2,6 +2,7 @@ {

"name": "@stealth-developers/api", "version": "1.0.50", "module": "src/index.js", + "types": "src/index.ts", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "bun run --watch src/index.ts"

@@ -9,9 +10,12 @@ },

"dependencies": { "@elysia/cors": "^1.4.2", "@elysia/openapi": "^1.4.15", + "@purrkit/ratelimit": "^1.0.1", + "@purrkit/rest": "^1.1.2", + "@purrkit/types": "^1.1.1", "@stealth-developers/config": "workspace:*", "@stealth-developers/db": "workspace:*", - "elysia": "latest" + "elysia": "^1.4.29" }, "devDependencies": { "bun-types": "latest"
M apps/api/src/discord.tsapps/api/src/discord.ts

@@ -1,7 +1,7 @@

import type { RESTError } from "discord-api-types/v10"; +import { RESTClient } from "@purrkit/rest"; import config from "@stealth-developers/config"; -import { REST } from "discord.js"; export interface OAuth2ErrorResponse { error:

@@ -13,8 +13,8 @@ | "invalid_scope";

error_description?: string; } -export const client = new REST({ - version: "10", +export const client = RESTClient({ + token: config.discord.token, }); export async function makeRequest<T>(

@@ -35,18 +35,11 @@ return (await response.json()) as T;

} export const createClient = (token: string) => { - console.log("Creating client with token:", token); - return new REST({ - version: "10", - }).setToken(token); -}; + const tokenType = token.startsWith("Bot") ? "Bot" : "Bearer"; + const endToken = token.replace(/^(Bot|Bearer)\s+/, ""); -export const isOAuth2Error = (data: unknown): data is OAuth2ErrorResponse => { - return typeof data === "object" && data !== null && "error" in data; -}; - -export const isError = (data: unknown): data is RESTError => { - return typeof data === "object" && data !== null && "code" in data; + return RESTClient({ + token: endToken, + tokenType, + }); }; - -client.setToken(config.discord.token);
M apps/api/src/index.tsapps/api/src/index.ts

@@ -7,7 +7,7 @@

import { authPlugin } from "./lib/auth"; import { getLinkedActors } from "./lib/db"; import { sanitiseActors, sanitiseUser } from "./lib/sanitise"; -import { ActorSchema, ErrorSchema, UserSchema } from "./lib/schemas"; +import { ActorWithGuildSchema, ErrorSchema, UserSchema } from "./lib/schemas"; import * as routes from "./routes"; const app = new Elysia()

@@ -38,14 +38,14 @@ ]);

return { user: user ? sanitiseUser(user) : null, - actors: sanitiseActors(actors), + actors: sanitiseActors(actors, false), }; }, { response: { 200: t.Object({ user: t.Nullable(UserSchema), - actors: t.Array(ActorSchema), + actors: t.Array(ActorWithGuildSchema), }), 401: ErrorSchema, 500: ErrorSchema,

@@ -59,3 +59,4 @@ .listen(config.api.port);

console.log(`🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`); export type App = typeof app; +export * from "./lib/schemas";
M apps/api/src/lib/db.tsapps/api/src/lib/db.ts

@@ -1,4 +1,4 @@

-import db, { TicketWithRelations, ticketWithRelations } from "@stealth-developers/db"; +import db, { Actor, Guild, TicketWithRelations, ticketWithRelations } from "@stealth-developers/db"; export interface PaginationOptions { limit?: number;

@@ -26,7 +26,8 @@

return ticketsList; } -export async function getLinkedActors(userId: number) { +export type ActorWithGuild = Actor & { guild: Guild | null }; +export async function getLinkedActors(userId: number): Promise<ActorWithGuild[]> { const actors = await db.query.actors.findMany({ where: { userId: userId,
M apps/api/src/lib/sanitise.tsapps/api/src/lib/sanitise.ts

@@ -9,6 +9,7 @@ TicketWithRelations,

User, } from "@stealth-developers/db"; +import { ActorWithGuild } from "./db"; import { type SanitisedActor, type SanitisedGuild,

@@ -62,7 +63,7 @@ return users.map(sanitiseUser);

} // Actor -export function sanitiseActor(actor: Actor): SanitisedActor { +export function sanitiseActor(actor: ActorWithGuild, excludeGuild = true): SanitisedActor { return { actorId: actor.id, guildId: actor.guildId,

@@ -75,11 +76,13 @@ avatar: actor.avatar,

createdAt: actor.createdAt, updatedAt: actor.updatedAt, + + ...(actor.guild && !excludeGuild ? { guild: sanitiseGuild(actor.guild) } : {}), }; } -export function sanitiseActors(actors: Actor[]): SanitisedActor[] { - return actors.map(sanitiseActor); +export function sanitiseActors(actors: ActorWithGuild[], excludeGuild = true): SanitisedActor[] { + return actors.map((actor) => sanitiseActor(actor, excludeGuild)); } // Ticket
M apps/api/src/lib/schemas.tsapps/api/src/lib/schemas.ts

@@ -39,6 +39,12 @@ updatedAt: t.Date(),

}); export type SanitisedActor = Static<typeof ActorSchema>; +export const ActorWithGuildSchema = t.Object({ + ...ActorSchema.properties, + guild: t.Nullable(GuildSchema), +}); +export type SanitisedActorWithGuild = Static<typeof ActorWithGuildSchema>; + export const TicketSchema = t.Object({ id: t.String(), guildId: t.Number(),
M apps/api/src/routes/auth.tsapps/api/src/routes/auth.ts

@@ -1,14 +1,10 @@

+import type { Error, ProvisionalTokenResponse } from "@purrkit/types"; + import { IS_PROD } from "@stealth-developers/config"; import db, { sessions, upsertUser } from "@stealth-developers/db"; -import { - RESTGetAPICurrentUserResult, - RESTPostOAuth2AccessTokenResult, - RouteBases, - Routes, -} from "discord.js"; import Elysia, { t } from "elysia"; -import { OAuth2ErrorResponse, isError, isOAuth2Error, makeRequest } from "../discord"; +import { createClient } from "../discord"; import { CLIENT_ID, CLIENT_SECRET, FRONTEND_URL, REDIRECT_URI } from "../lib/constants"; export const authRoutes = new Elysia().group("/auth", (app) =>

@@ -39,17 +35,13 @@ method: "POST",

body: params, headers: { "Content-Type": "application/x-www-form-urlencoded" }, }); - const tokenData = (await tokenResponse.json()) as - | OAuth2ErrorResponse - | RESTPostOAuth2AccessTokenResult; - if (isOAuth2Error(tokenData)) - throw new Error(tokenData.error_description ?? tokenData.error); + const tokenData = (await tokenResponse.json()) as Error | ProvisionalTokenResponse; + if ("code" in tokenData) throw new Error(tokenData.message); - const userData = await makeRequest<RESTGetAPICurrentUserResult>( - `${RouteBases.api}${Routes.user()}`, - tokenData.access_token, - ); - if (isError(userData)) throw new Error(userData.message); + const client = createClient(`${tokenData.token_type} ${tokenData.access_token}`); + const userRes = await client.users.me.get(); + if (!userRes.ok) throw new Error(userRes.data.message); + const userData = userRes.data; const upsertedUser = await upsertUser({ avatar: userData.avatar,