From 8046d33fc8f283251bcc35e24fb0b3cf9006b381 Mon Sep 17 00:00:00 2001 From: maamokun/MikanDev Date: Thu, 12 Dec 2024 20:23:37 +0900 Subject: [PATCH] add: better error handling wih remote logging --- .github/workflows/docker.yml | 2 +- Dockerfile | 44 ++++++++++++++++++++++++++--------- bun.lockb | Bin 19741 -> 32206 bytes package.json | 4 +++- src/api/server.ts | 6 ++++- src/commands/rank.ts | 1 - src/handlers/command.ts | 40 ++++++++++++++++++++++++++----- src/index.ts | 6 ++++- 8 files changed, 81 insertions(+), 22 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 49d4e39..d3ea7b8 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -23,7 +23,7 @@ jobs: with: registry: ghcr.io username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} + password: ${{ secrets.CR_PAT }} - name: Build and push uses: docker/build-push-action@v4 diff --git a/Dockerfile b/Dockerfile index cd94c9a..c96d22f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,39 @@ -FROM oven/bun +# use the official Bun image +# see all versions at https://hub.docker.com/r/oven/bun/tags +FROM oven/bun:1 AS base +WORKDIR /usr/src/app -WORKDIR /app +# install dependencies into temp directory +# this will cache them and speed up future builds +FROM base AS install +RUN mkdir -p /temp/dev +COPY package.json bun.lockb /temp/dev/ +RUN cd /temp/dev && bun install --frozen-lockfile -COPY package.json . -COPY bun.lockb . +# install with --production (exclude devDependencies) +RUN mkdir -p /temp/prod +COPY package.json bun.lockb /temp/prod/ +RUN cd /temp/prod && bun install --frozen-lockfile --production -RUN bun install --production +# copy Prisma schema and generate Prisma client +COPY prisma /usr/src/app/prisma +COPY prisma /temp/prod/prisma +RUN cd /temp/prod && bunx prisma generate -COPY src src -COPY prisma prisma -COPY tsconfig.json . +# copy node_modules from temp directory +# then copy all (non-ignored) project files into the image +FROM base AS prerelease +COPY --from=install /temp/dev/node_modules node_modules +COPY . . -ENV NODE_ENV production -CMD ["bun", "src/index.ts"] +# copy production dependencies and source code into final image +FROM base AS release +COPY --from=install /temp/prod/node_modules node_modules +COPY --from=prerelease /usr/src/app/main.ts . +COPY --from=prerelease /usr/src/app/package.json . +COPY --from=prerelease /usr/src/app/prisma ./prisma -EXPOSE 3000 \ No newline at end of file +# run the app +USER bun +EXPOSE 3000/tcp +ENTRYPOINT [ "bun", "run", "src/index.ts" ] \ No newline at end of file diff --git a/bun.lockb b/bun.lockb index a9f30d1017bc14a7f64561485698b75d0493bcb6..52a8796e437070738469b4aedb96a54014a2a3bd 100644 GIT binary patch delta 11156 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 delta 3581 zcmcgueQZVA1lN{$Zp8Vz@FYmI{L!fg~&8I_VyL+4DRdR zv$wA&*on#O(9Z@fFvd&FLZm}p4&()+3#C9#g8cZdM}l2JAFJ$u9@vXcl9q5ts^n_93-G z-j1VCvR{^3XaW$j&@A9}3wcxgQdlXJ;y0~^b9^+J;4|F^ceUu;ac~do+~2@`Tjv@O z=77$PfO|;i-Uqi%=L!(~h+YU>MDG%~GI}D#XIhM))H0X%1-KB*!>&1uLIH8Rg#LW= zC!d)2rsLM`H^^?bnfh^X9?&N^2hOK+pMTl63h$RujnyZgOs3gXzr2u4-X-2J93Yr@ z30R=F|Aah-lc<}56R5X)AuARJUazItQi{xuO%M;v^y*60ra*`RgC`d}kr_@+Y< zzDG=SWA5(%KZYVK=3=3*`wfQzpX8UZpy$)~(t+#%ZM3WOhwK2dlQTynONL4#IRV8) z(~y1%sm2jd;^=9IM%y!0x&g^bRk<43vQ&B@H=rcY$B@oLT9+44RC+#7qkY*beE}(% z9&l=um!r}v&VVwHK7(`xQd@pNSwKI|*J#+Gl3EZ@7Ew!qMwPiLO+s2s)TLmQt`tqx+pIy$fj>rMfhlf%Jqcprq3Um!{Y$V}+(<&;Xv9bP>-i zau#b!HVxsKL(_OV$nDmYTzcA#eHE#cQx#C0w7UxXa;bC~QUPUnu`fsmyaA<%E_yY^ zMNXflte_!0i)q@|HGZ>UqoiVUEREG#Of@(=R#F;9>6~dH-K0w-le24S3%2%YG3^_OOp+`E`k4Gc00kjf?%S+q`;@rgd zb_pmOgez1m2XXGPgV?S-&@d~Zonb|^`0B??Jbrtry)PK*?%hhY&1#r8;{vgMI*8}N zt)*{}6Emk4P9N+5b`y#lJqdH-MPZ{m1y_QY4`SPKs&(6W51diBkKf^2ogK)!+{eir z<6+&?+!>vi9l?sM#7V{-LuQn%y`xgFhPUIl22S$0@0_ zyIqwogn*;KBe!-u_^kclhvzU-=5pgN>ZnaCk^2SNT0C-8P-BZto)omTWvetxnu4@I zQc{~uW=gVevPrk3v{qYM9fARW4!-o*!ry%N;31DxN?mSDCq$E^rdC^d2Nd9+!F@k} z@A8!oGB?iI60JwixBls))~WV8QYmqjxp+4RB^}4y(L(ko)7!4^oH>+^SBqVxE*z7{ z+;qLQ^e%JGz^cfAoF7ZOAdN{H-sE9Z&unT;i)H1Ix|y%x0S`G1K8T8@;i)G%Z@$zVzFp11EO;E{Lfsu@`r-_;;rn=G|+a z@@Q3jWJT+U<8)1xR5}*4&6irR5zqxk05DJ8WsuT01HH3)@rq z13gkP4xs2)sRs(tBE9O=%9SbdnNg`6!Z(Q%g>s!|h)k4<3kBLuU#}gl;lKCEws(i; z|2b2_4fvrr`f9ZH8_{xFsJhb@pTc<(t*IJGc_HoTbkXrn&yq>x0_2Lprz*?dQT8R> c88p-mG}86XMtZQTcKo$%TN20rK5!-GU+>a<(*OVf diff --git a/package.json b/package.json index d284601..54d55ed 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,11 @@ "typescript": "^5.0.0" }, "dependencies": { + "@baselime/pino-transport": "^0.1.5", "@biomejs/biome": "^1.8.2", "@prisma/client": "^5.15.1", "discord.js": "^14.15.3", - "elysia": "^1.1.5" + "elysia": "^1.1.5", + "pino": "^9.5.0" } } diff --git a/src/api/server.ts b/src/api/server.ts index d0e04d6..6cf0103 100644 --- a/src/api/server.ts +++ b/src/api/server.ts @@ -38,7 +38,11 @@ app.post("/accLink", async ({ query, body }) => { }, }); - await dmUser(uid, "MikanDev Accounts", `Your account has been linked!\n\n**MikanDev UID:** ${acc}\n**Discord ID:**${uid}`); + await dmUser( + uid, + "MikanDev Accounts", + `Your account has been linked!\n\n**MikanDev UID:** ${acc}\n**Discord ID:**${uid}`, + ); return new Response("Account linked", { status: 200 }); }); diff --git a/src/commands/rank.ts b/src/commands/rank.ts index 0ef378b..d0380fa 100644 --- a/src/commands/rank.ts +++ b/src/commands/rank.ts @@ -1,6 +1,5 @@ import { type CommandInteraction, AttachmentBuilder } from "discord.js"; import { PrismaClient } from "@prisma/client"; -import { start } from "../api/server"; const prisma = new PrismaClient(); diff --git a/src/handlers/command.ts b/src/handlers/command.ts index d23bc8e..c6ddfd5 100644 --- a/src/handlers/command.ts +++ b/src/handlers/command.ts @@ -1,8 +1,31 @@ -import type { CommandInteraction } from "discord.js"; +import { + WebhookClient, + EmbedBuilder, + type CommandInteraction, +} from "discord.js"; +import crypto from "node:crypto"; import { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); +const logginWebhook = new WebhookClient({ + url: process.env.LOGGING_WEBHOOK_URL as string, +}); + +const sendLog = async (message: string) => { + const logId = crypto.randomBytes(4).toString("hex"); + const embed = new EmbedBuilder() + .setTitle(`Error Log`) + .setDescription(`\`\`\`ts\n${message}\n\`\`\``) + .setColor("#FF0000") + .setTimestamp(); + + await logginWebhook + .send({ content: `EID ${logId}`, embeds: [embed] }) + .catch(console.error); + return logId; +}; + export async function handleCommand(interaction: CommandInteraction) { try { const userDb = await prisma.user.findUnique({ @@ -33,10 +56,15 @@ export async function handleCommand(interaction: CommandInteraction) { ); if (command.slashCommand.enabled) command.interactionRun(interaction); } catch (error) { - console.error(error); - interaction.reply({ - content: "There was an error while executing this command!", - ephemeral: true, - }); + const logId = await sendLog(error.message); + console.error(`Error while executing command: ${logId}`); + const errorEmbed = new EmbedBuilder() + .setTitle("An error occurred while executing this command!") + .setDescription( + `An error occurred while executing this command.\n\nEID ${logId}\n\nPlease contact support if this persists.`, + ) + .setColor("#FF0000") + .setTimestamp(); + await interaction.reply({ embeds: [errorEmbed], ephemeral: true }); } } diff --git a/src/index.ts b/src/index.ts index 1c7faaa..d75efbd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -39,7 +39,11 @@ client.on("ready", () => { client.on("interactionCreate", async (interaction) => { if (interaction.isCommand()) { console.log(`Received command: ${interaction.commandName}`); - await handleCommand(interaction); + try { + await handleCommand(interaction); + } catch (e) { + console.error(e); + } } if (interaction.isButton()) { console.log(`Received button: ${interaction.customId}`);