I LOVE GD COLOGNE
This commit is contained in:
parent
677281cbf3
commit
0588183828
7
.env
Normal file
7
.env
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#
|
||||||
|
# IMPORTANT!
|
||||||
|
# COPY THIS FILE AND NAME IT ".env.local" TO CREATE THE NON-GITHUB TRACKED VERSION!
|
||||||
|
# CHANGE ALL THE VALUES ACCORDINGLY AND SAVE!
|
||||||
|
#
|
||||||
|
|
||||||
|
VITE_SERVER_ROOT_URL=https://partypack.mcthe.dev/
|
7
.env.development
Normal file
7
.env.development
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#
|
||||||
|
# IMPORTANT!
|
||||||
|
# COPY THIS FILE AND NAME IT ".env.development.local" TO CREATE THE NON-GITHUB TRACKED VERSION!
|
||||||
|
# CHANGE ALL THE VALUES ACCORDINGLY AND SAVE!
|
||||||
|
#
|
||||||
|
|
||||||
|
VITE_SERVER_ROOT_URL=http://localhost:6677/
|
7
.env.production
Normal file
7
.env.production
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#
|
||||||
|
# IMPORTANT!
|
||||||
|
# COPY THIS FILE AND NAME IT ".env.production.local" TO CREATE THE NON-GITHUB TRACKED VERSION!
|
||||||
|
# CHANGE ALL THE VALUES ACCORDINGLY AND SAVE!
|
||||||
|
#
|
||||||
|
|
||||||
|
VITE_SERVER_ROOT_URL=https://partypack.mcthe.dev/
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -11,6 +11,7 @@ node_modules
|
||||||
dist
|
dist
|
||||||
dist-ssr
|
dist-ssr
|
||||||
*.local
|
*.local
|
||||||
|
.env.staging
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
@ -24,3 +25,5 @@ dist-ssr
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
*.private.*
|
*.private.*
|
||||||
|
|
||||||
|
Out/
|
2
Server/.gitignore
vendored
2
Server/.gitignore
vendored
|
@ -74,6 +74,8 @@ web_modules/
|
||||||
|
|
||||||
# dotenv environment variable files
|
# dotenv environment variable files
|
||||||
.env
|
.env
|
||||||
|
.env.staging
|
||||||
|
.env.prod
|
||||||
.env.development.local
|
.env.development.local
|
||||||
.env.test.local
|
.env.test.local
|
||||||
.env.production.local
|
.env.production.local
|
||||||
|
|
|
@ -1,13 +1,22 @@
|
||||||
import { DataSource } from "typeorm";
|
import { DataSource } from "typeorm";
|
||||||
import { ENVIRONMENT } from "../Modules/Constants";
|
import { ENVIRONMENT } from "../Modules/Constants";
|
||||||
import { join } from "path";
|
import { Song } from "../Schemas/Song";
|
||||||
|
import { ForcedCategory } from "../Schemas/ForcedCategory";
|
||||||
|
import { User } from "../Schemas/User";
|
||||||
|
import { Rating } from "../Schemas/Rating";
|
||||||
|
|
||||||
export const DBSource = new DataSource({
|
export const DBSource = new DataSource({
|
||||||
type: "better-sqlite3",
|
type: "better-sqlite3",
|
||||||
database: `Partypack${ENVIRONMENT !== "prod" ? `-${ENVIRONMENT}` : ""}.db`,
|
database: `Partypack${ENVIRONMENT !== "prod" ? `-${ENVIRONMENT}` : ""}.db`,
|
||||||
synchronize: true,
|
synchronize: true,
|
||||||
logging: false,
|
logging: false,
|
||||||
entities: [join(__dirname, "..", "Schemas") + "\\*{.js,.ts}"],
|
entities: [
|
||||||
|
Song,
|
||||||
|
ForcedCategory,
|
||||||
|
User,
|
||||||
|
Rating
|
||||||
|
/*join(__dirname, "..", "Schemas") + "\\*{.js,.ts}"*/ // does not work in prod
|
||||||
|
],
|
||||||
subscribers: [],
|
subscribers: [],
|
||||||
migrations: [],
|
migrations: [],
|
||||||
enableWAL: true
|
enableWAL: true
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import e from "express"
|
import e from "express"
|
||||||
import fs from "fs";
|
import fs, { existsSync, mkdirSync } from "fs";
|
||||||
import { BODY_SIZE_LIMIT, COOKIE_SIGN_KEY, DASHBOARD_ROOT, ENDPOINT_AUTHENTICATION_ENABLED, ENDPOINT_AUTH_HEADER, ENDPOINT_AUTH_VALUE, IS_DEBUG, PORT, PROJECT_NAME, SERVER_URL } from "../Modules/Constants";
|
import { BODY_SIZE_LIMIT, COOKIE_SIGN_KEY, DASHBOARD_ROOT, ENDPOINT_AUTHENTICATION_ENABLED, ENDPOINT_AUTH_HEADER, ENDPOINT_AUTH_VALUE, IS_DEBUG, PORT, PROJECT_NAME, SERVER_URL } from "../Modules/Constants";
|
||||||
import { Debug, Msg, Warn } from "../Modules/Logger";
|
import { Debug, Msg, Warn } from "../Modules/Logger";
|
||||||
import { italic, magenta, red, yellow } from "colorette";
|
import { italic, magenta, red, yellow } from "colorette";
|
||||||
|
@ -19,6 +19,11 @@ export const App = e()
|
||||||
.use(e.urlencoded({ limit: BODY_SIZE_LIMIT, extended: false }));
|
.use(e.urlencoded({ limit: BODY_SIZE_LIMIT, extended: false }));
|
||||||
|
|
||||||
async function Initialize() {
|
async function Initialize() {
|
||||||
|
if (!existsSync("./Saved") || !existsSync("./Saved/Songs"))
|
||||||
|
mkdirSync("./Saved/Songs", { recursive: true });
|
||||||
|
|
||||||
|
Debug(`The CWD is ${magenta(process.cwd())}.`);
|
||||||
|
|
||||||
const Files = fs
|
const Files = fs
|
||||||
.readdirSync(path.join(".", Symbol.for("ts-node.register.instance") in process ? "Source" : "bin", "Routes"))
|
.readdirSync(path.join(".", Symbol.for("ts-node.register.instance") in process ? "Source" : "bin", "Routes"))
|
||||||
.filter((F) => F.endsWith(".js") || F.endsWith(".ts"));
|
.filter((F) => F.endsWith(".js") || F.endsWith(".ts"));
|
||||||
|
@ -43,11 +48,18 @@ async function Initialize() {
|
||||||
Msg(`Loaded route ${italic(File)}!`);
|
Msg(`Loaded route ${italic(File)}!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
App.use((_, res) => res.status(404).json({ errorMessage: "Not Found" }));
|
if (existsSync("./dist")) {
|
||||||
|
Warn(`Detected ${yellow("dist")} folder! Using as static.`);
|
||||||
|
App.use("/", e.static("dist"));
|
||||||
|
App.use("/assets", e.static("dist/assets"));
|
||||||
|
}
|
||||||
|
|
||||||
App.use((err, _, res, __) => {
|
App.use("/api/*", (_, res) => res.status(404).json({ errorMessage: "Not Found" }));
|
||||||
|
App.use("*", (_, res) => res.sendFile(path.join(process.cwd(), "dist", "index.html")));
|
||||||
|
|
||||||
|
App.use((err, req, res, _) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
res.status(500).json({ errorMessage: IS_DEBUG ? err : "Oops! Something broke on our end. Sorry!" });
|
res.status(500).json({ errorMessage: IS_DEBUG ? err.message : "Oops! Something broke on our end. Sorry!" });
|
||||||
})
|
})
|
||||||
|
|
||||||
App.listen(PORT, () => Msg(`${magenta(PROJECT_NAME)} now up on port ${magenta(PORT)} ${(IS_DEBUG ? red("(debug environment)") : "")}`));
|
App.listen(PORT, () => Msg(`${magenta(PROJECT_NAME)} now up on port ${magenta(PORT)} ${(IS_DEBUG ? red("(debug environment)") : "")}`));
|
||||||
|
|
|
@ -10,9 +10,9 @@ 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 = !IS_DEBUG; //false; //Boolean(process.env.USE_HTTPS); // todo: fix this shit
|
export const USE_HTTPS = process.env.USE_HTTPS?.toLowerCase() === "true";
|
||||||
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}${!USE_HTTPS ? `:${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 JWT_KEY = process.env.JWT_KEY; // Secret that will be required to sign JSON Web Tokens (JWTs).
|
||||||
|
|
|
@ -121,37 +121,47 @@ ValidateBody(j.array().items(j.object({
|
||||||
Songs: j.array().items(j.string().uuid()).unique().min(1).max(20).required(),
|
Songs: j.array().items(j.string().uuid()).unique().min(1).max(20).required(),
|
||||||
Priority: j.number().min(-50000).max(50000).required(),
|
Priority: j.number().min(-50000).max(50000).required(),
|
||||||
Header: j.string().min(3).max(125).required(),
|
Header: j.string().min(3).max(125).required(),
|
||||||
Action: j.string().valid("CREATE", "UPDATE", "DELETE").required()
|
ShouldDelete: j.boolean().required()
|
||||||
})).max(100)),
|
})).max(15)),
|
||||||
async (req, res) => {
|
async (req, res) => {
|
||||||
const b = req.body as { ID: string, Songs: string[], Priority: number, Header: string, Action: "CREATE" | "UPDATE" | "DELETE" }[];
|
const b = req.body as { ID: string, Songs: string[], Priority: number, Header: string, ShouldDelete: boolean }[];
|
||||||
const Failures: { Regarding: string, Message: string }[] = [];
|
const Failures: { Regarding: string, Message: string }[] = [];
|
||||||
const Successes: { Regarding: string, Message: string }[] = [];
|
const Successes: { Regarding: string, Message: string }[] = [];
|
||||||
|
|
||||||
for (const Entry of b) {
|
for (const Entry of b) {
|
||||||
switch (Entry.Action) {
|
let Category = await ForcedCategory.findOne({ where: { ID: Entry.ID } });
|
||||||
case "CREATE":
|
if (Entry.ShouldDelete) { // DELETION
|
||||||
|
if (!Category) {
|
||||||
|
Failures.push({ Regarding: Entry.ID, Message: "Cannot delete non-existent category." });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Category.remove();
|
||||||
|
Successes.push({ Regarding: Entry.ID, Message: "Successfully deleted category." });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Category) // CREATION
|
||||||
|
Category = await ForcedCategory.create({
|
||||||
|
Header: Entry.Header,
|
||||||
|
Activated: true,
|
||||||
|
Priority: Entry.Priority,
|
||||||
|
Songs: []
|
||||||
|
});
|
||||||
|
|
||||||
|
// MODIFICATION
|
||||||
const Songs = await Promise.all(Entry.Songs.map(x => Song.findOne({ where: { ID: x } })));
|
const Songs = await Promise.all(Entry.Songs.map(x => Song.findOne({ where: { ID: x } })));
|
||||||
if (Songs.includes(null)) {
|
if (Songs.includes(null)) {
|
||||||
Failures.push({ Regarding: Entry.ID, Message: `Creation request for custom category "${Entry.Header}" tried to request a non-existing song.` });
|
Failures.push({ Regarding: Entry.ID, Message: `Cannot modify "${Entry.ID}" songs as it includes a non-existent song` });
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "DELETE":
|
|
||||||
const DBEntry = await ForcedCategory.findOne({ where: { ID: Entry.ID } });
|
|
||||||
if (!DBEntry) {
|
|
||||||
Failures.push({ Regarding: Entry.ID, Message: `Custom category "${Entry.ID}" doesn't exist.` });
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
await DBEntry.remove();
|
Category.Header = Entry.Header;
|
||||||
Successes.push({ Regarding: Entry.ID, Message: `Successfully removed "${Entry.ID}" from the database.` });
|
Category.Priority = Entry.Priority;
|
||||||
break;
|
Category.Songs = Songs as Song[];
|
||||||
|
Category.save();
|
||||||
|
|
||||||
case "UPDATE":
|
Successes.push({ Regarding: Entry.ID, Message: `Successfully created/modified category "${Category.ID}".` });
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(Failures.length > Successes.length ? 400 : 200).json({
|
res.status(Failures.length > Successes.length ? 400 : 200).json({
|
||||||
|
@ -162,5 +172,5 @@ async (req, res) => {
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
App,
|
App,
|
||||||
DefaultAPI: "/admin/api"
|
DefaultAPI: "/api/admin"
|
||||||
}
|
}
|
|
@ -15,7 +15,7 @@ App.get("/", async (req, res) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json([
|
res.json([
|
||||||
...ForcedCategories.map(x => { return { ...x, Custom: true }; }),
|
...ForcedCategories.map(x => { return { ...x, Custom: true, Songs: x.Songs.map(y => y.Package()) }; }),
|
||||||
New
|
New
|
||||||
].sort((a, b) => a.Priority - b.Priority))
|
].sort((a, b) => a.Priority - b.Priority))
|
||||||
});
|
});
|
||||||
|
|
|
@ -63,10 +63,10 @@ App.get("/song/download/:InternalID/:File", async (req, res) => {
|
||||||
res.send(readFileSync(`${SongData.Directory}/Chunks/${req.params.File}`));
|
res.send(readFileSync(`${SongData.Directory}/Chunks/${req.params.File}`));
|
||||||
});
|
});
|
||||||
|
|
||||||
App.get("/:InternalID", async (req, res) => {
|
App.get("/:InternalID", async (req, res, next) => {
|
||||||
const SongData = await Song.findOne({ where: { ID: req.params.InternalID } });
|
const SongData = await Song.findOne({ where: { ID: req.params.InternalID } });
|
||||||
if (!SongData)
|
if (!SongData)
|
||||||
return res.status(404).json({ errorMessage: "Song not found." });
|
return next(); // trust me bro
|
||||||
|
|
||||||
const BaseURL = `${FULL_SERVER_ROOT}/song/download/${SongData.ID}/`;
|
const BaseURL = `${FULL_SERVER_ROOT}/song/download/${SongData.ID}/`;
|
||||||
res.set("content-type", "application/json");
|
res.set("content-type", "application/json");
|
||||||
|
|
|
@ -20,16 +20,19 @@ ValidateBody(j.object({
|
||||||
ToOverride: j.string().pattern(/^sid_placeholder_(\d){1,3}$/i).required()
|
ToOverride: j.string().pattern(/^sid_placeholder_(\d){1,3}$/i).required()
|
||||||
})),
|
})),
|
||||||
async (req, res) => {
|
async (req, res) => {
|
||||||
if (req.user?.Library.findIndex(x => x.SongID.toLowerCase() === req.body.SongID.toLowerCase() || x.Overriding.toLowerCase() === req.body.ToOverride.toLowerCase()) !== -1)
|
if (req.user!.Library!.length >= 15)
|
||||||
|
return res.status(400).json({ errorMessage: "You have too many active songs. Please deactivate some to free up space." });
|
||||||
|
|
||||||
|
if (req.user!.Library.findIndex(x => x.SongID.toLowerCase() === req.body.SongID.toLowerCase() || x.Overriding.toLowerCase() === req.body.ToOverride.toLowerCase()) !== -1)
|
||||||
return res.status(400).json({ errorMessage: "This song is already activated." });
|
return res.status(400).json({ errorMessage: "This song is already activated." });
|
||||||
|
|
||||||
if (!await Song.exists({ where: { ID: req.body.SongID } }))
|
if (!await Song.exists({ where: { ID: req.body.SongID } }))
|
||||||
return res.status(404).json({ errorMessage: "Provided song doesn't exist." });
|
return res.status(404).json({ errorMessage: "Provided song doesn't exist." });
|
||||||
|
|
||||||
req.user?.Library.push({ SongID: req.body.SongID.toLowerCase(), Overriding: req.body.ToOverride.toLowerCase() });
|
req.user!.Library.push({ SongID: req.body.SongID.toLowerCase(), Overriding: req.body.ToOverride.toLowerCase() });
|
||||||
req.user?.save();
|
req.user!.save();
|
||||||
|
|
||||||
res.json(req.user?.Library);
|
res.json(req.user!.Library);
|
||||||
})
|
})
|
||||||
|
|
||||||
App.post("/me/deactivate",
|
App.post("/me/deactivate",
|
||||||
|
|
|
@ -30,12 +30,12 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/cookie-parser": "^1.4.6",
|
"@types/cookie-parser": "^1.4.6",
|
||||||
|
"@types/cors": "^2.8.17",
|
||||||
"@types/express": "^4.17.18",
|
"@types/express": "^4.17.18",
|
||||||
|
"@types/fluent-ffmpeg": "^2.1.24",
|
||||||
"@types/jsonwebtoken": "^9.0.5",
|
"@types/jsonwebtoken": "^9.0.5",
|
||||||
"@types/node": "^20.6.3",
|
"@types/node": "^20.6.3",
|
||||||
"@types/underscore": "^1.11.15",
|
"@types/underscore": "^1.11.15",
|
||||||
"@types/cors": "^2.8.17",
|
|
||||||
"@types/fluent-ffmpeg": "^2.1.24",
|
|
||||||
"@types/uuid": "^9.0.7",
|
"@types/uuid": "^9.0.7",
|
||||||
"tslib": "^2.6.2",
|
"tslib": "^2.6.2",
|
||||||
"typescript": "^5.2.2"
|
"typescript": "^5.2.2"
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||||
"sourceMap": true /* Create source map files for emitted JavaScript files. */,
|
"sourceMap": true /* Create source map files for emitted JavaScript files. */,
|
||||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||||
"outDir": "./bin/" /* Specify an output folder for all emitted files. */,
|
"outDir": "../Out/bin/" /* Specify an output folder for all emitted files. */,
|
||||||
// "removeComments": true, /* Disable emitting comments. */
|
// "removeComments": true, /* Disable emitting comments. */
|
||||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||||
"importHelpers": true /* Allow importing helper functions from tslib once per project, instead of including them per-file. */,
|
"importHelpers": true /* Allow importing helper functions from tslib once per project, instead of including them per-file. */,
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/png" href="/favicon.webp" />
|
<link rel="icon" type="image/png" href="/public/favicon.webp" />
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link
|
<link
|
||||||
|
@ -11,10 +11,10 @@
|
||||||
rel="stylesheet">
|
rel="stylesheet">
|
||||||
<link href="https://unpkg.com/@primer/css/dist/primer.css" rel="stylesheet" />
|
<link href="https://unpkg.com/@primer/css/dist/primer.css" rel="stylesheet" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta content="goku doing party party emote fortnite" property="og:title" />
|
<meta content="Partypack - #1 Festival Modding Tool" property="og:title" />
|
||||||
<meta content="party party party party" property="og:description" />
|
<meta content="Join hundreds of users in Partypack - the one and only open-source Fortnite Festival modding utility available now!" property="og:description" />
|
||||||
<meta content="https://partypack.mcthe.dev" property="og:url" />
|
<meta content="https://partypack.mcthe.dev" property="og:url" />
|
||||||
<meta content="#FFC300" data-react-helmet="true" name="theme-color" />
|
<meta content="#40A0ED" data-react-helmet="true" name="theme-color" />
|
||||||
<title>Partypack</title>
|
<title>Partypack</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
16
package.json
16
package.json
|
@ -1,13 +1,19 @@
|
||||||
{
|
{
|
||||||
"name": "snippets",
|
"name": "partypack",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.0",
|
"version": "1.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc && vite build",
|
"build:prod": "vite build",
|
||||||
"create": "mkdir ./Out && npm run build && mv ./dist ./Out/dist && cd Server && tsc && cd .. && cp ./Server/.env ./Out && cp ./Server/package.json ./Out && cp ./Server/package-lock.json ./Out",
|
"build:stage": "vite build --mode staging",
|
||||||
"publish": "npm run create && ssh shady \"cd /home/PartypackProd; rm -rf ./Out\" && scp -r ./Out shady:/home/PartypackProd && ssh shady \"cd /home/PartypackProd/Out; npm i; pm2 restart PartypackProd --update-env\" && rm -rf ./Out",
|
|
||||||
|
"create:prod": "mkdir ./Out && npm run build:prod && mv ./dist ./Out/dist && cd Server && tsc && cd .. && cp ./Server/.env.prod ./Out/.env && cp ./Server/package.json ./Out && cp ./Server/package-lock.json ./Out",
|
||||||
|
"publish:prod": "npm run create:prod && ssh partypack \"cd /home/PartypackProd; rm -rf ./Out\" && scp -r ./Out partypack:/home/PartypackProd && ssh partypack \"cd /home/PartypackProd/Out && npm i && pm2 restart PartypackProd --update-env\" && rm -rf ./Out",
|
||||||
|
|
||||||
|
"create:stage": "mkdir ./Out && npm run build:stage && mv ./dist ./Out/dist && cd Server && tsc && cd .. && cp ./Server/.env.staging ./Out/.env && cp ./Server/package.json ./Out && cp ./Server/package-lock.json ./Out",
|
||||||
|
"publish:stage": "npm run create:stage && ssh partypack \"cd /home/PartypackStage; rm -rf ./Out\" && scp -r ./Out partypack:/home/PartypackStage && ssh partypack \"cd /home/PartypackStage/Out && npm i && pm2 restart PartypackStage --update-env\" && rm -rf ./Out",
|
||||||
|
|
||||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"dev:all": "start cmd.exe /k \"cd ./Server && npm run dev:watch\" && vite"
|
"dev:all": "start cmd.exe /k \"cd ./Server && npm run dev:watch\" && vite"
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Box, Heading, IconButton, Text } from "@primer/react";
|
||||||
import { TrashIcon, PencilIcon, ChevronUpIcon, ChevronDownIcon } from "@primer/octicons-react"
|
import { TrashIcon, PencilIcon, ChevronUpIcon, ChevronDownIcon } from "@primer/octicons-react"
|
||||||
import { Song } from "./Song";
|
import { Song } from "./Song";
|
||||||
|
|
||||||
export function AdminCategory({ categoryName, songs, isForced, moveUp, moveDown, onDelete, priority }: { categoryName: string, songs: any[], isForced: boolean, moveUp: () => void, moveDown: () => void, onDelete: () => void, priority: number }) {
|
export function AdminCategory({ categoryName, songs, isForced, moveUp, moveDown, onEdit, onDelete, priority }: { categoryName: string, songs: any[], isForced: boolean, moveUp: () => void, moveDown: () => void, onEdit: () => void, onDelete: () => void, priority: number }) {
|
||||||
return (
|
return (
|
||||||
<Box m={2} sx={{ overflow: "hidden", width: "100%", padding: 3, borderRadius: 10, border: "solid", borderColor: "border.default" }}>
|
<Box m={2} sx={{ overflow: "hidden", width: "100%", padding: 3, borderRadius: 10, border: "solid", borderColor: "border.default" }}>
|
||||||
<Box>
|
<Box>
|
||||||
|
@ -11,13 +11,13 @@ export function AdminCategory({ categoryName, songs, isForced, moveUp, moveDown,
|
||||||
{
|
{
|
||||||
isForced ?
|
isForced ?
|
||||||
<Text>You cannot edit the songs inside of this category, as it is forced.</Text> :
|
<Text>You cannot edit the songs inside of this category, as it is forced.</Text> :
|
||||||
<Text>This is a custom, category managed by this instance's admins.</Text>
|
<Text>This is a custom category managed by this instance's admins.</Text>
|
||||||
}
|
}
|
||||||
<Box sx={{ display: "inline-flex", gap: 2, float: "right" }}>
|
<Box sx={{ display: "inline-flex", gap: 2, float: "right" }}>
|
||||||
{
|
{
|
||||||
!isForced ?
|
!isForced ?
|
||||||
<>
|
<>
|
||||||
<IconButton icon={PencilIcon} variant="primary" aria-label="Default" />
|
<IconButton icon={PencilIcon} variant="primary" aria-label="Default" onClick={onEdit} />
|
||||||
<IconButton icon={TrashIcon} variant="danger" aria-label="Default" onClick={onDelete} />
|
<IconButton icon={TrashIcon} variant="danger" aria-label="Default" onClick={onDelete} />
|
||||||
<IconButton icon={ChevronUpIcon} aria-label="Default" onClick={moveUp} />
|
<IconButton icon={ChevronUpIcon} aria-label="Default" onClick={moveUp} />
|
||||||
<IconButton icon={ChevronDownIcon} aria-label="Default" onClick={moveDown} />
|
<IconButton icon={ChevronDownIcon} aria-label="Default" onClick={moveDown} />
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { toast } from "react-toastify";
|
||||||
import { Buffer } from "buffer/";
|
import { Buffer } from "buffer/";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
const style = { paddingTop: 3 };
|
const formControlStyle = { paddingTop: 3 };
|
||||||
|
|
||||||
export function AdminCreateTrack() {
|
export function AdminCreateTrack() {
|
||||||
const formRef = useRef<HTMLFormElement>(null);
|
const formRef = useRef<HTMLFormElement>(null);
|
||||||
|
@ -16,28 +16,28 @@ export function AdminCreateTrack() {
|
||||||
<>
|
<>
|
||||||
<Heading>Create a New Track</Heading>
|
<Heading>Create a New Track</Heading>
|
||||||
<form method="GET" action="" ref={formRef}>
|
<form method="GET" action="" ref={formRef}>
|
||||||
<FormControl required={true} sx={style}>
|
<FormControl required={true} sx={formControlStyle}>
|
||||||
<FormControl.Label>Song Name</FormControl.Label>
|
<FormControl.Label>Song Name</FormControl.Label>
|
||||||
<TextInput />
|
<TextInput />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl required={true} sx={style}>
|
<FormControl required={true} sx={formControlStyle}>
|
||||||
<FormControl.Label>Artist</FormControl.Label>
|
<FormControl.Label>Artist</FormControl.Label>
|
||||||
<FormControl.Caption>If there are multiple artists, separate them with a comma.</FormControl.Caption>
|
<FormControl.Caption>If there are multiple artists, separate them with a comma.</FormControl.Caption>
|
||||||
<TextInput />
|
<TextInput />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl required={true} sx={style}>
|
<FormControl required={true} sx={formControlStyle}>
|
||||||
<FormControl.Label>Album</FormControl.Label>
|
<FormControl.Label>Album</FormControl.Label>
|
||||||
<TextInput />
|
<TextInput />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl required={true} sx={style}>
|
<FormControl required={true} sx={formControlStyle}>
|
||||||
<FormControl.Label>Release Year</FormControl.Label>
|
<FormControl.Label>Release Year</FormControl.Label>
|
||||||
<TextInput type="number" />
|
<TextInput type="number" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl required={true} sx={style}>
|
<FormControl required={true} sx={formControlStyle}>
|
||||||
<FormControl.Label>Length (in seconds)</FormControl.Label>
|
<FormControl.Label>Length (in seconds)</FormControl.Label>
|
||||||
<TextInput type="number" />
|
<TextInput type="number" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl required={true} sx={style}>
|
<FormControl required={true} sx={formControlStyle}>
|
||||||
<FormControl.Label>Key</FormControl.Label>
|
<FormControl.Label>Key</FormControl.Label>
|
||||||
<ActionMenu>
|
<ActionMenu>
|
||||||
<ActionMenu.Button>{Key}</ActionMenu.Button>
|
<ActionMenu.Button>{Key}</ActionMenu.Button>
|
||||||
|
@ -54,7 +54,7 @@ export function AdminCreateTrack() {
|
||||||
</ActionMenu.Overlay>
|
</ActionMenu.Overlay>
|
||||||
</ActionMenu>
|
</ActionMenu>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl required={true} sx={style}>
|
<FormControl required={true} sx={formControlStyle}>
|
||||||
<FormControl.Label>Scale</FormControl.Label>
|
<FormControl.Label>Scale</FormControl.Label>
|
||||||
<ActionMenu>
|
<ActionMenu>
|
||||||
<ActionMenu.Button>{Scale}</ActionMenu.Button>
|
<ActionMenu.Button>{Scale}</ActionMenu.Button>
|
||||||
|
@ -64,7 +64,7 @@ export function AdminCreateTrack() {
|
||||||
</ActionMenu.Overlay>
|
</ActionMenu.Overlay>
|
||||||
</ActionMenu>
|
</ActionMenu>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl required={true} sx={style}>
|
<FormControl required={true} sx={formControlStyle}>
|
||||||
<FormControl.Label>Lead Type</FormControl.Label>
|
<FormControl.Label>Lead Type</FormControl.Label>
|
||||||
<FormControl.Caption>This is defining what lead instrument the song is going to start with. You can change the instrument mid-game with [keytar] and [guitar] text events.</FormControl.Caption>
|
<FormControl.Caption>This is defining what lead instrument the song is going to start with. You can change the instrument mid-game with [keytar] and [guitar] text events.</FormControl.Caption>
|
||||||
<ActionMenu>
|
<ActionMenu>
|
||||||
|
@ -75,33 +75,33 @@ export function AdminCreateTrack() {
|
||||||
</ActionMenu.Overlay>
|
</ActionMenu.Overlay>
|
||||||
</ActionMenu>
|
</ActionMenu>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl required={true} sx={style}>
|
<FormControl required={true} sx={formControlStyle}>
|
||||||
<FormControl.Label>Tempo</FormControl.Label>
|
<FormControl.Label>Tempo</FormControl.Label>
|
||||||
<TextInput type="number" />
|
<TextInput type="number" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl required={true} sx={style}>
|
<FormControl required={true} sx={formControlStyle}>
|
||||||
<FormControl.Label>MIDI File</FormControl.Label>
|
<FormControl.Label>MIDI File</FormControl.Label>
|
||||||
<TextInput type="file" />
|
<TextInput type="file" />
|
||||||
<FormControl.Caption>You can use the #tools-and-resources channel to find useful resources on how to create MIDIs.</FormControl.Caption>
|
<FormControl.Caption>You can use the #tools-and-resources channel to find useful resources on how to create MIDIs.</FormControl.Caption>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl required={true} sx={style}>
|
<FormControl required={true} sx={formControlStyle}>
|
||||||
<FormControl.Label>Cover Image</FormControl.Label>
|
<FormControl.Label>Cover Image</FormControl.Label>
|
||||||
<TextInput type="file" />
|
<TextInput type="file" />
|
||||||
<FormControl.Caption>Must be a 1:1 ratio. Max: 2048x2048, min: 512x512</FormControl.Caption>
|
<FormControl.Caption>Must be a 1:1 ratio. Max: 2048x2048, min: 512x512</FormControl.Caption>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl required={true} sx={style}>
|
<FormControl required={true} sx={formControlStyle}>
|
||||||
<FormControl.Label>Lead Difficulty</FormControl.Label>
|
<FormControl.Label>Lead Difficulty</FormControl.Label>
|
||||||
<TextInput type="number" />
|
<TextInput type="number" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl required={true} sx={style}>
|
<FormControl required={true} sx={formControlStyle}>
|
||||||
<FormControl.Label>Drums Difficulty</FormControl.Label>
|
<FormControl.Label>Drums Difficulty</FormControl.Label>
|
||||||
<TextInput type="number" />
|
<TextInput type="number" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl required={true} sx={style}>
|
<FormControl required={true} sx={formControlStyle}>
|
||||||
<FormControl.Label>Vocals Difficulty</FormControl.Label>
|
<FormControl.Label>Vocals Difficulty</FormControl.Label>
|
||||||
<TextInput type="number" />
|
<TextInput type="number" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl required={true} sx={style}>
|
<FormControl required={true} sx={formControlStyle}>
|
||||||
<FormControl.Label>Bass Difficulty</FormControl.Label>
|
<FormControl.Label>Bass Difficulty</FormControl.Label>
|
||||||
<TextInput type="number" />
|
<TextInput type="number" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
@ -128,7 +128,7 @@ export function AdminCreateTrack() {
|
||||||
const VocalsDifficulty = (formRef.current[13] as HTMLInputElement).valueAsNumber;
|
const VocalsDifficulty = (formRef.current[13] as HTMLInputElement).valueAsNumber;
|
||||||
const BassDifficulty = (formRef.current[14] as HTMLInputElement).valueAsNumber;
|
const BassDifficulty = (formRef.current[14] as HTMLInputElement).valueAsNumber;
|
||||||
|
|
||||||
const SongData = await axios.post("/admin/api/create/song", {
|
const SongData = await axios.post("/api/admin/create/song", {
|
||||||
Name,
|
Name,
|
||||||
ArtistName,
|
ArtistName,
|
||||||
Album,
|
Album,
|
||||||
|
@ -149,7 +149,7 @@ export function AdminCreateTrack() {
|
||||||
if (SongData.status !== 200)
|
if (SongData.status !== 200)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const MidiRes = await axios.post("/admin/api/upload/midi", {
|
const MidiRes = await axios.post("/api/admin/upload/midi", {
|
||||||
Data: Buffer.from(
|
Data: Buffer.from(
|
||||||
await Midi.arrayBuffer()
|
await Midi.arrayBuffer()
|
||||||
).toString("hex"),
|
).toString("hex"),
|
||||||
|
@ -157,7 +157,7 @@ export function AdminCreateTrack() {
|
||||||
});
|
});
|
||||||
toast(MidiRes.data, { type: MidiRes.status === 200 ? "success" : "error" });
|
toast(MidiRes.data, { type: MidiRes.status === 200 ? "success" : "error" });
|
||||||
|
|
||||||
const CoverRes = await axios.post("/admin/api/upload/cover", { Data: Buffer.from(await Cover.arrayBuffer()).toString("hex"), TargetSong: SongData.data.ID });
|
const CoverRes = await axios.post("/api/admin/upload/cover", { Data: Buffer.from(await Cover.arrayBuffer()).toString("hex"), TargetSong: SongData.data.ID });
|
||||||
toast(CoverRes.data, { type: CoverRes.status === 200 ? "success" : "error" });
|
toast(CoverRes.data, { type: CoverRes.status === 200 ? "success" : "error" });
|
||||||
}}>Create</Button>
|
}}>Create</Button>
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,27 @@
|
||||||
import { Box, Button, Dialog, Heading } from "@primer/react";
|
import { Box, Button, Dialog, FormControl, Heading, TextInput } from "@primer/react";
|
||||||
import { AdminCategory } from "../components/AdminCategory";
|
import { AdminCategory } from "../components/AdminCategory";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { moveElement } from "../utils/Extensions";
|
import { moveElement } from "../utils/Extensions";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
|
export interface LibraryObject {
|
||||||
|
ID: string,
|
||||||
|
Header: string,
|
||||||
|
Songs: unknown[],
|
||||||
|
Custom: boolean,
|
||||||
|
Priority: number,
|
||||||
|
ShouldDelete: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const formControlStyle = { paddingBottom: 3 };
|
||||||
|
|
||||||
export function AdminFeaturedTab() {
|
export function AdminFeaturedTab() {
|
||||||
const [library, setLibrary] = useState<{ ID: string, Header: string, Songs: unknown[], Custom: boolean, Priority: number }[] | null>(null);
|
const formRef = useRef<HTMLFormElement>(null);
|
||||||
const [hackyRevertChanges, setHackyRevertChanges] = useState<boolean>(false);
|
const [library, setLibrary] = useState<LibraryObject[] | null>(null);
|
||||||
|
const [hackyRevertChanges, setHackyRevertChanges] = useState<boolean>(false); // trust this
|
||||||
|
const [addSongsOpen, setAddSongsOpen] = useState<boolean>(false);
|
||||||
|
const [editedCategory, setEditedCategory] = useState<{ Obj: LibraryObject, Index: number }>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
|
@ -15,16 +29,53 @@ export function AdminFeaturedTab() {
|
||||||
if (Featured.status !== 200)
|
if (Featured.status !== 200)
|
||||||
return toast("Something went wrong while loading discovery. Try again later.", { type: "error" });
|
return toast("Something went wrong while loading discovery. Try again later.", { type: "error" });
|
||||||
|
|
||||||
setLibrary(Featured.data);
|
setLibrary(Featured.data.map(x => {
|
||||||
|
return {
|
||||||
|
...x,
|
||||||
|
ShouldDelete: false
|
||||||
|
};
|
||||||
|
}));
|
||||||
})();
|
})();
|
||||||
}, [hackyRevertChanges]);
|
}, [hackyRevertChanges]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Dialog></Dialog>
|
<Dialog isOpen={addSongsOpen} onDismiss={() => setAddSongsOpen(false)}>
|
||||||
|
<Dialog.Header>Modify Category</Dialog.Header>
|
||||||
|
<Box p={3}>
|
||||||
|
<form method="GET" action="" ref={formRef}>
|
||||||
|
<FormControl required={true} sx={formControlStyle}>
|
||||||
|
<FormControl.Label>Category Name</FormControl.Label>
|
||||||
|
<TextInput placeholder={editedCategory?.Obj.Header} />
|
||||||
|
</FormControl>
|
||||||
|
<FormControl required={true} sx={formControlStyle}>
|
||||||
|
<FormControl.Label>Custom Priority</FormControl.Label>
|
||||||
|
<TextInput placeholder={editedCategory?.Obj.Priority.toString()} type="number" />
|
||||||
|
<FormControl.Caption>The lower, the higher the category goes.</FormControl.Caption>
|
||||||
|
</FormControl>
|
||||||
|
</form>
|
||||||
|
<Box sx={{ float: "right", display: "inline-flex", gap: 2, marginBottom: 3 }}>
|
||||||
|
<Button variant="primary" type="submit" onClick={async e => {
|
||||||
|
e.preventDefault();
|
||||||
|
setAddSongsOpen(false);
|
||||||
|
|
||||||
|
const Name = (formRef.current[0] as HTMLInputElement).value;
|
||||||
|
const Priority = (formRef.current[1] as HTMLInputElement).valueAsNumber;
|
||||||
|
|
||||||
|
library[editedCategory.Index] = {
|
||||||
|
...editedCategory.Obj,
|
||||||
|
Header: Name.trim() === "" ? editedCategory.Obj.Header : Name,
|
||||||
|
Priority: isNaN(Priority) ? editedCategory.Obj.Priority : Priority
|
||||||
|
};
|
||||||
|
setLibrary(library.sort((a, b) => a.Priority - b.Priority));
|
||||||
|
}}>Confirm changes</Button>
|
||||||
|
<Button onClick={() => setAddSongsOpen(false)}>Cancel</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Dialog>
|
||||||
<Heading>Featured Tabs</Heading>
|
<Heading>Featured Tabs</Heading>
|
||||||
{
|
{
|
||||||
library?.map((x, i) => {
|
library?.filter(x => !x.ShouldDelete).map((x, i) => {
|
||||||
return (
|
return (
|
||||||
<AdminCategory
|
<AdminCategory
|
||||||
priority={x.Priority}
|
priority={x.Priority}
|
||||||
|
@ -72,13 +123,37 @@ export function AdminFeaturedTab() {
|
||||||
}; }).sort((a, b) => a.Priority - b.Priority));
|
}; }).sort((a, b) => a.Priority - b.Priority));
|
||||||
}}
|
}}
|
||||||
onDelete={() => {
|
onDelete={() => {
|
||||||
|
library[library.findIndex(y => y.ID === x.ID)].ShouldDelete = true;
|
||||||
|
setLibrary([...library])
|
||||||
|
}}
|
||||||
|
onEdit={() => {
|
||||||
|
setEditedCategory({ Obj: x, Index: i });
|
||||||
|
setAddSongsOpen(true);
|
||||||
}} />
|
}} />
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
<Box sx={{ float: "right", display: "inline-flex", gap: 2 }}>
|
<Box sx={{ float: "right", display: "inline-flex", gap: 2 }}>
|
||||||
<Button variant="primary">Save</Button>
|
<Button variant="primary" onClick={async () => {
|
||||||
|
console.log(library);
|
||||||
|
const SaveResponse = await axios.post("/api/admin/update/discovery", [
|
||||||
|
...library!.filter(x => x.Custom).map(
|
||||||
|
x => {
|
||||||
|
return {
|
||||||
|
...x,
|
||||||
|
Custom: undefined,
|
||||||
|
Activated: undefined,
|
||||||
|
Songs: x.Songs.map(y => y.ID)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (SaveResponse.status === 200)
|
||||||
|
return toast("Saved changes successfully.", { type: "success" });
|
||||||
|
|
||||||
|
toast("An unknown error has occured. Please check the console.", { type: "error" });
|
||||||
|
}}>Save</Button>
|
||||||
<Button onClick={() => setHackyRevertChanges(!hackyRevertChanges)}>Revert changes</Button>
|
<Button onClick={() => setHackyRevertChanges(!hackyRevertChanges)}>Revert changes</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -11,7 +11,7 @@ export function AdminTrackList() {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const Tracks = await axios.get("/admin/api/tracks");
|
const Tracks = await axios.get("/api/admin/tracks");
|
||||||
if (Tracks.status !== 200)
|
if (Tracks.status !== 200)
|
||||||
return toast("Error while requesting tracks!");
|
return toast("Error while requesting tracks!");
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { AxInstance } from "./Requests";
|
import { AxInstance } from "./Requests";
|
||||||
|
|
||||||
export async function VerifyAdminKey(Key: string): Promise<{ Success: boolean; Message: string; }> {
|
export async function VerifyAdminKey(Key: string): Promise<{ Success: boolean; Message: string; }> {
|
||||||
const { status, data } = await AxInstance.post("/admin/api/key", { Key });
|
const { status, data } = await AxInstance.post("/api/admin/key", { Key });
|
||||||
return { Success: status === 200, Message: data };
|
return { Success: status === 200, Message: data };
|
||||||
}
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
// TODO: grab this data from somewhere idk
|
axios.defaults.baseURL = import.meta.env.VITE_SERVER_ROOT_URL ?? "http://localhost:6677";
|
||||||
axios.defaults.baseURL = import.meta.env.DEV ? "http://localhost:6677" : "https://partypack.mcthe.dev";
|
|
||||||
axios.defaults.withCredentials = true
|
axios.defaults.withCredentials = true
|
||||||
axios.defaults.validateStatus = () => true;
|
axios.defaults.validateStatus = () => true;
|
||||||
|
|
||||||
|
|
8
src/vite-env.d.ts
vendored
8
src/vite-env.d.ts
vendored
|
@ -1 +1,9 @@
|
||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
interface ImportMetaEnv {
|
||||||
|
readonly VITE_SERVER_ROOT_URL: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImportMeta {
|
||||||
|
readonly env: ImportMetaEnv;
|
||||||
|
}
|
|
@ -15,11 +15,12 @@
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
|
|
||||||
/* Linting */
|
/* Linting */
|
||||||
"strict": true,
|
/* Fuck you typescript no linting for you */
|
||||||
|
"strict": false,
|
||||||
"noImplicitAny": false,
|
"noImplicitAny": false,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": false,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": false,
|
||||||
"noFallthroughCasesInSwitch": true
|
"noFallthroughCasesInSwitch": false
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": ["src"],
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user