Compare commits

...

14 Commits

Author SHA1 Message Date
sky
5770f28acb Merge pull request 'v2' (#1) from development into main
Reviewed-on: #1
2025-10-26 12:54:21 -04:00
sky
ea8b1a6642 release 2025-10-26 10:26:58 -04:00
sky
ba9b82cf21 update 2025-10-13 16:09:27 -04:00
sky
1bb39463e4 fix hotline miami?? 2025-09-29 15:48:24 -04:00
sky
31479019f5 optimizations 2025-09-27 20:54:40 -04:00
sky
36bb49aa80 online users 2025-09-07 16:29:24 -04:00
sky
bcda82b065 minor fixes 2025-08-23 17:24:30 -04:00
sky
4e315c993a mobile, idek 2025-08-17 19:14:11 -04:00
sky
db94e9fe49 add processing server check 2025-08-12 14:14:36 -04:00
sky
9389d0c7e4 music control 2025-07-10 04:55:08 -04:00
sky
daf54267b4 mostly finish custom music 2025-07-09 02:35:38 -04:00
sky
acd6d2e9e2 finish up profiles, begin custom songs 2025-07-08 22:01:16 -04:00
sky
cbdf039eab begin work on redesign for user pages 2025-07-08 00:23:29 -04:00
sky
fa52034fb7 fix stuff after moving to resources 2025-06-30 03:19:11 -04:00
23 changed files with 1743 additions and 1396 deletions

View File

@ -4,4 +4,8 @@ HCAPTCHA_SECRET=hcaptcha
ANNOUNCEMENT_KEY=text.
GROQ_API_KEY=["groq_api"]
NTFY_ALERT=ntfy_channel
DATA_PATH=where_to_store_data
DATA_PATH=where_to_store_data
NODE_ENV=production
DISCORD_AI_WEBHOOK=discord_webhook_url
PROCESSING_SERVER=https://git.ceres.rip/selenite/processing
PROCESSING_SERVER_SECRET=secret

View File

@ -1,3 +1,3 @@
import { accs, friends } from "../database.js";
// import { accs, friends } from "../database.js";
export { };

153
accounts/music.js Normal file
View File

@ -0,0 +1,153 @@
import Soundcloud from 'lucida/streamers/soundcloud/main.js'
let clientId = process.env.SOUNDCLOUD_CLIENT_ID;
let sc = new Soundcloud({
// oauthToken: clientId
})
async function search(query) {
let data = sc.search(query);
return data;
}
// so um fuck lucida
// based on https://github.com/imputnet/cobalt/blob/58ea4aed01383ead74d5e32e75335eddc2f015be/api/src/processing/services/soundcloud.js
const cachedID = {
version: '',
id: ''
}
async function findClientID() {
try {
const sc = await fetch('https://soundcloud.com/').then(r => r.text()).catch(() => {});
const scVersion = String(sc.match(/<script>window\.__sc_version="[0-9]{10}"<\/script>/)[0].match(/[0-9]{10}/));
if (cachedID.version === scVersion) {
return cachedID.id;
}
const scripts = sc.matchAll(/<script.+src="(.+)">/g);
let clientid;
for (let script of scripts) {
const url = script[1];
if (!url?.startsWith('https://a-v2.sndcdn.com/')) {
return;
}
const scrf = await fetch(url).then(r => r.text()).catch(() => {});
const id = scrf.match(/\("client_id=[A-Za-z0-9]{32}"\)/);
if (id && typeof id[0] === 'string') {
clientid = id[0].match(/[A-Za-z0-9]{32}/)[0];
break;
}
}
cachedID.version = scVersion;
cachedID.id = clientid;
return clientid;
} catch {}
}
const findBestForPreset = (transcodings, preset) => {
let inferior;
for (const entry of transcodings) {
const protocol = entry?.format?.protocol;
if (entry.snipped || protocol?.includes('encrypted')) {
continue;
}
if (entry?.preset?.startsWith(`${preset}_`)) {
if (protocol === 'progressive') {
return entry;
}
inferior = entry;
}
}
return inferior;
}
async function download(obj) {
const clientId = await findClientID();
if (!clientId) return { error: "fetch.fail" };
let link = obj;
const resolveURL = new URL("https://api-v2.soundcloud.com/resolve");
resolveURL.searchParams.set("url", link);
resolveURL.searchParams.set("client_id", clientId);
const json = await fetch(resolveURL).then(r => r.json()).catch(() => {});
if (!json) return { error: "fetch.fail" };
if (json.duration > 60 * 30 * 1000) {
return { error: "content.too_long" };
}
if (json.policy === "BLOCK") {
return { error: "content.region" };
}
if (json.policy === "SNIP") {
return { error: "content.paid" };
}
if (!json.media?.transcodings || !json.media?.transcodings.length === 0) {
return { error: "fetch.empty" };
}
let bestAudio = "opus",
selectedStream = findBestForPreset(json.media.transcodings, "opus");
const mp3Media = findBestForPreset(json.media.transcodings, "mp3");
// use mp3 if present if user prefers it or if opus isn't available
if (mp3Media && (obj.format === "mp3" || !selectedStream)) {
selectedStream = mp3Media;
bestAudio = "mp3"
}
if (!selectedStream) {
return { error: "fetch.empty" };
}
const fileUrl = new URL(selectedStream.url);
fileUrl.searchParams.set("client_id", clientId);
fileUrl.searchParams.set("track_authorization", json.track_authorization);
const file = await fetch(fileUrl)
.then(async r => new URL((await r.json()).url))
.catch(() => {});
if (!file) return { error: "fetch.empty" };
const artist = json.user?.username?.trim();
const fileMetadata = {
title: json.title?.trim(),
album: json.publisher_metadata?.album_title?.trim(),
artist,
album_artist: artist,
composer: json.publisher_metadata?.writer_composer?.trim(),
genre: json.genre?.trim(),
date: json.display_date?.trim().slice(0, 10),
copyright: json.license?.trim(),
}
return {
urls: file.toString(),
filenameAttributes: {
service: "soundcloud",
id: json.id,
...fileMetadata
},
bestAudio,
fileMetadata,
isHLS: file.pathname.endsWith('.m3u8'),
}
}
export { search, download };

View File

@ -4,9 +4,11 @@ import sanitizeHtml from "sanitize-html";
import sharp from "sharp";
import { accs } from "../database.js";
import { getUserFromCookie, isBanned, decryptCookie, verifyCookie } from "./manage.js";
import { download } from "./music.js";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime.js";
import { error } from "node:console";
dayjs.extend(relativeTime);
const sanitizeConfig = {
@ -37,8 +39,8 @@ let rawProfileHTML = fs.readFileSync("./html/profile.html").toString();
let rawEditProfileHTML = fs.readFileSync("./html/profile_edit.html").toString();
let profile404 = fs.readFileSync("./html/profile_404.html").toString();
let profileBan = fs.readFileSync("./html/profile_ban.html").toString();
let gamesJSON = JSON.parse(fs.readFileSync("./selenite/data/games.json").toString());
let appsJSON = JSON.parse(fs.readFileSync("./selenite/data/apps.json").toString());
let gamesJSON = JSON.parse(fs.readFileSync("./public/resources/games.json").toString());
let appsJSON = JSON.parse(fs.readFileSync("./public/resources/apps.json").toString());
let profileReadyJSON = {};
for (let i = 0; i < gamesJSON.length; i++) {
profileReadyJSON[gamesJSON[i].directory] = { name: gamesJSON[i].name, image: gamesJSON[i].image };
@ -193,21 +195,62 @@ async function editProfile(body, token, admin) {
let fileType = (await fileTypeFromBuffer(pfp))["ext"];
if (["png", "jpg", "gif", "avif", "webp", "tiff"].includes(fileType)) {
let url;
let dir = `${process.env.DATA_PATH}/data/${existingAccount.id}/`;
let dir = `${process.env.DATA_PATH}/data/${userData.id}/`;
let uuid = crypto.randomUUID();
let path = `${process.env.DATA_PATH}/data/${existingAccount.id}/${uuid}.webp`;
url = `/data/${existingAccount.id}/${uuid}.webp`;
let path = `${process.env.DATA_PATH}/data/${userData.id}/${uuid}.webp`;
url = `/data/${userData.id}/${uuid}.webp`;
fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(path, "");
await sharp(pfp, { animated: fileType == "gif" })
.resize({ width: 300, withoutEnlargement: true })
.resize({ width: 400, withoutEnlargement: true })
.webp({ quality: 70, effort: 4 })
.toFile(path);
await fs.unlink(`${__dirname}/${existingAccount.pfp_url}`, () => {});
await fs.unlink(`${__dirname}/${userData.pfp_url}`, () => {});
const updateAccount = accs.query(`UPDATE accounts SET pfp_url = $url WHERE username = $user`)
updateAccount.get({ $url: url, $user: user });
}
}
if (body.artist) {
let checkStatus = new Request({
url: process.env.PROCESSING_SERVER + "/status"
});
let checkStatusRequest = await fetch(checkStatus);
if(checkStatusRequest.status != 200) {
return { success: false, err: "processing server is down, try again later"};
}
let data = await download(body.url);
if(data.error) {
if(data.error == "content.too_long") {
return { success: false, err: "the song was too long, pick something shorter!"};
} else if(data.error == "content.region") {
return { success: false, err: "the song could not be downloaded due to the region of the server."};
} else if(data.error == "content.paid") {
return { success: false, err: "the song is go+ only, and we're too poor to pay for that :( pick another song"};
} else if(data.error == "fetch.empty") {
return { success: false, err: "we could not fetch this song successfully"};
} else {
return { success: false, err: "an unknown error happened! error code: " + error};
}
}
let streamingURL = data.urls;
let request = new Request({
url: process.env.PROCESSING_SERVER + "/process",
method: "POST",
body: JSON.stringify({url: streamingURL}),
headers: {
"X-Authentication": process.env.PROCESSING_SERVER_SECRET
}
});
let oggFile = await fetch(request);
let filePath = `/data/${userData.id}/${crypto.randomUUID()}.ogg`;
await Bun.write(process.env.DATA_PATH + filePath, oggFile);
const updateAccount = accs.query(`UPDATE accounts SET music = $music WHERE username = $user`)
updateAccount.get({ $music: JSON.stringify({
path: filePath,
name: body.title,
artist: body.artist
}), $user: user });
}
return { success: true };
}
@ -228,6 +271,7 @@ async function generateAccountPage(name, cookie, admin) {
}
let modifiedHTML = rawProfileHTML;
let songData = JSON.parse(userData.music) || false;
modifiedHTML = modifiedHTML.replaceAll("{{ name }}", sanitizeHtml(userData.name, allowNone));
modifiedHTML = modifiedHTML.replaceAll("{{ join_date }}", dayjs(userData.createdAt).fromNow());
modifiedHTML = modifiedHTML.replaceAll("{{ about }}", sanitizeHtml(userData.about, sanitizeConfig) || "No about me available..");
@ -235,7 +279,15 @@ async function generateAccountPage(name, cookie, admin) {
modifiedHTML = modifiedHTML.replaceAll("{{ user_pfp }}", userData.pfp_url || "/img/user.svg");
modifiedHTML = modifiedHTML.replaceAll("{{ custom_css }}", userData.custom_css || "");
modifiedHTML = modifiedHTML.replaceAll("{{ online_time }}", dayjs(userData.last_login).fromNow());
modifiedHTML = modifiedHTML.replaceAll("{{ played_games }}", buildGameHTML(userData));
modifiedHTML = modifiedHTML.replaceAll("{{ username }}", sanitizeHtml(userData.username, allowNone));
if(songData) {
modifiedHTML = modifiedHTML.replaceAll("{{ song_title }}", sanitizeHtml(songData.name, allowNone));
modifiedHTML = modifiedHTML.replaceAll("{{ song_artist }}", sanitizeHtml(songData.artist, allowNone));
modifiedHTML = modifiedHTML.replaceAll("{{ song_url }}", sanitizeHtml(songData.path, allowNone));
modifiedHTML = modifiedHTML.replaceAll("{{ is_music }}", "true");
} else {
modifiedHTML = modifiedHTML.replaceAll("{{ is_music }}", "false");
}
let badges_html = "";
if (userData.badges !== null) {
@ -260,6 +312,7 @@ async function generateAccountPage(name, cookie, admin) {
return modified_ban;
}
let modifiedHTML = rawEditProfileHTML;
let songData = JSON.parse(userData.music) || false;
modifiedHTML = modifiedHTML.replaceAll("{{ name }}", sanitizeHtml(userData.name, sanitizeConfig));
modifiedHTML = modifiedHTML.replaceAll("{{ username }}", userData.username);
modifiedHTML = modifiedHTML.replaceAll("{{ join_date }}", dayjs(userData.createdAt).fromNow());
@ -269,7 +322,17 @@ async function generateAccountPage(name, cookie, admin) {
modifiedHTML = modifiedHTML.replaceAll("{{ url_gen }}", `https://selenite.cc/u/${userData.username}`);
modifiedHTML = modifiedHTML.replaceAll("{{ online_time }}", dayjs(userData.last_login).fromNow());
modifiedHTML = modifiedHTML.replaceAll("{{ css_edit }}", (userData.badges ? userData.badges.length : 0) > 0 ? '<img src="/img/edit.svg" id="edit" />' : "");
modifiedHTML = modifiedHTML.replaceAll("{{ played_games }}", buildGameHTML(userData));
modifiedHTML = modifiedHTML.replaceAll("{{ staff_buttons }}", await isAdmin(cookie) ? "<a href='/admin'>admin panel</a>" : "");
if(songData) {
modifiedHTML = modifiedHTML.replaceAll("{{ song_title }}", sanitizeHtml(songData.name, allowNone));
modifiedHTML = modifiedHTML.replaceAll("{{ song_artist }}", sanitizeHtml(songData.artist, allowNone));
modifiedHTML = modifiedHTML.replaceAll("{{ song_url }}", sanitizeHtml(songData.path, allowNone));
modifiedHTML = modifiedHTML.replaceAll("{{ is_music }}", "true");
} else {
modifiedHTML = modifiedHTML.replaceAll("{{ is_music }}", "false");
modifiedHTML = modifiedHTML.replaceAll("{{ song_title }}", "no song selected...");
modifiedHTML = modifiedHTML.replaceAll("{{ song_artist }}", "");
}
let badges_html = "";
if (userData.badges !== null) {
@ -282,43 +345,7 @@ async function generateAccountPage(name, cookie, admin) {
return modifiedHTML;
}
}
function buildGameHTML(existingAccount) {
if (existingAccount.playedgames) {
let games = JSON.parse(existingAccount.playedgames);
let sortedGames = Object.keys(games).sort((a, b) => games[b] - games[a]);
let return_data = [];
if (Object.keys(games).length < 10) {
for (let i = 0; i < sortedGames.length; i++) {
try {
let origin = gamesExceptions[sortedGames[i]] ? "sppa" : "semag";
sortedGames[i] = gamesExceptions[sortedGames[i]] ? gamesExceptions[sortedGames[i]] : sortedGames[i];
return_data[i] = { name: profileReadyJSON[sortedGames[i]].name, image: profileReadyJSON[sortedGames[i]].image, path: sortedGames[i], origin: origin, valid: true };
} catch (e) {
return_data[i] = { valid: false };
}
}
} else {
for (let i = 0; i < 10; i++) {
try {
let origin = gamesExceptions[sortedGames[i]] ? "sppa" : "semag";
sortedGames[i] = gamesExceptions[sortedGames[i]] ? gamesExceptions[sortedGames[i]] : sortedGames[i];
return_data[i] = { name: profileReadyJSON[sortedGames[i]].name, image: profileReadyJSON[sortedGames[i]].image, path: sortedGames[i], origin: origin, valid: true };
} catch (e) {
return_data[i] = { valid: false };
}
}
}
let return_html = "";
for (let i = 0; i < Object.keys(return_data).length; i++) {
if (return_data[i].valid) {
return_html += `<div class="played-game"><img src="/${return_data[i].origin}/${return_data[i].path}/${return_data[i].image}"/><p>${return_data[i].name}</p></div>`;
}
}
return return_html;
} else {
return "<h3>Play some games to view things appear here!</h3>";
}
}
async function getUsers(page, search) {
let amount = 12;
if (!page) {

25
ai.js
View File

@ -1,6 +1,21 @@
import OpenAI from "openai";
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
import { generateText } from 'ai';
const client = new OpenAI({
apiKey: process.env.GROQ_API_KEY,
baseURL: "https://api.groq.com/openai/v1"
});
const model = createOpenAICompatible({
name: 'model',
apiKey: process.env.OPENAI_API_KEY,
baseURL: process.env.OPENAI_BASE_URL,
includeUsage: true, // Include usage information in streaming responses
});
async function callAI(chatHistory) {
console.log("call ai called")
let { text } = await generateText({
model: model(process.env.OPENAI_MODEL),
system: "You are Zen, a helpful AI assistant built by Selenite. Your response should be accurate without hallucination." +
"Over the course of conversation, adapt to the user's tone and preferences. Try to match the user's vibe, tone, and generally how they are speaking. You want the conversation to feel natural. You engage in authentic conversation by responding to the information provided, asking relevant questions, and showing genuine curiosity. If natural, use information you know about the user to personalize your responses and ask a follow up question.",
messages: chatHistory
});
console.log(text);
}
export { callAI };

417
bun.lock Normal file
View File

@ -0,0 +1,417 @@
{
"lockfileVersion": 1,
"workspaces": {
"": {
"name": "rewrite",
"dependencies": {
"@ai-sdk/openai-compatible": "^1.0.22",
"ai": "^5.0.68",
"axios": "^1.12.2",
"body-parser": "^2.2.0",
"chalk": "^5.6.2",
"compression": "^1.8.1",
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"dayjs": "^1.11.18",
"dotenv": "^16.6.1",
"express": "^5.1.0",
"file-type": "^19.6.0",
"lucida": "^2.0.0-54",
"mime-types": "^2.1.35",
"rword": "^3.2.1",
"sanitize-html": "^2.17.0",
"sharp": "^0.33.5",
"ws": "^8.18.3",
},
},
},
"packages": {
"@ai-sdk/gateway": ["@ai-sdk/gateway@1.0.39", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.12", "@vercel/oidc": "3.0.2" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ijYCKG2sbn2RBVfIgaXNXvzHAf2HpFXxQODtjMI+T7Z4CLryflytchsZZ9qrGtsjiQVopKOV6m6kj4lq5fnbsg=="],
"@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.22", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.12" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Q+lwBIeMprc/iM+vg1yGjvzRrp74l316wDpqWdbmd4VXXlllblzGsUgBLTeKvcEapFTgqk0FRETvSb58Y6dsfA=="],
"@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="],
"@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.12", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZtbdvYxdMoria+2SlNarEk6Hlgyf+zzcznlD55EAl+7VZvJaSg2sqPvwArY7L6TfDEDJsnCq0fdhBSkYo0Xqdg=="],
"@borewit/text-codec": ["@borewit/text-codec@0.1.1", "", {}, "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA=="],
"@emnapi/runtime": ["@emnapi/runtime@1.2.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ=="],
"@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="],
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="],
"@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="],
"@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="],
"@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="],
"@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="],
"@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="],
"@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="],
"@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="],
"@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="],
"@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="],
"@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="],
"@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="],
"@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="],
"@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="],
"@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="],
"@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="],
"@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="],
"@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="],
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="],
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
"@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="],
"@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="],
"@protobufjs/codegen": ["@protobufjs/codegen@2.0.4", "", {}, "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="],
"@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="],
"@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="],
"@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="],
"@protobufjs/inquire": ["@protobufjs/inquire@1.1.0", "", {}, "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="],
"@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="],
"@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="],
"@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="],
"@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="],
"@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
"@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
"@types/node": ["@types/node@20.14.8", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-DO+2/jZinXfROG7j7WKFn/3C6nFwxy2lLpgLjEXJz+0XKphZlTLJ14mo8Vfg8X5BWN6XjyESXq+LcYdT7tR3bA=="],
"@vercel/oidc": ["@vercel/oidc@3.0.2", "", {}, "sha512-JekxQ0RApo4gS4un/iMGsIL1/k4KUBe3HmnGcDvzHuFBdQdudEJgTqcsJC7y6Ul4Yw5CeykgvQbX2XeEJd0+DA=="],
"accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
"ai": ["ai@5.0.68", "", { "dependencies": { "@ai-sdk/gateway": "1.0.39", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.12", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-SB6r+4TkKVlSg2ozGBSfuf6Is5hrcX/bpGBzOoyHIN3b4ILGhaly0IHEvP8+3GGIHXqtkPVEUmR6V05jKdjNlg=="],
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
"axios": ["axios@1.12.2", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw=="],
"blowfish-cbc": ["blowfish-cbc@1.0.1", "", { "dependencies": { "node-addon-api": "^8.1.0" } }, "sha512-o1JN6g6+ATW/4k7q1BZzy14VqLxwYa1mCii47qT4kmAaYD0NAfdM6/pz6uizbhTra/xunsPQI27LZt08OQS4sA=="],
"body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="],
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
"chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
"color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
"color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="],
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
"compressible": ["compressible@2.0.18", "", { "dependencies": { "mime-db": ">= 1.43.0 < 2" } }, "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg=="],
"compression": ["compression@1.8.1", "", { "dependencies": { "bytes": "3.1.2", "compressible": "~2.0.18", "debug": "2.6.9", "negotiator": "~0.6.4", "on-headers": "~1.1.0", "safe-buffer": "5.2.1", "vary": "~1.1.2" } }, "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w=="],
"content-disposition": ["content-disposition@1.0.0", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg=="],
"content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
"cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
"cookie-parser": ["cookie-parser@1.4.7", "", { "dependencies": { "cookie": "0.7.2", "cookie-signature": "1.0.6" } }, "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw=="],
"cookie-signature": ["cookie-signature@1.0.6", "", {}, "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="],
"cors": ["cors@2.8.5", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="],
"dayjs": ["dayjs@1.11.18", "", {}, "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA=="],
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
"destroy": ["destroy@1.2.0", "", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="],
"detect-libc": ["detect-libc@2.0.3", "", {}, "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="],
"dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
"domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
"domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
"domutils": ["domutils@3.1.0", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA=="],
"dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
"escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
"etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
"eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="],
"express": ["express@5.1.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="],
"file-type": ["file-type@19.6.0", "", { "dependencies": { "get-stream": "^9.0.1", "strtok3": "^9.0.1", "token-types": "^6.0.0", "uint8array-extras": "^1.3.0" } }, "sha512-VZR5I7k5wkD0HgFnMsq5hOsSc710MJMu5Nc5QYsbe38NN5iPV/XTObYLc/cpttRTf6lX538+5uO1ZQRhYibiZQ=="],
"finalhandler": ["finalhandler@2.1.0", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q=="],
"follow-redirects": ["follow-redirects@1.15.6", "", {}, "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA=="],
"form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="],
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
"fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
"get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="],
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
"htmlparser2": ["htmlparser2@8.0.2", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1", "entities": "^4.4.0" } }, "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA=="],
"http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="],
"iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"image-size": ["image-size@1.2.1", "", { "dependencies": { "queue": "6.0.2" }, "bin": "bin/image-size.js" }, "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw=="],
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
"is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="],
"is-plain-object": ["is-plain-object@5.0.0", "", {}, "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="],
"is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
"is-stream": ["is-stream@4.0.1", "", {}, "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A=="],
"json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="],
"librespot": ["librespot@0.2.22", "", { "dependencies": { "protobufjs": "^7.2.5", "undici": "^5.27.0" } }, "sha512-szLknWSrgF+mRuypNXQTmdq8PGGhgPRAazm8sJmD8wiCAbtO9LYHihZhQcRmCzmVz818sRHZM/BU6K6XslXSHQ=="],
"long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
"lucida": ["lucida@2.0.0-54", "", { "dependencies": { "blowfish-cbc": "^1.0.1", "image-size": "^1.1.1", "librespot": "^0.2.21", "undici": "^6.19.4", "xmldom-qsa": "^1.1.3" } }, "sha512-3Y24WkCI1Ks6EjOAElYAeYXkxT/HtunEW+SnhDKFb681B/QEMhS/EzLzFi8D5l95/pOVODUZQMMxYNKmMMC1fQ=="],
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
"media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
"merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"nanoid": ["nanoid@3.3.8", "", { "bin": "bin/nanoid.cjs" }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="],
"negotiator": ["negotiator@0.6.4", "", {}, "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w=="],
"node-addon-api": ["node-addon-api@8.4.0", "", {}, "sha512-D9DI/gXHvVmjHS08SVch0Em8G5S1P+QWtU31appcKT/8wFSPRcdHadIFSAntdMMVM5zz+/DL+bL/gz3UDppqtg=="],
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
"on-headers": ["on-headers@1.1.0", "", {}, "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A=="],
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
"parse-srcset": ["parse-srcset@1.0.2", "", {}, "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="],
"parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
"path-to-regexp": ["path-to-regexp@8.1.0", "", {}, "sha512-Bqn3vc8CMHty6zuD+tG23s6v2kwxslHEhTj4eYaVKGIEB+YX/2wd0/rgXLFD9G9id9KCtbVy/3ZgmvZjpa0UdQ=="],
"peek-readable": ["peek-readable@5.4.2", "", {}, "sha512-peBp3qZyuS6cNIJ2akRNG1uo1WJ1d0wTxg/fxMdZ0BqCVhx242bSFHM9eNqflfJVS9SsgkzgT/1UgnsurBOTMg=="],
"picocolors": ["picocolors@1.0.1", "", {}, "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew=="],
"postcss": ["postcss@8.4.39", "", { "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.0.1", "source-map-js": "^1.2.0" } }, "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw=="],
"protobufjs": ["protobufjs@7.5.3", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw=="],
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
"proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
"qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="],
"queue": ["queue@6.0.2", "", { "dependencies": { "inherits": "~2.0.3" } }, "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA=="],
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
"raw-body": ["raw-body@3.0.0", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.6.3", "unpipe": "1.0.0" } }, "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g=="],
"router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
"rword": ["rword@3.2.1", "", {}, "sha512-E1PhzHCM8enXQJoVv0ZKvqMNojkbRqMsv66pD0P4mRl1imb+0g+3Q19CtXobZIqOhi3zNZi1br//U87vc/s6OA=="],
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
"sanitize-html": ["sanitize-html@2.17.0", "", { "dependencies": { "deepmerge": "^4.2.2", "escape-string-regexp": "^4.0.0", "htmlparser2": "^8.0.0", "is-plain-object": "^5.0.0", "parse-srcset": "^1.0.2", "postcss": "^8.3.11" } }, "sha512-dLAADUSS8rBwhaevT12yCezvioCA+bmUTPH/u57xKPT8d++voeYE6HeluA/bPbQ15TwDBG2ii+QZIEmYx8VdxA=="],
"semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
"send": ["send@1.1.0", "", { "dependencies": { "debug": "^4.3.5", "destroy": "^1.2.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^0.5.2", "http-errors": "^2.0.0", "mime-types": "^2.1.35", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-v67WcEouB5GxbTWL/4NeToqcZiAWEq90N888fczVArY8A79J0L4FD7vj5hm3eUMua5EpoQ59wa/oovY6TLvRUA=="],
"serve-static": ["serve-static@2.2.0", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ=="],
"setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
"sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="],
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
"side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
"side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="],
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
"simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="],
"source-map-js": ["source-map-js@1.2.0", "", {}, "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg=="],
"statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="],
"strtok3": ["strtok3@9.1.1", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^5.3.1" } }, "sha512-FhwotcEqjr241ZbjFzjlIYg6c5/L/s4yBGWSMvJ9UoExiSqL+FnFA/CaeZx17WGaZMS/4SOZp8wH18jSS4R4lw=="],
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
"token-types": ["token-types@6.1.1", "", { "dependencies": { "@borewit/text-codec": "^0.1.0", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ=="],
"tslib": ["tslib@2.6.3", "", {}, "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="],
"type-is": ["type-is@2.0.0", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-gd0sGezQYCbWSbkZr75mln4YBidWUN60+devscpLF5mtRDUpiaTvKpBNrdaCvel1NdR2k6vclXybU5fBd2i+nw=="],
"uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="],
"undici": ["undici@6.21.3", "", {}, "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw=="],
"undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
"ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
"xmldom-qsa": ["xmldom-qsa@1.1.3", "", {}, "sha512-IJBOczBpAYrIBJFFsmCBwfBhwe4zdMR3Xz0ZBX0OFtgO49rLy/BWbhkegOwsthdBWb1gUtFK6ZZnGdT8ZqPRBA=="],
"zod": ["zod@4.1.12", "", {}, "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ=="],
"accepts/mime-types": ["mime-types@3.0.0", "", { "dependencies": { "mime-db": "^1.53.0" } }, "sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w=="],
"accepts/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
"compression/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
"express/cookie-signature": ["cookie-signature@1.2.1", "", {}, "sha512-78KWk9T26NhzXtuL26cIJ8/qNHANyJ/ZYrmEXFzUmhZdjpBv+DlWlOANRTGBt48YcyslsLrj0bMLFTmXvLRCOw=="],
"express/mime-types": ["mime-types@3.0.0", "", { "dependencies": { "mime-db": "^1.53.0" } }, "sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w=="],
"express/type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
"librespot/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
"send/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
"send/fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="],
"serve-static/send": ["send@1.2.0", "", { "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.0", "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="],
"type-is/mime-types": ["mime-types@3.0.0", "", { "dependencies": { "mime-db": "^1.53.0" } }, "sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w=="],
"accepts/mime-types/mime-db": ["mime-db@1.53.0", "", {}, "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg=="],
"compression/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
"express/mime-types/mime-db": ["mime-db@1.53.0", "", {}, "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg=="],
"serve-static/send/mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="],
"type-is/mime-types/mime-db": ["mime-db@1.53.0", "", {}, "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg=="],
"serve-static/send/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
}
}

View File

@ -1,15 +1,50 @@
import fs from "bun:fs";
import { Database } from "bun:sqlite";
const accs = new Database(`${process.env.DATA_PATH}/accounts.sqlite`);
const infdb = new Database(`${process.env.DATA_PATH}/infinitecraft.sqlite`);
const friends = new Database(`${process.env.DATA_PATH}/friends.sqlite`);
const polytrack = new Database(`${process.env.DATA_PATH}/polytrack.sqlite`);
const ai_chats = new Database(`${process.env.DATA_PATH}/ai_chats.sqlite`);
if(!await (Bun.file(process.env.DATA_PATH)).exists()) {
fs.mkdirSync(process.env.DATA_PATH, { recursive: true });
}
const accs = new Database(`${process.env.DATA_PATH}/accounts.sqlite`, { create: true });
// const infdb = new Database(`${process.env.DATA_PATH}/infinitecraft.sqlite`);
// const friends = new Database(`${process.env.DATA_PATH}/friends.sqlite`);
// const polytrack = new Database(`${process.env.DATA_PATH}/polytrack.sqlite`);
// const ai_chats = new Database(`${process.env.DATA_PATH}/ai_chats.sqlite`);
infdb.exec("PRAGMA journal_mode = WAL;");
accs.exec("PRAGMA journal_mode = WAL;");
friends.exec("PRAGMA journal_mode = WAL;");
polytrack.exec("PRAGMA journal_mode = WAL;");
ai_chats.exec("PRAGMA journal_mode = WAL;");
// infdb.exec("PRAGMA journal_mode = WAL;");
// friends.exec("PRAGMA journal_mode = WAL;");
// polytrack.exec("PRAGMA journal_mode = WAL;");
// ai_chats.exec("PRAGMA journal_mode = WAL;");
export { accs, infdb, friends, polytrack, ai_chats };
let accountColumns = [
`"id" INTEGER`,
`"username" TEXT UNIQUE`,
`"name" TEXT`,
`"hashed_pass" INTEGER NOT NULL`,
`"secret_key" TEXT`,
`"about" TEXT`,
`"badges" TEXT`,
`"last_login" TEXT`,
`"type" TEXT`,
`"pfp_url" TEXT`,
`"createdAt" DATETIME NOT NULL`,
`"updatedAt" DATETIME NOT NULL`,
`"banned" TEXT`,
`"music" TEXT`,
`PRIMARY KEY("id")`,
];
let createAccountQuery = `CREATE TABLE IF NOT EXISTS "accounts" (`
accountColumns.forEach((e) => {
if(createAccountQuery.length == 39) {
createAccountQuery+=`${e}`
} else {
createAccountQuery+=`,${e}`
}
});
createAccountQuery+=`)`
// console.log(createAccountQuery)
accs.query(createAccountQuery).run();
// export { accs, infdb, friends, polytrack, ai_chats };
export { accs };

View File

@ -1,96 +1,71 @@
<!DOCTYPE html>
<html class="sl-theme-dark" lang="en">
<html lang="en">
<head>
<!-- initialize theme vars
https://coolors.co/10002b-240046-3c096c-5a189a-7b2cbf-9d4edd-c77dff-e0aaff -->
<!-- initialize externals -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.0/jquery.min.js"></script>
<script src=" https://cdn.jsdelivr.net/npm/js-cookie@3.0.5/dist/js.cookie.min.js "></script>
<meta property="og:title" content="Selenite" />
<meta property="description" content="Selenite is the best unblocked games site. With over 400 games and an account system, no other websites come close to Selenite." />
<meta content="/favicon.png" property="og:image" />
<meta content="#c77dff" data-react-helmet="true" name="theme-color" />
<meta name="googlebot" content="index, follow, snippet" />
<link rel="canonical" href="https://selenite.cc/" />
<meta property="og:description" content="Selenite is the best unblocked games site. With over 400 games and an account system, no other websites come close to Selenite." />
<meta property="og:title" content="Selenite">
<meta property="og:type" content="website">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Selenite",
"alternateName": "selenite.cc",
"url": "https://selenite.cc",
"logo": "https://selenite.cc/favicon.png",
"sameAs": [
"https://github.com/selenite-cc",
"https://youtube.com/@selenitecc",
"https://tiktok.com/@selenitecc",
"https://selenite.cc",
]
}
</script>
<!-- initialize my stuff -->
<script src="/js/all.min.js"></script>
<script src="/js/all.js"></script>
<script src="/js/main.js"></script>
<link rel="stylesheet" href="/style.css" />
<link rel="stylesheet" href="/css/main.css" />
<link rel="stylesheet" href="/css/pages.css" />
<link rel="manifest" href="/manifest.json" />
<!-- seo + other things -->
<title>Profile</title>
<title>Selenite</title>
<link rel="icon" href="/favicon.ico" />
</head>
<alerts> </alerts>
<body id="noscroll">
<header>
<a href="/index.html">Home</a>
<a href="/bookmarklets.html">Bookmarklets</a>
<a href="/projects.html">Games</a>
<a href="/apps.html">Apps</a>
<a href="/settings.html">Settings</a>
<a id="blank" href="#">Open Blank</a>
<a href="/u/" class="usericon"><img src="/img/user.svg" /></a>
</header>
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415518411898563" crossorigin="anonymous"></script>
<script>
async function addBadge() {
let name = prompt("username");
let badge = prompt("badge name");
let run = await fetch("/api/admin/badge", {
method: "POST",
body: JSON.stringify({
username: name,
badge: badge
}),
headers: {
"Content-type": "application/json; charset=UTF-8",
},
});
alert(JSON.stringify(run));
}
function announce() {
let key = prompt("enter key");
let announcement = prompt("enter announcement");
socket.send(`${key}.${announcement}`)
}
async function removeAccount() {
let name = prompt("enter username");
let run = await fetch("/api/admin/removeAcc", {
method: "POST",
body: JSON.stringify({
username: name
}),
headers: {
"Content-type": "application/json; charset=UTF-8",
},
});
}
async function ban() {
let name = prompt("enter username");
let reason = prompt("enter reason");
let run = await fetch("/api/admin/ban", {
method: "POST",
body: JSON.stringify({
name: name,
reason: reason,
}),
headers: {
"Content-type": "application/json; charset=UTF-8",
},
});
function sendAnnouncement() {
let title = prompt("title");
let message = prompt("message");
if(prompt("are you sure (y/N)") == "y") {
let cookies = document.cookie.split("; ");
for (let i = 0; i < cookies.length; i++) {
if (cookies[i].trim().startsWith("token=")) {
let socket = new WebSocket("/socket");
socket.addEventListener("open", () => {
socket.send(`annc;;${cookies[i].trim().split("=")[1]};;${title};;${message}`);
})
}
}
}
}
</script>
<main>
<button onclick="addBadge()">Add a badge</button>
<button onclick="announce()">Send announcement</button>
<button onclick="removeAccount()">remove account</button>
<button onclick="ban()">ban account</button>
</main>
<footer class="noscroll">
<a href="https://gitlab.com/skysthelimit.dev/selenite">Source</a>
<a href="https://discord.gg/7jyufnwJNf">Discord</a>
<a href="/suggest.html">Suggestions & Bugs</a>
<a href="/contact.html">Contact</a>
<a href="/support.html">Donate</a>
<a href="/about.html">About</a>
</footer>
</head>
<alerts> </alerts>
<body>
<h1 class="title">admin page</h1>
<sections>
<button onclick="sendAnnouncement()">send announcement</button>
</sections>
</body>
</html>

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html class="sl-theme-dark" lang="en">
<html lang="en">
<head>
<!-- initialize theme vars
https://coolors.co/10002b-240046-3c096c-5a189a-7b2cbf-9d4edd-c77dff-e0aaff -->

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html class="sl-theme-dark" lang="en">
<html lang="en">
<head>
<!-- initialize theme vars
https://coolors.co/10002b-240046-3c096c-5a189a-7b2cbf-9d4edd-c77dff-e0aaff -->
@ -14,7 +14,8 @@
<script src="https://js.hcaptcha.com/1/api.js" async defer></script>
<link rel="stylesheet" href="/style.css" />
<link rel="stylesheet" href="/css/main.css" />
<link rel="stylesheet" href="/css/pages.css" />
<!-- seo + other things -->
<title>Login | Selenite</title>
@ -60,27 +61,17 @@
</script>
</head>
<alerts> </alerts>
<body id="noscroll">
<header>
<a href="/index.html">Home</a>
<a href="/bookmarklets.html">Bookmarklets</a>
<a href="/projects.html">Games</a>
<a href="/apps.html">Apps</a>
<a href="/settings.html">Settings</a>
<a id="blank" href="#">Open Blank</a>
<a href="/u/" class="usericon"><img src="/img/user.svg" /></a>
</header>
<main id="main" class="noscroll">
<h2>Login to your account</h2>
<body>
<h2>login..</h2>
<form onsubmit="return false;" id="login">
<input type="text" id="username" placeholder="username" />
<input type="password" id="password" placeholder="password" />
<br />
<a href="/reset">Forgot password?</a> or <a href="/register">Create a free account</a><br />
<div class="h-captcha" id="hcaptcha" data-sitekey="1774ec96-39be-4fb0-9e82-f4c62354b8fa"></div>
<button type="submit" value="Submit">Login</button>
<a href="/reset">forgot password?</a><br />
<a href="/register">create a free account</a><br />
<button type="submit" value="Submit">login</button>
</form>
</main>
<popups>
<div id="popup" style="display: none">
<h1 id="title"></h1>
@ -88,13 +79,5 @@
<button id="close">X</button>
</div>
</popups>
<footer class="noscroll">
<a href="https://gitlab.com/skysthelimit.dev/selenite">Source</a>
<a href="https://discord.gg/7jyufnwJNf">Discord</a>
<a href="/suggest.html">Suggestions & Bugs</a>
<a href="/contact.html">Contact</a>
<a href="/support.html">Donate</a>
<a href="/about.html">About</a>
</footer>
</body>
</html>

View File

@ -1,67 +1,140 @@
<!DOCTYPE html>
<html class="sl-theme-dark" lang="en">
<html lang="en">
<head>
<!-- initialize theme vars
https://coolors.co/10002b-240046-3c096c-5a189a-7b2cbf-9d4edd-c77dff-e0aaff -->
<meta content="{{ name }}" property="og:title" />
<meta content="{{ about_none }}" property="og:description" />
<meta content="{{ user_pfp }}" property="og:image" />
<meta content="#c77dff" data-react-helmet="true" name="theme-color" />
<!-- initialize externals -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.0/jquery.min.js"></script>
<script src=" https://cdn.jsdelivr.net/npm/js-cookie@3.0.5/dist/js.cookie.min.js "></script>
<meta property="og:title" content="Selenite" />
<meta property="description" content="Selenite is the best unblocked games site. With over 400 games and an account system, no other websites come close to Selenite." />
<meta content="/favicon.png" property="og:image" />
<meta content="#c77dff" data-react-helmet="true" name="theme-color" />
<meta name="googlebot" content="index, follow, snippet" />
<link rel="canonical" href="https://selenite.cc/" />
<meta property="og:description" content="Selenite is the best unblocked games site. With over 400 games and an account system, no other websites come close to Selenite." />
<meta property="og:title" content="Selenite">
<meta property="og:type" content="website">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Selenite",
"alternateName": "selenite.cc",
"url": "https://selenite.cc",
"logo": "https://selenite.cc/favicon.png",
"sameAs": [
"https://github.com/selenite-cc",
"https://youtube.com/@selenitecc",
"https://tiktok.com/@selenitecc",
"https://selenite.cc",
]
}
</script>
<!-- initialize my stuff -->
<script src="/js/all.min.js"></script>
<script src="/js/main.js"></script>
<link rel="stylesheet" href="/style.css" />
<link rel="stylesheet" href="/css/main.css" />
<link rel="stylesheet" href="/css/pages.css" />
<link rel="stylesheet" href="/css/profile.css" />
<link rel="manifest" href="/manifest.json" />
<!-- seo + other things -->
<style>{{ custom_css }}</style>
<title>{{ name }}'s Profile | Selenite</title>
<link rel="icon" href="/favicon.ico" />
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415518411898563" crossorigin="anonymous"></script>
<script>
document.addEventListener("DOMContentLoaded", ()=>{
let music = {{ is_music }};
let audioObject;
if(music) {
document.body.classList = "enter-on";
let url = "{{ song_url }}";
audioObject = new Audio(url);
audioObject.addEventListener("loadeddata", () => {
document.getElementById("totalLength").innerText = `${Math.floor(audioObject.duration/60)}:${String(Math.floor(audioObject.duration%60)).padStart(2, "0")}`;
document.getElementById("playbar").value = 0;
})
document.getElementById("enter").addEventListener("click", async ()=>{
document.getElementById("enter").style.backgroundColor = "#00000000"
document.getElementById("enter").style.backdropFilter = "blur(0px)"
document.getElementById("enter").style.opacity = "0";
setTimeout(() => {
audioObject.play();
audioObject.loop = true;
document.body.classList = "";
}, 500)
setTimeout( () => {
document.getElementById("enter").style.display = "none";
}, 950);
});
} else {
document.getElementById("enter").style.display = "none";
document.querySelectorAll("section")[0].querySelectorAll(".profile-element")[1].style.display = "none";
}
document.getElementById("playPause").addEventListener("click", ()=>{
if(audioObject.paused) {
document.getElementById("playPause").src = "/img/pause.svg";
audioObject.play();
} else {
document.getElementById("playPause").src = "/img/play.svg";
audioObject.pause();
}
});
document.getElementById("mute").addEventListener("click", ()=>{
if(audioObject.volume == 0) {
document.getElementById("mute").src = "/img/volume.svg";
audioObject.volume = 1;
} else {
document.getElementById("mute").src = "/img/muted.svg";
audioObject.volume = 0;
}
});
document.getElementById("playbar").addEventListener("input", (e) => {
audioObject.currentTime = document.getElementById("playbar").value/1000*audioObject.duration;
});
audioObject.addEventListener("timeupdate", (e) => {
document.getElementById("totalLength").innerText = `${Math.floor(audioObject.duration/60)}:${String(Math.floor(audioObject.duration%60)).padStart(2, "0")}`
document.getElementById("curPos").innerText = `${Math.floor(audioObject.currentTime/60)}:${String(Math.floor(audioObject.currentTime%60)).padStart(2, "0")}`
document.getElementById("playbar").value = (audioObject.currentTime / audioObject.duration) * 1000;
})
})
</script>
</head>
<alerts> </alerts>
<body class="profile">
<header>
<a href="/index.html">Home</a>
<a href="/bookmarklets.html">Bookmarklets</a>
<a href="/projects.html">Games</a>
<a href="/apps.html">Apps</a>
<a href="/settings.html">Settings</a>
<a id="blank" href="#">Open Blank</a>
<a href="/u/" class="usericon"><img src="/img/user.svg" /></a>
</header>
<main>
<div class="profile top">
<img src="{{ user_pfp }}" class="pfp" />
<div class="profile top text">
<h1>{{ name }}</h1>
<div class="samerow">{{ badges }}</div>
<h2>Joined {{ join_date }}</h2>
<h2>Last online {{ online_time }}</h2>
</div>
<div class="profile top text right">
<h1>About Me</h1>
<h2 class="about">{{ about }}</h2>
</div>
<body>
<div id="enter"><h1 class="title">click to enter</h1><p>this user selected a song, please mute the tab if you don't want to listen</p></div>
<h1 class="title">{{ name }}'s profile</h1>
<section>
<img src="{{ user_pfp }}" class="pfp" />
<div class="profile-element">
<h1>{{ name }}</h1>
<p>/u/{{ username }}</p>
<div class="badges">{{ badges }}</div>
</div>
<div class="profile played">
<h2>Top Games:</h2>
<div id="played-games">
{{ played_games }}
</div>
<div class="profile-element">
<h2>{{ song_title }}</h2>
<h3>{{ song_artist }}</h3>
<div class="samerow">
<p id="curPos">0:00</p>
<input type="range" id="playbar" min="1" max="1000" />
<p id="totalLength">9:99</p>
</div>
<div class="samerow">
<img id="playPause" src="/img/pause.svg" class="controls"/>
<img id="mute" src="/img/volume.svg" class="controls"/>
</div>
</div>
</main>
<footer>
<a href="https://gitlab.com/skysthelimit.dev/selenite">Source</a>
<a href="https://discord.gg/7jyufnwJNf">Discord</a>
<a href="/suggest.html">Suggestions & Bugs</a>
<a href="/contact.html">Contact</a>
<a href="/support.html">Donate</a>
<a href="/about.html">About</a>
</footer>
<div class="profile-element">
<h2>Joined {{ join_date }}</h2>
<h2>Last online {{ online_time }}</h2>
</div>
</section>
<section class="column" id="about">
<h1>about me</h1>
<p>{{ about }}</p>
</section>
</body>
</html>

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html class="sl-theme-dark" lang="en">
<html lang="en">
<head>
<!-- initialize theme vars
https://coolors.co/10002b-240046-3c096c-5a189a-7b2cbf-9d4edd-c77dff-e0aaff -->

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html class="sl-theme-dark" lang="en">
<html lang="en">
<head>
<!-- initialize theme vars
https://coolors.co/10002b-240046-3c096c-5a189a-7b2cbf-9d4edd-c77dff-e0aaff -->

View File

@ -1,25 +1,73 @@
<!DOCTYPE html>
<html class="sl-theme-dark" lang="en">
<html lang="en">
<head>
<!-- initialize theme vars
https://coolors.co/10002b-240046-3c096c-5a189a-7b2cbf-9d4edd-c77dff-e0aaff -->
<!-- initialize externals -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.0/jquery.min.js"></script>
<script src=" https://cdn.jsdelivr.net/npm/js-cookie@3.0.5/dist/js.cookie.min.js "></script>
<meta property="og:title" content="Selenite" />
<meta property="description" content="Selenite is the best unblocked games site. With over 400 games and an account system, no other websites come close to Selenite." />
<meta content="/favicon.png" property="og:image" />
<meta content="#c77dff" data-react-helmet="true" name="theme-color" />
<meta name="googlebot" content="index, follow, snippet" />
<link rel="canonical" href="https://selenite.cc/" />
<meta property="og:description" content="Selenite is the best unblocked games site. With over 400 games and an account system, no other websites come close to Selenite." />
<meta property="og:title" content="Selenite">
<meta property="og:type" content="website">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Selenite",
"alternateName": "selenite.cc",
"url": "https://selenite.cc",
"logo": "https://selenite.cc/favicon.png",
"sameAs": [
"https://github.com/selenite-cc",
"https://youtube.com/@selenitecc",
"https://tiktok.com/@selenitecc",
"https://selenite.cc",
]
}
</script>
<!-- initialize my stuff -->
<script src="/js/all.min.js"></script>
<script src="/js/main.js"></script>
<link rel="stylesheet" href="/style.css" />
<link rel="stylesheet" href="/css/main.css" />
<link rel="stylesheet" href="/css/pages.css" />
<link rel="stylesheet" href="/css/profile.css" />
<link rel="manifest" href="/manifest.json" />
<!-- seo + other things -->
<title>{{ name }}'s Profile | Selenite</title>
<link rel="icon" href="/favicon.ico" />
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415518411898563" crossorigin="anonymous"></script>
<!-- <script>
let audioObject;
document.addEventListener("DOMContentLoaded", ()=>{
let music = false; // {{ is_music }}
if(music) {
let url = "/api/music/download?url=https://soundcloud.com/archive5077/fostered-alcoholism-grayskies"; // {{ music_url }}
let audioObject = new Audio(url);
document.getElementById("enter").addEventListener("click", async ()=>{
document.getElementById("enter").style.backgroundColor = "#00000000"
document.getElementById("enter").style.backdropFilter = "blur(0px)"
document.getElementById("enter").style.opacity = "0";
setTimeout( () => {
document.getElementById("enter").style.display = "none";
audioObject.play();
}, 700);
});
}
})
</script> -->
<script>
let username = "{{ username }}";
let userData;
let songData;
(async () => {
userData = await (await fetch("/u/raw")).json();
})();
@ -53,6 +101,9 @@
console.log("promise finished");
body = { pfp: fileData };
console.log("body set");
} else if (state == "song") {
body = songData;
console.log("body set");
} else if (state == "clearpfp") {
body = { pfp: "del" };
} else if (state == "close") {
@ -79,10 +130,12 @@
document.getElementById("title").innerText = "Upload successful!";
document.getElementById("text").style.display = "none";
document.getElementById("popup").style.display = "flex";
document.getElementById("blur").style.display = "flex";
} else {
document.getElementById("title").innerText = "Upload failed. This probably means something bad happened, send an email to support@selenite.cc or ping @skysthelimit.dev";
document.getElementById("text").style.display = "none";
document.getElementById("popup").style.display = "flex";
document.getElementById("blur").style.display = "flex";
}
return;
} else if (state == "download") {
@ -105,11 +158,13 @@
document.getElementById("title").innerText = "Download successful!";
document.getElementById("text").style.display = "none";
document.getElementById("popup").style.display = "flex";
document.getElementById("blur").style.display = "flex";
} else {
document.getElementById("title").innerText = "Download failed.";
document.getElementById("body-text").innerText = data.reason;
document.getElementById("body-text").style.display = "flex";
document.getElementById("popup").style.display = "flex";
document.getElementById("blur").style.display = "flex";
}
console.log(data);
data = JSON.parse(data.data);
@ -131,7 +186,19 @@
}
console.log("sending");
body.username = username;
await fetch("/api/profile/edit", {
document.getElementById("title").innerText = "processing...";
document.getElementById("pfp_upload").style.display = "none";
document.getElementById("pfp_reminder").style.display = "none";
document.getElementById("body-text").style.display = "none";
document.getElementById("clear").style.display = "none";
document.getElementById("text").style.display = "none";
document.getElementById("counter").style.display = "none";
document.getElementById("submit").style.display = "none";
document.getElementById("search").style.display = "none";
document.querySelector("results").style.display = "none";
document.getElementById("popup").style.display = "flex";
document.getElementById("blur").style.display = "flex";
let data = await fetch("/api/profile/edit", {
credentials: "include",
headers: {
"Content-type": "application/json; charset=UTF-8",
@ -140,9 +207,24 @@
method: "POST",
mode: "cors",
});
console.log("sent");
document.getElementById("popup").style.display = "none";
location.reload();
let resp = await data.json();
if(data.status == 200) {
location.reload();
} else {
document.getElementById("title").innerText = "error!";
document.getElementById("pfp_upload").style.display = "none";
document.getElementById("pfp_reminder").style.display = "none";
document.getElementById("body-text").style.display = "flex";
document.getElementById("body-text").innerText = resp.err;
document.getElementById("clear").style.display = "none";
document.getElementById("text").style.display = "none";
document.getElementById("counter").style.display = "none";
document.getElementById("submit").style.display = "none";
document.getElementById("search").style.display = "none";
document.querySelector("results").style.display = "none";
document.getElementById("popup").style.display = "flex";
document.getElementById("blur").style.display = "flex";
}
}
document.addEventListener("DOMContentLoaded", async () => {
document.getElementById("pfp_upload").addEventListener("change", (e) => {
@ -153,71 +235,108 @@
});
document.getElementById("submit").addEventListener("click", await setProfile);
document.getElementById("clear").addEventListener("click", async()=>{state="clearpfp";await setProfile()});
document.querySelectorAll("#edit").forEach((element) => {
element.addEventListener("click", (e) => {
console.log(e.target.parentElement.children[0].id);
if (e.target.parentElement.children[0].id == "name") {
state = "name";
document.getElementById("title").innerText = "Set your name.";
document.getElementById("text").setAttribute("maxlength", "20");
document.getElementById("pfp_upload").style.display = "none";
document.getElementById("pfp_reminder").style.display = "none";
document.getElementById("body-text").style.display = "none";
document.getElementById("clear").style.display = "none";
document.getElementById("text").style.display = "flex";
document.getElementById("text").value = userData["name"];
document.getElementById("counter").innerText = `${document.getElementById("text").value.length} / ${document.getElementById("text").attributes.maxlength.value}`;
document.getElementById("counter").style.display = "flex";
document.getElementById("submit").style.display = "flex";
document.getElementById("popup").style.display = "flex";
} else if (e.target.parentElement.children[0].id == "bio") {
state = "bio";
document.getElementById("title").innerText = "Set your about me.";
document.getElementById("text").setAttribute("maxlength", "200");
document.getElementById("pfp_upload").style.display = "none";
document.getElementById("pfp_reminder").style.display = "none";
document.getElementById("body-text").style.display = "none";
document.getElementById("clear").style.display = "none";
document.getElementById("text").style.display = "flex";
document.getElementById("text").value = userData["about"];
document.getElementById("counter").innerText = `${document.getElementById("text").value.length} / ${document.getElementById("text").attributes.maxlength.value}`;
document.getElementById("counter").style.display = "flex";
document.getElementById("submit").style.display = "flex";
document.getElementById("popup").style.display = "flex";
} else if (e.target.parentElement.children[0].id == "custom") {
state = "custom";
document.getElementById("title").innerText = "Set your custom CSS.";
document.getElementById("text").setAttribute("maxlength", "2048");
document.getElementById("pfp_upload").style.display = "none";
document.getElementById("pfp_reminder").style.display = "none";
document.getElementById("body-text").style.display = "none";
document.getElementById("clear").style.display = "none";
document.getElementById("text").style.display = "flex";
document.getElementById("text").value = userData["css"];
document.getElementById("counter").innerText = `${document.getElementById("text").value.length} / ${document.getElementById("text").attributes.maxlength.value}`;
document.getElementById("counter").style.display = "flex";
document.getElementById("submit").style.display = "flex";
document.getElementById("popup").style.display = "flex";
} else if (e.target.parentElement.children[0].id == "pfp") {
state = "pfp";
document.getElementById("title").innerText = "Set your new profile picture.";
document.getElementById("pfp_upload").style.display = "flex";
document.getElementById("pfp_reminder").style.display = "flex";
document.getElementById("body-text").style.display = "none";
document.getElementById("clear").style.display = "flex";
document.getElementById("text").style.display = "none";
document.getElementById("counter").style.display = "none";
document.getElementById("submit").style.display = "flex";
document.getElementById("popup").style.display = "flex";
}
});
document.getElementById("pfp").addEventListener("click", () => {
state = "pfp";
document.getElementById("title").innerText = "upload a new profile picture";
document.getElementById("pfp_upload").style.display = "flex";
document.getElementById("pfp_reminder").style.display = "flex";
document.getElementById("body-text").style.display = "none";
document.getElementById("clear").style.display = "flex";
document.getElementById("text").style.display = "none";
document.getElementById("counter").style.display = "none";
document.getElementById("submit").style.display = "flex";
document.getElementById("popup").style.display = "flex";
document.getElementById("blur").style.display = "flex";
document.getElementById("search").style.display = "none";
document.querySelector("results").style.display = "none";
});
document.getElementById("name").addEventListener("click", () => {
state = "name";
document.getElementById("title").innerText = "change your name";
document.getElementById("text").setAttribute("maxlength", "20");
document.getElementById("pfp_upload").style.display = "none";
document.getElementById("pfp_reminder").style.display = "none";
document.getElementById("body-text").style.display = "none";
document.getElementById("clear").style.display = "none";
document.getElementById("text").style.display = "flex";
document.getElementById("text").value = userData["name"];
document.getElementById("counter").innerText = `${document.getElementById("text").value.length} / ${document.getElementById("text").attributes.maxlength.value}`;
document.getElementById("counter").style.display = "flex";
document.getElementById("submit").style.display = "flex";
document.getElementById("popup").style.display = "flex";
document.getElementById("blur").style.display = "flex";
document.getElementById("search").style.display = "none";
document.querySelector("results").style.display = "none";
});
document.getElementById("about").addEventListener("click", () => {
state = "bio";
document.getElementById("title").innerText = "change your about me";
document.getElementById("text").setAttribute("maxlength", "200");
document.getElementById("pfp_upload").style.display = "none";
document.getElementById("pfp_reminder").style.display = "none";
document.getElementById("body-text").style.display = "none";
document.getElementById("clear").style.display = "none";
document.getElementById("text").style.display = "flex";
document.getElementById("text").value = userData["about"];
document.getElementById("counter").innerText = `${document.getElementById("text").value.length} / ${document.getElementById("text").attributes.maxlength.value}`;
document.getElementById("counter").style.display = "flex";
document.getElementById("submit").style.display = "flex";
document.getElementById("popup").style.display = "flex";
document.getElementById("blur").style.display = "flex";
document.getElementById("search").style.display = "none";
document.querySelector("results").style.display = "none";
});
document.getElementById("song").addEventListener("click", () => {
state = "song";
document.getElementById("title").innerText = "pick a song";
document.getElementById("text").setAttribute("maxlength", "2048");
document.getElementById("pfp_upload").style.display = "none";
document.getElementById("pfp_reminder").style.display = "none";
document.getElementById("body-text").style.display = "none";
document.getElementById("clear").style.display = "none";
document.getElementById("text").style.display = "none";
document.getElementById("counter").style.display = "none";
document.getElementById("submit").style.display = "none";
document.getElementById("popup").style.display = "flex";
document.getElementById("blur").style.display = "flex";
document.getElementById("search").style.display = "flex";
document.querySelector("results").style.display = "flex";
})
document.getElementById("close").addEventListener("click", () => {
document.getElementById("popup").style.display = "none";
document.getElementById("blur").style.display = "none";
});
document.getElementById("text").addEventListener("input", () => {
document.getElementById("counter").innerText = `${document.getElementById("text").value.length} / ${document.getElementById("text").attributes.maxlength.value}`;
});
document.getElementById("search").addEventListener("input", async ()=>{
if(document.getElementById("search").value.length > 0) {
let query = document.getElementById("search").value;
let results = await fetch("/api/music/search?q=" + encodeURIComponent(document.getElementById("search").value));
let json = await results.json();
if(document.getElementById("search").value == query) {
document.querySelector("results").display = "flex";
console.log("searched")
let i = 0;
json.tracks.forEach((e)=>{
document.querySelectorAll("result")[i].querySelector("h2").innerText = e.title;
document.querySelectorAll("result")[i].querySelectorAll("p")[0].innerText = e.artists[0].name;
document.querySelectorAll("result")[i].querySelectorAll("p")[1].innerText = e.url.slice(22);
document.querySelectorAll("result")[i].setAttribute("data-url", e.url);
document.querySelectorAll("result")[i].style.display = "flex";
i++;
})
}
} else {
document.querySelectorAll("result").forEach((e) => {
e.removeAttribute("data-url");
e.querySelector("h2").innerText = null;
e.querySelectorAll("p")[0].innerText = null;
e.querySelectorAll("p")[1].innerText = null;
e.style.display = "none";
})
}
})
document.getElementById("upload").addEventListener("click", async () => {
state = "upload";
document.getElementById("title").innerText = "Warning";
@ -230,6 +349,7 @@
document.getElementById("counter").style.display = "none";
document.getElementById("submit").style.display = "flex";
document.getElementById("popup").style.display = "flex";
document.getElementById("blur").style.display = "flex";
});
document.getElementById("download").addEventListener("click", async () => {
state = "download";
@ -243,79 +363,75 @@
document.getElementById("counter").style.display = "none";
document.getElementById("submit").style.display = "flex";
document.getElementById("popup").style.display = "flex";
document.getElementById("blur").style.display = "flex";
});
document.querySelectorAll("result").forEach(async e => {
e.addEventListener("click", async (element) => {
songData = {
url: e.getAttribute("data-url"),
title: e.childNodes[0].innerText,
artist: e.childNodes[1].innerText,
}
console.log(songData);
await setProfile();
})
});
});
</script>
</head>
<alerts> </alerts>
<body>
<header>
<a href="/index.html">Home</a>
<a href="/bookmarklets.html">Bookmarklets</a>
<a href="/projects.html">Games</a>
<a href="/apps.html">Apps</a>
<a href="/settings.html">Settings</a>
<a id="blank" href="#">Open Blank</a>
<a href="/u/" class="usericon"><img src="/img/user.svg" /></a>
</header>
<main>
<!-- <a class="friend-icon" href="/friends"><img src="/img/friend.svg"></a> -->
<input type="text" readonly value="{{ url_gen }}" />
<div class="samerow">
<button id="download">Download Backup</button>
<button id="upload">Upload Backup</button>
<div id="blur"></div>
<div id="popup" style="display: none">
<h1 id="title"></h1>
<p id="body-text"></p>
<input type="text" id="text" />
<input type="text" id="search" placeholder="search a song title here.."/>
<results id="results">
<result><h2></h2><p></p><p></p></result>
<result><h2></h2><p></p><p></p></result>
<result><h2></h2><p></p><p></p></result>
<result><h2></h2><p></p><p></p></result>
<result><h2></h2><p></p><p></p></result>
<result><h2></h2><p></p><p></p></result>
<result><h2></h2><p></p><p></p></result>
<result><h2></h2><p></p><p></p></result>
<result><h2></h2><p></p><p></p></result>
<result><h2></h2><p></p><p></p></result>
</results>
<p id="counter">0 / 0</p>
<input type="file" id="pfp_upload" name="filename" accept=".png,.jpg,.jpeg,.gif,.avif,.webp,.tiff,.svg" />
<p id="pfp_reminder">4 MB file upload max</p>
<button id="clear">Clear Profile Picture</button>
<button id="submit">Submit</button>
<button id="close">X</button>
</div>
<div class="samerow">
<button id="download">download cloud backup</button>
<button id="upload">upload cloud backup</button>
</div>
<h1 class="title">{{ name }}'s profile</h1>
<h3>click on an area to edit</h3>
<p style="font-size: 16px;">share your profile at {{ url_gen }}</p>
{{ staff_buttons }}
<section>
<img src="{{ user_pfp }}" class="pfp edit" id="pfp" />
<div class="profile-element edit" id="name">
<h1>{{ name }}</h1>
<p>/u/{{ username }}</p>
<div class="badges">{{ badges }}</div>
</div>
<div>
<div id="custom"></div>
{{ css_edit }}
<div class="profile-element edit" id="song">
<h2>{{ song_title }}</h2>
<h3>{{ song_artist }}</h3>
</div>
<div class="profile top">
<img src="{{ user_pfp }}" class="pfp" id="pfp" />
<img src="/img/edit.svg" id="edit" />
<div class="profile top text">
<div class="samerow edit" id="name">
<h1 id="name">{{ name }}</h1>
<img src="/img/edit.svg" id="edit" />
</div>
<div class="samerow">{{ badges }}</div>
<h2>Joined {{ join_date }}</h2>
<h2>Last online {{ online_time }}</h2>
</div>
<div class="profile top text right">
<div class="samerow edit" id="about">
<h1 id="bio">Bio</h1>
<img src="/img/edit.svg" id="edit" />
</div>
<h2 id="about">{{ about }}</h2>
</div>
<div class="profile-element">
<h2>Joined {{ join_date }}</h2>
<h2>Last online {{ online_time }}</h2>
</div>
<div class="profile played">
<h2>Top Games:</h2>
<div id="played-games">
{{ played_games }}
</div>
</div>
</main>
<popups>
<div id="popup" style="display: none">
<h1 id="title"></h1>
<p id="body-text"></p>
<input type="text" id="text" />
<p id="counter">0 / 0</p>
<input type="file" id="pfp_upload" name="filename" accept=".png,.jpg,.jpeg,.gif,.avif,.webp,.tiff,.svg" />
<p id="pfp_reminder">4 MB file upload max</p>
<button id="clear">Clear Profile Picture</button>
<button id="submit">Submit</button>
<button id="close">X</button>
</div>
</popups>
<footer>
<a href="https://gitlab.com/skysthelimit.dev/selenite">Source</a>
<a href="https://discord.gg/7jyufnwJNf">Discord</a>
<a href="/suggest.html">Suggestions & Bugs</a>
<a href="/contact.html">Contact</a>
<a href="/support.html">Donate</a>
<a href="/about.html">About</a>
</footer>
</section>
<section class="column edit" id="about">
<h1>about me</h1>
<p class="about">{{ about }}</p>
</section>
</body>
</html>

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html class="sl-theme-dark" lang="en">
<html lang="en">
<head>
<!-- initialize theme vars
https://coolors.co/10002b-240046-3c096c-5a189a-7b2cbf-9d4edd-c77dff-e0aaff -->
@ -14,91 +14,82 @@
<script src="https://js.hcaptcha.com/1/api.js" async defer></script>
<link rel="stylesheet" href="/style.css" />
<link rel="stylesheet" href="/css/main.css" />
<link rel="stylesheet" href="/css/pages.css" />
<!-- seo + other things -->
<title>Register | Selenite</title>
<title>Login | Selenite</title>
<link rel="icon" href="/favicon.ico" />
<script>
document.addEventListener("DOMContentLoaded", () => {
document.getElementById("close").addEventListener("click", () => {
document.getElementById("popup").style.display = "none";
if (document.getElementById("title").innerText == "Registered successfully") {
location.href = "/login";
}
});
document.getElementById("register").addEventListener("click", async () => {
let username = document.getElementById("username").value;
let password = document.getElementById("password").value;
let captcha = document.getElementById("hcaptcha").firstChild.dataset.hcaptchaResponse;
if (!document.getElementById("hcaptcha").firstChild.dataset.hcaptchaResponse) {
document.getElementById("title").innerText = "Failed to register";
document.getElementById("reason").innerText = "You have not done the CAPTCHA. Please complete the CAPTCHA before continuing.";
document.getElementById("popup").style.display = "flex";
return;
}
if (username.length < 17 && username.length > 2 && !/[^a-zA-Z0-9._-]/.test(username)) {
} else {
document.getElementById("title").innerText = "Failed to register";
document.getElementById("reason").innerText = "Your username is invalid. Please make your username within the limits described.";
document.getElementById("popup").style.display = "flex";
return;
}
if (!/^((?=\S*?[A-Z])(?=\S*?[a-z])(?=\S*?[0-9]).{5,})\S$/.test(password)) {
document.getElementById("title").innerText = "Failed to register";
document.getElementById("reason").innerText = "Your password is invalid. Please make your password within the limits described.";
document.getElementById("popup").style.display = "flex";
return;
}
let data = await (
await fetch("/register", {
method: "POST",
body: JSON.stringify({
username: username,
password: password,
"h-captcha-response": captcha,
}),
headers: {
"Content-type": "application/json; charset=UTF-8",
},
})
).json();
console.log(data);
if (data.success) {
document.getElementById("title").innerText = "Registered successfully";
document.getElementById("reason").innerText = `Please save the following, this is required to reset your password.\n\n${data.key}\n\nExit to continue`;
} else {
console.log("Error: ", data.reason);
document.getElementById("title").innerText = "Failed to register";
document.getElementById("reason").innerText = data.reason;
}
document.getElementById("popup").style.display = "flex";
});
document.addEventListener("DOMContentLoaded", () => {
document.getElementById("close").addEventListener("click", () => {
document.getElementById("popup").style.display = "none";
if (document.getElementById("title").innerText == "Registered successfully") {
location.href = "/login";
}
});
document.getElementById("register").addEventListener("click", async () => {
let username = document.getElementById("username").value;
let password = document.getElementById("password").value;
let captcha = document.getElementById("hcaptcha").firstChild.dataset.hcaptchaResponse;
if (!document.getElementById("hcaptcha").firstChild.dataset.hcaptchaResponse) {
document.getElementById("title").innerText = "Failed to register";
document.getElementById("reason").innerText = "You have not done the CAPTCHA. Please complete the CAPTCHA before continuing.";
document.getElementById("popup").style.display = "flex";
return;
}
if (username.length < 17 && username.length > 2 && !/[^a-zA-Z0-9._-]/.test(username)) {
} else {
document.getElementById("title").innerText = "Failed to register";
document.getElementById("reason").innerText = "Your username is invalid. Please make your username within the limits described.";
document.getElementById("popup").style.display = "flex";
return;
}
if (!/^((?=\S*?[A-Z])(?=\S*?[a-z])(?=\S*?[0-9]).{5,})\S$/.test(password)) {
document.getElementById("title").innerText = "Failed to register";
document.getElementById("reason").innerText = "Your password is invalid. Please make your password within the limits described.";
document.getElementById("popup").style.display = "flex";
return;
}
let data = await (
await fetch("/register", {
method: "POST",
body: JSON.stringify({
username: username,
password: password,
"h-captcha-response": captcha,
}),
headers: {
"Content-type": "application/json; charset=UTF-8",
},
})
).json();
console.log(data);
if (data.success) {
document.getElementById("title").innerText = "Registered successfully";
document.getElementById("reason").innerText = `Please save the following, this is required to reset your password.\n\n${data.key}\n\nExit to continue`;
} else {
console.log("Error: ", data.reason);
document.getElementById("title").innerText = "Failed to register";
document.getElementById("reason").innerText = data.reason;
}
document.getElementById("popup").style.display = "flex";
});
});
</script>
</head>
<alerts> </alerts>
<body id="noscroll">
<header>
<a href="/index.html">Home</a>
<a href="/bookmarklets.html">Bookmarklets</a>
<a href="/projects.html">Games</a>
<a href="/apps.html">Apps</a>
<a href="/settings.html">Settings</a>
<a id="blank" href="#">Open Blank</a>
<a href="/u/" class="usericon"><img src="/img/user.svg" /></a>
</header>
<main id="main" class="noscroll">
<h2>Register a new account</h2>
<body>
<h2>register..</h2>
<div>
<input type="text" id="username" placeholder="username" />
<p>3-16 characters<br />capital, lowercase, numbers, dash, underscore, and dots allowed</p>
<input type="password" id="password" placeholder="password" />
<p>6+ characters<br />one uppercase, lowercase, and number at least</p>
<div class="h-captcha" id="hcaptcha" data-sitekey="1774ec96-39be-4fb0-9e82-f4c62354b8fa"></div>
<p><a href="/login">Or login</a></p>
<button type="submit" value="Submit" id="register">Register</button>
</main>
<p><a href="/login">already have an account? login here.</a></p>
<button type="submit" value="Submit" id="register">create an account</button>
</div>
<popups>
<div id="popup" style="display: none">
<h1 id="title"></h1>
@ -106,13 +97,5 @@
<button id="close">X</button>
</div>
</popups>
<footer class="noscroll">
<a href="https://gitlab.com/skysthelimit.dev/selenite">Source</a>
<a href="https://discord.gg/7jyufnwJNf">Discord</a>
<a href="/suggest.html">Suggestions & Bugs</a>
<a href="/contact.html">Contact</a>
<a href="/support.html">Donate</a>
<a href="/about.html">About</a>
</footer>
</body>
</html>

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html class="sl-theme-dark" lang="en">
<html lang="en">
<head>
<!-- initialize theme vars
https://coolors.co/10002b-240046-3c096c-5a189a-7b2cbf-9d4edd-c77dff-e0aaff -->

View File

@ -0,0 +1,51 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- initialize theme vars
https://coolors.co/10002b-240046-3c096c-5a189a-7b2cbf-9d4edd-c77dff-e0aaff -->
<!-- initialize externals -->
<meta property="og:title" content="Selenite" />
<meta property="description" content="Selenite is the best unblocked games site. With over 400 games and an account system, no other websites come close to Selenite." />
<meta content="/favicon.png" property="og:image" />
<meta content="#c77dff" data-react-helmet="true" name="theme-color" />
<meta name="googlebot" content="index, follow, snippet" />
<link rel="canonical" href="https://selenite.cc/" />
<meta property="og:description" content="Selenite is the best unblocked games site. With over 400 games and an account system, no other websites come close to Selenite." />
<meta property="og:title" content="Selenite">
<meta property="og:type" content="website">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Selenite",
"alternateName": "selenite.cc",
"url": "https://selenite.cc",
"logo": "https://selenite.cc/favicon.png",
"sameAs": [
"https://github.com/selenite-cc",
"https://youtube.com/@selenitecc",
"https://tiktok.com/@selenitecc",
"https://selenite.cc",
]
}
</script>
<!-- initialize my stuff -->
<script src="/js/all.js"></script>
<script src="/js/main.js"></script>
<link rel="stylesheet" href="/css/main.css" />
<link rel="stylesheet" href="/css/pages.css" />
<link rel="manifest" href="/manifest.json" />
<!-- seo + other things -->
<title>Selenite</title>
<link rel="icon" href="/favicon.ico" />
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415518411898563" crossorigin="anonymous"></script>
</head>
<alerts> </alerts>
<body>
<h1 class="title">stats</h1>
</body>
</html>

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html class="sl-theme-dark" lang="en">
<html lang="en">
<head>
<!-- initialize theme vars
https://coolors.co/10002b-240046-3c096c-5a189a-7b2cbf-9d4edd-c77dff-e0aaff -->
@ -7,7 +7,7 @@
<!-- initialize externals -->
<meta property="og:title" content="Selenite" />
<meta property="description" content="Selenite is the best unblocked games site. With over 400 games and an account system, no other websites come close to Selenite." />
<meta name="keywords" content="proxy, web proxy, unblock websites, unblock chromebook, free web proxy, proxy list, proxy sites, un block chromebook, online proxy, proxy server, proxysite, proxy youtube, bypass securly, bypass iboss, bypass lightspeed filter, chromebooks, unblock youtube, youtube proxy, unblocked youtube, youtube unblocked, unblock games, selenite, unblocked games, free games">
<meta content="/favicon.png" property="og:image" />
<meta content="#c77dff" data-react-helmet="true" name="theme-color" />
<meta name="googlebot" content="index, follow, snippet" />
@ -34,7 +34,7 @@
</script>
<!-- initialize my stuff -->
<script src="/js/all.js"></script>
<script src="/js/all.min.js"></script>
<script src="/js/main.js"></script>
<!-- <script src="/js/widget.js"></script> -->
<script>
@ -42,17 +42,93 @@
<link rel="stylesheet" href="/css/main.css" />
<link rel="stylesheet" href="/css/pages.css" />
<link rel="stylesheet" href="/css/users.css" />
<link rel="manifest" href="/manifest.json" />
<!-- seo + other things -->
<title>Selenite</title>
<script>
async function loadUsers(query, page) {
let above = false;
let below = false;
let data = await (await fetch(`/api/getUsers?page=${page - 1}&query=${query}`)).json();
let users = data['users'];
let url = new URL(location.href);
pages = Math.ceil(data.count / 12);
document.getElementById("users").innerHTML = "";
for (let i = 0; i < Object.keys(users).length; i++) {
document.getElementById("users").innerHTML += `<a href="/u/${users[i].username}" class="users"><img class="pfp" src="${users[i].pfp_url}"/><div class="user_info"><h1>${users[i].name}</h1><p>${users[i].about}</p></div></a>`;
}
document.getElementById("pages").innerHTML = "";
for (let i = 1; i < pages + 1; i++) {
if (i + 6 > page && i - 6 < page) {
let curPage = parseInt(page);
let element = document.createElement("a");
if (!(i == curPage)) {
url.searchParams.set("page", i);
element.setAttribute("href", url);
}
element.setAttribute("class", `pages-btn`);
element.innerText = i;
document.getElementById("pages").append(element);
} else {
if (i + 6 > page) {
above = true;
}
if (i - 6 < page) {
below = true;
}
}
}
if (above) {
let element = document.createElement("a");
element.setAttribute("class", "pages-btn");
element.innerText = "...";
document.getElementById("pages").append(element);
}
if (below) {
let element = document.createElement("a");
element.setAttribute("class", "pages-btn");
element.innerText = "...";
document.getElementById("pages").prepend(element);
}
}
let pages;
document.addEventListener("DOMContentLoaded", async () => {
let params = location.search.substring(1).split("&");
let query;
let page;
if (params) {
for (let i = 0; i < params.length; i++) {
if (params[i].startsWith("query")) {
query = params[i].substring(6);
}
if (params[i].startsWith("page")) {
page = params[i].substring(5);
if (isNaN(page)) {
page = 1;
}
}
}
}
await loadUsers(query ? query : "", page ? page : 0);
document.getElementById("search").addEventListener("keydown", async (e) => {
if (e.key == "Enter") {
let url = new URL(location.href);
url.searchParams.set("query", document.getElementById("search").value);
url.searchParams.set("page", 1);
location.href = url;
}
});
});
</script>
<link rel="icon" href="/favicon.ico" />
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415518411898563" crossorigin="anonymous"></script>
</head>
<alerts> </alerts>
<body>
<h1 class="title">users</h1>
<div id="users">
</div>
<div id="users"></div>
<div id="pages"></div>
</body>
</html>

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html class="sl-theme-dark" lang="en">
<html lang="en">
<head>
<!-- initialize theme vars
https://coolors.co/10002b-240046-3c096c-5a189a-7b2cbf-9d4edd-c77dff-e0aaff -->

232
index.js
View File

@ -1,4 +1,3 @@
const io = require('@pm2/io')
import { log } from "./log.js";
import bodyParser from "body-parser";
import express from "express";
@ -8,12 +7,14 @@ 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 { accs, infdb, polytrack } from "./database.js";
import { accs } 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 { callAI } from "./ai.js";
import os from "node:os";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
@ -21,88 +22,59 @@ const __dirname = dirname(__filename);
const port = process.env.PORT || 3000;
const app = express();
// why the fuck does this have to exist?
app.use("/resources/semag/hotline-miami/", (req,res,next) => {
if(req.method == "HEAD") {
req.socket.destroy();
}
next();
});
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',
});
// app.use("/", (req, res, next) => {
// next();
// });
// setInterval(()=>{
// }, 1000)
import WebSocket, { WebSocketServer } from "ws";
import { request } from "node:http";
const wss = new WebSocketServer({ noServer: true });
let openSockets = 0;
// let openSockets = 0;
wss.on("connection", function connection(ws, req, res) {
openSockets++;
sockets.set(openSockets);
ws.send(`online=${wss.clients.size}`);
setInterval(() => {
ws.send("ping");
}, 30000);
ws.send(`online=${wss.clients.size}`);
}, 10000);
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)))) {
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")) {
} else if (message.startsWith("1")) {
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 });
}
}
} else if (message.startsWith("annc")) {
let splitMessage = message.split(";;");
if(await isAdmin(splitMessage[1])) {
wss.clients.forEach(client => {
client.send(`annc;;${splitMessage[2]};;${splitMessage[3]}`);
})
}
}
});
ws.on("close", () => {openSockets--;
sockets.set(openSockets);});
ws.on("close", () => {});
});
app.post(
"/api/event",
@ -120,11 +92,6 @@ app.post(
})
}
);
// 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"]) {
@ -173,24 +140,16 @@ app.post("/api/account/upload", async (req, res, next) => {
res.status(400).send(status);
}
} else {
return "KILL YOURSELF";
// 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
@ -227,110 +186,6 @@ 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<leaderboard.length;i++) {
returnValue["entries"][i] = {};
returnValue["entries"][i]["id"] = leaderboard[i]["id"];
returnValue["entries"][i]["name"] = decodeURIComponent(leaderboard[i]["username"]);
returnValue["entries"][i]["carColors"] = leaderboard[i]["colors"];
returnValue["entries"][i]["frames"] = leaderboard[i]["frames"];
returnValue["entries"][i]["verifiedState"] = true;
returnValue["entries"][i]["isSelf"] = false;
}
res.send(returnValue);
}
} else if(path == "recording") {
let recordingQuery = polytrack.query(`SELECT * FROM polytrack WHERE id = $id`).get({ $id: req.query.recordingId });
res.send({
"recording": recordingQuery.recording,
"frames": recordingQuery.frames,
"verifiedState": true,
"carColors": recordingQuery.colors
});
}
})
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);
@ -370,7 +225,6 @@ app.use("/api/stats", async (req, res, next) => {
// "cpu": os.cpus(),
"ram": `${(os.totalmem()-os.freemem())/1000000000}GB / ${os.totalmem()/1000000000}GB`,
"cpuUsage": os.loadavg(),
"openWebSockets": openSockets,
"uptime": `${os.uptime()}s`
});
} else {
@ -387,7 +241,7 @@ app.use("/ai", async (req, res, next) => {
next();
}
});
app.use("/", express.static("./selenite", { extensions: ["html"] }));
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);
@ -395,11 +249,12 @@ app.use("/data/:id/:file", async (req, res) => {
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);
res.status(200).send(await fs.readFile(filePath));
} else if (mime.lookup(filePath) == "audio/ogg") {
res.type("audio/ogg");
res.status(200).send(await fs.readFile(filePath));
} else {
res.status(404).send("File not found");
}
@ -477,6 +332,15 @@ 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))
});
app.use("/api/music/download", async (req, res, next) => {
res.status(200).send(await download(req.query.url))
});
const server = app.listen(port, () => {
console.log(log.success("Express is online."));
console.log("- " + log.info("http://localhost:" + port));
@ -490,6 +354,6 @@ server.on("upgrade", (request, socket, head) => {
app.use(async (req, res) => {
res
.type("text/html")
.send(await fs.readFile(`./selenite/404.html`))
.send(await fs.readFile(`./public/404.html`))
.status(404);
});
});

1083
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -12,25 +12,23 @@
"license": "ISC",
"type": "module",
"dependencies": {
"@pm2/io": "^6.1.0",
"axios": "^1.3.1",
"body-parser": "^2.0.2",
"chalk": "^5.3.0",
"compression": "^1.7.4",
"cookie-parser": "^1.4.6",
"dayjs": "^1.11.11",
"dotenv": "^16.4.5",
"express": "^5.0.0",
"fast-glob": "^3.3.2",
"file-type": "^19.0.0",
"install": "^0.13.0",
"@ai-sdk/openai-compatible": "^1.0.22",
"ai": "^5.0.68",
"axios": "^1.12.2",
"body-parser": "^2.2.0",
"chalk": "^5.6.2",
"compression": "^1.8.1",
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"dayjs": "^1.11.18",
"dotenv": "^16.6.1",
"express": "^5.1.0",
"file-type": "^19.6.0",
"lucida": "^2.0.0-54",
"mime-types": "^2.1.35",
"node-html-parser": "^6.1.13",
"openai": "^4.85.3",
"rword": "^3.2.1",
"sanitize-html": "^2.13.0",
"sequelize": "^6.37.3",
"sharp": "^0.33.4",
"ws": "^8.18.0"
"sanitize-html": "^2.17.0",
"sharp": "^0.33.5",
"ws": "^8.18.3"
}
}

View File

@ -2,4 +2,8 @@ module.exports = {
name: "Selenite", // Name of your application
script: "index.js", // Entry point of your application
interpreter: "bun", // Path to the Bun interpreter
watch: true,
cron_restart: '0 0 * * *',
instances : "max",
exec_mode : "cluster"
};