UNPKG

@sidequest/dashboard

Version:

@sidequest/dashboard is the web dashboard for Sidequest, a distributed background job queue system.

237 lines (234 loc) 8.93 kB
import { createBackendFromDriver } from '@sidequest/backend'; import { logger } from '@sidequest/core'; import express from 'express'; import basicAuth from 'express-basic-auth'; import expressLayouts from 'express-ejs-layouts'; import morgan from 'morgan'; import path from 'node:path'; import { createDashboardRouter } from './resources/dashboard.js'; import { createJobsRouter } from './resources/jobs.js'; import { createQueuesRouter } from './resources/queues.js'; /** * A dashboard server for monitoring and managing Sidequest jobs and queues. * * The SidequestDashboard class provides a web-based interface for viewing job status, * queue information, and other Sidequest-related data. It sets up an Express.js server * with EJS templating, optional basic authentication, and connects to a configurable backend. * * @example * ```typescript * const dashboard = new SidequestDashboard(); * await dashboard.start({ * port: 3000, * enabled: true, * auth: { * user: 'admin', * password: 'secret' * }, * backendConfig: { * driver: '@sidequest/sqlite-backend' * } * }); * ``` */ class SidequestDashboard { /** * The Express application instance used by the dashboard server. * This property is optional and may be undefined if the server has not been initialized. */ app; /** * Optional dashboard configuration object that defines the behavior and appearance * of the dashboard component. If not provided, default configuration will be used. */ config; /** * The HTTP server instance used by the dashboard application. * This server handles incoming requests and serves the dashboard interface. * Will be undefined until the server is started. */ server; /** * Backend instance used for server communication and data operations. * When undefined, indicates that no backend connection has been established. */ backend; constructor() { this.app = express(); } /** * Starts the dashboard server with the provided configuration. * * @param config - Optional dashboard configuration object. If not provided, default values will be used. * @returns A promise that resolves when the server setup is complete. * * @remarks * - If the server is already running, a warning is logged and the method returns early * - If the dashboard is disabled in config, a debug message is logged and the method returns early * - The method sets up the Express app, backend driver, middlewares, authentication, EJS templating, routes, and starts listening * * @example * ```typescript * await dashboard.start({ * enabled: true, * port: 3000, * backendConfig: { driver: "@sidequest/sqlite-backend" } * }); * ``` */ async start(config) { if (this.server?.listening) { logger("Dashboard").warn("Dashboard is already running. Please stop it before starting again."); return; } this.config = { enabled: true, backendConfig: { driver: "@sidequest/sqlite-backend", }, port: 8678, ...config, }; if (!this.config.enabled) { logger("Dashboard").debug(`Dashboard is disabled`); return; } this.app ??= express(); this.backend = await createBackendFromDriver(this.config.backendConfig); this.setupMiddlewares(); this.setupAuth(); this.setupEJS(); this.setupRoutes(); this.listen(); } /** * Sets up middleware for the Express application. * * Configures HTTP request logging using Morgan middleware when debug logging is enabled. * The middleware uses the "combined" format for comprehensive request logging. * * @remarks * - Only adds Morgan logging middleware when debug mode is active * - Uses Apache combined log format for detailed request information * - Logs are handled through the Dashboard logger instance */ setupMiddlewares() { logger("Dashboard").debug(`Setting up Middlewares`); if (logger().isDebugEnabled()) { this.app?.use(morgan("combined")); } } /** * Sets up basic authentication for the dashboard application. * * If authentication configuration is provided, this method configures * HTTP Basic Authentication middleware using the specified username and password. * The middleware will challenge unauthorized requests with a 401 response. * * @remarks * - Only sets up authentication if `this.config.auth` is defined * - Uses a single user/password combination from the configuration * - Enables challenge mode to prompt for credentials in browsers * * @example * ```typescript * // Assuming config.auth = { user: "admin", password: "secret" } * dashboard.setupAuth(); // Sets up basic auth for user "admin" * ``` */ setupAuth() { if (this.config.auth) { const auth = this.config.auth; logger("Dashboard").debug(`Basic auth setup with User: ${auth.user}`); const users = {}; users[auth.user] = auth.password; this.app.use(basicAuth({ users: users, challenge: true, })); } } /** * Sets up EJS templating engine for the dashboard application. * This method configures the Express application to use EJS as the view engine, * sets the views directory, and specifies the layout file. * * @remarks * - Uses `express-ejs-layouts` for layout support * - Sets the views directory to the `views` folder within the package * - Serves static files from the `public` directory * - Ensures that the EJS engine is ready to render views with layouts */ setupEJS() { logger("Dashboard").debug(`Setting up EJS`); this.app.use(expressLayouts); this.app.set("view engine", "ejs"); this.app.set("views", path.join(import.meta.dirname, "views")); this.app.set("layout", path.join(import.meta.dirname, "views", "layout")); this.app.use("/public", express.static(path.join(import.meta.dirname, "public"))); } /** * Sets up the main application routes for the dashboard. * * This method initializes and attaches the dashboard, jobs, and queues routers * to the Express application instance. It also logs the setup process for debugging purposes. * * @remarks * - Assumes that `this.app` and `this.backend` are initialized. * - Uses the routers created by `createDashboardRouter`, `createJobsRouter`, and `createQueuesRouter`. */ setupRoutes() { logger("Dashboard").debug(`Setting up routes`); this.app.use(...createDashboardRouter(this.backend)); this.app.use(...createJobsRouter(this.backend)); this.app.use(...createQueuesRouter(this.backend)); } /** * Starts the dashboard server on the configured port. * Logs the startup process and handles any errors that occur during server initialization. * * @remarks * If no port is specified in the configuration, the default port 8678 is used. * * @returns void */ listen() { const port = this.config.port ?? 8678; logger("Dashboard").debug(`Starting Dashboard with port ${port}`); this.server = this.app.listen(port, (error) => { if (error) { logger("Dashboard").error("Failed to start Sidequest Dashboard!", error); } else { logger("Dashboard").info(`Server running on http://localhost:${port}`); } }); } /** * Closes the dashboard by shutting down the backend and server, * and cleaning up associated resources. * * - Awaits the closure of the backend if it exists. * - Closes the server and logs a message when stopped. * - Resets backend, server, config, and app properties to `undefined`. * - Logs a debug message indicating resources have been cleaned up. * * @returns {Promise<void>} Resolves when all resources have been closed and cleaned up. */ async close() { await this.backend?.close(); await new Promise((resolve) => { this.server?.close(() => { logger("Dashboard").info("Sidequest Dashboard stopped"); resolve(); }); }); this.backend = undefined; this.server = undefined; this.config = undefined; this.app = undefined; logger("Dashboard").debug("Dashboard resources cleaned up"); } } export { SidequestDashboard }; //# sourceMappingURL=index.js.map