actual backend implementation lol
This commit is contained in:
parent
df13e78af6
commit
8d4436a123
BIN
Saved/Songs/Example/Dogsong.mp3
Normal file
BIN
Saved/Songs/Example/Dogsong.mp3
Normal file
Binary file not shown.
|
@ -5,4 +5,8 @@ SERVER_URL=localhost
|
||||||
DASHBOARD_URL=localhost:5173 # set this to wherever you're hosting the Partypack dashboard
|
DASHBOARD_URL=localhost:5173 # set this to wherever you're hosting the Partypack dashboard
|
||||||
ENVIRONMENT=dev
|
ENVIRONMENT=dev
|
||||||
COOKIE_SIGN_KEY=
|
COOKIE_SIGN_KEY=
|
||||||
|
ADMIN_KEY=
|
||||||
|
BOT_TOKEN=
|
||||||
|
DISCORD_CLIENT_ID=
|
||||||
|
DISCORD_CLIENT_SECRET=
|
||||||
USE_HTTPS=true # set this to false if you're debugging on your computer
|
USE_HTTPS=true # set this to false if you're debugging on your computer
|
4
Server/.gitignore
vendored
4
Server/.gitignore
vendored
|
@ -130,3 +130,7 @@ dist
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
bin/
|
bin/
|
||||||
|
|
||||||
|
*.db
|
||||||
|
*.db-shm
|
||||||
|
*.db-wal
|
36
Server/Source/Handlers/Database.ts
Normal file
36
Server/Source/Handlers/Database.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import { DataSource } from "typeorm";
|
||||||
|
import { ENVIRONMENT } from "../Modules/Constants";
|
||||||
|
import { join } from "path";
|
||||||
|
|
||||||
|
export const DBSource = new DataSource({
|
||||||
|
type: "better-sqlite3",
|
||||||
|
database: `Partypack${ENVIRONMENT !== "prod" ? `-${ENVIRONMENT}` : ""}.db`,
|
||||||
|
synchronize: true,
|
||||||
|
logging: false,
|
||||||
|
entities: [join(__dirname, "..", "Schemas") + "\\*{.js,.ts}"],
|
||||||
|
subscribers: [],
|
||||||
|
migrations: [],
|
||||||
|
enableWAL: true
|
||||||
|
});
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
await DBSource.initialize();
|
||||||
|
})();
|
||||||
|
|
||||||
|
/*
|
||||||
|
User
|
||||||
|
- discord id (primary)
|
||||||
|
- list of all songs in user's library
|
||||||
|
- list of all songs in user's published
|
||||||
|
|
||||||
|
Song
|
||||||
|
- length
|
||||||
|
- bpm
|
||||||
|
- key
|
||||||
|
- scale
|
||||||
|
- keytar/guitar
|
||||||
|
- icon url
|
||||||
|
- name
|
||||||
|
- artist
|
||||||
|
- release year
|
||||||
|
*/
|
|
@ -51,6 +51,8 @@ Initialize();
|
||||||
// set up both utils for usage
|
// set up both utils for usage
|
||||||
import { LoadSongs } from "../Modules/FestivalUtil";
|
import { LoadSongs } from "../Modules/FestivalUtil";
|
||||||
import { CacheFortnitePages } from "../Modules/PagesUtil";
|
import { CacheFortnitePages } from "../Modules/PagesUtil";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
axios.defaults.validateStatus = () => true;
|
||||||
LoadSongs();
|
LoadSongs();
|
||||||
CacheFortnitePages();
|
CacheFortnitePages();
|
18
Server/Source/Modules/BLURL.ts
Normal file
18
Server/Source/Modules/BLURL.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// massive shoutout to yls for providing me with the blurl standard doc :fire:
|
||||||
|
|
||||||
|
import { deflateSync, inflateSync } from "zlib";
|
||||||
|
|
||||||
|
export function CreateBlurl(Data: unknown) { // mildly unsafe
|
||||||
|
const Buf = Buffer.from(JSON.stringify(Data));
|
||||||
|
const DefBuf = deflateSync(Buf);
|
||||||
|
const Length = Buffer.alloc(4);
|
||||||
|
Length.writeIntBE(Buf.toString().length, 0, 4)
|
||||||
|
|
||||||
|
return Buffer.concat([Buffer.from("blul"), Length, DefBuf]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DeBlurl(Data: Buffer) { // i have absolutely ZERO clue whether this works so do not try it
|
||||||
|
const Buf = inflateSync(Data.subarray(4));
|
||||||
|
|
||||||
|
return Buf;
|
||||||
|
}
|
|
@ -1,3 +1,6 @@
|
||||||
|
export const ENVIRONMENT = process.env.ENVIRONMENT ?? "dev";
|
||||||
|
export const IS_DEBUG = ENVIRONMENT.toLowerCase() === "dev" || ENVIRONMENT.toLowerCase() === "stage"; // IS_DEBUG can be used to enable test endpoints, unsafe code and more.
|
||||||
|
|
||||||
export const PROJECT_NAME = process.env.PROJECT_NAME ?? "BasedServer"; // Default prefix for the logger module.
|
export const PROJECT_NAME = process.env.PROJECT_NAME ?? "BasedServer"; // Default prefix for the logger module.
|
||||||
export const BODY_SIZE_LIMIT = process.env.BODY_SIZE_LIMIT ?? "10mb"; // Doesn't accept requests with body sizes larger than this value.
|
export const BODY_SIZE_LIMIT = process.env.BODY_SIZE_LIMIT ?? "10mb"; // Doesn't accept requests with body sizes larger than this value.
|
||||||
export const SERVER_URL = process.env.SERVER_URL ?? "localhost"; // The server's URL. Not used for a lot by default.
|
export const SERVER_URL = process.env.SERVER_URL ?? "localhost"; // The server's URL. Not used for a lot by default.
|
||||||
|
@ -7,11 +10,12 @@ export const ENDPOINT_AUTHENTICATION_ENABLED = !!process.env.ENDPOINT_AUTHENTICA
|
||||||
export const _ENDPOINT_AUTHENTICATION_ENV = process.env.ENDPOINT_AUTHENTICATION;
|
export const _ENDPOINT_AUTHENTICATION_ENV = process.env.ENDPOINT_AUTHENTICATION;
|
||||||
export const ENDPOINT_AUTH_HEADER = _ENDPOINT_AUTHENTICATION_ENV?.split(":")[0]; // Header name for endpoint auth.
|
export const ENDPOINT_AUTH_HEADER = _ENDPOINT_AUTHENTICATION_ENV?.split(":")[0]; // Header name for endpoint auth.
|
||||||
export const ENDPOINT_AUTH_VALUE = _ENDPOINT_AUTHENTICATION_ENV?.split(":")[1]; // Value of the header for endpoint auth.
|
export const ENDPOINT_AUTH_VALUE = _ENDPOINT_AUTHENTICATION_ENV?.split(":")[1]; // Value of the header for endpoint auth.
|
||||||
export const USE_HTTPS = false;//Boolean(process.env.USE_HTTPS); // todo: fix this shit
|
export const USE_HTTPS = !IS_DEBUG; //false; //Boolean(process.env.USE_HTTPS); // todo: fix this shit
|
||||||
export const DASHBOARD_ROOT = `http${USE_HTTPS ? "s" : ""}://${DASHBOARD_URL}`; // A shortcut so that you don't need to type this out every time you wanna display the dashboard URL.
|
export const DASHBOARD_ROOT = `http${USE_HTTPS ? "s" : ""}://${DASHBOARD_URL}`; // A shortcut so that you don't need to type this out every time you wanna display the dashboard URL.
|
||||||
export const FULL_SERVER_ROOT = `http${USE_HTTPS ? "s" : ""}://${SERVER_URL}:${PORT}`; // A shortcut so that you don't need to type this out every time you wanna display the server URL.
|
export const FULL_SERVER_ROOT = `http${USE_HTTPS ? "s" : ""}://${SERVER_URL}:${PORT}`; // A shortcut so that you don't need to type this out every time you wanna display the server URL.
|
||||||
export const COOKIE_SIGN_KEY = process.env.COOKIE_SIGN_KEY; // Secret that will be used to sign cookies.
|
export const COOKIE_SIGN_KEY = process.env.COOKIE_SIGN_KEY; // Secret that will be used to sign cookies.
|
||||||
export const ADMIN_KEY = process.env.ADMIN_KEY; // Secret that will be required to sign into the Admin Dashboard.
|
export const ADMIN_KEY = process.env.ADMIN_KEY; // Secret that will be required to sign into the Admin Dashboard.
|
||||||
|
export const JWT_KEY = process.env.JWT_KEY; // Secret that will be required to sign JSON Web Tokens (JWTs).
|
||||||
export const ENVIRONMENT = process.env.ENVIRONMENT ?? "dev";
|
export const BOT_TOKEN = process.env.BOT_TOKEN; // Used for Discord-related stuff.
|
||||||
export const IS_DEBUG = ENVIRONMENT.toLowerCase() === "dev" || ENVIRONMENT.toLowerCase() === "stage"; // IS_DEBUG can be used to enable test endpoints, unsafe code and more.
|
export const DISCORD_CLIENT_ID = process.env.DISCORD_CLIENT_ID; // Client ID for authentication and checking what role you have on the Discord server.
|
||||||
|
export const DISCORD_CLIENT_SECRET = process.env.DISCORD_CLIENT_SECRET; // Client secret for authentication and checking what role you have on the Discord server.
|
47
Server/Source/Routes/Authentication.ts
Normal file
47
Server/Source/Routes/Authentication.ts
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import axios from "axios";
|
||||||
|
import jwt from "jsonwebtoken";
|
||||||
|
import qs from "querystring";
|
||||||
|
import { Response, Router } from "express";
|
||||||
|
import { DASHBOARD_ROOT, DISCORD_CLIENT_ID, DISCORD_CLIENT_SECRET, FULL_SERVER_ROOT, JWT_KEY } from "../Modules/Constants";
|
||||||
|
|
||||||
|
const App = Router();
|
||||||
|
|
||||||
|
// ? hacky, if you want, make it less hacky
|
||||||
|
async function QuickRevokeToken(res: Response, Token: string) {
|
||||||
|
await axios.post("https://discord.com/api/oauth2/token/revoke", qs.stringify({ token: Token, token_type_hint: "access_token" }), { auth: { username: DISCORD_CLIENT_ID!, password: DISCORD_CLIENT_SECRET! } })
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
App.get("/discord/url", (_ ,res) => res.send(`https://discord.com/api/oauth2/authorize?client_id=${qs.escape(DISCORD_CLIENT_ID!)}&response_type=code&redirect_uri=${qs.escape(`${FULL_SERVER_ROOT}/api/discord`)}&scope=identify`))
|
||||||
|
|
||||||
|
App.get("/discord", async (req, res) => {
|
||||||
|
if (!req.query.code)
|
||||||
|
return res.status(400).send("Please authorize with Discord first.");
|
||||||
|
|
||||||
|
if (!/^(\w|\d)+$/gi.test(req.query.code as string))
|
||||||
|
return res.status(400).send("Malformed code.");
|
||||||
|
|
||||||
|
const Discord = await axios.post(`https://discord.com/api/oauth2/token`, qs.stringify({ grant_type: "authorization_code", code: req.query.code as string, redirect_uri: `${FULL_SERVER_ROOT}/api/discord` }), { auth: { username: DISCORD_CLIENT_ID!, password: DISCORD_CLIENT_SECRET! } });
|
||||||
|
|
||||||
|
if (Discord.status !== 200)
|
||||||
|
return res.status(500).send("Failed to request OAuth token from Discord's services.");
|
||||||
|
|
||||||
|
if (!Discord.data.scope.includes("identify"))
|
||||||
|
return (await QuickRevokeToken(res, Discord.data.access_token)).status(400).send("Missing identify scope. Please check if your OAuth link is correctly set up!");
|
||||||
|
|
||||||
|
const UserData = await axios.get(`https://discord.com/api/users/@me`, { headers: { Authorization: `${Discord.data.token_type} ${Discord.data.access_token}` } });
|
||||||
|
if (UserData.status !== 200)
|
||||||
|
return (await QuickRevokeToken(res, Discord.data.access_token)).status(500).send("Failed to request user data from Discord's services.");
|
||||||
|
|
||||||
|
await QuickRevokeToken(res, Discord.data.access_token);
|
||||||
|
|
||||||
|
res
|
||||||
|
.cookie("Token", jwt.sign({ ID: UserData.data.id }, JWT_KEY!, { algorithm: "HS256" }))
|
||||||
|
.cookie("UserDetails", Buffer.from(JSON.stringify({ ID: UserData.data.id, Username: UserData.data.username, GlobalName: UserData.data.global_name, Avatar: `https://cdn.discordapp.com/avatars/${UserData.data.id}/${UserData.data.avatar}.webp` })).toString("base64"))
|
||||||
|
.redirect(`${DASHBOARD_ROOT}/profile`);
|
||||||
|
})
|
||||||
|
|
||||||
|
export default {
|
||||||
|
App,
|
||||||
|
DefaultAPI: "/api"
|
||||||
|
}
|
67
Server/Source/Routes/Downloads.ts
Normal file
67
Server/Source/Routes/Downloads.ts
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
import { Router } from "express";
|
||||||
|
import { existsSync, readFileSync } from "fs";
|
||||||
|
import { AvailableFestivalSongs } from "../Modules/FestivalUtil";
|
||||||
|
import { FULL_SERVER_ROOT } from "../Modules/Constants";
|
||||||
|
import { CreateBlurl } from "../Modules/BLURL";
|
||||||
|
|
||||||
|
const App = Router();
|
||||||
|
|
||||||
|
App.get("/song/download/:SongUUID/:File", (req, res) => {
|
||||||
|
const Song = AvailableFestivalSongs.find(x => x.UUID === req.params.SongUUID);
|
||||||
|
if (!Song)
|
||||||
|
return res.sendStatus(404);
|
||||||
|
|
||||||
|
const BaseURL = `${FULL_SERVER_ROOT}/song/download/${Song.UUID}/`;
|
||||||
|
switch (req.params.File.toLowerCase()) {
|
||||||
|
case "master.blurl":
|
||||||
|
case "main.blurl":
|
||||||
|
return res.set("content-type", "text/plain").send(
|
||||||
|
CreateBlurl({
|
||||||
|
playlists: [
|
||||||
|
{
|
||||||
|
type: "main",
|
||||||
|
language: "en",
|
||||||
|
url: `${BaseURL}master.blurl`,
|
||||||
|
data: readFileSync(`${Song.Directory}/Manifest.mpd`).toString().replaceAll("{BASEURL}", BaseURL)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
type: "vod",
|
||||||
|
audioonly: true
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
case "manifest":
|
||||||
|
case "manifest.mpd":
|
||||||
|
return res.set("content-type", "application/dash+xml").send(Buffer.from(readFileSync(`${Song.Directory}/Manifest.mpd`).toString().replaceAll("{BASEURL}", BaseURL)));
|
||||||
|
|
||||||
|
case "cover":
|
||||||
|
case "cover.png":
|
||||||
|
return existsSync(`${Song.Directory}/Cover.png`) ? res.set("content-type", "image/png").send(readFileSync(`${Song.Directory}/Cover.png`)) : res.sendStatus(404);
|
||||||
|
|
||||||
|
// ! we are not risking a lawsuit
|
||||||
|
//case "midi.dat": // dont forget to encrypt!
|
||||||
|
//return existsSync(`${Song.Directory}/Data.mid`) ? res.set("content-type", "application/octet-stream").send(AesEncrypt(readFileSync(`${Song.Directory}/Data.mid`))) : res.sendStatus(404);
|
||||||
|
|
||||||
|
// funny little tip: you dont actually need to encrypt midis LMFAO
|
||||||
|
case "midi":
|
||||||
|
case "midi.mid":
|
||||||
|
case "midi.midi": // forget to encrypt!
|
||||||
|
return existsSync(`${Song.Directory}/Data.mid`) ? res.set("content-type", "application/octet-stream").send(readFileSync(`${Song.Directory}/Data.mid`)) : res.sendStatus(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^[\w\-.]+$/g.test(req.params.File))
|
||||||
|
return res.status(400).send("File name failed validation.");
|
||||||
|
|
||||||
|
if (!req.params.File.endsWith(".m4s"))
|
||||||
|
return res.sendStatus(403);
|
||||||
|
|
||||||
|
if (!existsSync(`${Song.Directory}/Chunks/${req.params.File}`))
|
||||||
|
return res.sendStatus(404);
|
||||||
|
|
||||||
|
res.set("content-type", "video/mp4")
|
||||||
|
res.send(readFileSync(`${Song.Directory}/Chunks/${req.params.File}`));
|
||||||
|
})
|
||||||
|
|
||||||
|
export default {
|
||||||
|
App
|
||||||
|
}
|
|
@ -1,11 +0,0 @@
|
||||||
import { Router } from "express";
|
|
||||||
|
|
||||||
const App = Router();
|
|
||||||
|
|
||||||
App.get("/", (_, res) => res.send("This content should be served on <b>/welcome</b>!<br><a href=\"/welcome/sub\">Go to sub-page</a>"))
|
|
||||||
App.get("/sub", (_, res) => res.send("Welcome to the sub-page! This content should be served on <b>/welcome/sub</b>!<br><a href=\"/welcome\">Go back</a>"))
|
|
||||||
|
|
||||||
export default {
|
|
||||||
App,
|
|
||||||
DefaultAPI: "/welcome"
|
|
||||||
}
|
|
14
Server/Source/Routes/Library.ts
Normal file
14
Server/Source/Routes/Library.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { Router } from "express";
|
||||||
|
|
||||||
|
const App = Router();
|
||||||
|
|
||||||
|
App.get("/song/data/:InternalID", (req, res) => {
|
||||||
|
res.json({
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
export default {
|
||||||
|
App,
|
||||||
|
DefaultAPI: "/api/library"
|
||||||
|
}
|
|
@ -1,9 +0,0 @@
|
||||||
import { Router } from "express";
|
|
||||||
|
|
||||||
const App = Router();
|
|
||||||
|
|
||||||
App.get("/", (_, res) => res.send("Welcome to the root page. <br><a href=\"/welcome\">Log in</a>"))
|
|
||||||
|
|
||||||
export default {
|
|
||||||
App
|
|
||||||
}
|
|
40
Server/Source/Schemas/Song.ts
Normal file
40
Server/Source/Schemas/Song.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from "typeorm";
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class Song extends BaseEntity {
|
||||||
|
@PrimaryGeneratedColumn("uuid")
|
||||||
|
ID: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
Name: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
Year: number;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
ArtistName: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
Length: number;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
Scale: "Minor" | "Major";
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
Key: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
Album: string;
|
||||||
|
|
||||||
|
@Column({ default: "Guitar" })
|
||||||
|
GuitarStarterType: "Keytar" | "Guitar";
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
Tempo: number;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
Cover: string;
|
||||||
|
|
||||||
|
@Column({ nullable: true })
|
||||||
|
Lipsync?: string;
|
||||||
|
}
|
0
Server/Source/Schemas/User.ts
Normal file
0
Server/Source/Schemas/User.ts
Normal file
|
@ -1,6 +1,7 @@
|
||||||
import { config } from "dotenv";
|
import { config } from "dotenv";
|
||||||
config();
|
config();
|
||||||
|
|
||||||
|
import "./Handlers/Database";
|
||||||
import "./Handlers/Server";
|
import "./Handlers/Server";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
1522
Server/package-lock.json
generated
1522
Server/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -31,6 +31,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/cookie-parser": "^1.4.6",
|
"@types/cookie-parser": "^1.4.6",
|
||||||
"@types/express": "^4.17.18",
|
"@types/express": "^4.17.18",
|
||||||
|
"@types/jsonwebtoken": "^9.0.5",
|
||||||
"@types/node": "^20.6.3",
|
"@types/node": "^20.6.3",
|
||||||
"tslib": "^2.6.2",
|
"tslib": "^2.6.2",
|
||||||
"typescript": "^5.2.2"
|
"typescript": "^5.2.2"
|
||||||
|
@ -38,11 +39,14 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/cors": "^2.8.17",
|
"@types/cors": "^2.8.17",
|
||||||
"axios": "^1.6.5",
|
"axios": "^1.6.5",
|
||||||
|
"better-sqlite3": "^9.3.0",
|
||||||
"colorette": "^2.0.20",
|
"colorette": "^2.0.20",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"express": "^4.18.2"
|
"express": "^4.18.2",
|
||||||
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"typeorm": "^0.3.19"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/McMistrzYT/BasedServer#readme"
|
"homepage": "https://github.com/McMistrzYT/BasedServer#readme"
|
||||||
}
|
}
|
||||||
|
|
194
package-lock.json
generated
194
package-lock.json
generated
|
@ -8,15 +8,20 @@
|
||||||
"name": "snippets",
|
"name": "snippets",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@esbuild-plugins/node-globals-polyfill": "^0.2.3",
|
||||||
|
"@esbuild-plugins/node-modules-polyfill": "^0.2.2",
|
||||||
"@primer/react": "^36.5.0",
|
"@primer/react": "^36.5.0",
|
||||||
"axios": "^1.6.5",
|
"axios": "^1.6.5",
|
||||||
|
"buffer": "^6.0.3",
|
||||||
"deepmerge": "^4.3.1",
|
"deepmerge": "^4.3.1",
|
||||||
"node-watch": "^0.7.4",
|
"node-watch": "^0.7.4",
|
||||||
|
"querystring-es3": "^0.2.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-cookie": "^7.0.1",
|
"react-cookie": "^7.0.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-router-dom": "^6.21.3",
|
"react-router-dom": "^6.21.3",
|
||||||
"react-toastify": "^9.1.3",
|
"react-toastify": "^9.1.3",
|
||||||
|
"rollup-plugin-node-polyfills": "^0.2.1",
|
||||||
"styled-components": "^5.3.11"
|
"styled-components": "^5.3.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -434,6 +439,37 @@
|
||||||
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
|
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
|
||||||
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
|
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@esbuild-plugins/node-globals-polyfill": {
|
||||||
|
"version": "0.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.2.3.tgz",
|
||||||
|
"integrity": "sha512-r3MIryXDeXDOZh7ih1l/yE9ZLORCd5e8vWg02azWRGj5SPTuoh69A2AIyn0Z31V/kHBfZ4HgWJ+OK3GTTwLmnw==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"esbuild": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild-plugins/node-modules-polyfill": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild-plugins/node-modules-polyfill/-/node-modules-polyfill-0.2.2.tgz",
|
||||||
|
"integrity": "sha512-LXV7QsWJxRuMYvKbiznh+U1ilIop3g2TeKRzUxOG5X3YITc8JyyTa90BmLwqqv0YnX4v32CSlG+vsziZp9dMvA==",
|
||||||
|
"dependencies": {
|
||||||
|
"escape-string-regexp": "^4.0.0",
|
||||||
|
"rollup-plugin-node-polyfills": "^0.2.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"esbuild": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild-plugins/node-modules-polyfill/node_modules/escape-string-regexp": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@esbuild/aix-ppc64": {
|
"node_modules/@esbuild/aix-ppc64": {
|
||||||
"version": "0.19.11",
|
"version": "0.19.11",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.11.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.11.tgz",
|
||||||
|
@ -441,7 +477,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"aix"
|
"aix"
|
||||||
|
@ -457,7 +492,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"android"
|
"android"
|
||||||
|
@ -473,7 +507,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"android"
|
"android"
|
||||||
|
@ -489,7 +522,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"android"
|
"android"
|
||||||
|
@ -505,7 +537,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
|
@ -521,7 +552,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
|
@ -537,7 +567,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"freebsd"
|
"freebsd"
|
||||||
|
@ -553,7 +582,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"freebsd"
|
"freebsd"
|
||||||
|
@ -569,7 +597,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
|
@ -585,7 +612,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
|
@ -601,7 +627,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
|
@ -617,7 +642,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
|
@ -633,7 +657,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"mips64el"
|
"mips64el"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
|
@ -649,7 +672,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
|
@ -665,7 +687,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
|
@ -681,7 +702,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
|
@ -697,7 +717,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
|
@ -713,7 +732,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"netbsd"
|
"netbsd"
|
||||||
|
@ -729,7 +747,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"openbsd"
|
"openbsd"
|
||||||
|
@ -745,7 +762,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"sunos"
|
"sunos"
|
||||||
|
@ -761,7 +777,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
|
@ -777,7 +792,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
|
@ -793,7 +807,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
|
@ -1545,6 +1558,17 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz",
|
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz",
|
||||||
"integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
|
"integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "20.11.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.5.tgz",
|
||||||
|
"integrity": "sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~5.26.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/prop-types": {
|
"node_modules/@types/prop-types": {
|
||||||
"version": "15.7.11",
|
"version": "15.7.11",
|
||||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
|
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
|
||||||
|
@ -1959,6 +1983,25 @@
|
||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/base64-js": {
|
||||||
|
"version": "1.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
|
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"node_modules/binary-extensions": {
|
"node_modules/binary-extensions": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||||
|
@ -2020,6 +2063,29 @@
|
||||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/buffer": {
|
||||||
|
"version": "6.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||||
|
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "^1.3.1",
|
||||||
|
"ieee754": "^1.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/callsites": {
|
"node_modules/callsites": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||||
|
@ -2318,7 +2384,6 @@
|
||||||
"version": "0.19.11",
|
"version": "0.19.11",
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.11.tgz",
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.11.tgz",
|
||||||
"integrity": "sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==",
|
"integrity": "sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==",
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"esbuild": "bin/esbuild"
|
"esbuild": "bin/esbuild"
|
||||||
|
@ -2641,6 +2706,11 @@
|
||||||
"node": ">=4.0"
|
"node": ">=4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/estree-walker": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w=="
|
||||||
|
},
|
||||||
"node_modules/esutils": {
|
"node_modules/esutils": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
|
||||||
|
@ -2966,6 +3036,25 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/ieee754": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"node_modules/ignore": {
|
"node_modules/ignore": {
|
||||||
"version": "5.3.0",
|
"version": "5.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz",
|
||||||
|
@ -3259,6 +3348,14 @@
|
||||||
"yallist": "^3.0.2"
|
"yallist": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/magic-string": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"sourcemap-codec": "^1.4.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mdast-util-definitions": {
|
"node_modules/mdast-util-definitions": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz",
|
||||||
|
@ -4071,6 +4168,14 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/querystring-es3": {
|
||||||
|
"version": "0.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
|
||||||
|
"integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"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",
|
||||||
|
@ -4330,6 +4435,33 @@
|
||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/rollup-plugin-inject": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup-plugin-inject/-/rollup-plugin-inject-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==",
|
||||||
|
"deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject.",
|
||||||
|
"dependencies": {
|
||||||
|
"estree-walker": "^0.6.1",
|
||||||
|
"magic-string": "^0.25.3",
|
||||||
|
"rollup-pluginutils": "^2.8.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rollup-plugin-node-polyfills": {
|
||||||
|
"version": "0.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup-plugin-node-polyfills/-/rollup-plugin-node-polyfills-0.2.1.tgz",
|
||||||
|
"integrity": "sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA==",
|
||||||
|
"dependencies": {
|
||||||
|
"rollup-plugin-inject": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rollup-pluginutils": {
|
||||||
|
"version": "2.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz",
|
||||||
|
"integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"estree-walker": "^0.6.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/run-parallel": {
|
"node_modules/run-parallel": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||||
|
@ -4466,6 +4598,12 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sourcemap-codec": {
|
||||||
|
"version": "1.4.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
|
||||||
|
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
|
||||||
|
"deprecated": "Please use @jridgewell/sourcemap-codec instead"
|
||||||
|
},
|
||||||
"node_modules/space-separated-tokens": {
|
"node_modules/space-separated-tokens": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
|
||||||
|
@ -4665,6 +4803,14 @@
|
||||||
"node": ">=14.17"
|
"node": ">=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "5.26.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||||
|
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
"node_modules/unified": {
|
"node_modules/unified": {
|
||||||
"version": "10.1.2",
|
"version": "10.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz",
|
||||||
|
|
|
@ -12,15 +12,20 @@
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@esbuild-plugins/node-globals-polyfill": "^0.2.3",
|
||||||
|
"@esbuild-plugins/node-modules-polyfill": "^0.2.2",
|
||||||
"@primer/react": "^36.5.0",
|
"@primer/react": "^36.5.0",
|
||||||
"axios": "^1.6.5",
|
"axios": "^1.6.5",
|
||||||
|
"buffer": "^6.0.3",
|
||||||
"deepmerge": "^4.3.1",
|
"deepmerge": "^4.3.1",
|
||||||
"node-watch": "^0.7.4",
|
"node-watch": "^0.7.4",
|
||||||
|
"querystring-es3": "^0.2.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-cookie": "^7.0.1",
|
"react-cookie": "^7.0.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-router-dom": "^6.21.3",
|
"react-router-dom": "^6.21.3",
|
||||||
"react-toastify": "^9.1.3",
|
"react-toastify": "^9.1.3",
|
||||||
|
"rollup-plugin-node-polyfills": "^0.2.1",
|
||||||
"styled-components": "^5.3.11"
|
"styled-components": "^5.3.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
34
src/App.tsx
34
src/App.tsx
|
@ -1,21 +1,27 @@
|
||||||
import { ToastContainer } from "react-toastify";
|
import { ToastContainer } from "react-toastify";
|
||||||
import { BaseStyles, ThemeProvider, theme } from "@primer/react";
|
import { BaseStyles, ThemeProvider, theme } from "@primer/react";
|
||||||
import { SiteHeader } from "./components/SiteHeader";
|
import { SiteHeader } from "./components/SiteHeader";
|
||||||
|
import { VerifyAdmin } from "./components/VerifyAdmin";
|
||||||
import { BrowserRouter, Route, Routes } from "react-router-dom";
|
import { BrowserRouter, Route, Routes } from "react-router-dom";
|
||||||
import { CookiesProvider } from "react-cookie";
|
import { CookiesProvider } from "react-cookie";
|
||||||
import { Home } from "./routes/Home";
|
import { Home } from "./routes/Home";
|
||||||
import { AdminTrackList } from "./routes/AdminTrackList";
|
import { AdminTrackList } from "./routes/AdminTrackList";
|
||||||
import { AdminHome } from "./routes/AdminHome";
|
import { AdminHome } from "./routes/AdminHome";
|
||||||
import { AdminLogin } from "./routes/AdminLogin";
|
import { AdminLogin } from "./routes/AdminLogin";
|
||||||
import { VerifyAdmin } from "./components/VerifyAdmin";
|
import { Download } from "./routes/Download";
|
||||||
|
import { Profile } from "./routes/Profile";
|
||||||
|
import { SiteContext, SiteState } from "./utils/State";
|
||||||
import merge from "deepmerge";
|
import merge from "deepmerge";
|
||||||
|
|
||||||
import "react-toastify/dist/ReactToastify.css";
|
import "react-toastify/dist/ReactToastify.css";
|
||||||
import "./css/index.css";
|
import "./css/index.css";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
const DefaultTheme = merge(theme, {}); // we'll use this!! eventually!!!
|
const DefaultTheme = merge(theme, {}); // we'll use this!! eventually!!!
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
const [reactState, setReactState] = useState<SiteState>({} as SiteState);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider colorMode="dark" theme={DefaultTheme}>
|
<ThemeProvider colorMode="dark" theme={DefaultTheme}>
|
||||||
<BaseStyles>
|
<BaseStyles>
|
||||||
|
@ -23,18 +29,22 @@ function App() {
|
||||||
<CookiesProvider />
|
<CookiesProvider />
|
||||||
<ToastContainer theme="dark" position="bottom-right" draggable={false} pauseOnHover={false} pauseOnFocusLoss={false} />
|
<ToastContainer theme="dark" position="bottom-right" draggable={false} pauseOnHover={false} pauseOnFocusLoss={false} />
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<SiteHeader />
|
<SiteContext.Provider value={{ state: reactState, setState: setReactState }}>
|
||||||
<div className="content">
|
<SiteHeader />
|
||||||
<Routes>
|
<div className="content">
|
||||||
{/* User-accessible routes */}
|
<Routes>
|
||||||
<Route path="/" element={<Home />} />
|
{/* User-accessible routes */}
|
||||||
|
<Route path="/" element={<Home />} />
|
||||||
|
<Route path="/download" element={<Download />} />
|
||||||
|
<Route path="/profile" element={<Profile />} />
|
||||||
|
|
||||||
{/* Admin routes */}
|
{/* Admin routes */}
|
||||||
<Route path="/admin/login" element={<AdminLogin />} />
|
<Route path="/admin" element={<VerifyAdmin><AdminHome /></VerifyAdmin>} />
|
||||||
<Route path="/admin" element={<VerifyAdmin><AdminHome /></VerifyAdmin>} />
|
<Route path="/admin/login" element={<AdminLogin />} /> {/* this is the only publically available admin endpoint */}
|
||||||
<Route path="/admin/tracks" element={<VerifyAdmin><AdminTrackList /></VerifyAdmin>} />
|
<Route path="/admin/tracks" element={<VerifyAdmin><AdminTrackList /></VerifyAdmin>} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
|
</SiteContext.Provider>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</div>
|
</div>
|
||||||
</BaseStyles>
|
</BaseStyles>
|
||||||
|
|
|
@ -1,26 +1,59 @@
|
||||||
import { Header } from "@primer/react";
|
import { Avatar, Box, Header } from "@primer/react";
|
||||||
|
import { SignInIcon } from "@primer/octicons-react"
|
||||||
import { useCookies } from "react-cookie";
|
import { useCookies } from "react-cookie";
|
||||||
import Favicon from "../assets/favicon.webp";
|
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { useContext, useEffect, useState } from "react";
|
||||||
|
import { SiteContext, UserDetailInterface } from "../utils/State";
|
||||||
|
import { Buffer } from "buffer/";
|
||||||
|
import Favicon from "../assets/favicon.webp";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
export function SiteHeader() {
|
export function SiteHeader() {
|
||||||
|
const [discordUrl, setDiscordUrl] = useState("");
|
||||||
|
const {state, setState} = useContext(SiteContext);
|
||||||
const [cookies] = useCookies();
|
const [cookies] = useCookies();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
const Data = await axios.get("/api/discord/url");
|
||||||
|
if (Data.status === 200)
|
||||||
|
setDiscordUrl(Data.data);
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!cookies["UserDetails"])
|
||||||
|
return;
|
||||||
|
|
||||||
|
const Details: UserDetailInterface = JSON.parse(decodeURI(Buffer.from(cookies["UserDetails"], "base64").toString()));
|
||||||
|
setState({
|
||||||
|
...state,
|
||||||
|
UserDetails: Details
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(Details);
|
||||||
|
}, [cookies["UserDetails"]])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Header>
|
<Header>
|
||||||
<Header.Item sx={{ cursor: "pointer" }} onClick={() => navigate("/")}>
|
<Header.Item sx={{ cursor: "pointer" }} onClick={() => navigate("/")}>
|
||||||
<img src={Favicon} style={{ width: 32, height: "auto", paddingRight: 5 }} />
|
<img src={Favicon} style={{ width: 32, height: "auto", paddingRight: 5 }} />
|
||||||
<b>Partypack</b>
|
<b>Partypack</b>
|
||||||
</Header.Item>
|
</Header.Item>
|
||||||
<Header.Item sx={{ cursor: "pointer" }}>Daily Rotation</Header.Item>
|
<Header.Item full sx={{ cursor: "pointer" }}>Daily Rotation</Header.Item>
|
||||||
<Header.Item sx={{ cursor: "pointer" }}>Leaderboards</Header.Item>
|
<Header.Item full sx={{ cursor: "pointer" }}>Leaderboards</Header.Item>
|
||||||
<Header.Item sx={{ cursor: "pointer" }}>Tracks</Header.Item>
|
<Header.Item full sx={{ cursor: "pointer" }}>Tracks</Header.Item>
|
||||||
<Header.Item sx={{ cursor: "pointer" }}>Tutorials</Header.Item>
|
<Header.Item full sx={{ cursor: "pointer" }}>Tutorials</Header.Item>
|
||||||
<Header.Item sx={{ cursor: "pointer" }}>FAQ</Header.Item>
|
<Header.Item full sx={{ cursor: "pointer" }}>FAQ</Header.Item>
|
||||||
<Header.Item sx={{ cursor: "pointer" }} onClick={() => window.open("https://discord.gg/KaxknAbqDS")}>Discord</Header.Item>
|
<Header.Item full sx={{ cursor: "pointer" }} onClick={() => window.open("https://discord.gg/KaxknAbqDS")}>Discord</Header.Item>
|
||||||
<Header.Item sx={{ cursor: "pointer", color: "accent.emphasis" }}>Download</Header.Item>
|
<Header.Item full sx={{ cursor: "pointer", color: "accent.emphasis" }}>Download</Header.Item>
|
||||||
{ cookies["AdminKey"] ? <Header.Item onClick={() => navigate("/admin")} sx={{ cursor: "pointer", color: "danger.emphasis" }}>Admin</Header.Item> : <></> }
|
{ cookies["AdminKey"] ? <Header.Item full onClick={() => navigate("/admin")} sx={{ cursor: "pointer", color: "danger.emphasis" }}>Admin</Header.Item> : <></> }
|
||||||
|
{
|
||||||
|
cookies["Token"] && state.UserDetails ?
|
||||||
|
<Header.Item sx={{ mr: 0, cursor: "pointer" }} onClick={() => navigate("/profile")}><Avatar src={state.UserDetails.Avatar} size={25} alt={`${state.UserDetails.GlobalName} (@${state.UserDetails.Username})`}/></Header.Item> :
|
||||||
|
<Box sx={{ cursor: "pointer" }} onClick={() => discordUrl ? window.location.assign(discordUrl) : console.log("no discord url :(")}><SignInIcon size={16} /></Box>
|
||||||
|
}
|
||||||
</Header>
|
</Header>
|
||||||
);
|
);
|
||||||
}
|
}
|
16
src/components/Song.tsx
Normal file
16
src/components/Song.tsx
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { Box, Text } from "@primer/react";
|
||||||
|
import { Divider } from "@primer/react/lib-esm/ActionList/Divider";
|
||||||
|
|
||||||
|
export function Song({ children }: { children: JSX.Element[] }) {
|
||||||
|
return (
|
||||||
|
<Box sx={{ overflow: "hidden", minWidth: 50, maxWidth: 200, padding: 2, borderRadius: 10, border: "solid", borderColor: "border.default" }}>
|
||||||
|
<img src="https://cdn2.unrealengine.com/d5yc9zpe97um68u6-512x512-a7f5fc0d3c2f.png" style={{ width: "100%", borderRadius: 10 }} />
|
||||||
|
<center>
|
||||||
|
<Text sx={{ fontSize: 1, display: "block", textOverflow: "ellipsis", overflow: "hidden", whiteSpace: "nowrap" }}>Someone</Text>
|
||||||
|
<Text sx={{ display: "block", textOverflow: "ellipsis", overflow: "hidden", whiteSpace: "nowrap" }}>Someone's Song Someone's Song</Text>
|
||||||
|
<Divider />
|
||||||
|
</center>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,3 +1,8 @@
|
||||||
.content {
|
.content {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.songCategory {
|
||||||
|
display: inline-flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
|
@ -1,12 +1,14 @@
|
||||||
import { Box, Text } from "@primer/react";
|
import { Box, Button, Text } from "@primer/react";
|
||||||
import { PageHeader } from "@primer/react/drafts";
|
import { PageHeader } from "@primer/react/drafts";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { VerifyAdminKey } from "../utils/AdminUtil";
|
import { VerifyAdminKey } from "../utils/AdminUtil";
|
||||||
import { useCookies } from "react-cookie";
|
import { useCookies } from "react-cookie";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
export function AdminHome() {
|
export function AdminHome() {
|
||||||
const [keyValid, setKeyValid] = useState(false);
|
const [keyValid, setKeyValid] = useState(false);
|
||||||
const [cookies] = useCookies();
|
const [cookies, , removeCookie] = useCookies(); // how in the fuck is this valid ts syntax???????????
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async() => setKeyValid((await VerifyAdminKey(cookies["AdminKey"])).Success))();
|
(async() => setKeyValid((await VerifyAdminKey(cookies["AdminKey"])).Success))();
|
||||||
|
@ -20,6 +22,7 @@ export function AdminHome() {
|
||||||
</PageHeader.TitleArea>
|
</PageHeader.TitleArea>
|
||||||
<PageHeader.Description>
|
<PageHeader.Description>
|
||||||
Your admin key is { keyValid ? <Text sx={{ color: "accent.emphasis" }}>VALID</Text> : <Text sx={{ color: "danger.emphasis" }}>INVALID</Text> }
|
Your admin key is { keyValid ? <Text sx={{ color: "accent.emphasis" }}>VALID</Text> : <Text sx={{ color: "danger.emphasis" }}>INVALID</Text> }
|
||||||
|
<Button variant="danger" size="small" onClick={() => {removeCookie("AdminKey"); navigate("/admin/login")}}>Log out</Button>
|
||||||
</PageHeader.Description>
|
</PageHeader.Description>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Box, Button, Text, TextInput } from "@primer/react";
|
import { Box, Button, Text, TextInput } from "@primer/react";
|
||||||
import { useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { VerifyAdminKey } from "../utils/AdminUtil";
|
import { VerifyAdminKey } from "../utils/AdminUtil";
|
||||||
import { useCookies } from "react-cookie";
|
import { useCookies } from "react-cookie";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
@ -7,10 +7,16 @@ import { useNavigate } from "react-router-dom";
|
||||||
export function AdminLogin() {
|
export function AdminLogin() {
|
||||||
const [errorMessage, setErrorMessage] = useState("");
|
const [errorMessage, setErrorMessage] = useState("");
|
||||||
const [success, setSuccess] = useState(false);
|
const [success, setSuccess] = useState(false);
|
||||||
const [_, setCookie, removeCookie] = useCookies();
|
const [cookies, setCookie, removeCookie] = useCookies();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const KeyInputRef = useRef<HTMLInputElement>(null);
|
const KeyInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (cookies["AdminKey"])
|
||||||
|
navigate("/admin");
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<center>
|
<center>
|
||||||
|
|
6
src/routes/Download.tsx
Normal file
6
src/routes/Download.tsx
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
export function Download() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
59
src/routes/Profile.tsx
Normal file
59
src/routes/Profile.tsx
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import { Avatar, Box, Button, Heading, Text } from "@primer/react"
|
||||||
|
import { Divider } from "@primer/react/lib-esm/ActionList/Divider";
|
||||||
|
import { PageHeader } from "@primer/react/drafts";
|
||||||
|
import { useContext } from "react";
|
||||||
|
import { SiteContext } from "../utils/State";
|
||||||
|
import { useCookies } from "react-cookie";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { Song } from "../components/Song";
|
||||||
|
|
||||||
|
export function Profile() {
|
||||||
|
const { state, setState } = useContext(SiteContext);
|
||||||
|
const [, , removeCookie] = useCookies();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{
|
||||||
|
state.UserDetails ?
|
||||||
|
<Box sx={{ marginLeft: "15%", marginRight: "15%" }}>
|
||||||
|
<PageHeader>
|
||||||
|
<PageHeader.TitleArea variant="large">
|
||||||
|
<PageHeader.LeadingVisual>
|
||||||
|
<Avatar src={state.UserDetails.Avatar} size={32} alt={`${state.UserDetails.GlobalName} (@${state.UserDetails.Username})`} />
|
||||||
|
</PageHeader.LeadingVisual>
|
||||||
|
<PageHeader.Title>
|
||||||
|
{state.UserDetails.GlobalName} (@{state.UserDetails.Username})
|
||||||
|
</PageHeader.Title>
|
||||||
|
<PageHeader.Actions>
|
||||||
|
<Button size="large" variant="danger" onClick={() => { removeCookie("UserDetails"); removeCookie("Token"); setState({ ...state, UserDetails: null }); navigate("/"); }}>Log out</Button>
|
||||||
|
</PageHeader.Actions>
|
||||||
|
</PageHeader.TitleArea>
|
||||||
|
</PageHeader>
|
||||||
|
<Divider />
|
||||||
|
<Heading sx={{ marginBottom: 2 }}>Active Songs</Heading>
|
||||||
|
<Box className="songCategory">
|
||||||
|
<Song>
|
||||||
|
<center>Overriding: <Text sx={{ display: "block", fontWeight: "700", textOverflow: "ellipsis", overflow: "hidden", whiteSpace: "nowrap" }}>Party Rock Anthem</Text></center>
|
||||||
|
<Button sx={{ width: "100%", marginTop: 2 }} variant="danger">Remove from Active</Button>
|
||||||
|
</Song>
|
||||||
|
</Box>
|
||||||
|
<Heading sx={{ marginTop: 2, marginBottom: 2 }}>My Bookmarks</Heading>
|
||||||
|
<Box className="songCategory">
|
||||||
|
<Song>
|
||||||
|
<Button sx={{ width: "100%", marginBottom: 1 }} variant="primary">Add to Active</Button>
|
||||||
|
<Button sx={{ width: "100%" }} variant="danger">Remove from Bookmarks</Button>
|
||||||
|
</Song>
|
||||||
|
<Song>
|
||||||
|
<Button sx={{ width: "100%", marginBottom: 1 }} disabled>Add to Active</Button>
|
||||||
|
<Button sx={{ width: "100%" }} variant="danger">Remove from Bookmarks</Button>
|
||||||
|
</Song>
|
||||||
|
</Box>
|
||||||
|
</Box> :
|
||||||
|
<>
|
||||||
|
<Text>You are not logged in.<br />Log in using the button in the top right.</Text>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
15
src/utils/State.ts
Normal file
15
src/utils/State.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// massive shoutout to not-nullptr for the site state code from our old project
|
||||||
|
import { createContext } from "react";
|
||||||
|
|
||||||
|
export const SiteContext = createContext<IContext>({} as IContext);
|
||||||
|
|
||||||
|
export interface UserDetailInterface { ID: string, Username: string, GlobalName: string, Avatar: string }
|
||||||
|
|
||||||
|
export interface SiteState {
|
||||||
|
UserDetails: UserDetailInterface | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IContext {
|
||||||
|
state: SiteState;
|
||||||
|
setState: (newState: SiteState) => void;
|
||||||
|
}
|
|
@ -1,7 +1,19 @@
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from "vite";
|
||||||
import react from '@vitejs/plugin-react'
|
import react from "@vitejs/plugin-react";
|
||||||
|
import { NodeGlobalsPolyfillPlugin } from "@esbuild-plugins/node-globals-polyfill";
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
resolve: {
|
||||||
})
|
alias: {
|
||||||
|
querystring: "querystring-es3",
|
||||||
|
Buffer: "buffer/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
react(),
|
||||||
|
NodeGlobalsPolyfillPlugin({
|
||||||
|
buffer: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue
Block a user