const fastify = require('fastify')({ logger: true }); const path = require('path'); const { spawn } = require('child_process'); const fs = require('fs'); const jwt = require("jsonwebtoken"); const { initHeadless } = require("./dist/shared/headless"); const { initDatabase } = require('./dist/shared/database'); const { loadExtensions } = require('./dist/shared/extensions'); const { init } = require('./dist/api/rpc/rpc.controller'); const dotenv = require('dotenv'); dotenv.config(); const viewsRoutes = require('./dist/views/views.routes'); const animeRoutes = require('./dist/api/anime/anime.routes'); const booksRoutes = require('./dist/api/books/books.routes'); const proxyRoutes = require('./dist/api/proxy/proxy.routes'); const extensionsRoutes = require('./dist/api/extensions/extensions.routes'); const galleryRoutes = require('./dist/api/gallery/gallery.routes'); const rpcRoutes = require('./dist/api/rpc/rpc.routes'); const userRoutes = require('./dist/api/user/user.routes'); const anilistRoute = require('./dist/api/anilist'); fastify.addHook("preHandler", async (request) => { const auth = request.headers.authorization; if (!auth) return; try { const token = auth.replace("Bearer ", ""); request.user = jwt.verify(token, process.env.JWT_SECRET); } catch (e) { } }); fastify.register(require('@fastify/static'), { root: path.join(__dirname, 'public'), prefix: '/public/', decorateReply: false }); fastify.register(require('@fastify/static'), { root: path.join(__dirname, 'views'), prefix: '/views/', decorateReply: false }); fastify.register(require('@fastify/static'), { root: path.join(__dirname, 'src', 'scripts'), prefix: '/src/scripts/', decorateReply: false }); fastify.register(viewsRoutes); fastify.register(animeRoutes, { prefix: '/api' }); fastify.register(booksRoutes, { prefix: '/api' }); fastify.register(proxyRoutes, { prefix: '/api' }); fastify.register(extensionsRoutes, { prefix: '/api' }); fastify.register(galleryRoutes, { prefix: '/api' }); fastify.register(rpcRoutes, { prefix: '/api' }); fastify.register(userRoutes, { prefix: '/api' }); fastify.register(anilistRoute, { prefix: '/api' }); function startCppScraper() { const exePath = path.join( __dirname, 'src', 'metadata', process.platform === 'win32' ? 'anilist.exe' : 'anilist' ); const dllPath = path.join(__dirname, 'src', 'metadata', 'binaries'); if (!fs.existsSync(exePath)) { console.error(`❌ C++ Error: Could not find executable at: ${exePath}`); console.error(" Did you compile it? (g++ src/metadata/anilist.cpp -o src/metadata/anilist.exe ...)"); return; } const env = { ...process.env }; env.PATH = `${dllPath};${env.PATH}`; console.log("⚡ Starting WaifuBoard Scraper Engine (C++)..."); const scraper = spawn(exePath, [], { stdio: 'inherit', cwd: __dirname, env: env }); scraper.on('error', (err) => { console.error('❌ Failed to spawn C++ process:', err); }); scraper.on('close', (code) => { console.log(`✅ Scraper process finished with code ${code}`); }); } const start = async () => { try { initDatabase("anilist"); initDatabase("favorites"); initDatabase("cache"); initDatabase("userdata"); init() await loadExtensions(); await fastify.listen({ port: 54322, host: '0.0.0.0' }); console.log(`Server running at http://localhost:54322`); startCppScraper(); await initHeadless(); } catch (err) { fastify.log.error(err); process.exit(1); } }; start();