const io = require('@pm2/io') import { log } from "./log.js"; import bodyParser from "body-parser"; import express from "express"; import cookieParser from "cookie-parser"; import fs from "node:fs/promises"; import { fileURLToPath } from "url"; import path, { dirname } from "node:path"; import mime from "mime-types"; import compression from "compression"; import { accs, infdb, polytrack } from "./database.js"; import { } from "./accounts/friend.js"; import { banUser, removeAccount, verifyCookie, getUserFromCookie, createAccount, resetPassword, loginAccount, addBadge } from "./accounts/manage.js"; import { } from "./accounts/misc.js"; import { getRawData, generateAccountPage, editProfile, saveData, getUsers, isAdmin, retrieveData } from "./accounts/profile.js"; // import { infiniteCraft, chatBot } from "./ai.js"; import os from "node:os"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const port = process.env.PORT || 3000; const app = express(); app.use(compression()); app.use(cookieParser()); app.use(express.json({ limit: "10mb" })); app.use(express.urlencoded({ extended: false })); app.use(express.text()); let requests = 0; const requestsPerSec = io.meter({ name: 'req/sec', id: 'app/requests/sec' }); const requestsPerMin = io.meter({ name: 'req/min', id: 'app/requests/min' }); app.use("/", (req, res, next) => { requestsPerSec.mark(); requestsPerMin.mark(); next(); }); const sockets = io.metric({ name: 'Open Websockets', id: 'app/requests/sockets', }); // setInterval(()=>{ // }, 1000) import WebSocket, { WebSocketServer } from "ws"; import { request } from "node:http"; const wss = new WebSocketServer({ noServer: true }); let openSockets = 0; wss.on("connection", function connection(ws, req, res) { openSockets++; sockets.set(openSockets); setInterval(() => { ws.send("ping"); }, 30000); ws.on("error", console.error); ws.on("message", async function message(data, isBinary) { let message = Buffer.from(data).toString(); if (message.startsWith(process.env.ANNOUNCEMENT_KEY)) { wss.clients.forEach(function each(client) { if (client.readyState === WebSocket.OPEN) { client.send(message.replace(process.env.ANNOUNCEMENT_KEY, "announce.")); } }); } else if (message.startsWith("token") && (await verifyCookie(message.substring(6)))) { ws.id = await getUserFromCookie(message.substring(6)); ws.send(ws.id); const updateAccount = accs.query(`UPDATE accounts SET last_login = $login WHERE username = $user`) updateAccount.get({ $login: new Date().toUTCString(), $user: ws.id }); } else if (message.startsWith("pong")) { if (ws.id) { const updateAccount = accs.query(`UPDATE accounts SET last_login = $login WHERE username = $user`) updateAccount.get({ $login: new Date().toUTCString(), $user: ws.id }); if (message.substring(4)) { const existingAccount = accs.query(`SELECT * FROM accounts WHERE username LIKE $1`) let userData = existingAccount.get({ $1: ws.id }); if (userData == null) { return { success: false, reason: "The account doesn't exist." }; } let games; if (userData.playedgames) { games = JSON.parse(userData.playedgames); } else { games = {}; } if (games[message.substring(4)]) { games[message.substring(4)] += 30; } else { games[message.substring(4)] = 30; } const updateAccount = accs.query(`UPDATE accounts SET playedgames = $playedgames WHERE username = $user`) updateAccount.get({ $playedgames: JSON.stringify(games), $user: ws.id }); } } } }); ws.on("close", () => {openSockets--; sockets.set(openSockets);}); }); app.post( "/api/event", (req, res) => { fetch("https://analytics.skysthelimit.dev/api/event", { method: "post", headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: req.body }).then(async (response) => { res.send(response.status); }) } ); // app.use("*.json", async (req, res, next) => { // optimize json // console.log("got data"); // next() // }); app.post("/register", async (req, res) => { let status = await createAccount(req.body.username, req.body.password, req.body["h-captcha-response"]); if (status["success"]) { res.status(200).send(status); } else { res.status(400).send(status); } }); app.post("/login", async (req, res) => { let status = await loginAccount(req.body.username, req.body.password, req.body["h-captcha-response"]); if (status["success"]) { res.status(200).send(status); } else { res.status(400).send(status); } }); app.use(["/register", "/login"], async (req, res, next) => { if (req.cookies.token && (await verifyCookie(req.cookies.token))) { res.redirect("/u/"); } else { res .type("text/html") .status(200) .send(await fs.readFile(`./html${req.baseUrl}.html`)); } }); app.use("/users", async (req, res, next) => { res .type("text/html") .status(200) .send(await fs.readFile(`./html/users.html`)); }); app.use("/reset", async (req, res, next) => { res .type("text/html") .status(200) .send(await fs.readFile(`./html/reset.html`)); }); app.post("/api/account/upload", async (req, res, next) => { if (req.cookies.token && (await verifyCookie(req.cookies.token))) { let status = await saveData(req.cookies.token, req.body.data); if (status["success"]) { res.status(200).send(status); } else { res.status(400).send(status); } } else { return "KILL YOURSELF"; } }); // ai endpoints app.post("/api/ai/createChat", async (req, res) => { // create chat in database and store some metadata about it // ie: last model, time created, etc etc }) app.post("/api/ai/sendMessage", async (req, res) => { // take in chat id and message // stream back reply }) app.get("/api/ai/messages", async (req, res) => { // take in chat id // use unique uuid to store every chat // return messages }) // friends endpoints app.get("/api/friends/list", async (req, res) => { // use auth token to get user // just return array of user infos }) app.get("/api/friends/requests", async (req, res) => { // view friend requests }) app.post("/api/friends/sendRequest", async (req, res) => { // takes in user // send friend request }) app.post("/api/friends/reply", async (req, res) => { // accepted? true or false }) // chat endpoints app.post("/api/chat/create", async (req, res) => { // create a new chat // create one for new friends maybe? // generates metadata // time created, last message sent, people in the chat, id, owner }) app.post("/api/chat/send", async (req, res) => { // send chat message // id, auth, message }) app.post("/api/chat/addUser", async (req, res) => { // add user to chat }) app.post("/api/chat/removeUser", async (req, res) => { // remove user from chat }) app.get("/api/chat/recent", async (req, res) => { // get last 50 recent messages // offset by a page param }) app.get("/api/infinite/get", async (req, res, next) => { if (req.query[1] && req.query[2]) { let success = false; let data; try { let search1Query = infdb.query(`SELECT * FROM caches WHERE 1 = $one AND 2 = $two`) let search1 = await search1Query.get({ $one: req.query[1], $two: req.query[2] }); console.log(search1); if (search1 && search1.length > 0) { data = { item: search1[0].result_item, emoji: search1[0].result_emoji, new: false }; success = true; } else { let search2Query = infdb.query(`SELECT * FROM caches WHERE 1 = $two AND 2 = $one`) let search2 = await search2Query.get({ $one: req.query[1], $two: req.query[2] }); console.log(search2); if (search2 && search2.length > 0) { data = { item: search2[0].result_item, emoji: search2[0].result_emoji, new: false }; success = true; } } } catch (error) { console.error(error); } if (success) { console.log("success"); res.send(data); return; } data = await infiniteCraft(req.query[1], req.query[2]); try { let parse = JSON.parse(data); let keys = Object.keys(parse); if (keys.indexOf("item") > -1 && keys.indexOf("emoji") > -1) { parse.new = true; data = parse; const createCached = infdb.query(`INSERT INTO caches (1, 2, result_item, result_emoji) VALUES ($one, $two, $item, $emoji)`) createCached.run({ $one: req.query[1], $two: req.query[2], $item: data.item, $emoji: data.emoji }); res.send(data); } } catch (error) { console.error(error); data = { item: "N/A", emoji: "N/A" }; res.send(data); } } }); app.use("/semag/polytrack/data/", async (req, res, next) => { let path = req.path.substring(1, req.path.length); if(path == "user") { res.sendStatus(200); } else if(path == "leaderboard") { let data = {}; if(req.method == "POST") { req.body.split("&").forEach((item) => { data[item.split("=")[0]] = item.split("=")[1] }); console.log(data); const getExistingRuns = polytrack.query(`SELECT * FROM polytrack WHERE userid = $usrid AND trackid = $trackid`); let existingRuns = getExistingRuns.all({ $usrid: data["userToken"], $trackid: data["trackId"] }); let saveRun = true; if(existingRuns !== null) { existingRuns.forEach((item) => { if(saveRun) { if(data.frames > item.frames) { saveRun = false; } else { let deleteRun = polytrack.query(`DELETE FROM polytrack WHERE id = $id`); deleteRun.run({ $id: item.id }) } } }) } if(saveRun) { const addRun = polytrack.query(`INSERT INTO polytrack (trackid, username, colors, recording, frames, userid) VALUES ($id, $usr, $clr, $record, $frames, $usrid)`) let runData = addRun.run({ $id: data["trackId"], $usr: data["name"], $clr: data["carColors"], $record: data["recording"], $usrid: data["userToken"], $frames: data["frames"] }); console.log("run", runData); res.send(runData.lastInsertRowid); } } else { let leaderboard = polytrack.query(`SELECT * FROM polytrack WHERE trackid = $id LIMIT $limit OFFSET $offset`).all({ $id: req.query.trackId, $limit: req.query.amount, $offset: req.query.skip }) console.log(leaderboard); let returnValue = {"total": leaderboard.length, "entries":[]} for(let i = 0; i { if (req.cookies.token && (await verifyCookie(req.cookies.token))) { let status = await retrieveData(req.cookies.token); if (status["success"]) { res.status(200).send(status); } else { res.status(400).send(status); } } else { res.status(200).send("No token"); } }); app.use("/api/getUsers", async (req, res, next) => { console.log(req.query) let status = await getUsers(req.query.page, req.query.query); res.status(200).send(status); }); app.use("/admin", async (req, res, next) => { if ((await isAdmin(req.cookies.token)) && (await verifyCookie(req.cookies.token))) { res .type("text/html") .status(200) .send(await fs.readFile(`./html/admin.html`)); } else { next(); } }); app.use("/api/stats", async (req, res, next) => { if ((await isAdmin(req.cookies.token)) && (await verifyCookie(req.cookies.token))) { res .type("text/json") .status(200) .send({ "users": accs.query(`SELECT COUNT(*) FROM accounts`).get()["COUNT(*)"], // "cpu": os.cpus(), "ram": `${(os.totalmem()-os.freemem())/1000000000}GB / ${os.totalmem()/1000000000}GB`, "cpuUsage": os.loadavg(), "openWebSockets": openSockets, "uptime": `${os.uptime()}s` }); } else { next(); } }); app.use("/ai", async (req, res, next) => { if (await verifyCookie(req.cookies.token) && isAdmin(req.cookies.token)) { res .type("text/html") .status(200) .send(await fs.readFile(`./html/ai.html`)); } else { next(); } }); app.use("/", express.static("./selenite", { extensions: ["html"] })); app.use("/data/:id/:file", async (req, res) => { const id = path.basename(req.params.id); const file = path.basename(req.params.file); const filePath = path.join(process.env.DATA_PATH, "data", id, file); try { await fs.access(filePath); const image = await fs.readFile(filePath); if (mime.lookup(filePath) == "image/webp") { res.type("image/webp"); res.status(200).send(image); } else { res.status(404).send("File not found"); } } catch (error) { console.error(error); res.status(404).send("File not found"); } }); app.use("/u/raw", async (req, res) => { if (req.cookies.token && (await verifyCookie(req.cookies.token))) { res.send(await getRawData(req.cookies.token)); } else { res.redirect("/register"); } }); app.use("/u/:username/edit", async (req, res, next) => { if (await isAdmin(req.cookies.token)) { res.send(await generateAccountPage(req.params.username, req.cookies.token, true)); return; } next(); }); app.use("/u/:username", async (req, res) => { if (["skysthelimit.dev", "selenite.cc", "selenite", "owner"].includes(req.params.username)) { res.redirect("/u/sky"); return; } res.send(await generateAccountPage(req.params.username, req.cookies.token)); }); app.use("/u/", async (req, res) => { if (req.cookies.token && (await verifyCookie(req.cookies.token))) { res.send(await generateAccountPage(req.params.username, req.cookies.token)); } else { res.redirect("/register"); } }); app.post("/api/account/reset", async (req, res) => { let status = await resetPassword(req.body.username, req.body.key, req.body.password, req.body["h-captcha-response"]); if (status["success"]) { res.status(200).send(status); } else { res.status(400).send(status); } }); app.post("/api/profile/edit", async (req, res) => { let status = await editProfile(req.body, req.cookies.token, false); if (status["success"]) { res.status(200).send(status); } else { res.status(400).send(status); } }); app.post("/api/admin/badge", async (req, res) => { let status = await addBadge(req.body.username, req.body.badge, req.cookies.token); if (status["success"]) { res.status(200).send(status); } else { res.status(400).send(status); } }); app.post("/api/admin/removeAcc", async (req, res) => { let status = await removeAccount(req.body.username, req.cookies.token); res.status(200).send(status); }); app.post("/api/admin/removeAcc", async (req, res) => { let status = await editProfile(req.body, req.cookies.token, true); res.status(200).send(status); }); app.post("/api/admin/ban", async (req, res) => { let status = await banUser(req.body.name, req.body.reason, req.cookies.token); res.status(200).send(status); }); const server = app.listen(port, () => { console.log(log.success("Express is online.")); console.log("- " + log.info("http://localhost:" + port)); }); server.on("upgrade", (request, socket, head) => { wss.handleUpgrade(request, socket, head, (socket) => { wss.emit("connection", socket, request); }); }); app.use(async (req, res) => { res .type("text/html") .send(await fs.readFile(`./selenite/404.html`)) .status(404); });