/* ______ __ ______ / \ / | / \ /$$$$$$ | _______ _$$ |_ ______ ______ /$$$$$$ | ______ _____ ____ $$ |__$$ | / |/ $$ | / \ / \ $$ | $$/ / \ / \/ \ $$ $$ |/$$$$$$$/ $$$$$$/ /$$$$$$ |/$$$$$$ |$$ | /$$$$$$ |$$$$$$ $$$$ | $$$$$$$$ |$$ \ $$ | __ $$ | $$/ $$ | $$ |$$ | __ $$ | $$ |$$ | $$ | $$ | $$ | $$ | $$$$$$ | $$ |/ |$$ | $$ \__$$ |$$ \__/ |$$ \__$$ |$$ | $$ | $$ | $$ | $$ |/ $$/ $$ $$/ $$ | $$ $$/ $$ $$/ $$ $$/ $$ | $$ | $$ | $$/ $$/ $$$$$$$/ $$$$/ $$/ $$$$$$/ $$$$$$/ $$$$$$/ $$/ $$/ $$/ Made with <3 by Chris Chrome and the rest of the AstroCom team */ require("dotenv").config(); const { execSync } = require('child_process'); const express = require('express'); const expressSession = require('express-session'); const ejs = require("ejs") const mariadb = require('mariadb'); const bcrypt = require("bcrypt") const crypto = require("crypto") const app = express(); const path = require('path'); const fs = require('fs'); const port = process.env.SERVER_PORT || 3000; const pool = 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 || '', database: process.env.DB_NAME || 'astrocom', connectionLimit: 10 }); const saltRounds = 10; global.db_pool = pool; // Make the pool global so we can use it in other files const analytics = require("./analytics"); // Run migrations and reset admin user if desired pool.getConnection().then((conn) => { require("./migrations")(pool).then(() => { conn.query("SELECT * FROM users WHERE id = 1").then((row) => { if (!row || process.env.RESET_ADMIN == "true") { // delete all users (The big scary one lol) conn.query("DELETE FROM users").then(() => { // Generate 32 char random string const passwd = crypto.randomBytes(32).toString('hex'); bcrypt.hash(passwd, 10).then((hash) => { conn.query("INSERT INTO users (id, username, passwordHash) VALUES (1, 'admin', ?)", [hash]).then(() => { console.log(`Created admin user with password: ${passwd}`); }); }); }); } }); }).finally(() => { conn.release(); }); }); const gitCommitHashShort = execSync('git rev-parse --short HEAD').toString().trim(); const branch = execSync('git rev-parse --abbrev-ref HEAD').toString().trim(); const version = `${gitCommitHashShort}-${branch}`; console.log(`Version: ${version}`); app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(expressSession({ store: expressSession.MemoryStore(), secret: process.env.SESSION_SECRET || 'default_secret', resave: false, saveUninitialized: false, cookie: { secure: process.env.NODE_ENV === 'production', maxAge: 24 * 60 * 60 * 1000 // 24 hours } })); app.set('view engine', 'ejs'); app.set('views', __dirname + '/views'); // Static files app.use(express.static('public')); app.use((req, res, next) => { if (req.path.startsWith("/api/v1")) { analytics.addAnalytic("apiCalls"); }; next(); }) // Other public endpoints that need special handling discordInviteCache = { time: 0, url: "" }; app.get("/discord", (req, res) => { // fetch from process.env.WIDGET_URL, get json body, redirect to body.instant_invite. Cache url for 5 minutes if (Date.now() - discordInviteCache.time < 300000) { res.redirect(discordInviteCache.url); return; } fetch(process.env.WIDGET_URL) .then(response => response.json()) .then(data => { discordInviteCache.time = Date.now(); discordInviteCache.url = data.instant_invite; res.redirect(data.instant_invite); }) .catch(error => { console.error('Error fetching discord invite:', error); res.status(500).send('Internal server error'); }); }); app.get("/footer", (req, res) => { res.render("footer", { version }); }); const startup = () => { // Load all route modules from the 'routes' folder const routesPath = path.join(__dirname, 'routes'); fs.readdirSync(routesPath).forEach((file) => { const route = require(path.join(routesPath, file)); const routeName = `/${file.replace('.js', '')}`; // Use filename as route base app.use(routeName, route); console.log(`Using ${routeName}`) }); // Start server app.listen(port, () => { console.log(`Listening on port ${port}`); }); } startup();