diff --git a/.env.example b/.env.example index ece814264..665a3591e 100644 --- a/.env.example +++ b/.env.example @@ -20,7 +20,7 @@ DISCORD_PROD_SERVER_ID="528728562780501729853789" DISCORD_PROD_VERIFY_CHANNEL_ID="5287378262780501729853789" DISCORD_SECRET_TOKEN="FHBAHJABDhbHSUeyi7398.S72ljd.HKBVAfug73gifa-74gfwiyB-BHJBDHV07hAJf83" HK_ENV="development" -INTERNAL_AUTH_KEY="bwgybgidbsi-4784gyfgs-475hhkdsbfs-bwgybgidbsi-4784gyfgs-475hhkdsbfs-bwgybgidbsi-4784gyfgs-475hhkdsbfs-fbnauib4783-bhfbsjfbs" +SHARED_SECRET="bwgybgidbsi-4784gyfgs-475hhkdsbfs-bwgybgidbsi-4784gyfgs-475hhkdsbfs-bwgybgidbsi-4784gyfgs-475hhkdsbfs-fbnauib4783-bhfbsjfbs" NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_YRGIBHSBIbabffjdhvbuYGI7BK" NEXT_PUBLIC_CLERK_SIGN_IN_URL="/sign-in" NEXT_PUBLIC_CLERK_SIGN_UP_URL="/sign-up" diff --git a/apps/bot/.gitignore b/apps/bot/.gitignore new file mode 100644 index 000000000..3e2212924 --- /dev/null +++ b/apps/bot/.gitignore @@ -0,0 +1 @@ +/dist \ No newline at end of file diff --git a/apps/bot/bot.ts b/apps/bot/bot.ts index 8a39735ca..ef08d0e04 100644 --- a/apps/bot/bot.ts +++ b/apps/bot/bot.ts @@ -1,23 +1,12 @@ +import { Client, Collection, GatewayIntentBits } from "discord.js"; import { - Client, - Collection, - Events, - GatewayIntentBits, - EmbedBuilder, - ButtonBuilder, - ButtonStyle, - ActionRowBuilder, -} from "discord.js"; -import { readdirSync } from "node:fs"; -import path from "node:path"; -import { Hono } from "hono"; -import { serve } from "bun"; -import c from "config"; -import { db } from "db"; -import { eq } from "db/drizzle"; -import { discordVerification } from "db/schema"; -import { getHacker } from "db/functions"; -import { nanoid } from "nanoid"; + loadCommands, + loadEvents, + loadInteractions, + loadReceivers, +} from "./utils/loaders"; +import express from "express"; +import sharedSecretMiddleware from "./middleware"; /* DISCORD BOT */ @@ -31,224 +20,24 @@ const client = new Client({ }); client.commands = new Collection(); +(client as any).interactions = new Collection(); -const commandsPath = path.join(__dirname, "commands"); -const commandFiles = readdirSync(commandsPath).filter((file) => - file.endsWith(".ts"), -); -for (const file of commandFiles) { - console.log(`[Loading Command] ${file}`); - const filePath = path.join(commandsPath, file); - const command = require(filePath); - // Set a new item in the Collection with the key as the command name and the value as the exported module - if ("data" in command && "execute" in command) { - client.commands.set(command.data.name, command); - } else { - console.log( - `[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`, - ); - } -} -console.log(`Loaded ${client.commands.size} Commands`); +// Load commands and events and interactions from according folders. +loadCommands(client); +loadEvents(client); +loadInteractions(client); -client.on(Events.InteractionCreate, async (interaction) => { - if (interaction.isChatInputCommand()) { - const command = interaction.client.commands.get( - interaction.commandName, - ); +// Start an Express server for webhooks so other applications can communicate with the bot. +const expressApp = express(); +expressApp.use(express.json()); +expressApp.use(sharedSecretMiddleware); - if (!command) { - console.error( - `No command matching ${interaction.commandName} was found.`, - ); - return; - } +//Load receivers for bot's webhooks +loadReceivers(expressApp, client); - try { - await command.execute(interaction); - } catch (error) { - console.error(error); - if (interaction.replied || interaction.deferred) { - await interaction.followUp({ - content: "There was an error while executing this command!", - ephemeral: true, - }); - } else { - await interaction.reply({ - content: "There was an error while executing this command!", - ephemeral: true, - }); - } - } - } else if (interaction.isButton()) { - if (interaction.customId === "verify") { - console.log("Button Pressed"); - const user = interaction.member?.user; - if (!user) { - interaction.reply({ - content: "There was an error while executing this command!", - ephemeral: true, - }); - return; - } - const vCode = nanoid(20); - console.log(interaction.guildId); - const verification = await db - .insert(discordVerification) - .values({ - code: vCode, - discordName: user.username, - discordProfilePhoto: user.avatar || "", - discordUserID: user.id as string, - discordUserTag: user.discriminator as string, - status: "pending", - guild: interaction.guildId as string, - }) - .returning(); - - interaction.reply({ - content: `Please click [this link](${c.siteUrl}/discord-verify?code=${vCode}) to verify your registration!`, - ephemeral: true, - }); - } - } +const RECEIVERS_PORT = process.env.PORT ? parseInt(process.env.PORT) : 4000; +expressApp.listen(RECEIVERS_PORT, () => { + console.log(`Bot receivers listening on port ${RECEIVERS_PORT}`); }); client.login(process.env.DISCORD_SECRET_TOKEN); - -/* WEB SERVER */ - -const app = new Hono(); -app.get("/postMsgToServer", (h) => { - const internalAuthKey = h.req.query("access"); - const serverType: string | undefined | "dev" | "prod" = h.req.query("env"); - if (!internalAuthKey || internalAuthKey != process.env.INTERNAL_AUTH_KEY) { - return h.text("access denied"); - } - if (!serverType || (serverType !== "dev" && serverType !== "prod")) { - return h.text("invalid env"); - } - - const verifyBtn = new ButtonBuilder() - .setCustomId("verify") - .setLabel("Verify") - .setStyle(ButtonStyle.Primary); - - const verifyEmbed = new EmbedBuilder() - .setColor(0x0099ff) - .setTitle("Verification") - .setURL(c.siteUrl) - .setAuthor({ - name: c.botName, - iconURL: c.siteUrl + c.icon.md, - url: c.siteUrl, - }) - .setDescription( - `**Verify your registration for ${c.hackathonName} ${c.itteration} to gain access to the rest of the server!**\n\nClick the "verify" button below to begin the verification process.\n\u200B`, - ) - .setThumbnail(`${c.siteUrl}${c.icon.md}`) - .setFooter({ - text: "Questions or issues? Contact an organizer :)", - iconURL: "https://static.acmutsa.org/Info_Simple.svg.png", - }); - - const channel = client.channels.cache.get( - serverType === "dev" - ? (process.env.DISCORD_DEV_VERIFY_CHANNEL_ID as string) - : (process.env.DISCORD_PROD_VERIFY_CHANNEL_ID as string), - ); - - if (!channel || !channel.isTextBased()) { - return h.text("Invalid channel"); - } - - const row = new ActionRowBuilder().addComponents(verifyBtn); - - channel.send({ - embeds: [verifyEmbed], - components: [row], - }); - return h.text(`Posted to channel!`); -}); - -app.get("/health", (h) => { - return h.text("ok"); -}); - -app.post("/api/checkDiscordVerification", async (h) => { - const body = await h.req.json(); - const internalAuthKey = h.req.query("access"); - if (!internalAuthKey || internalAuthKey != process.env.INTERNAL_AUTH_KEY) { - console.log("denied access"); - return h.text("access denied"); - } - - if (body.code === undefined || typeof body.code !== "string") { - console.log("failed cause of malformed body"); - return h.json({ success: false }); - } - - console.log("got here 1"); - const verification = await db.query.discordVerification.findFirst({ - where: eq(discordVerification.code, body.code), - }); - console.log("got here 1 with verification ", verification); - - if (!verification || !verification.clerkID) { - console.log("failed cause of no verification or missing clerkID"); - return h.json({ success: false }); - } - console.log("got here 2"); - - const user = await getHacker(verification.clerkID); - console.log("got here 2 with user", user); - if (!user) { - console.log("failed cause of no user in db"); - return h.json({ success: false }); - } - - const { discordRole: userGroupRoleName } = ( - c.groups as Record - )[Object.keys(c.groups)[user.hackerData.group]]; - - const guild = client.guilds.cache.get(verification.guild); - if (!guild) { - console.log("failed cause of no guild on intereaction"); - return h.json({ success: false }); - } - - const role = guild.roles.cache.find( - (role) => role.name === c.botParticipantRole, - ); - const userGroupRole = guild.roles.cache.find( - (role) => role.name === userGroupRoleName, - ); - - if (!role || !userGroupRole) { - console.log( - "failed cause could not find a role, was looking for group " + - user.hackerData.group + - " called " + - userGroupRoleName, - ); - return h.json({ success: false }); - } - - const member = guild.members.cache.get(verification.discordUserID); - - if (!member) { - console.log("failed cause could not find member"); - return h.json({ success: false }); - } - - await member.roles.add(role); - await member.roles.add(userGroupRole); - await member.setNickname(user.firstName + " " + user.lastName); - - return h.json({ success: true }); -}); - -serve({ - fetch: app.fetch, - port: process.env.PORT ? parseInt(process.env.PORT) : 4000, -}); diff --git a/apps/bot/commands/postVerify.ts b/apps/bot/commands/postVerify.ts new file mode 100644 index 000000000..36041f349 --- /dev/null +++ b/apps/bot/commands/postVerify.ts @@ -0,0 +1,86 @@ +import { + SlashCommandBuilder, + ChatInputCommandInteraction, + EmbedBuilder, + ButtonBuilder, + ButtonStyle, + ActionRowBuilder, + PermissionsBitField, +} from "discord.js"; +import c from "config"; + +export const data = new SlashCommandBuilder() + .setName("post-verify") + .setDescription("Post the verification embed to this channel (admin only)"); + +export const execute = async (interaction: ChatInputCommandInteraction) => { + // Ensure command is used in a guild + if (!interaction.inGuild() || !interaction.guild) { + await interaction.reply({ + content: "This command can only be used in a server.", + ephemeral: true, + }); + return; + } + + // Check for administrator permission + const hasAdmin = interaction.memberPermissions?.has( + PermissionsBitField.Flags.Administrator, + ); + if (!hasAdmin) { + await interaction.reply({ + content: + "You must have Administrator permissions to run this command.", + ephemeral: true, + }); + return; + } + + const channel = interaction.channel; + if (!channel || !channel.isTextBased()) { + await interaction.reply({ + content: "This channel cannot receive messages from the bot.", + ephemeral: true, + }); + return; + } + + const verifyBtn = new ButtonBuilder() + .setCustomId("verify") + .setLabel("Verify") + .setStyle(ButtonStyle.Primary); + + const verifyEmbed = new EmbedBuilder() + .setColor(0x0099ff) + .setTitle("Verification") + .setURL(c.siteUrl) + .setAuthor({ + name: c.botName, + iconURL: c.siteUrl + c.icon.md, + url: c.siteUrl, + }) + .setDescription( + `**Verify your registration for ${c.hackathonName} ${c.itteration} to gain access to the rest of the server!**\n\nClick the "verify" button below to begin the verification process.\n\u200B`, + ) + .setThumbnail(`${c.siteUrl}${c.icon.md}`) + .setFooter({ + text: "Questions or issues? Contact an organizer :)", + iconURL: "https://static.acmutsa.org/Info_Simple.svg.png", + }); + + const row = new ActionRowBuilder().addComponents(verifyBtn); + + try { + await channel.send({ embeds: [verifyEmbed], components: [row] }); + await interaction.reply({ + content: "Posted verification message.", + ephemeral: true, + }); + } catch (err) { + console.error("Failed to post verification message:", err); + await interaction.reply({ + content: "Failed to post verification message.", + ephemeral: true, + }); + } +}; diff --git a/apps/bot/deploy-commands.ts b/apps/bot/deploy-commands.ts deleted file mode 100644 index 32d67e8ad..000000000 --- a/apps/bot/deploy-commands.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { REST, Routes } from "discord.js"; -import fs from "node:fs"; -import path from "node:path"; - -const args = process.execArgv; - -if (!args.includes("--prod") && !args.includes("--dev")) { - console.error( - "You must specify either --prod or --dev when deploying commands.", - ); - process.exit(1); -} - -const runType: "dev" | "prod" = args.includes("--prod") ? "prod" : "dev"; - -const commands = []; -// Grab all the command folders from the commands directory you created earlier -const commandsPath = path.join(__dirname, "commands"); -const commandFiles = fs - .readdirSync(commandsPath) - .filter((file) => file.endsWith(".ts")); -// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment -for (const file of commandFiles) { - const filePath = path.join(commandsPath, file); - const command = require(filePath); - if ("data" in command && "execute" in command) { - commands.push(command.data.toJSON()); - } else { - console.log( - `[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`, - ); - } -} - -// Construct and prepare an instance of the REST module -const rest = new REST().setToken(process.env.DISCORD_SECRET_TOKEN as string); - -// and deploy your commands! -(async () => { - try { - console.log( - `Started refreshing ${commands.length} application (/) commands.`, - ); - - // The put method is used to fully refresh all commands in the guild with the current set - if (runType === "dev") { - const data = await rest.put( - Routes.applicationGuildCommands( - process.env.DISCORD_CLIENT_ID as string, - process.env.DISCORD_DEV_SERVER_ID as string, - ), - { - body: commands, - }, - ); - console.log( - `Successfully reloaded ${(data as any).length} application (/) commands.`, - ); - } else { - const data = await rest.put( - Routes.applicationGuildCommands( - process.env.DISCORD_CLIENT_ID as string, - process.env.DISCORD_PROD_SERVER_ID as string, - ), - { - body: commands, - }, - ); - await rest.put( - Routes.applicationCommands( - process.env.DISCORD_CLIENT_ID as string, - ), - { - body: commands, - }, - ); - console.log( - `Successfully reloaded ${(data as any).length} application (/) commands.`, - ); - } - } catch (error) { - // And of course, make sure you catch and log any errors! - console.error(error); - } -})(); diff --git a/apps/bot/events/interactionCreate.ts b/apps/bot/events/interactionCreate.ts new file mode 100644 index 000000000..48e0ac442 --- /dev/null +++ b/apps/bot/events/interactionCreate.ts @@ -0,0 +1,59 @@ +export const name = "interactionCreate"; +export const once = false; + +export async function execute(interaction: any) { + try { + if (interaction.isChatInputCommand()) { + const command = interaction.client.commands.get( + interaction.commandName, + ); + + if (!command) { + console.error( + `No command matching ${interaction.commandName} was found.`, + ); + return; + } + + try { + await command.execute(interaction); + } catch (error) { + console.error(error); + if (interaction.replied || interaction.deferred) { + await interaction.followUp({ + content: + "There was an error while executing this command!", + ephemeral: true, + }); + } else { + await interaction.reply({ + content: + "There was an error while executing this command!", + ephemeral: true, + }); + } + } + } else if (interaction.isButton()) { + // Dispatch button interactions to loaded interaction handlers keyed by customId + const handler = interaction.client.interactions?.get( + interaction.customId, + ); + if (!handler) { + console.warn( + `No interaction handler for id ${interaction.customId}`, + ); + return; + } + try { + await handler.execute(interaction); + } catch (err) { + console.error( + `Error executing interaction handler ${interaction.customId}:`, + err, + ); + } + } + } catch (err) { + console.error("Error in interaction handler:", err); + } +} diff --git a/apps/bot/events/ready.ts b/apps/bot/events/ready.ts new file mode 100644 index 000000000..9d3ee8055 --- /dev/null +++ b/apps/bot/events/ready.ts @@ -0,0 +1,57 @@ +import { db } from "db"; +import { discordVerification } from "db/schema"; +import { REST, Routes } from "discord.js"; + +export const name = "ready"; +export const once = true; +export async function execute(client: any) { + try { + console.log(`Ready! Logged in as ${client.user?.tag}`); + + await db.delete(discordVerification).all(); + console.log("Cleared discord verification entries from database."); + + const token: string | undefined = process.env.DISCORD_SECRET_TOKEN; + const clientId: string | undefined = process.env.DISCORD_CLIENT_ID; + if (!token || !clientId) { + console.warn( + "Missing DISCORD_SECRET_TOKEN or DISCORD_CLIENT_ID; skipping command registration.", + ); + return; + } + + // Only deploy when the process is started with --deploy + const shouldDeploy = process.argv.includes("--deploy"); + if (!shouldDeploy) { + console.warn( + "Command deployment skipped; run the bot with --deploy to deploy application commands.", + ); + return; + } + + const commands: any[] = []; + client.commands?.forEach((cmd: any) => { + if (cmd?.data && typeof cmd.data.toJSON === "function") { + commands.push(cmd.data.toJSON()); + } + }); + + const rest = new REST({ version: "10" }).setToken(token); + + await rest.put(Routes.applicationCommands(clientId), { body: [] }); + console.log("Deleted existing application commands."); + + if (commands.length === 0) { + console.warn("No commands to register after deletion."); + return; + } + + // Register globally; propagate changes may take up to 1 hour. + await rest.put(Routes.applicationCommands(clientId), { + body: commands, + }); + console.log(`Registered ${commands.length} application commands.`); + } catch (err) { + console.error("Error in ready handler:", err); + } +} diff --git a/apps/bot/interactions/verify.ts b/apps/bot/interactions/verify.ts new file mode 100644 index 000000000..861c23af9 --- /dev/null +++ b/apps/bot/interactions/verify.ts @@ -0,0 +1,53 @@ +import c from "config"; +import { db } from "db"; +import { discordVerification } from "db/schema"; +import { nanoid } from "nanoid"; + +export const id = "verify"; + +export async function execute(interaction: any) { + try { + console.log("Verify interaction triggered"); + const user = interaction.member?.user; + if (!user) { + await interaction.reply({ + content: "There was an error while executing this interaction!", + ephemeral: true, + }); + return; + } + + const vCode = nanoid(20); + const verification = await db + .insert(discordVerification) + .values({ + code: vCode, + discordName: user.username, + discordProfilePhoto: user.avatar || "", + discordUserID: user.id as string, + discordUserTag: user.discriminator as string, + status: "pending", + guild: interaction.guildId as string, + }) + .returning(); + + await interaction.reply({ + content: `Please click [this link](${c.siteUrl}/discord-verify?code=${vCode}) to verify your registration!`, + ephemeral: true, + }); + } catch (err) { + console.error("Error in verify interaction:", err); + try { + if (!interaction.replied && !interaction.deferred) { + await interaction.reply({ + content: "Internal error", + ephemeral: true, + }); + } + } catch (e) { + console.error("Failed to reply after verify error:", e); + } + } +} + +export default { id, execute }; diff --git a/apps/bot/middleware.ts b/apps/bot/middleware.ts new file mode 100644 index 000000000..c1146cbe5 --- /dev/null +++ b/apps/bot/middleware.ts @@ -0,0 +1,27 @@ +import { Request, Response, NextFunction } from "express"; + +export function sharedSecretMiddleware( + req: Request, + res: Response, + next: NextFunction, +) { + const expected = process.env.SHARED_SECRET; + if (!expected) { + console.error("SHARED_SECRET not configured"); + process.exit(1); + return next(); + } + + const provided = (req.header("X-Shared-Secret") as string) || ""; + if (!provided || provided !== expected) { + res.status(401).json({ + success: false, + error: "invalid_shared_secret", + }); + return; + } + + return next(); +} + +export default sharedSecretMiddleware; diff --git a/apps/bot/package.json b/apps/bot/package.json index 91291941d..dfc59be5e 100644 --- a/apps/bot/package.json +++ b/apps/bot/package.json @@ -4,10 +4,9 @@ "description": "", "main": "bot.ts", "scripts": { - "dev": "bun run --env-file=../../.env --hot bot.ts", - "start": "bun run --env-file=../../.env ./bot.ts", - "deploy:dev": "bun run --env-file=../../.env --dev deploy-commands.ts", - "deploy:prod": "bun run --env-file=../../.env --prod deploy-commands.ts" + "dev": "tsx watch --env-file=../../.env bot.ts", + "build": "tsc -p tsconfig.json", + "start": "tsx --env-file=../../.env bot.ts" }, "keywords": [], "author": "", @@ -16,12 +15,15 @@ "config": "workspace:*", "db": "workspace:*", "discord.js": "^14.15.3", - "hono": "^4.5.0", + "express": "^5.2.1", "nanoid": "^5.0.7", "zod": "^3.25.67" }, "devDependencies": { - "@types/bun": "^1.1.6" + "@types/express": "^5.0.6", + "@types/node": "20.14.11", + "tsx": "^4.0.0", + "typescript": "5.5.3" }, "packageManager": "pnpm@8.3.1" } diff --git a/apps/bot/receivers/discord_verification.ts b/apps/bot/receivers/discord_verification.ts new file mode 100644 index 000000000..c8a9ba556 --- /dev/null +++ b/apps/bot/receivers/discord_verification.ts @@ -0,0 +1,86 @@ +import { Request, Response } from "express"; +import { db } from "db"; +import { eq } from "db/drizzle"; +import { discordVerification } from "db/schema"; +import { getHacker } from "db/functions"; +import c from "config"; +import { RequestWithClient } from "../utils/loaders"; + +export async function handler(req: RequestWithClient, res: Response) { + const client = req.client; + try { + const body = req.body; + if (!body || typeof body.code !== "string") { + return res + .status(400) + .json({ success: false, error: "missing_code" }); + } + + const verification = await db.query.discordVerification.findFirst({ + where: eq(discordVerification.code, body.code), + }); + + if (!verification || !verification.clerkID) { + console.log("failed cause of no verification or missing clerkID"); + return res.json({ success: false }); + } + + const user = await getHacker(verification.clerkID); + if (!user) { + console.log("failed cause of no user in db"); + return res.json({ success: false }); + } + + const userGroupRoleName = ( + c.groups as Record + )[Object.keys(c.groups)[user.hackerData.group]].discordRole; + + console.log(userGroupRoleName); + + if (!client) { + console.error( + "No client mounted on request in discordVerification handler", + ); + return res.status(500).json({ success: false }); + } + + const guild = client.guilds.cache.get(verification.guild); + if (!guild) { + console.log("failed cause of no guild on interaction"); + return res.json({ success: false }); + } + + const role = guild.roles.cache.find( + (r: any) => r.name === c.botParticipantRole, + ); + const userGroupRole = guild.roles.cache.find( + (r: any) => r.name === userGroupRoleName, + ); + + if (!role || !userGroupRole) { + console.log( + "failed cause could not find a role", + role, + userGroupRole, + ); + return res.json({ success: false }); + } + + const member = guild.members.cache.get(verification.discordUserID); + if (!member) { + console.log("failed cause could not find member"); + return res.json({ success: false }); + } + + await member.roles.add(role); + await member.roles.add(userGroupRole); + await member.setNickname(user.firstName + " " + user.lastName); + + return res.json({ success: true }); + } catch (err) { + console.error("Error in discord-verification receiver:", err); + return res.status(500).json({ success: false }); + } +} + +export default { handler }; diff --git a/apps/bot/tsconfig.json b/apps/bot/tsconfig.json index 90c53c403..261496d6f 100644 --- a/apps/bot/tsconfig.json +++ b/apps/bot/tsconfig.json @@ -1,14 +1,16 @@ { "compilerOptions": { - "typeRoots": ["src/@types", "node_modules/@types"], + "typeRoots": ["./@types", "node_modules/@types"], "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "isolatedModules": true, "moduleResolution": "node", "preserveWatchOutput": true, "skipLibCheck": true, - "noEmit": true, - "strict": true - }, - "exclude": ["node_modules"] + "strict": true, + "outDir": "dist", + "noEmit": false, + "target": "ES2020", + "module": "CommonJS" + } } diff --git a/apps/bot/utils/loaders.ts b/apps/bot/utils/loaders.ts new file mode 100644 index 000000000..77e7499dd --- /dev/null +++ b/apps/bot/utils/loaders.ts @@ -0,0 +1,154 @@ +import { ne } from "db"; +import { NextFunction, Request, Response } from "express"; +import fs from "node:fs"; +import path from "node:path"; + +export function loadCommands(client: any) { + const commandsPath = path.join(__dirname, "../commands"); + if (!fs.existsSync(commandsPath)) { + console.log("No commands folder found, aborting.."); + process.exit(1); + return; + } + const commandFiles = fs + .readdirSync(commandsPath) + .filter((file) => file.endsWith(".ts") || file.endsWith(".js")); + for (const file of commandFiles) { + try { + console.log(`[Loading Command] ${file}`); + const filePath = path.join(commandsPath, file); + const command = require(filePath); + if (command && "data" in command && "execute" in command) { + client.commands.set(command.data.name, command); + } else { + console.warn( + `The command at ${filePath} is missing a required "data" or "execute" property.`, + ); + } + } catch (err) { + console.error(`Failed loading command ${file}:`, err); + } + } + console.log(`Loaded ${client.commands?.size ?? 0} Commands`); +} + +export function loadEvents(client: any) { + const eventsPath = path.join(__dirname, "../events"); + if (!fs.existsSync(eventsPath)) { + console.log("No events folder found, aborting.."); + process.exit(1); + return; + } + const eventFiles = fs + .readdirSync(eventsPath) + .filter((file) => file.endsWith(".ts") || file.endsWith(".js")); + for (const file of eventFiles) { + try { + const filePath = path.join(eventsPath, file); + const event = require(filePath); + if (!event || !event.name || !event.execute) { + console.warn( + `Event at ${filePath} is missing name or execute.`, + ); + continue; + } + if (event.once) { + client.once(event.name, (...args: any[]) => + event.execute(...args), + ); + } else { + client.on(event.name, (...args: any[]) => + event.execute(...args), + ); + } + console.log(`[Loaded Event] ${event.name}`); + } catch (err) { + console.error(`Failed loading event ${file}:`, err); + } + } +} + +export function loadInteractions(client: any) { + const interactionsPath = path.join(__dirname, "../interactions"); + if (!fs.existsSync(interactionsPath)) { + console.log("No interactions folder found, aborting.."); + process.exit(1); + return; + } + const interactionFiles = fs + .readdirSync(interactionsPath) + .filter((file) => file.endsWith(".ts") || file.endsWith(".js")); + for (const file of interactionFiles) { + try { + console.log(`[Loading Interaction] ${file}`); + const filePath = path.join(interactionsPath, file); + const interaction = require(filePath); + // interaction should export `id` and `execute` or `customId` and `execute` + const key = + interaction.id || interaction.customId || interaction.name; + if (!key || !interaction.execute) { + console.warn( + `Interaction at ${filePath} is missing id/customId/name or execute.`, + ); + continue; + } + client.interactions.set(key, interaction); + } catch (err) { + console.error(`Failed loading interaction ${file}:`, err); + } + } + console.log(`Loaded ${client.interactions?.size ?? 0} Interactions`); +} + +export default { loadCommands, loadEvents, loadInteractions }; + +export type RequestWithClient = Request & { client?: any }; + +export function loadReceivers(app: any, client: any) { + const receiversPath = path.join(__dirname, "../receivers"); + if (!fs.existsSync(receiversPath)) { + console.log("No receivers folder found, aborting.."); + process.exit(1); + return; + } + const receiverFiles = fs + .readdirSync(receiversPath) + .filter((file) => file.endsWith(".ts") || file.endsWith(".js")); + for (const file of receiverFiles) { + try { + const filePath = path.join(receiversPath, file); + const receiver = require(filePath); + + const base = path.basename(file, path.extname(file)); + const kebab = base + .replace(/([a-z])([A-Z])/g, "$1-$2") + .replace(/_/g, "-") + .toLowerCase(); + const route = `/${kebab}`; + + console.log(`[Loading Receiver] ${route}`); + + if (receiver && "handler" in receiver) { + app.post( + route, + ( + req: RequestWithClient, + res: Response, + next: NextFunction, + ) => { + req.client = client; + next(); + }, + receiver.handler, + ); + } else { + console.warn( + `The receiver at ${filePath} is missing a required "handler" property.`, + ); + } + } catch (err) { + console.error(`Failed loading receiver ${file}:`, err); + } + } + console.log(`Loaded receivers: ${receiverFiles.length}`); +} diff --git a/apps/web/src/actions/discord-verify.ts b/apps/web/src/actions/discord-verify.ts index 970cd8c13..02019501c 100644 --- a/apps/web/src/actions/discord-verify.ts +++ b/apps/web/src/actions/discord-verify.ts @@ -26,27 +26,27 @@ export const confirmVerifyDiscord = authenticatedAction }; } - // TODO: set some kind of thing that will revert the verification if the bot api call fails - await db .update(discordVerification) .set({ status: "accepted", clerkID: userId }) .where(eq(discordVerification.code, code)); - const res = await fetch( - env.BOT_API_URL + - "/api/checkDiscordVerification?access=" + - env.INTERNAL_AUTH_KEY, - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ code }), + // Call bot receivers endpoint with shared secret header + const res = await fetch(`${env.BOT_API_URL}/discord-verification`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-Shared-Secret": env.SHARED_SECRET, }, - ); - let resJson = await res.json(); - console.log(resJson); + body: JSON.stringify({ code }), + }); + let resJson = {}; + try { + resJson = await res.json(); + console.log(resJson); + } catch (e) { + console.warn("discord receiver returned no JSON", e); + } return { success: true, diff --git a/apps/web/src/app/discord-verify/page.tsx b/apps/web/src/app/discord-verify/page.tsx index 10c00a00d..eb0e1550a 100644 --- a/apps/web/src/app/discord-verify/page.tsx +++ b/apps/web/src/app/discord-verify/page.tsx @@ -20,8 +20,6 @@ export default async function Page({ const passedCode = searchParams?.code; if (!passedCode || typeof passedCode !== "string") { - console.log("no code"); - console.log(passedCode); return notFound(); } diff --git a/apps/web/src/env.ts b/apps/web/src/env.ts index 29cab8a34..4bf7d1c8b 100644 --- a/apps/web/src/env.ts +++ b/apps/web/src/env.ts @@ -8,8 +8,8 @@ export const env = createEnv({ AWS_SES_SECRET_ACCESS_KEY: z.string(), AWS_REGION: z.string(), AWS_SES_EMAIL_FROM: z.string(), - INTERNAL_AUTH_KEY: z.string().min(64, { - message: "INTERNAL_AUTH_KEY must be at least 64 characters", + SHARED_SECRET: z.string().min(64, { + message: "SHARED_SECRET must be at least 64 characters", }), BOT_API_URL: z.string(), HK_ENV: z.string().min(1), diff --git a/package.json b/package.json index 53c6781a0..e53b6f0f8 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,6 @@ "migrations:drop": "pnpm --filter=db migrations:drop", "studio": "pnpm --filter=db studio", "tunnel": "pnpm --filter=devtunnel opentunnel", - "deploy-discord:dev": "pnpm --filter=bot deploy:dev", - "deploy-discord:prod": "pnpm --filter=bot deploy:prod", "format": "prettier --write .", "format-check": "prettier --check ." }, diff --git a/packages/config/hackkit.config.ts b/packages/config/hackkit.config.ts index 5957964c9..358c2883b 100644 --- a/packages/config/hackkit.config.ts +++ b/packages/config/hackkit.config.ts @@ -808,7 +808,7 @@ const softwareExperienceOptions = [ const c = { hackathonName: "HackKit", itteration: "I", - siteUrl: "https://rowdyhacks.org", // Do not have a trailing slash + siteUrl: "http://localhost:3000", // Do not have a trailing slash defaultMetaDataDescription: "Your Metadata Description Here", rsvpDefaultLimit: 500, botName: "HackKit", @@ -855,34 +855,34 @@ const c = { }, groups: { "Guild A | Group A": { - discordRole: "Guild A Role", + discordRole: "Group A", }, "Guild A | Group B": { - discordRole: "Guild A Role", + discordRole: "Group A", }, "Guild B | Group A": { - discordRole: "Guild B Role", + discordRole: "Group B", }, "Guild B | Group B": { - discordRole: "Guild B Role", + discordRole: "Group B", }, "Guild C | Group A": { - discordRole: "Guild C Role", + discordRole: "Group C", }, "Guild C | Group B": { - discordRole: "Guild C Role", + discordRole: "Group C", }, "Guild D | Group A": { - discordRole: "Guild D Role", + discordRole: "Group D", }, "Guild D | Group B": { - discordRole: "Guild D Role", + discordRole: "Group D", }, "Guild E | Group A": { - discordRole: "Guild E Role", + discordRole: "Group E", }, "Guild E | Group B": { - discordRole: "Guild E Role", + discordRole: "Group E", }, }, issueEmail: "team@rowdyhacks.org", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 76e6854e1..08b737994 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -69,9 +69,9 @@ importers: discord.js: specifier: ^14.15.3 version: 14.15.3 - hono: - specifier: ^4.5.0 - version: 4.5.0 + express: + specifier: ^5.2.1 + version: 5.2.1 nanoid: specifier: ^5.0.7 version: 5.0.7 @@ -79,9 +79,18 @@ importers: specifier: ^3.25.67 version: 3.25.67 devDependencies: - '@types/bun': - specifier: ^1.1.6 - version: 1.1.6 + '@types/express': + specifier: ^5.0.6 + version: 5.0.6 + '@types/node': + specifier: 20.14.11 + version: 20.14.11 + tsx: + specifier: ^4.0.0 + version: 4.0.0 + typescript: + specifier: 5.5.3 + version: 5.5.3 apps/infrastructure-migrator: dependencies: @@ -2379,7 +2388,7 @@ packages: '@babel/traverse': 7.24.8 '@babel/types': 7.24.9 convert-source-map: 2.0.0 - debug: 4.4.0 + debug: 4.4.3 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -2572,7 +2581,7 @@ packages: '@babel/helper-split-export-declaration': 7.24.7 '@babel/parser': 7.24.8 '@babel/types': 7.24.9 - debug: 4.4.0 + debug: 4.4.3 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -4056,7 +4065,7 @@ packages: engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dependencies: '@eslint/object-schema': 2.1.4 - debug: 4.3.5 + debug: 4.4.3 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -4067,7 +4076,7 @@ packages: engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dependencies: '@eslint/object-schema': 2.1.6 - debug: 4.4.1 + debug: 4.4.3 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -4097,7 +4106,7 @@ packages: engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dependencies: ajv: 6.12.6 - debug: 4.3.5 + debug: 4.4.3 espree: 10.1.0 globals: 14.0.0 ignore: 5.3.1 @@ -4114,7 +4123,7 @@ packages: engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dependencies: ajv: 6.12.6 - debug: 4.4.1 + debug: 4.4.3 espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 @@ -9749,10 +9758,17 @@ packages: update-check: 1.5.4 dev: true - /@types/bun@1.1.6: - resolution: {integrity: sha512-uJgKjTdX0GkWEHZzQzFsJkWp5+43ZS7HC8sZPFnOwnSo1AsNl2q9o2bFeS23disNDqbggEgyFkKCHl/w8iZsMA==} + /@types/body-parser@1.19.6: + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} dependencies: - bun-types: 1.1.17 + '@types/connect': 3.4.38 + '@types/node': 20.14.11 + dev: true + + /@types/connect@3.4.38: + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + dependencies: + '@types/node': 20.14.11 dev: true /@types/cookie@0.4.1: @@ -9836,6 +9852,23 @@ packages: /@types/estree@1.0.8: resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + /@types/express-serve-static-core@5.1.0: + resolution: {integrity: sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==} + dependencies: + '@types/node': 20.14.11 + '@types/qs': 6.14.0 + '@types/range-parser': 1.2.7 + '@types/send': 1.2.1 + dev: true + + /@types/express@5.0.6: + resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==} + dependencies: + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 5.1.0 + '@types/serve-static': 2.2.0 + dev: true + /@types/glob@7.2.0: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: @@ -9847,6 +9880,10 @@ packages: resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} dev: false + /@types/http-errors@2.0.5: + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + dev: true + /@types/inquirer@6.5.0: resolution: {integrity: sha512-rjaYQ9b9y/VFGOpqBEXRavc3jh0a+e6evAbI31tMda8VlPaSy0AZJfXsvmIe3wklc7W6C3zCSfleuMXR7NOyXw==} dependencies: @@ -9889,12 +9926,6 @@ packages: resolution: {integrity: sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==} dev: true - /@types/node@20.12.14: - resolution: {integrity: sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg==} - dependencies: - undici-types: 5.26.5 - dev: true - /@types/node@20.14.11: resolution: {integrity: sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==} dependencies: @@ -9916,6 +9947,14 @@ packages: resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} dev: false + /@types/qs@6.14.0: + resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} + dev: true + + /@types/range-parser@1.2.7: + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + dev: true + /@types/react-dom@18.3.0: resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} dependencies: @@ -9941,6 +9980,19 @@ packages: resolution: {integrity: sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==} dev: false + /@types/send@1.2.1: + resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} + dependencies: + '@types/node': 20.14.11 + dev: true + + /@types/serve-static@2.2.0: + resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 20.14.11 + dev: true + /@types/stack-utils@2.0.3: resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} dev: false @@ -9984,6 +10036,7 @@ packages: resolution: {integrity: sha512-4+q7P5h3SpJxaBft0Dzpbr6lmMaqh0Jr2tbhJZ/luAwvD7ohSCniYkwz/pLxuT2h0EOa6QADgJj1Ko+TzRfZ+w==} dependencies: '@types/node': 20.14.11 + dev: false /@types/yargs-parser@21.0.3: resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} @@ -10385,6 +10438,14 @@ packages: negotiator: 0.6.3 dev: false + /accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + dependencies: + mime-types: 3.0.2 + negotiator: 1.0.0 + dev: false + /acorn-globals@7.0.1: resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} dependencies: @@ -10432,7 +10493,7 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} dependencies: - debug: 4.4.0 + debug: 4.4.3 transitivePeerDependencies: - supports-color dev: false @@ -10441,7 +10502,7 @@ packages: resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} engines: {node: '>= 14'} dependencies: - debug: 4.4.0 + debug: 4.4.3 transitivePeerDependencies: - supports-color dev: true @@ -10719,6 +10780,23 @@ packages: resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} dev: true + /body-parser@2.2.1: + resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==} + engines: {node: '>=18'} + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.3 + http-errors: 2.0.1 + iconv-lite: 0.7.0 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 3.0.2 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + dev: false + /bowser@2.11.0: resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} dev: false @@ -10771,13 +10849,6 @@ packages: dependencies: node-gyp-build: 4.8.1 - /bun-types@1.1.17: - resolution: {integrity: sha512-Z4+OplcSd/YZq7ZsrfD00DKJeCwuNY96a1IDJyR73+cTBaFIS7SC6LhpY/W3AMEXO9iYq5NJ58WAwnwL1p5vKg==} - dependencies: - '@types/node': 20.12.14 - '@types/ws': 8.5.11 - dev: true - /busboy@1.6.0: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} @@ -10790,6 +10861,11 @@ packages: engines: {node: '>= 0.8'} dev: true + /bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + dev: false + /cacheable-lookup@7.0.0: resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} engines: {node: '>=14.16'} @@ -10808,6 +10884,22 @@ packages: responselike: 3.0.0 dev: false + /call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + dev: false + + /call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + dev: false + /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -11113,11 +11205,21 @@ packages: upper-case: 1.1.3 dev: true + /content-disposition@1.0.1: + resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} + engines: {node: '>=18'} + dev: false + /content-type@1.0.4: resolution: {integrity: sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==} engines: {node: '>= 0.6'} dev: true + /content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + dev: false + /convert-hrtime@3.0.0: resolution: {integrity: sha512-7V+KqSvMiHp8yWDuwfww06XleMWVVB9b9tURBx+G7UTADuo5hYPuowKloz4OzOqbPezxgo+fdQ1522WzPG4OeA==} engines: {node: '>=8'} @@ -11127,6 +11229,11 @@ packages: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} dev: false + /cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + dev: false + /cookie@0.4.2: resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==} engines: {node: '>= 0.6'} @@ -11140,7 +11247,6 @@ packages: /cookie@0.7.2: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} - dev: true /cookie@1.0.2: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} @@ -11376,8 +11482,8 @@ packages: dependencies: ms: 2.1.2 - /debug@4.4.0: - resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + /debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -11386,9 +11492,10 @@ packages: optional: true dependencies: ms: 2.1.3 + dev: false - /debug@4.4.1: - resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + /debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -11473,6 +11580,11 @@ packages: engines: {node: '>= 0.6'} dev: true + /depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + dev: false + /dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -11745,6 +11857,15 @@ packages: zod: 3.25.67 dev: false + /dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + dev: false + /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -11775,6 +11896,10 @@ packages: semver: 7.7.2 dev: false + /ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + dev: false + /electron-to-chromium@1.4.832: resolution: {integrity: sha512-cTen3SB0H2SGU7x467NRe1eVcQgcuS6jckKfWJHia2eo0cHIGOqHoAxevIYZD4eRHcWjkvFzo93bi3vJ9W+1lA==} dev: false @@ -11815,6 +11940,11 @@ packages: /emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + /encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + dev: false + /end-of-stream@1.1.0: resolution: {integrity: sha512-EoulkdKF/1xa92q25PbjuDcgJ9RDHYU2Rs3SCIvs2/dSQ3BpmxneNHmA/M7fe60M3PrV7nNGTTNbkK62l6vXiQ==} dependencies: @@ -11873,6 +12003,16 @@ packages: engines: {node: '>=0.12'} dev: false + /es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + dev: false + + /es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + dev: false + /es-module-lexer@1.4.1: resolution: {integrity: sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==} dev: true @@ -11881,6 +12021,13 @@ packages: resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} dev: false + /es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + dev: false + /esbuild-android-64@0.14.47: resolution: {integrity: sha512-R13Bd9+tqLVFndncMHssZrPWe6/0Kpv2/dt4aA69soX4PRxlzsVpCvoJeFE8sOEoeVEiBkI0myjlkDodXlHa0g==} engines: {node: '>=12'} @@ -12482,6 +12629,10 @@ packages: engines: {node: '>=6'} dev: false + /escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + dev: false + /escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} @@ -12602,7 +12753,7 @@ packages: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.1 + debug: 4.4.3 escape-string-regexp: 4.0.0 eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -12724,7 +12875,6 @@ packages: /etag@1.8.1: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} - dev: true /eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} @@ -12759,6 +12909,42 @@ packages: engines: {node: '>=6'} dev: true + /express@5.2.1: + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + engines: {node: '>= 18'} + dependencies: + accepts: 2.0.0 + body-parser: 2.2.1 + content-disposition: 1.0.1 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.1 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.1 + fresh: 2.0.0 + http-errors: 2.0.1 + merge-descriptors: 2.0.0 + mime-types: 3.0.2 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.14.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.0 + serve-static: 2.2.0 + statuses: 2.0.2 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + dev: false + /exsolve@1.0.7: resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} dev: true @@ -12874,6 +13060,20 @@ packages: dependencies: to-regex-range: 5.0.1 + /finalhandler@2.1.1: + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} + dependencies: + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + dev: false + /find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -12931,6 +13131,11 @@ packages: fetch-blob: 3.2.0 dev: false + /forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + dev: false + /fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} dev: false @@ -12972,6 +13177,11 @@ packages: tslib: 2.6.3 dev: false + /fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + dev: false + /fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} @@ -13035,11 +13245,35 @@ packages: engines: {node: 6.* || 8.* || >= 10.*} dev: false + /get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + dev: false + /get-nonce@1.0.1: resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} engines: {node: '>=6'} dev: false + /get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + dev: false + /get-source@2.0.12: resolution: {integrity: sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==} dependencies: @@ -13063,7 +13297,7 @@ packages: dependencies: basic-ftp: 5.0.5 data-uri-to-buffer: 6.0.2 - debug: 4.4.0 + debug: 4.4.3 fs-extra: 11.2.0 transitivePeerDependencies: - supports-color @@ -13142,6 +13376,11 @@ packages: slash: 3.0.0 dev: true + /gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + dev: false + /got@12.6.1: resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} engines: {node: '>=14.16'} @@ -13195,6 +13434,11 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + /has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + dev: false + /hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} @@ -13208,11 +13452,6 @@ packages: upper-case: 1.1.3 dev: true - /hono@4.5.0: - resolution: {integrity: sha512-ZbezypZfn4odyApjCCv+Fw5OgweBqRLA/EsMyc4FUknFvBJcBIKhHy4sqmD1rWpBc/3wUlaQ6tqOPjk36R1ckg==} - engines: {node: '>=16.0.0'} - dev: false - /hono@4.8.3: resolution: {integrity: sha512-jYZ6ZtfWjzBdh8H/0CIFfCBHaFL75k+KMzaM177hrWWm2TWL39YMYaJgB74uK/niRc866NMlH9B8uCvIo284WQ==} engines: {node: '>=16.9.0'} @@ -13268,13 +13507,24 @@ packages: toidentifier: 1.0.0 dev: true + /http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + dev: false + /http-proxy-agent@5.0.0: resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} engines: {node: '>= 6'} dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.4.0 + debug: 4.4.3 transitivePeerDependencies: - supports-color dev: false @@ -13284,7 +13534,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.1 - debug: 4.4.0 + debug: 4.4.3 transitivePeerDependencies: - supports-color dev: true @@ -13302,7 +13552,7 @@ packages: engines: {node: '>= 6'} dependencies: agent-base: 6.0.2 - debug: 4.4.0 + debug: 4.4.3 transitivePeerDependencies: - supports-color dev: false @@ -13312,7 +13562,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.1 - debug: 4.4.0 + debug: 4.4.3 transitivePeerDependencies: - supports-color dev: true @@ -13322,7 +13572,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.3 - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color dev: true @@ -13346,6 +13596,13 @@ packages: safer-buffer: 2.1.2 dev: false + /iconv-lite@0.7.0: + resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: false + /ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -13480,6 +13737,11 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: false + /ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + dev: false + /is-arrayish@0.3.2: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} dev: true @@ -13554,6 +13816,10 @@ packages: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} dev: false + /is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + dev: false + /is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -13978,6 +14244,11 @@ packages: hasBin: true dev: false + /math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + dev: false + /md-to-react-email@5.0.2(react@18.3.1): resolution: {integrity: sha512-x6kkpdzIzUhecda/yahltfEl53mH26QdWu4abUF9+S0Jgam8P//Ciro8cdhyMHnT5MQUJYrIbO6ORM2UxPiNNA==} peerDependencies: @@ -13987,6 +14258,16 @@ packages: react: 18.3.1 dev: false + /media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + dev: false + + /merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + dev: false + /merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -14024,6 +14305,11 @@ packages: engines: {node: '>= 0.6'} dev: false + /mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + dev: false + /mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} @@ -14031,6 +14317,13 @@ packages: mime-db: 1.52.0 dev: false + /mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + dependencies: + mime-db: 1.54.0 + dev: false + /mime@3.0.0: resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} engines: {node: '>=10.0.0'} @@ -14218,6 +14511,11 @@ packages: engines: {node: '>= 0.6'} dev: false + /negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + dev: false + /neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} @@ -14483,6 +14781,11 @@ packages: engines: {node: '>= 6'} dev: false + /object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + dev: false + /obuf@1.1.2: resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} dev: false @@ -14491,6 +14794,13 @@ packages: resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} dev: true + /on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + dependencies: + ee-first: 1.1.1 + dev: false + /once@1.3.3: resolution: {integrity: sha512-6vaNInhu+CHxtONf3zw3vq4SP2DOQhjBvIa3rNcG0+P7eKWlYH6Peu7rHizSloRU2EwMz6GraLieis9Ac9+p1w==} dependencies: @@ -14501,7 +14811,6 @@ packages: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 - dev: true /onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} @@ -14592,7 +14901,7 @@ packages: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 agent-base: 7.1.1 - debug: 4.4.0 + debug: 4.4.3 get-uri: 6.0.3 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.5 @@ -14650,6 +14959,11 @@ packages: peberminta: 0.9.0 dev: false + /parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + dev: false + /pascal-case@2.0.1: resolution: {integrity: sha512-qjS4s8rBOJa2Xm0jmxXiyh1+OFf6ekCWOvUaRgAQSktzlTbMotS0nmG9gyYAybCWBcuP4fsBeRCKNwGBnMe2OQ==} dependencies: @@ -14712,6 +15026,10 @@ packages: resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} dev: true + /path-to-regexp@8.3.0: + resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} + dev: false + /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -15099,12 +15417,20 @@ packages: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} dev: false + /proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + dev: false + /proxy-agent@6.4.0: resolution: {integrity: sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==} engines: {node: '>= 14'} dependencies: agent-base: 7.1.1 - debug: 4.3.5 + debug: 4.4.3 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.5 lru-cache: 7.18.3 @@ -15146,6 +15472,13 @@ packages: resolution: {integrity: sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ==} dev: false + /qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.1.0 + dev: false + /querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} dev: false @@ -15164,6 +15497,11 @@ packages: safe-buffer: 5.2.1 dev: false + /range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + dev: false + /raw-body@2.4.1: resolution: {integrity: sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA==} engines: {node: '>= 0.8'} @@ -15174,6 +15512,16 @@ packages: unpipe: 1.0.0 dev: true + /raw-body@3.0.2: + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} + engines: {node: '>= 0.10'} + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.0 + unpipe: 1.0.0 + dev: false + /rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true @@ -15757,6 +16105,19 @@ packages: glob: 7.2.3 dev: true + /router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + dependencies: + debug: 4.4.1 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.3.0 + transitivePeerDependencies: + - supports-color + dev: false + /run-async@2.4.1: resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} engines: {node: '>=0.12.0'} @@ -15841,6 +16202,25 @@ packages: engines: {node: '>=10'} hasBin: true + /send@1.2.0: + resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + engines: {node: '>= 18'} + dependencies: + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + dev: false + /sentence-case@2.1.1: resolution: {integrity: sha512-ENl7cYHaK/Ktwk5OTD+aDbQ3uC8IByu/6Bkg+HDv8Mm+XnBnppVNalcfJTNsp1ibstKh030/JKQQWglDvtKwEQ==} dependencies: @@ -15854,6 +16234,18 @@ packages: randombytes: 2.1.0 dev: false + /serve-static@2.2.0: + resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} + engines: {node: '>= 18'} + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.0 + transitivePeerDependencies: + - supports-color + dev: false + /server-only@0.0.1: resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} dev: false @@ -15862,6 +16254,10 @@ packages: resolution: {integrity: sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==} dev: true + /setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + dev: false + /shallowequal@1.1.0: resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} dev: false @@ -15912,6 +16308,46 @@ packages: reghex: 1.0.2 dev: true + /side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + dev: false + + /side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + dev: false + + /side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + dev: false + + /side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + dev: false + /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -16018,7 +16454,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.1 - debug: 4.4.0 + debug: 4.4.3 socks: 2.8.3 transitivePeerDependencies: - supports-color @@ -16112,6 +16548,11 @@ packages: engines: {node: '>= 0.6'} dev: true + /statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + dev: false + /std-env@3.8.1: resolution: {integrity: sha512-vj5lIj3Mwf9D79hBkltk5qmkFI+biIKWS2IBxEyEU3AX1tUf7AoL8nSazCOiiqQsGKIq01SClsKEzweu34uwvA==} dev: false @@ -16525,6 +16966,11 @@ packages: engines: {node: '>=0.6'} dev: true + /toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + dev: false + /tough-cookie@4.1.4: resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} engines: {node: '>=6'} @@ -16647,6 +17093,18 @@ packages: /tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + /tsx@4.0.0: + resolution: {integrity: sha512-jd3C5kw9tR68gtvqHUYo/2IwxaA46/CyKvcVQ4DsKRAPb19/vWgl7zF9mYNjFRY6KcGKiwne41RU91ll31IggQ==} + engines: {node: '>=18.0.0'} + hasBin: true + dependencies: + esbuild: 0.18.20 + get-tsconfig: 4.10.0 + source-map-support: 0.5.21 + optionalDependencies: + fsevents: 2.3.3 + dev: true + /turbo-darwin-64@2.0.9: resolution: {integrity: sha512-owlGsOaExuVGBUfrnJwjkL1BWlvefjSKczEAcpLx4BI7Oh6ttakOi+JyomkPkFlYElRpjbvlR2gP8WIn6M/+xQ==} cpu: [x64] @@ -16741,6 +17199,15 @@ packages: engines: {node: '>=16'} dev: false + /type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.2 + dev: false + /typescript@4.9.5: resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} engines: {node: '>=4.2.0'} @@ -16826,7 +17293,6 @@ packages: /unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} - dev: true /update-browserslist-db@1.1.0(browserslist@4.23.2): resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} @@ -17272,7 +17738,6 @@ packages: /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - dev: true /ws@8.17.1: resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} diff --git a/turbo.json b/turbo.json index 8e2cd20e8..0af33a3b5 100644 --- a/turbo.json +++ b/turbo.json @@ -22,7 +22,7 @@ "DISCORD_PROD_VERIFY_CHANNEL_ID", "DISCORD_SECRET_TOKEN", "HK_ENV", - "INTERNAL_AUTH_KEY", + "SHARED_SECRET", "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY", "NEXT_PUBLIC_CLERK_SIGN_IN_URL", "NEXT_PUBLIC_CLERK_SIGN_UP_URL",