/** * More info about this file: * https://v2.quasar.dev/quasar-cli-vite/developing-ssr/ssr-webserver * * Runs in Node context. */ /** * Make sure to yarn add / npm install (in your project root) * anything you import here (except for express and compression). */ import express from 'express' import compression from 'compression' import { defineSsrCreate, defineSsrListen, defineSsrClose, defineSsrServeStaticContent, defineSsrRenderPreloadTag } from '#q-app/wrappers' import prisma from './database.js'; // Import the prisma client instance import apiRoutes from './routes/api.js'; import cron from 'node-cron'; import { generateAndStoreMantisSummary } from './services/mantisSummarizer.js'; /** * Create your webserver and return its instance. * If needed, prepare your webserver to receive * connect-like middlewares. * * Can be async: defineSsrCreate(async ({ ... }) => { ... }) */ export const create = defineSsrCreate((/* { ... } */) => { const app = express() // Initialize the database (now synchronous) try { console.log('Prisma Client is ready.'); // Log Prisma readiness // Schedule the Mantis summary task after DB initialization // Run daily at 1:00 AM server time (adjust as needed) cron.schedule('0 1 * * *', async () => { console.log('Running scheduled Mantis summary task...'); try { await generateAndStoreMantisSummary(); console.log('Scheduled Mantis summary task completed.'); } catch (error) { console.error('Error running scheduled Mantis summary task:', error); } }, { scheduled: true, timezone: "Europe/London" // Example: Set to your server's timezone }); console.log('Mantis summary cron job scheduled.'); // Optional: Run once immediately on server start if needed generateAndStoreMantisSummary().catch(err => console.error('Initial Mantis summary failed:', err)); } catch (error) { console.error('Error during server setup:', error); // Optionally handle the error more gracefully, e.g., prevent server start process.exit(1); // Exit if setup fails } // attackers can use this header to detect apps running Express // and then launch specifically-targeted attacks app.disable('x-powered-by') // Add JSON body parsing middleware app.use(express.json()); // Add API routes app.use('/api', apiRoutes); // place here any middlewares that // absolutely need to run before anything else if (process.env.PROD) { app.use(compression()) } return app }) /** * You need to make the server listen to the indicated port * and return the listening instance or whatever you need to * close the server with. * * The "listenResult" param for the "close()" definition below * is what you return here. * * For production, you can instead export your * handler for serverless use or whatever else fits your needs. * * Can be async: defineSsrListen(async ({ app, devHttpsApp, port }) => { ... }) */ export const listen = defineSsrListen(({ app, devHttpsApp, port }) => { const server = devHttpsApp || app return server.listen(port, () => { if (process.env.PROD) { console.log('Server listening at port ' + port) } }) }) /** * Should close the server and free up any resources. * Will be used on development mode when the server needs * to be restarted, or when the application shuts down. * * Can be async: defineSsrClose(async ({ ... }) => { ... }) */ export const close = defineSsrClose(async ({ listenResult }) => { // Close the database connection when the server shuts down try { await prisma.$disconnect(); console.log('Prisma Client disconnected.'); } catch (e) { console.error('Error disconnecting Prisma Client:', e); } return listenResult.close() }) const maxAge = process.env.DEV ? 0 : 1000 * 60 * 60 * 24 * 30 /** * Should return a function that will be used to configure the webserver * to serve static content at "urlPath" from "pathToServe" folder/file. * * Notice resolve.urlPath(urlPath) and resolve.public(pathToServe) usages. * * Can be async: defineSsrServeStaticContent(async ({ app, resolve }) => { * Can return an async function: return async ({ urlPath = '/', pathToServe = '.', opts = {} }) => { */ export const serveStaticContent = defineSsrServeStaticContent(({ app, resolve }) => { return ({ urlPath = '/', pathToServe = '.', opts = {} }) => { const serveFn = express.static(resolve.public(pathToServe), { maxAge, ...opts }) app.use(resolve.urlPath(urlPath), serveFn) } }) const jsRE = /\.js$/ const cssRE = /\.css$/ const woffRE = /\.woff$/ const woff2RE = /\.woff2$/ const gifRE = /\.gif$/ const jpgRE = /\.jpe?g$/ const pngRE = /\.png$/ /** * Should return a String with HTML output * (if any) for preloading indicated file */ export const renderPreloadTag = defineSsrRenderPreloadTag((file/* , { ssrContext } */) => { if (jsRE.test(file) === true) { return `` } if (cssRE.test(file) === true) { return `` } if (woffRE.test(file) === true) { return `` } if (woff2RE.test(file) === true) { return `` } if (gifRE.test(file) === true) { return `` } if (jpgRE.test(file) === true) { return `` } if (pngRE.test(file) === true) { return `` } return '' })