require("dotenv").config(); const express = require("express"); const cors = require("cors"); const fs = require("node:fs"); const wol = require("wol"); const mariadb = require("mariadb"); const bodyParser = require("body-parser"); const escapeString = require("sql-escape-string"); const multer = require("multer"); const { exec } = require("node:child_process"); const pool = mariadb.createPool({ host: process.env.DB_HOST, user: process.env.DB_USER, password: process.env.DB_PASS, connectionLimit: 5, }); // initializing app const app = express(); // initializing cors middleware for all routes const corsOptions = { origin: "*", credentials: true, }; const storage = multer.diskStorage({ destination: (_req, _file, cb) => { cb(null, "../TriggerFrontend/backgrounds/"); }, filename: (_req, file, cb) => { cb(null, file.originalname); }, }); const uploads = multer({ storage: storage }); app.use(cors(corsOptions)); app.use(bodyParser.json({ limit: "50mb" })); app.use(bodyParser.urlencoded({ limit: "50mb", extended: true })); app.use( "/trigger/api/backgroundImages", express.static("../TriggerFrontend/backgrounds"), ); ////////////////////////////////////////////// // Personal Data // ////////////////////////////////////////////// app.get("/trigger/api/personalData", async (_req, res) => { const conn = await pool.getConnection(); const query = "SELECT * FROM trigger_database.personalData;"; conn.query(query).then((rows) => { res.status(200).send(rows); }); conn.end(); }); app.patch("/trigger/api/personalData", async (req, res) => { const conn = await pool.getConnection(); const query = `UPDATE trigger_database.personalData SET firstName = ${escapeString(req.body.firstName)}, lastName = ${escapeString(req.body.lastName)}, email = ${escapeString(req.body.email)}, physicalAddress = ${escapeString(req.body.physicalAddress)};`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); ////////////////////////////////////////////// // Messages // ////////////////////////////////////////////// app.get("/trigger/api/messages", async (_req, res) => { const conn = await pool.getConnection(); const query = "SELECT * FROM trigger_database.messages;"; conn.query(query).then((rows) => { res.status(200).send(rows); }); conn.end(); }); app.get("/trigger/api/displayedGreeting", async (_req, res) => { const conn = await pool.getConnection(); const query = "SELECT * FROM trigger_database.messages ORDER BY RAND() LIMIT 1;"; conn.query(query).then((rows) => { res.status(200).send(rows); }); conn.end(); }); app.post("/trigger/api/messages", async (req, res) => { const conn = await pool.getConnection(); const query = `INSERT INTO trigger_database.messages (message) VALUES (${escapeString(req.body.message)});`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); app.patch("/trigger/api/messages", async (req, res) => { const conn = await pool.getConnection(); const query = `UPDATE trigger_database.messages SET message = ${escapeString(req.body.message)} WHERE id = ${req.body.id};`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); app.delete("/trigger/api/messages", async (req, res) => { const conn = await pool.getConnection(); const query = `DELETE FROM trigger_database.messages WHERE id = ${req.body.id};`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); ////////////////////////////////////////////// // Application Data // ////////////////////////////////////////////// app.get("/trigger/api/applications", async (_req, res) => { const conn = await pool.getConnection(); const query = "SELECT * FROM trigger_database.applications;"; conn.query(query).then((rows) => { res.status(200).send(rows); }); conn.end(); }); app.get("/trigger/api/enabledApplications", async (_req, res) => { const conn = await pool.getConnection(); const query = "SELECT * FROM trigger_database.applications WHERE enabled = 1;"; conn.query(query).then((rows) => { res.status(200).send(rows); }); conn.end(); }); app.post("/trigger/api/applications", async (req, res) => { const conn = await pool.getConnection(); const query = `INSERT INTO trigger_database.applications (name, url, icon, baseUrl, filterName, enabled, \`index\`) VALUES (${escapeString(req.body.name)}, ${escapeString(req.body.url)}, ${escapeString(req.body.icon)}, ${escapeString(req.body.baseUrl)}, ${escapeString(req.body.filterName)}, ${req.body.enabled}, ${req.body.index});`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); app.patch("/trigger/api/applications", async (req, res) => { const conn = await pool.getConnection(); const query = `UPDATE trigger_database.applications SET name = ${escapeString(req.body.name)}, url = ${escapeString(req.body.url)}, icon = ${escapeString(req.body.icon)}, baseUrl = ${escapeString(req.body.baseUrl)}, filterName = ${escapeString(req.body.filterName)}, enabled = ${req.body.enabled} WHERE id = ${req.body.id};`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); app.delete("/trigger/api/applications", async (req, res) => { const conn = await pool.getConnection(); const query = `DELETE FROM trigger_database.applications WHERE id = ${req.body.id}`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); app.get("/trigger/api/appCategories", async (_req, res) => { const conn = await pool.getConnection(); const query = "SELECT filterName FROM trigger_database.applications UNION SELECT filterName FROM trigger_database.bookmarkCategories;"; conn.query(query).then((rows) => { res.status(200).send(rows); }); conn.end(); }); app.patch("/trigger/api/applicationOrder", async (req, res) => { const conn = await pool.getConnection(); req.body.map((link) => { conn .query( "UPDATE trigger_database.applications SET `index` = ? where id IN (?)", [link.index, link.id], ) .then((_rows) => { res.status(200); }); }); res.send({ status: "success" }); conn.end(); }); ////////////////////////////////////////////// // Bookmark Data // ////////////////////////////////////////////// app.get("/trigger/api/bookmarks", async (_req, res) => { const conn = await pool.getConnection(); const query = "SELECT * FROM trigger_database.bookmarks;"; conn.query(query).then((rows) => { res.status(200).send(rows); }); conn.end(); }); app.post("/trigger/api/bookmarks", async (req, res) => { const conn = await pool.getConnection(); const query = `INSERT INTO trigger_database.bookmarks (name, url, category, \`index\`) VALUES (${escapeString(req.body.name)}, ${escapeString(req.body.url)}, ${escapeString(req.body.category)}, ${req.body.index});`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); app.patch("/trigger/api/bookmarks", async (req, res) => { const conn = await pool.getConnection(); const query = `UPDATE trigger_database.bookmarks SET name = ${escapeString(req.body.name)}, url = ${escapeString(req.body.url)}, category = ${escapeString(req.body.category)} WHERE id = ${req.body.id}`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); app.delete("/trigger/api/bookmarks", async (req, res) => { const conn = await pool.getConnection(); if (req.body.length > 1) { req.body.map((link) => { conn .query("DELETE FROM trigger_database.bookmarks where id IN (?)", [ link.id, ]) .then(() => { res.status(200); }); }); res.send({ status: "success" }); } else { const query = `DELETE FROM trigger_database.bookmarks WHERE id = ${req.body.id}`; conn .query(query) .then(() => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); } conn.end(); }); app.patch("/trigger/api/bookmarkOrder", async (req, res) => { const conn = await pool.getConnection(); req.body.map((link) => { conn .query( "UPDATE trigger_database.bookmarks SET `index` = ? where id IN (?)", [link.index, link.id], ) .then(() => { res.status(200); }); }); res.send({ status: "success" }); conn.end(); }); app.get("/trigger/api/bookmarkCategories", async (_req, res) => { const conn = await pool.getConnection(); const query = "SELECT * from trigger_database.bookmarkCategories;"; conn.query(query).then((rows) => { res.status(200).send(rows); }); conn.end(); }); app.post("/trigger/api/bookmarkCategories", async (req, res) => { const conn = await pool.getConnection(); const query = `INSERT INTO trigger_database.bookmarkCategories (categoryName, categoryIndex, filterName) VALUES (${escapeString(req.body.categoryName)},${req.body.categoryIndex || 0},${escapeString(req.body.filterName)})`; conn.query(query).then((_rows) => { res.status(200).send({ status: "success" }); }); conn.end(); }); app.patch("/trigger/api/bookmarkCategories", async (req, res) => { const conn = await pool.getConnection(); const query = `UPDATE trigger_database.bookmarkCategories SET categoryName = ${escapeString(req.body.categoryName)}, filterName = ${escapeString(req.body.filterName)} WHERE id = ${req.body.id};`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); app.delete("/trigger/api/bookmarkCategories", async (req, res) => { const conn = await pool.getConnection(); const query = `DELETE FROM trigger_database.bookmarkCategories WHERE id = ${req.body.id}`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); app.patch("/trigger/api/bookmarkCategoryOrder", async (req, res) => { const conn = await pool.getConnection(); req.body.map((category) => { conn .query( "UPDATE trigger_database.bookmarkCategories SET categoryIndex = ? where id IN (?)", [category.index, category.id], ) .then((_rows) => { res.status(200); }); }); res.send({ status: "success" }); conn.end(); }); ////////////////////////////////////////////// // Provider Data // ////////////////////////////////////////////// app.get("/trigger/api/providers", async (_req, res) => { const conn = await pool.getConnection(); const query = "SELECT * FROM trigger_database.providers;"; conn.query(query).then((rows) => { res.status(200).send(rows); }); conn.end(); }); app.post("/trigger/api/providers", async (req, res) => { const conn = await pool.getConnection(); const query = `INSERT INTO trigger_database.providers (name, url, prefix) VALUES (${escapeString(req.body.name)}, ${escapeString(req.body.url)}, ${escapeString(req.body.prefix)})`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); app.patch("/trigger/api/providers", async (req, res) => { const conn = await pool.getConnection(); const query = `UPDATE trigger_database.providers SET name = ${escapeString(req.body.name)}, url = ${escapeString(req.body.url)}, prefix = ${escapeString(req.body.prefix)} WHERE id = ${req.body.id}`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); app.delete("/trigger/api/providers", async (req, res) => { const conn = await pool.getConnection(); const query = `DELETE FROM trigger_database.providers WHERE id = ${req.body.id}`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); ////////////////////////////////////////////// // Theming // ////////////////////////////////////////////// app.get("/trigger/api/themes", async (_req, res) => { const conn = await pool.getConnection(); const query = "SELECT * FROM trigger_database.themes;"; conn.query(query).then((rows) => { res.status(200).send(rows); }); conn.end(); }); app.post("/trigger/api/themes", async (req, res) => { const conn = await pool.getConnection(); const query = `INSERT INTO trigger_database.themes (name, backgroundImage, backgroundColor, searchColor, primaryColor, accentColor, borderColor) VALUES (${escapeString(req.body.name)},${escapeString(req.body.backgroundImage)},${escapeString(req.body.backgroundColor)},${escapeString(req.body.searchColor)},${escapeString(req.body.primaryColor)},${escapeString(req.body.accentColor)},${escapeString(req.body.borderColor)})`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); app.patch("/trigger/api/themes", async (req, res) => { const conn = await pool.getConnection(); const query = `UPDATE trigger_database.themes SET name = ${escapeString(req.body.name)}, backgroundImage = ${escapeString(req.body.backgroundImage)}, backgroundColor = ${escapeString(req.body.backgroundColor)}, searchColor = ${escapeString(req.body.searchColor)}, primaryColor = ${escapeString(req.body.primaryColor)}, accentColor = ${escapeString(req.body.accentColor)}, borderColor = ${escapeString(req.body.borderColor)}, randomEnabled = ${req.body.randomEnabled ? 1 : 0} WHERE id = ${req.body.id};`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); app.delete("/trigger/api/themes", async (req, res) => { const conn = await pool.getConnection(); const query = `DELETE FROM trigger_database.themes WHERE id = ${req.body.id}`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); app.get("/trigger/api/themePlaylists", async (_req, res) => { const conn = await pool.getConnection(); const query = "SELECT * FROM trigger_database.themePlaylists;"; conn.query(query).then((rows) => { res.status(200).send(rows); }); conn.end(); }); app.post("/trigger/api/themePlaylists", async (req, res) => { const conn = await pool.getConnection(); const query = `INSERT INTO trigger_database.themePlaylists (name, themeList) VALUES (${escapeString(req.body.name)},${escapeString(req.body.themeList)});`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); app.patch("/trigger/api/themePlaylists", async (req, res) => { const conn = await pool.getConnection(); const query = `UPDATE trigger_database.themePlaylists SET name = ${escapeString(req.body.name)}, themeList = ${escapeString(req.body.themeList)} WHERE id = ${req.body.id};`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); app.delete("/trigger/api/themePlaylists", async (req, res) => { const conn = await pool.getConnection(); const query = `DELETE FROM trigger_database.themePlaylists WHERE id = ${req.body.id}`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); app.get("/trigger/api/backgrounds", async (_req, res) => { fs.readdir("../TriggerFrontend/backgrounds", (_err, files) => { res.status(200).send(files); }); }); app.post( "/trigger/api/backgrounds", uploads.single("background"), async (req, res) => { const image = req.file.path; exec(`convert ${image} -resize 'x1080' -define jpeg:extent=300kb ${image}`); res.status(200).send({ status: "success", statusText: image }); }, ); app.patch("/trigger/api/backgrounds", async (req, res) => { fs.rename( `../TriggerFrontend/backgrounds/${req.body.fileName}`, `../TriggerFrontend/backgrounds/${req.body.newFileName}`, (err) => { if (err) throw err; console.log("Rename complete!"); }, ); const conn = await pool.getConnection(); const query = `UPDATE trigger_database.themes SET backgroundImage = ${escapeString(req.body.newFileName)} WHERE backgroundImage = ${escapeString(req.body.fileName)};`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); app.delete("/trigger/api/backgrounds", async (req, res) => { fs.rm(`../TriggerFrontend/backgrounds/${req.body.fileName}`, (_err) => { res.status(200).send({ status: "success" }); }); const conn = await pool.getConnection(); const query = `UPDATE trigger_database.themes SET backgroundImage = 'none' WHERE backgroundImage = ${escapeString(req.body.fileName)};`; conn .query(query) .then((_rows) => { console.log("success"); }) .catch((err) => { console.log(err); }); conn.end(); }); ////////////////////////////////////////////// // Wake on LAN Clients // ////////////////////////////////////////////// app.get("/trigger/api/wolClients", async (_req, res) => { const conn = await pool.getConnection(); const query = "SELECT * from trigger_database.wolClients;"; conn.query(query).then((rows) => { res.status(200).send(rows); }); conn.end(); }); app.post("/trigger/api/wolClients", async (req, res) => { const conn = await pool.getConnection(); const query = `INSERT INTO trigger_database.wolClients (hostname, macAddress) VALUES (${escapeString(req.body.hostname)}, ${escapeString(req.body.macAddress)})`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { console.log(err); res.status(500).send({ error: err }); }); conn.end(); }); app.patch("/trigger/api/wolClients", async (req, res) => { const conn = await pool.getConnection(); const query = `UPDATE trigger_database.wolClients SET hostname = ${escapeString(req.body.hostname)}, macAddress = ${escapeString(req.body.macAddress)} WHERE id = ${req.body.id}`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { console.log(err); res.status(500).send({ error: err }); }); conn.end(); }); app.delete("/trigger/api/wolClients", async (req, res) => { const conn = await pool.getConnection(); const query = `DELETE FROM trigger_database.wolClients WHERE id = ${req.body.id}`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); app.post("/trigger/api/wakeClient", async (req, res) => { wol.wake(req.body.macAddress, () => { res.status(200).send({ status: "success" }); }); }); ////////////////////////////////////////////// // Search // ////////////////////////////////////////////// app.get("/trigger/api/searchHistory", async (_req, res) => { const conn = await pool.getConnection(); const query = "SELECT * FROM trigger_database.searchHistory ORDER BY dateTimeStamp DESC;"; conn.query(query).then((rows) => { res.status(200).send(rows); }); conn.end(); }); app.post("/trigger/api/searchHistory", async (req, res) => { const conn = await pool.getConnection(); const query = `INSERT INTO trigger_database.searchHistory (prefix, provider, searchTerm) VALUES (${escapeString(req.body.prefix)}, ${escapeString(req.body.provider)}, ${escapeString(req.body.searchTerm)})`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); app.delete("/trigger/api/searchHistory", async (req, res) => { const conn = await pool.getConnection(); const query = `DELETE FROM trigger_database.searchHistory WHERE id = ${req.body.id}`; conn .query(query) .then((_rows) => { res.status(200).send({ status: "success" }); }) .catch((err) => { res.status(500).send({ error: err }); }); conn.end(); }); // app is listening on port 5000 app.listen(5000, "0.0.0.0", () => {});