require("dotenv").config({quiet:true}); const express = require("express"); const session = require("express-session"); const SQLiteStore = require('connect-sqlite3')(session); const cors = require("cors"); const bcrypt = require('bcrypt'); var dbReady = false; const DSN = `mysql://${process.env.DB_USER || "root"}:${process.env.DB_PASSWORD || ""}@tcp(${process.env.DB_HOST || "127.0.0.1"}:${process.env.DB_PORT || "3306"})/${process.env.DB_NAME}`; const mariadb = require("mariadb"); const fs = require('fs'); const path = require('path'); const db = mariadb.createPool({ host: process.env.DB_HOST || "127.0.0.1", port: process.env.DB_PORT || 3306, user: process.env.DB_USER || "root", password: process.env.DB_PASSWORD || null, database: process.env.DB_NAME || "uhppoted", }); global.db = db; global.DSN = DSN; global.log = require("./logger.js") const app = express(); global.app = app; const expressWs = require('express-ws')(app); app.use(session({ secret: process.env.SESSION_SECRET || "uhppoted-secret-change-me", cookie: { maxAge: 24 * 60 * 60 * 1000 }, // 1 day resave: true, saveUninitialized: false, store: new SQLiteStore({ db: 'sessions.db', dir: './' }) })); global.hashPassword = async function(password) { const saltRounds = 12; return await bcrypt.hash(password, saltRounds); }; global.comparePassword = async function(password, hash) { return await bcrypt.compare(password, hash); }; global.checkACL = function(req, perm) { if (!req.session.user) return false; const perms = req.session.user.perms ? JSON.parse(req.session.user.perms) : []; if (perms.includes('*') || perms.includes(perm)) { return true; } return false; }; app.use((req, res, next) => { if (req.session.user) return next(); if (req.path === "/") return next(); if (req.path === '/login' || req.path === '/login/') return next(); if (req.path === '/logout' || req.path === '/logout/') return next(); if (req.path.startsWith('/public/') || req.path === '/favicon.ico' || req.path === '/robots.txt') return next(); res.redirect('/login?err=4'); }); app.use(cors()); app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(express.static("public")); app.set("view engine", "ejs"); app.set("views", "./views"); app.use((req, res, next) => { if (!dbReady) { return res.render('error', { error: 'Database is not ready. Please try again later.' }); } next(); }); const routersDir = path.join(__dirname, 'routes'); fs.readdirSync(routersDir).forEach(file => { const router = require(path.join(routersDir, file)); if (file.endsWith('.js')) { const route = '/' + file.replace('.js', ''); app.use(route, router); } }); const port = process.env.APP_PORT || 8080 app.listen(port, (err) => { if (err) { global.log.error(`Cannot start server: ${err}`); process.exit(1); } global.log.info(`Listening on port :${port}`); global.log.debug(`DSN: ${DSN}`); db.query("SELECT * FROM ACL LIMIT 1;") .then(async () => { dbReady = true; global.log.info("Database connection established"); // Do init stuff await db.query("CREATE TABLE IF NOT EXISTS users ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(255) UNIQUE, passwordHash VARCHAR(255), perms TEXT, createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP );"); db.query("SELECT COUNT(*) AS count FROM users;").then(async (res) => { if (res[0].count == 0) { const defaultUser = process.env.DEFAULT_USER || "admin"; const defaultPassword = process.env.DEFAULT_PASSWORD || [...Array(32)].map(() => (Math.random().toString(36)+Math.random().toString(32)).charAt(2)).join(''); const passwordHash = await global.hashPassword(defaultPassword); db.query("INSERT INTO users (username, passwordHash, perms) VALUES (?, ?, ?);", [defaultUser, passwordHash, JSON.stringify(["*", "login", "acl", "liveMonitor", "eventLog", "auditLog", "manageUsers"])]) .then(() => { global.log.info(`Created default admin user '${defaultUser}' with password '${defaultPassword}'`); }) .catch(err => { global.log.error(`Cannot create default user: ${err}`); }); } }); global.dbEvent = require("./eventWatcher.js"); }) .catch(err => { global.log.error(`Database connection failed: ${err}`); process.exit(1); }); })