Compare commits

6 Commits

Author SHA1 Message Date
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
17 changed files with 847 additions and 498 deletions

View File

@ -5,3 +5,7 @@ ANNOUNCEMENT_KEY=text.
GROQ_API_KEY=["groq_api"] GROQ_API_KEY=["groq_api"]
NTFY_ALERT=ntfy_channel 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

25
accounts/music.js Normal file
View File

@ -0,0 +1,25 @@
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;
}
async function download(url) {
try {
const info = await sc.getByUrl(url);
const { stream } = await info.getStream();
return stream.path;
} catch (err) {
console.error('Stream error:', err)
return(err.message || 'Failed to stream track')
}
}
export { search, download };

View File

@ -4,6 +4,7 @@ import sanitizeHtml from "sanitize-html";
import sharp from "sharp"; import sharp from "sharp";
import { accs } from "../database.js"; import { accs } from "../database.js";
import { getUserFromCookie, isBanned, decryptCookie, verifyCookie } from "./manage.js"; import { getUserFromCookie, isBanned, decryptCookie, verifyCookie } from "./manage.js";
import { download } from "./music.js";
import dayjs from "dayjs"; import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime.js"; import relativeTime from "dayjs/plugin/relativeTime.js";
@ -37,8 +38,8 @@ let rawProfileHTML = fs.readFileSync("./html/profile.html").toString();
let rawEditProfileHTML = fs.readFileSync("./html/profile_edit.html").toString(); let rawEditProfileHTML = fs.readFileSync("./html/profile_edit.html").toString();
let profile404 = fs.readFileSync("./html/profile_404.html").toString(); let profile404 = fs.readFileSync("./html/profile_404.html").toString();
let profileBan = fs.readFileSync("./html/profile_ban.html").toString(); let profileBan = fs.readFileSync("./html/profile_ban.html").toString();
let gamesJSON = JSON.parse(fs.readFileSync("./selenite/data/games.json").toString()); let gamesJSON = JSON.parse(fs.readFileSync("./public/resources/games.json").toString());
let appsJSON = JSON.parse(fs.readFileSync("./selenite/data/apps.json").toString()); let appsJSON = JSON.parse(fs.readFileSync("./public/resources/apps.json").toString());
let profileReadyJSON = {}; let profileReadyJSON = {};
for (let i = 0; i < gamesJSON.length; i++) { for (let i = 0; i < gamesJSON.length; i++) {
profileReadyJSON[gamesJSON[i].directory] = { name: gamesJSON[i].name, image: gamesJSON[i].image }; profileReadyJSON[gamesJSON[i].directory] = { name: gamesJSON[i].name, image: gamesJSON[i].image };
@ -193,21 +194,53 @@ async function editProfile(body, token, admin) {
let fileType = (await fileTypeFromBuffer(pfp))["ext"]; let fileType = (await fileTypeFromBuffer(pfp))["ext"];
if (["png", "jpg", "gif", "avif", "webp", "tiff"].includes(fileType)) { if (["png", "jpg", "gif", "avif", "webp", "tiff"].includes(fileType)) {
let url; 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 uuid = crypto.randomUUID();
let path = `${process.env.DATA_PATH}/data/${existingAccount.id}/${uuid}.webp`; let path = `${process.env.DATA_PATH}/data/${userData.id}/${uuid}.webp`;
url = `/data/${existingAccount.id}/${uuid}.webp`; url = `/data/${userData.id}/${uuid}.webp`;
fs.mkdirSync(dir, { recursive: true }); fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(path, ""); fs.writeFileSync(path, "");
await sharp(pfp, { animated: fileType == "gif" }) await sharp(pfp, { animated: fileType == "gif" })
.resize({ width: 300, withoutEnlargement: true }) .resize({ width: 300, withoutEnlargement: true })
.webp({ quality: 70, effort: 4 }) .webp({ quality: 70, effort: 4 })
.toFile(path); .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`) const updateAccount = accs.query(`UPDATE accounts SET pfp_url = $url WHERE username = $user`)
updateAccount.get({ $url: url, $user: 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 path = await download(body.url);
console.log("exit download");
let file = Bun.file(path);
let request = new Request({
url: process.env.PROCESSING_SERVER + "/process",
method: "POST",
body: await file.arrayBuffer(),
headers: {
"X-Authentication": process.env.PROCESSING_SERVER_SECRET
}
});
console.log("created request");
let oggFile = await fetch(request);
console.log("finished 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 });
console.log("database");
}
return { success: true }; return { success: true };
} }
@ -228,6 +261,7 @@ async function generateAccountPage(name, cookie, admin) {
} }
let modifiedHTML = rawProfileHTML; let modifiedHTML = rawProfileHTML;
let songData = JSON.parse(userData.music) || false;
modifiedHTML = modifiedHTML.replaceAll("{{ name }}", sanitizeHtml(userData.name, allowNone)); modifiedHTML = modifiedHTML.replaceAll("{{ name }}", sanitizeHtml(userData.name, allowNone));
modifiedHTML = modifiedHTML.replaceAll("{{ join_date }}", dayjs(userData.createdAt).fromNow()); modifiedHTML = modifiedHTML.replaceAll("{{ join_date }}", dayjs(userData.createdAt).fromNow());
modifiedHTML = modifiedHTML.replaceAll("{{ about }}", sanitizeHtml(userData.about, sanitizeConfig) || "No about me available.."); modifiedHTML = modifiedHTML.replaceAll("{{ about }}", sanitizeHtml(userData.about, sanitizeConfig) || "No about me available..");
@ -235,7 +269,15 @@ async function generateAccountPage(name, cookie, admin) {
modifiedHTML = modifiedHTML.replaceAll("{{ user_pfp }}", userData.pfp_url || "/img/user.svg"); modifiedHTML = modifiedHTML.replaceAll("{{ user_pfp }}", userData.pfp_url || "/img/user.svg");
modifiedHTML = modifiedHTML.replaceAll("{{ custom_css }}", userData.custom_css || ""); modifiedHTML = modifiedHTML.replaceAll("{{ custom_css }}", userData.custom_css || "");
modifiedHTML = modifiedHTML.replaceAll("{{ online_time }}", dayjs(userData.last_login).fromNow()); 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 = ""; let badges_html = "";
if (userData.badges !== null) { if (userData.badges !== null) {
@ -260,6 +302,7 @@ async function generateAccountPage(name, cookie, admin) {
return modified_ban; return modified_ban;
} }
let modifiedHTML = rawEditProfileHTML; let modifiedHTML = rawEditProfileHTML;
let songData = JSON.parse(userData.music) || false;
modifiedHTML = modifiedHTML.replaceAll("{{ name }}", sanitizeHtml(userData.name, sanitizeConfig)); modifiedHTML = modifiedHTML.replaceAll("{{ name }}", sanitizeHtml(userData.name, sanitizeConfig));
modifiedHTML = modifiedHTML.replaceAll("{{ username }}", userData.username); modifiedHTML = modifiedHTML.replaceAll("{{ username }}", userData.username);
modifiedHTML = modifiedHTML.replaceAll("{{ join_date }}", dayjs(userData.createdAt).fromNow()); modifiedHTML = modifiedHTML.replaceAll("{{ join_date }}", dayjs(userData.createdAt).fromNow());
@ -269,7 +312,14 @@ async function generateAccountPage(name, cookie, admin) {
modifiedHTML = modifiedHTML.replaceAll("{{ url_gen }}", `https://selenite.cc/u/${userData.username}`); modifiedHTML = modifiedHTML.replaceAll("{{ url_gen }}", `https://selenite.cc/u/${userData.username}`);
modifiedHTML = modifiedHTML.replaceAll("{{ online_time }}", dayjs(userData.last_login).fromNow()); 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("{{ css_edit }}", (userData.badges ? userData.badges.length : 0) > 0 ? '<img src="/img/edit.svg" id="edit" />' : "");
modifiedHTML = modifiedHTML.replaceAll("{{ played_games }}", buildGameHTML(userData)); 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 = ""; let badges_html = "";
if (userData.badges !== null) { if (userData.badges !== null) {
@ -282,43 +332,7 @@ async function generateAccountPage(name, cookie, admin) {
return modifiedHTML; 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) { async function getUsers(page, search) {
let amount = 12; let amount = 12;
if (!page) { if (!page) {

View File

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

View File

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

View File

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html class="sl-theme-dark" lang="en"> <html lang="en">
<head> <head>
<!-- initialize theme vars <!-- initialize theme vars
https://coolors.co/10002b-240046-3c096c-5a189a-7b2cbf-9d4edd-c77dff-e0aaff --> 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> <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 --> <!-- seo + other things -->
<title>Login | Selenite</title> <title>Login | Selenite</title>
@ -60,27 +61,17 @@
</script> </script>
</head> </head>
<alerts> </alerts> <alerts> </alerts>
<body id="noscroll"> <body>
<header> <h2>login..</h2>
<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>
<form onsubmit="return false;" id="login"> <form onsubmit="return false;" id="login">
<input type="text" id="username" placeholder="username" /> <input type="text" id="username" placeholder="username" />
<input type="password" id="password" placeholder="password" /> <input type="password" id="password" placeholder="password" />
<br /> <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> <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> </form>
</main>
<popups> <popups>
<div id="popup" style="display: none"> <div id="popup" style="display: none">
<h1 id="title"></h1> <h1 id="title"></h1>
@ -88,13 +79,5 @@
<button id="close">X</button> <button id="close">X</button>
</div> </div>
</popups> </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> </body>
</html> </html>

View File

@ -1,67 +1,139 @@
<!DOCTYPE html> <!DOCTYPE html>
<html class="sl-theme-dark" lang="en"> <html lang="en">
<head> <head>
<!-- initialize theme vars <!-- initialize theme vars
https://coolors.co/10002b-240046-3c096c-5a189a-7b2cbf-9d4edd-c77dff-e0aaff --> 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 --> <!-- initialize externals -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.0/jquery.min.js"></script> <meta property="og:title" content="Selenite" />
<script src=" https://cdn.jsdelivr.net/npm/js-cookie@3.0.5/dist/js.cookie.min.js "></script> <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" />
<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 --> <!-- initialize my stuff -->
<script src="/js/all.min.js"></script> <script src="/js/all.js"></script>
<script src="/js/main.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 --> <!-- seo + other things -->
<style>{{ custom_css }}</style>
<title>{{ name }}'s Profile | Selenite</title> <title>{{ name }}'s Profile | Selenite</title>
<link rel="icon" href="/favicon.ico" /> <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) {
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;
}, 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;
document.getElementById("playbar").value = (audioObject.currentTime / audioObject.duration) * 1000;
});
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> </head>
<alerts> </alerts> <alerts> </alerts>
<body class="profile"> <body>
<header> <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>
<a href="/index.html">Home</a> <h1 class="title">{{ name }}'s profile</h1>
<a href="/bookmarklets.html">Bookmarklets</a> <section>
<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" /> <img src="{{ user_pfp }}" class="pfp" />
<div class="profile top text"> <div class="profile-element">
<h1>{{ name }}</h1> <h1>{{ name }}</h1>
<div class="samerow">{{ badges }}</div> <p>/u/{{ username }}</p>
<div class="badges">{{ badges }}</div>
</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>
<div class="profile-element">
<h2>Joined {{ join_date }}</h2> <h2>Joined {{ join_date }}</h2>
<h2>Last online {{ online_time }}</h2> <h2>Last online {{ online_time }}</h2>
</div> </div>
<div class="profile top text right"> </section>
<h1>About Me</h1> <section class="column">
<h2 class="about">{{ about }}</h2> <h1>about me</h1>
</div> <p>{{ about }}</p>
</div> </section>
<div class="profile played">
<h2>Top Games:</h2>
<div id="played-games">
{{ played_games }}
</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>
</body> </body>
</html> </html>

View File

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

View File

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

View File

@ -1,25 +1,73 @@
<!DOCTYPE html> <!DOCTYPE html>
<html class="sl-theme-dark" lang="en"> <html lang="en">
<head> <head>
<!-- initialize theme vars <!-- initialize theme vars
https://coolors.co/10002b-240046-3c096c-5a189a-7b2cbf-9d4edd-c77dff-e0aaff --> https://coolors.co/10002b-240046-3c096c-5a189a-7b2cbf-9d4edd-c77dff-e0aaff -->
<!-- initialize externals --> <!-- initialize externals -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.0/jquery.min.js"></script> <meta property="og:title" content="Selenite" />
<script src=" https://cdn.jsdelivr.net/npm/js-cookie@3.0.5/dist/js.cookie.min.js "></script> <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" />
<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 --> <!-- initialize my stuff -->
<script src="/js/all.min.js"></script> <script src="/js/all.js"></script>
<script src="/js/main.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 --> <!-- seo + other things -->
<title>{{ name }}'s Profile | Selenite</title> <title>{{ name }}'s Profile | Selenite</title>
<link rel="icon" href="/favicon.ico" /> <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> <script>
let username = "{{ username }}"; let username = "{{ username }}";
let userData; let userData;
let songData;
(async () => { (async () => {
userData = await (await fetch("/u/raw")).json(); userData = await (await fetch("/u/raw")).json();
})(); })();
@ -53,6 +101,9 @@
console.log("promise finished"); console.log("promise finished");
body = { pfp: fileData }; body = { pfp: fileData };
console.log("body set"); console.log("body set");
} else if (state == "song") {
body = songData;
console.log("body set");
} else if (state == "clearpfp") { } else if (state == "clearpfp") {
body = { pfp: "del" }; body = { pfp: "del" };
} else if (state == "close") { } else if (state == "close") {
@ -79,10 +130,12 @@
document.getElementById("title").innerText = "Upload successful!"; document.getElementById("title").innerText = "Upload successful!";
document.getElementById("text").style.display = "none"; document.getElementById("text").style.display = "none";
document.getElementById("popup").style.display = "flex"; document.getElementById("popup").style.display = "flex";
document.getElementById("blur").style.display = "flex";
} else { } 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("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("text").style.display = "none";
document.getElementById("popup").style.display = "flex"; document.getElementById("popup").style.display = "flex";
document.getElementById("blur").style.display = "flex";
} }
return; return;
} else if (state == "download") { } else if (state == "download") {
@ -105,11 +158,13 @@
document.getElementById("title").innerText = "Download successful!"; document.getElementById("title").innerText = "Download successful!";
document.getElementById("text").style.display = "none"; document.getElementById("text").style.display = "none";
document.getElementById("popup").style.display = "flex"; document.getElementById("popup").style.display = "flex";
document.getElementById("blur").style.display = "flex";
} else { } else {
document.getElementById("title").innerText = "Download failed."; document.getElementById("title").innerText = "Download failed.";
document.getElementById("body-text").innerText = data.reason; document.getElementById("body-text").innerText = data.reason;
document.getElementById("body-text").style.display = "flex"; document.getElementById("body-text").style.display = "flex";
document.getElementById("popup").style.display = "flex"; document.getElementById("popup").style.display = "flex";
document.getElementById("blur").style.display = "flex";
} }
console.log(data); console.log(data);
data = JSON.parse(data.data); data = JSON.parse(data.data);
@ -131,7 +186,19 @@
} }
console.log("sending"); console.log("sending");
body.username = username; 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", credentials: "include",
headers: { headers: {
"Content-type": "application/json; charset=UTF-8", "Content-type": "application/json; charset=UTF-8",
@ -140,9 +207,24 @@
method: "POST", method: "POST",
mode: "cors", mode: "cors",
}); });
console.log("sent"); let resp = await data.json();
document.getElementById("popup").style.display = "none"; if(data.status == 200) {
location.reload(); 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.addEventListener("DOMContentLoaded", async () => {
document.getElementById("pfp_upload").addEventListener("change", (e) => { document.getElementById("pfp_upload").addEventListener("change", (e) => {
@ -153,12 +235,24 @@
}); });
document.getElementById("submit").addEventListener("click", await setProfile); document.getElementById("submit").addEventListener("click", await setProfile);
document.getElementById("clear").addEventListener("click", async()=>{state="clearpfp";await setProfile()}); document.getElementById("clear").addEventListener("click", async()=>{state="clearpfp";await setProfile()});
document.querySelectorAll("#edit").forEach((element) => { document.getElementById("pfp").addEventListener("click", () => {
element.addEventListener("click", (e) => { state = "pfp";
console.log(e.target.parentElement.children[0].id); document.getElementById("title").innerText = "upload a new profile picture";
if (e.target.parentElement.children[0].id == "name") { 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"; state = "name";
document.getElementById("title").innerText = "Set your name."; document.getElementById("title").innerText = "change your name";
document.getElementById("text").setAttribute("maxlength", "20"); document.getElementById("text").setAttribute("maxlength", "20");
document.getElementById("pfp_upload").style.display = "none"; document.getElementById("pfp_upload").style.display = "none";
document.getElementById("pfp_reminder").style.display = "none"; document.getElementById("pfp_reminder").style.display = "none";
@ -170,9 +264,13 @@
document.getElementById("counter").style.display = "flex"; document.getElementById("counter").style.display = "flex";
document.getElementById("submit").style.display = "flex"; document.getElementById("submit").style.display = "flex";
document.getElementById("popup").style.display = "flex"; document.getElementById("popup").style.display = "flex";
} else if (e.target.parentElement.children[0].id == "bio") { 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"; state = "bio";
document.getElementById("title").innerText = "Set your about me."; document.getElementById("title").innerText = "change your about me";
document.getElementById("text").setAttribute("maxlength", "200"); document.getElementById("text").setAttribute("maxlength", "200");
document.getElementById("pfp_upload").style.display = "none"; document.getElementById("pfp_upload").style.display = "none";
document.getElementById("pfp_reminder").style.display = "none"; document.getElementById("pfp_reminder").style.display = "none";
@ -184,40 +282,61 @@
document.getElementById("counter").style.display = "flex"; document.getElementById("counter").style.display = "flex";
document.getElementById("submit").style.display = "flex"; document.getElementById("submit").style.display = "flex";
document.getElementById("popup").style.display = "flex"; document.getElementById("popup").style.display = "flex";
} else if (e.target.parentElement.children[0].id == "custom") { document.getElementById("blur").style.display = "flex";
state = "custom"; document.getElementById("search").style.display = "none";
document.getElementById("title").innerText = "Set your custom CSS."; 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("text").setAttribute("maxlength", "2048");
document.getElementById("pfp_upload").style.display = "none"; document.getElementById("pfp_upload").style.display = "none";
document.getElementById("pfp_reminder").style.display = "none"; document.getElementById("pfp_reminder").style.display = "none";
document.getElementById("body-text").style.display = "none"; document.getElementById("body-text").style.display = "none";
document.getElementById("clear").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("text").style.display = "none";
document.getElementById("counter").style.display = "none"; document.getElementById("counter").style.display = "none";
document.getElementById("submit").style.display = "flex"; document.getElementById("submit").style.display = "none";
document.getElementById("popup").style.display = "flex"; 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("close").addEventListener("click", () => {
document.getElementById("popup").style.display = "none"; document.getElementById("popup").style.display = "none";
document.getElementById("blur").style.display = "none";
}); });
document.getElementById("text").addEventListener("input", () => { document.getElementById("text").addEventListener("input", () => {
document.getElementById("counter").innerText = `${document.getElementById("text").value.length} / ${document.getElementById("text").attributes.maxlength.value}`; 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 () => { document.getElementById("upload").addEventListener("click", async () => {
state = "upload"; state = "upload";
document.getElementById("title").innerText = "Warning"; document.getElementById("title").innerText = "Warning";
@ -230,6 +349,7 @@
document.getElementById("counter").style.display = "none"; document.getElementById("counter").style.display = "none";
document.getElementById("submit").style.display = "flex"; document.getElementById("submit").style.display = "flex";
document.getElementById("popup").style.display = "flex"; document.getElementById("popup").style.display = "flex";
document.getElementById("blur").style.display = "flex";
}); });
document.getElementById("download").addEventListener("click", async () => { document.getElementById("download").addEventListener("click", async () => {
state = "download"; state = "download";
@ -243,64 +363,41 @@
document.getElementById("counter").style.display = "none"; document.getElementById("counter").style.display = "none";
document.getElementById("submit").style.display = "flex"; document.getElementById("submit").style.display = "flex";
document.getElementById("popup").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> </script>
</head> </head>
<alerts> </alerts>
<body> <body>
<header> <div id="blur"></div>
<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>
<div>
<div id="custom"></div>
{{ css_edit }}
</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>
<div class="profile played">
<h2>Top Games:</h2>
<div id="played-games">
{{ played_games }}
</div>
</div>
</main>
<popups>
<div id="popup" style="display: none"> <div id="popup" style="display: none">
<h1 id="title"></h1> <h1 id="title"></h1>
<p id="body-text"></p> <p id="body-text"></p>
<input type="text" id="text" /> <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> <p id="counter">0 / 0</p>
<input type="file" id="pfp_upload" name="filename" accept=".png,.jpg,.jpeg,.gif,.avif,.webp,.tiff,.svg" /> <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> <p id="pfp_reminder">4 MB file upload max</p>
@ -308,14 +405,42 @@
<button id="submit">Submit</button> <button id="submit">Submit</button>
<button id="close">X</button> <button id="close">X</button>
</div> </div>
</popups> <div class="samerow">
<footer> <button id="download">Download Backup</button>
<a href="https://gitlab.com/skysthelimit.dev/selenite">Source</a> <button id="upload">Upload Backup</button>
<a href="https://discord.gg/7jyufnwJNf">Discord</a> </div>
<a href="/suggest.html">Suggestions & Bugs</a> <h1 class="title">{{ name }}'s profile</h1>
<a href="/contact.html">Contact</a> <h3>click on one of the following areas to edit</h3>
<a href="/support.html">Donate</a> <h3>profile picture, name, song, and about me</h3>
<a href="/about.html">About</a> <p style="font-size: 16px;">share your profile at {{ url_gen }}</p>
</footer> <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 class="profile-element edit" id="song">
<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>
<div class="profile-element">
<h2>Joined {{ join_date }}</h2>
<h2>Last online {{ online_time }}</h2>
</div>
</section>
<section class="column edit" id="about">
<h1>about me</h1>
<p class="about">{{ about }}</p>
</section>
</body> </body>
</html> </html>

View File

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html class="sl-theme-dark" lang="en"> <html lang="en">
<head> <head>
<!-- initialize theme vars <!-- initialize theme vars
https://coolors.co/10002b-240046-3c096c-5a189a-7b2cbf-9d4edd-c77dff-e0aaff --> https://coolors.co/10002b-240046-3c096c-5a189a-7b2cbf-9d4edd-c77dff-e0aaff -->
@ -14,10 +14,11 @@
<script src="https://js.hcaptcha.com/1/api.js" async defer></script> <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 --> <!-- seo + other things -->
<title>Register | Selenite</title> <title>Login | Selenite</title>
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<script> <script>
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
@ -50,7 +51,6 @@
document.getElementById("popup").style.display = "flex"; document.getElementById("popup").style.display = "flex";
return; return;
} }
let data = await ( let data = await (
await fetch("/register", { await fetch("/register", {
method: "POST", method: "POST",
@ -79,26 +79,17 @@
</script> </script>
</head> </head>
<alerts> </alerts> <alerts> </alerts>
<body id="noscroll"> <body>
<header> <h2>register..</h2>
<a href="/index.html">Home</a> <div>
<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>
<input type="text" id="username" placeholder="username" /> <input type="text" id="username" placeholder="username" />
<p>3-16 characters<br />capital, lowercase, numbers, dash, underscore, and dots allowed</p> <p>3-16 characters<br />capital, lowercase, numbers, dash, underscore, and dots allowed</p>
<input type="password" id="password" placeholder="password" /> <input type="password" id="password" placeholder="password" />
<p>6+ characters<br />one uppercase, lowercase, and number at least</p> <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> <div class="h-captcha" id="hcaptcha" data-sitekey="1774ec96-39be-4fb0-9e82-f4c62354b8fa"></div>
<p><a href="/login">Or login</a></p> <p><a href="/login">already have an account? login here.</a></p>
<button type="submit" value="Submit" id="register">Register</button> <button type="submit" value="Submit" id="register">create an account</button>
</main> </div>
<popups> <popups>
<div id="popup" style="display: none"> <div id="popup" style="display: none">
<h1 id="title"></h1> <h1 id="title"></h1>
@ -106,13 +97,5 @@
<button id="close">X</button> <button id="close">X</button>
</div> </div>
</popups> </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> </body>
</html> </html>

View File

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

View File

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html class="sl-theme-dark" lang="en"> <html lang="en">
<head> <head>
<!-- initialize theme vars <!-- initialize theme vars
https://coolors.co/10002b-240046-3c096c-5a189a-7b2cbf-9d4edd-c77dff-e0aaff --> https://coolors.co/10002b-240046-3c096c-5a189a-7b2cbf-9d4edd-c77dff-e0aaff -->
@ -42,17 +42,93 @@
<link rel="stylesheet" href="/css/main.css" /> <link rel="stylesheet" href="/css/main.css" />
<link rel="stylesheet" href="/css/pages.css" /> <link rel="stylesheet" href="/css/pages.css" />
<link rel="stylesheet" href="/css/users.css" />
<link rel="manifest" href="/manifest.json" /> <link rel="manifest" href="/manifest.json" />
<!-- seo + other things --> <!-- seo + other things -->
<title>Selenite</title> <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.floor(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" /> <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 async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415518411898563" crossorigin="anonymous"></script>
</head> </head>
<alerts> </alerts> <alerts> </alerts>
<body> <body>
<h1 class="title">users</h1> <h1 class="title">users</h1>
<div id="users"> <div id="users"></div>
<div id="pages"></div>
</div>
</body> </body>
</html> </html>

View File

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

167
index.js
View File

@ -1,4 +1,3 @@
const io = require('@pm2/io')
import { log } from "./log.js"; import { log } from "./log.js";
import bodyParser from "body-parser"; import bodyParser from "body-parser";
import express from "express"; import express from "express";
@ -10,6 +9,7 @@ import mime from "mime-types";
import compression from "compression"; import compression from "compression";
import { accs, infdb, polytrack } from "./database.js"; import { accs, infdb, polytrack } from "./database.js";
import { } from "./accounts/friend.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 { banUser, removeAccount, verifyCookie, getUserFromCookie, createAccount, resetPassword, loginAccount, addBadge } from "./accounts/manage.js";
import { } from "./accounts/misc.js"; import { } from "./accounts/misc.js";
import { getRawData, generateAccountPage, editProfile, saveData, getUsers, isAdmin, retrieveData } from "./accounts/profile.js"; import { getRawData, generateAccountPage, editProfile, saveData, getUsers, isAdmin, retrieveData } from "./accounts/profile.js";
@ -26,34 +26,17 @@ app.use(cookieParser());
app.use(express.json({ limit: "10mb" })); app.use(express.json({ limit: "10mb" }));
app.use(express.urlencoded({ extended: false })); app.use(express.urlencoded({ extended: false }));
app.use(express.text()); 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) => { app.use("/", (req, res, next) => {
requestsPerSec.mark();
requestsPerMin.mark();
next(); next();
}); });
const sockets = io.metric({
name: 'Open Websockets',
id: 'app/requests/sockets',
});
// setInterval(()=>{ // setInterval(()=>{
// }, 1000) // }, 1000)
import WebSocket, { WebSocketServer } from "ws"; import WebSocket, { WebSocketServer } from "ws";
import { request } from "node:http";
const wss = new WebSocketServer({ noServer: true }); const wss = new WebSocketServer({ noServer: true });
let openSockets = 0; let openSockets = 0;
wss.on("connection", function connection(ws, req, res) { wss.on("connection", function connection(ws, req, res) {
openSockets++; openSockets++;
sockets.set(openSockets);
setInterval(() => { setInterval(() => {
ws.send("ping"); ws.send("ping");
}, 30000); }, 30000);
@ -77,32 +60,11 @@ wss.on("connection", function connection(ws, req, res) {
if (ws.id) { if (ws.id) {
const updateAccount = accs.query(`UPDATE accounts SET last_login = $login WHERE username = $user`) const updateAccount = accs.query(`UPDATE accounts SET last_login = $login WHERE username = $user`)
updateAccount.get({ $login: new Date().toUTCString(), $user: ws.id }); updateAccount.get({ $login: new Date().toUTCString(), $user: ws.id });
if (message.substring(4)) {
const existingAccount = accs.query(`SELECT * FROM accounts WHERE username LIKE $1`)
let userData = existingAccount.get({ $1: ws.id });
if (userData == null) {
return { success: false, reason: "The account doesn't exist." };
}
let games;
if (userData.playedgames) {
games = JSON.parse(userData.playedgames);
} else {
games = {};
}
if (games[message.substring(4)]) {
games[message.substring(4)] += 30;
} else {
games[message.substring(4)] = 30;
}
const updateAccount = accs.query(`UPDATE accounts SET playedgames = $playedgames WHERE username = $user`)
updateAccount.get({ $playedgames: JSON.stringify(games), $user: ws.id });
}
} }
} }
}); });
ws.on("close", () => {openSockets--; ws.on("close", () => {openSockets--;});
sockets.set(openSockets);});
}); });
app.post( app.post(
"/api/event", "/api/event",
@ -120,11 +82,6 @@ app.post(
}) })
} }
); );
// app.use("*.json", async (req, res, next) => {
// optimize json
// console.log("got data");
// next()
// });
app.post("/register", async (req, res) => { app.post("/register", async (req, res) => {
let status = await createAccount(req.body.username, req.body.password, req.body["h-captcha-response"]); let status = await createAccount(req.body.username, req.body.password, req.body["h-captcha-response"]);
if (status["success"]) { if (status["success"]) {
@ -227,110 +184,6 @@ app.get("/api/chat/recent", async (req, res) => {
// get last 50 recent messages // get last 50 recent messages
// offset by a page param // 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) => { app.use("/api/account/load", async (req, res, next) => {
if (req.cookies.token && (await verifyCookie(req.cookies.token))) { if (req.cookies.token && (await verifyCookie(req.cookies.token))) {
let status = await retrieveData(req.cookies.token); let status = await retrieveData(req.cookies.token);
@ -387,7 +240,7 @@ app.use("/ai", async (req, res, next) => {
next(); next();
} }
}); });
app.use("/", express.static("./selenite", { extensions: ["html"] })); app.use("/", express.static("./public", { extensions: ["html"] }));
app.use("/data/:id/:file", async (req, res) => { app.use("/data/:id/:file", async (req, res) => {
const id = path.basename(req.params.id); const id = path.basename(req.params.id);
const file = path.basename(req.params.file); const file = path.basename(req.params.file);
@ -400,6 +253,9 @@ app.use("/data/:id/:file", async (req, res) => {
if (mime.lookup(filePath) == "image/webp") { if (mime.lookup(filePath) == "image/webp") {
res.type("image/webp"); res.type("image/webp");
res.status(200).send(image); res.status(200).send(image);
} else if (mime.lookup(filePath) == "audio/ogg") {
res.type("audio/ogg");
res.status(200).send(image);
} else { } else {
res.status(404).send("File not found"); res.status(404).send("File not found");
} }
@ -477,6 +333,15 @@ app.post("/api/admin/ban", async (req, res) => {
let status = await banUser(req.body.name, req.body.reason, req.cookies.token); let status = await banUser(req.body.name, req.body.reason, req.cookies.token);
res.status(200).send(status); 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, () => { const server = app.listen(port, () => {
console.log(log.success("Express is online.")); console.log(log.success("Express is online."));
console.log("- " + log.info("http://localhost:" + port)); console.log("- " + log.info("http://localhost:" + port));
@ -490,6 +355,6 @@ server.on("upgrade", (request, socket, head) => {
app.use(async (req, res) => { app.use(async (req, res) => {
res res
.type("text/html") .type("text/html")
.send(await fs.readFile(`./selenite/404.html`)) .send(await fs.readFile(`./public/404.html`))
.status(404); .status(404);
}); });

207
package-lock.json generated
View File

@ -21,6 +21,7 @@
"fast-glob": "^3.3.2", "fast-glob": "^3.3.2",
"file-type": "^19.0.0", "file-type": "^19.0.0",
"install": "^0.13.0", "install": "^0.13.0",
"lucida": "^2.0.0-54",
"mime-types": "^2.1.35", "mime-types": "^2.1.35",
"node-html-parser": "^6.1.13", "node-html-parser": "^6.1.13",
"openai": "^4.85.3", "openai": "^4.85.3",
@ -40,6 +41,15 @@
"tslib": "^2.4.0" "tslib": "^2.4.0"
} }
}, },
"node_modules/@fastify/busboy": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz",
"integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==",
"license": "MIT",
"engines": {
"node": ">=14"
}
},
"node_modules/@img/sharp-darwin-arm64": { "node_modules/@img/sharp-darwin-arm64": {
"version": "0.33.4", "version": "0.33.4",
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.4.tgz", "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.4.tgz",
@ -569,6 +579,70 @@
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
"license": "Apache-2.0" "license": "Apache-2.0"
}, },
"node_modules/@protobufjs/aspromise": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
"integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/base64": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
"integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/codegen": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
"integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/eventemitter": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
"integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/fetch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
"integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
"license": "BSD-3-Clause",
"dependencies": {
"@protobufjs/aspromise": "^1.1.1",
"@protobufjs/inquire": "^1.1.0"
}
},
"node_modules/@protobufjs/float": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
"integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/inquire": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
"integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/path": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
"integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/pool": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
"integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/utf8": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
"license": "BSD-3-Clause"
},
"node_modules/@tokenizer/token": { "node_modules/@tokenizer/token": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
@ -664,9 +738,9 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
}, },
"node_modules/axios": { "node_modules/axios": {
"version": "1.7.4", "version": "1.10.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz",
"integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"follow-redirects": "^1.15.6", "follow-redirects": "^1.15.6",
@ -674,6 +748,17 @@
"proxy-from-env": "^1.1.0" "proxy-from-env": "^1.1.0"
} }
}, },
"node_modules/blowfish-cbc": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/blowfish-cbc/-/blowfish-cbc-1.0.1.tgz",
"integrity": "sha512-o1JN6g6+ATW/4k7q1BZzy14VqLxwYa1mCii47qT4kmAaYD0NAfdM6/pz6uizbhTra/xunsPQI27LZt08OQS4sA==",
"deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"node-addon-api": "^8.1.0"
}
},
"node_modules/body-parser": { "node_modules/body-parser": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.0.2.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.0.2.tgz",
@ -1661,6 +1746,21 @@
} }
] ]
}, },
"node_modules/image-size": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz",
"integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==",
"license": "MIT",
"dependencies": {
"queue": "6.0.2"
},
"bin": {
"image-size": "bin/image-size.js"
},
"engines": {
"node": ">=16.x"
}
},
"node_modules/inflection": { "node_modules/inflection": {
"version": "1.13.4", "version": "1.13.4",
"resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz",
@ -1754,11 +1854,39 @@
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/librespot": {
"version": "0.2.22",
"resolved": "https://registry.npmjs.org/librespot/-/librespot-0.2.22.tgz",
"integrity": "sha512-szLknWSrgF+mRuypNXQTmdq8PGGhgPRAazm8sJmD8wiCAbtO9LYHihZhQcRmCzmVz818sRHZM/BU6K6XslXSHQ==",
"license": "MIT",
"dependencies": {
"protobufjs": "^7.2.5",
"undici": "^5.27.0"
}
},
"node_modules/librespot/node_modules/undici": {
"version": "5.29.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz",
"integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==",
"license": "MIT",
"dependencies": {
"@fastify/busboy": "^2.0.0"
},
"engines": {
"node": ">=14.0"
}
},
"node_modules/lodash": { "node_modules/lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
}, },
"node_modules/long": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
"integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
"license": "Apache-2.0"
},
"node_modules/lru-cache": { "node_modules/lru-cache": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@ -1771,6 +1899,19 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/lucida": {
"version": "2.0.0-54",
"resolved": "https://registry.npmjs.org/lucida/-/lucida-2.0.0-54.tgz",
"integrity": "sha512-3Y24WkCI1Ks6EjOAElYAeYXkxT/HtunEW+SnhDKFb681B/QEMhS/EzLzFi8D5l95/pOVODUZQMMxYNKmMMC1fQ==",
"license": "OQL",
"dependencies": {
"blowfish-cbc": "^1.0.1",
"image-size": "^1.1.1",
"librespot": "^0.2.21",
"undici": "^6.19.4",
"xmldom-qsa": "^1.1.3"
}
},
"node_modules/media-typer": { "node_modules/media-typer": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
@ -1897,6 +2038,15 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/node-addon-api": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.4.0.tgz",
"integrity": "sha512-D9DI/gXHvVmjHS08SVch0Em8G5S1P+QWtU31appcKT/8wFSPRcdHadIFSAntdMMVM5zz+/DL+bL/gz3UDppqtg==",
"license": "MIT",
"engines": {
"node": "^18 || ^20 || >= 21"
}
},
"node_modules/node-domexception": { "node_modules/node-domexception": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
@ -2125,6 +2275,30 @@
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
} }
}, },
"node_modules/protobufjs": {
"version": "7.5.3",
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.3.tgz",
"integrity": "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==",
"hasInstallScript": true,
"license": "BSD-3-Clause",
"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"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/proxy-addr": { "node_modules/proxy-addr": {
"version": "2.0.7", "version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@ -2157,6 +2331,15 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/queue": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz",
"integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==",
"license": "MIT",
"dependencies": {
"inherits": "~2.0.3"
}
},
"node_modules/queue-microtask": { "node_modules/queue-microtask": {
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@ -2826,6 +3009,15 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/undici": {
"version": "6.21.3",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz",
"integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==",
"license": "MIT",
"engines": {
"node": ">=18.17"
}
},
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "5.26.5", "version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
@ -2924,6 +3116,15 @@
} }
} }
}, },
"node_modules/xmldom-qsa": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/xmldom-qsa/-/xmldom-qsa-1.1.3.tgz",
"integrity": "sha512-IJBOczBpAYrIBJFFsmCBwfBhwe4zdMR3Xz0ZBX0OFtgO49rLy/BWbhkegOwsthdBWb1gUtFK6ZZnGdT8ZqPRBA==",
"license": "MIT",
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/yallist": { "node_modules/yallist": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",

View File

@ -24,6 +24,7 @@
"fast-glob": "^3.3.2", "fast-glob": "^3.3.2",
"file-type": "^19.0.0", "file-type": "^19.0.0",
"install": "^0.13.0", "install": "^0.13.0",
"lucida": "^2.0.0-54",
"mime-types": "^2.1.35", "mime-types": "^2.1.35",
"node-html-parser": "^6.1.13", "node-html-parser": "^6.1.13",
"openai": "^4.85.3", "openai": "^4.85.3",