A powerful and extendable Discord bot, with it's own module system :3 thevoid.cafe/projects/voidy

✨🚧 Testing some very dubious separation of bot/api, EXPERIMENTAL

Changed files
+121
packages
+2
packages/api/.env.example
··· 1 + DB_URI: #Your MongoDB connection URI - REQUIRED 2 + ACCESS_TOKEN: # Your Bearer token
+12
packages/api/Dockerfile
··· 1 + FROM oven/bun:1.1 2 + 3 + WORKDIR /app 4 + 5 + COPY . . 6 + 7 + RUN bun install 8 + 9 + WORKDIR /app/packages/api 10 + 11 + EXPOSE 3000 12 + CMD ["bun", "run", "dev"]
+25
packages/api/index.ts
··· 1 + import { Hono } from "hono"; 2 + import { v1 } from "./routes/api/v1"; 3 + import { connect } from "mongoose"; 4 + 5 + // Instantiate Hono 6 + const app = new Hono(); 7 + 8 + // Database URI validation and connection check 9 + if (!Bun.env.DB_URI) throw new Error("[Voidy] Missing database URI"); 10 + await connect(Bun.env.DB_URI) 11 + .then(() => { 12 + console.log("Connected to database"); 13 + }) 14 + .catch((error) => { 15 + console.error("Failed to connect to database:", error); 16 + }); 17 + 18 + // Define routes 19 + app.route("/api/v1", v1); 20 + 21 + // Export app configuration 22 + export default { 23 + fetch: app.fetch, 24 + port: Bun.env.PORT || 4300, 25 + }
+22
packages/api/middlewares/isAuthenticated.ts
··· 1 + import type { MiddlewareHandler } from "hono"; 2 + 3 + export const isAuthenticated: MiddlewareHandler = async (c, next) => { 4 + const auth = c.req.header("authorization"); 5 + 6 + if (!auth || !auth.startsWith("Bearer ")) { 7 + return c.json({ error: "Unauthorized" }, 401); 8 + } 9 + 10 + const token = auth.slice("Bearer ".length); 11 + 12 + if (!Bun.env.ACCESS_TOKEN) { 13 + console.error("ACCESS_TOKEN environment variable is not set"); 14 + return c.json({ error: "Internal server error" }, 500); 15 + } 16 + 17 + if (token !== Bun.env.ACCESS_TOKEN) { 18 + return c.json({ error: "Invalid token" }, 401); 19 + } 20 + 21 + await next(); 22 + };
+21
packages/api/package.json
··· 1 + { 2 + "name": "@voidy/api", 3 + "version": "0.1.0", 4 + "module": "src/index.ts", 5 + "type": "module", 6 + "private": true, 7 + "scripts": { 8 + "dev": "bun --watch ." 9 + }, 10 + "devDependencies": { 11 + "@types/bun": "latest" 12 + }, 13 + "peerDependencies": { 14 + "typescript": "^5.9.2" 15 + }, 16 + "dependencies": { 17 + "@voidy/bot": "workspace:*", 18 + "hono": "^4.11.1", 19 + "mongoose": "^9.0.1" 20 + } 21 + }
+33
packages/api/routes/api/v1/currency.ts
··· 1 + import { Hono } from "hono"; 2 + import { UserCurrency, UserCurrencyType, UserIntegration } from "@voidy/bot/db" 3 + import { isAuthenticated } from "../../../middlewares/isAuthenticated"; 4 + 5 + export const currency = new Hono(); 6 + 7 + async function findUserId(serviceType: string, serviceId: string) { 8 + const integration = await UserIntegration.findOne({ 9 + service: { 10 + type: serviceType, 11 + id: serviceId, 12 + } 13 + }); 14 + 15 + if (!integration) return null; 16 + 17 + return integration.userId; 18 + } 19 + 20 + currency.get("/", isAuthenticated, async (c) => { 21 + const serviceType = c.req.param("serviceType"); 22 + const serviceId = c.req.param("serviceId"); 23 + 24 + if (!serviceId || !serviceType) return c.json({ error: "Missing serviceId or serviceType" }, 400); 25 + 26 + const userId = await findUserId(serviceType, serviceId); 27 + if (!userId) return c.json({ error: "User not found" }, 404); 28 + 29 + const userCurrency = await UserCurrency.findOne({ userId, type: UserCurrencyType.BITS }); 30 + if (!userCurrency) return c.json({ error: "User currency not found" }, 404); 31 + 32 + return c.json(userCurrency); 33 + });
+6
packages/api/routes/api/v1/index.ts
··· 1 + import { Hono } from "hono"; 2 + import { currency } from "./currency"; 3 + 4 + export const v1 = new Hono(); 5 + 6 + v1.route("/currency", currency);