357 lines
10 KiB
JavaScript
Executable File
357 lines
10 KiB
JavaScript
Executable File
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 { search, download } from "./accounts/music.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());
|
|
app.use("/", (req, res, next) => {
|
|
next();
|
|
});
|
|
// setInterval(()=>{
|
|
|
|
// }, 1000)
|
|
import WebSocket, { WebSocketServer } from "ws";
|
|
const wss = new WebSocketServer({ noServer: true });
|
|
let openSockets = 0;
|
|
wss.on("connection", function connection(ws, req, res) {
|
|
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 });
|
|
}
|
|
}
|
|
});
|
|
|
|
ws.on("close", () => {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.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.use("/api/account/load", async (req, res, next) => {
|
|
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("./public", { 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 if (mime.lookup(filePath) == "audio/ogg") {
|
|
res.type("audio/ogg");
|
|
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);
|
|
});
|
|
|
|
|
|
app.use("/api/music/search", async (req, res, next) => {
|
|
res.status(200).send(await search(req.query.q))
|
|
});
|
|
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(`./public/404.html`))
|
|
.status(404);
|
|
});
|