From 2bd42038275e027a86cbf5d13d51d845314506f9 Mon Sep 17 00:00:00 2001 From: McMistrzYT <56406996+McMistrzYT@users.noreply.github.com> Date: Sun, 28 Jan 2024 23:36:28 +0100 Subject: [PATCH] s --- Server/Source/Routes/Drafting.ts | 33 +++++++++++++++++++++++++++++--- Server/Source/Routes/Library.ts | 15 ++++++++++----- Server/Source/Schemas/Song.ts | 3 +++ src/components/Song.tsx | 6 +++++- src/routes/AdminSubmissions.tsx | 22 +++++++++++++++++++++ src/routes/Profile.tsx | 19 ++++++++++++------ src/routes/TrackSubmission.tsx | 12 +++++------- 7 files changed, 88 insertions(+), 22 deletions(-) create mode 100644 src/routes/AdminSubmissions.tsx diff --git a/Server/Source/Routes/Drafting.ts b/Server/Source/Routes/Drafting.ts index 7a39911..d4e24a4 100644 --- a/Server/Source/Routes/Drafting.ts +++ b/Server/Source/Routes/Drafting.ts @@ -53,9 +53,13 @@ App.post("/upload/midi", if ((await fromBuffer(Decoded))?.ext !== "mid") return res.status(400).send("Uploaded MIDI file is not a valid MIDI."); - if (!await Song.exists({ where: { ID: req.body.TargetSong } })) + const SongData = await Song.findOne({ where: { ID: req.body.TargetSong }, relations: { Author: true } }) + if (!SongData) return res.status(404).send("The song you're trying to upload a MIDI for does not exist."); + if (req.user!.PermissionLevel! < UserPermissions.Administrator && SongData.Author.ID !== req.user!.ID) + return res.status(403).send("You don't have permission to upload to this song."); + writeFileSync(`./Saved/Songs/${req.body.TargetSong}/Data.mid`, Decoded); res.send(`${FULL_SERVER_ROOT}/song/download/${req.body.TargetSong}/midi.mid`); }); @@ -112,9 +116,13 @@ App.post("/upload/audio", if (!["mp3", "m4a", "ogg", "wav"].includes(ext)) return res.status(404).send("Invalid audio file. (supported: mp3, m4a, ogg, wav)"); - if (!await Song.exists({ where: { ID: req.body.TargetSong } })) + const SongData = await Song.findOne({ where: { ID: req.body.TargetSong }, relations: { Author: true } }) + if (!SongData) return res.status(404).send("The song you're trying to upload audio for does not exist."); + if (req.user!.PermissionLevel! < UserPermissions.Administrator && SongData.Author.ID !== req.user!.ID) + return res.status(403).send("You don't have permission to upload to this song."); + await writeFileSync(`./Saved/Songs/${req.body.TargetSong}/Audio.${ext}`, Decoded); ffmpeg() .input(`./Saved/Songs/${req.body.TargetSong}/Audio.${ext}`) @@ -142,9 +150,28 @@ App.post("/upload/audio", App.post("/submit", RequireAuthentication(), - ValidateBody(j.object({})), + ValidateBody(j.object({ + TargetSong: j.string().uuid().required() + })), async (req, res) => { + const SongData = await Song.findOne({ where: { ID: req.body.TargetSong }, relations: { Author: true } }) + if (!SongData) + return res.status(404).send("The song you're trying to submit for review does not exist."); + if (SongData.Author.ID !== req.user!.ID) + return res.status(403).send("You don't have permission to submit this song for approval."); + + if (!SongData.IsDraft) + return res.status(400).send("This song has already been approved and published."); + + if (SongData.DraftAwaitingReview) + return res.status(400).send("You already submitted this song for review."); + + SongData.DraftReviewSubmittedAt = new Date(); + SongData.DraftAwaitingReview = true; + await SongData.save(); + + return res.send("Song has been submitted for approval by admins."); }); export default { diff --git a/Server/Source/Routes/Library.ts b/Server/Source/Routes/Library.ts index 8cb7684..ef5f43a 100644 --- a/Server/Source/Routes/Library.ts +++ b/Server/Source/Routes/Library.ts @@ -3,6 +3,7 @@ import { RequireAuthentication, ValidateBody } from "../Modules/Middleware"; import { Song } from "../Schemas/Song"; import { OriginalSparks } from "../Modules/FNUtil"; import j from "joi"; +import { UserPermissions } from "../Schemas/User"; const App = Router(); @@ -27,9 +28,13 @@ async (req, res) => { 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." }); - if (!await Song.exists({ where: { ID: req.body.SongID } })) + const SongData = await Song.findOne({ where: { ID: req.body.SongID }, relations: { Author: true } }); + if (!SongData) return res.status(404).json({ errorMessage: "Provided song doesn't exist." }); + if (SongData.IsDraft && (req.user!.PermissionLevel < UserPermissions.Administrator && SongData.Author.ID !== req.user!.ID)) + return res.status(403).json({ errorMessage: "You cannot subscribe to this track, because it's a draft." }); + req.user!.Library.push({ SongID: req.body.SongID.toLowerCase(), Overriding: req.body.ToOverride.toLowerCase() }); req.user!.save(); @@ -65,7 +70,7 @@ async (req, res) => { if (!SongData) return res.status(404).json({ errorMessage: "Provided song doesn't exist." }); - if (SongData.IsDraft && SongData.Author.ID !== req.user.ID) + if (SongData.IsDraft && (req.user.PermissionLevel < UserPermissions.Administrator && SongData.Author.ID !== req.user.ID)) return res.status(403).json({ errorMessage: "You cannot subscribe to this track, because it's a draft." }); req.user?.BookmarkedSongs.push(SongData); @@ -93,12 +98,12 @@ async (req, res) => { App.get("/song/data/:InternalID", RequireAuthentication(), async (req, res) => { - const SongData = await Song.findOne({ where: { ID: req.params.InternalID }, relations: { Author: true } }); + const SongData = await Song.findOne({ where: { ID: req.body.SongID }, relations: { Author: true } }); if (!SongData) return res.status(404).json({ errorMessage: "Provided song doesn't exist." }); - if (SongData.IsDraft && SongData.Author.ID !== req.user!.ID) - return res.status(403).json({ errorMessage: "You cannot use this track, because it's a draft." }); + if (SongData.IsDraft && (req.user!.PermissionLevel < UserPermissions.Administrator && SongData.Author.ID !== req.user!.ID)) + return res.status(403).json({ errorMessage: "You cannot subscribe to this track, because it's a draft." }); res.json(SongData.Package()); }) diff --git a/Server/Source/Schemas/Song.ts b/Server/Source/Schemas/Song.ts index c30a673..1ac38e6 100644 --- a/Server/Source/Schemas/Song.ts +++ b/Server/Source/Schemas/Song.ts @@ -68,6 +68,9 @@ export class Song extends BaseEntity { @Column({ default: false }) DraftAwaitingReview: boolean; + @Column({ nullable: true }) + DraftReviewSubmittedAt?: Date; + @Column() CreationDate: Date; diff --git a/src/components/Song.tsx b/src/components/Song.tsx index 91b735d..eb9a90e 100644 --- a/src/components/Song.tsx +++ b/src/components/Song.tsx @@ -9,7 +9,11 @@ export function Song({ data, children }: { data: any, children?: JSX.Element[] | {data.ArtistName} {data.Name} { - data.IsDraft ? : <> + data.IsDraft ? + data.DraftAwaitingReview ? + : + : + <> } { children ? : <> diff --git a/src/routes/AdminSubmissions.tsx b/src/routes/AdminSubmissions.tsx new file mode 100644 index 0000000..303f4e4 --- /dev/null +++ b/src/routes/AdminSubmissions.tsx @@ -0,0 +1,22 @@ +import { useEffect } from "react"; +import { toast } from "react-toastify"; +import axios from "axios"; + +export function AdminSubmissions() { + useEffect(() => { + (async () => { + const Data = await axios.get("/api/admin/"); + const Overrides = await axios.get("/api/library/available"); + + if (Data.status !== 200 || Overrides.status !== 200) + return toast("An error has occured while getting the submitted songs!", { type: "error" }); + + + })(); + }, []); + + return ( + <> + + ) +} \ No newline at end of file diff --git a/src/routes/Profile.tsx b/src/routes/Profile.tsx index 338285f..57af646 100644 --- a/src/routes/Profile.tsx +++ b/src/routes/Profile.tsx @@ -4,7 +4,6 @@ import { PageHeader } from "@primer/react/drafts"; import { useContext, useEffect, useState } from "react"; import { SiteContext } from "../utils/State"; import { useCookies } from "react-cookie"; -import { useNavigate } from "react-router-dom"; import { Song } from "../components/Song"; import axios from "axios"; import { toast } from "react-toastify"; @@ -18,7 +17,6 @@ export function Profile() { const [draftsSongs, setDraftsSongs] = useState([]); const [availableOverrides, setAvailableOverrides] = useState<{ Name: string, Template: string }[]>([]); const [overriding, setOverriding] = useState({}); - const navigate = useNavigate(); useEffect(() => { (async () => { @@ -131,18 +129,27 @@ export function Profile() { { draftsSongs.length >= 1 ? - draftsSongs.map(x => { + draftsSongs.map((x, i) => { return - + ; }) diff --git a/src/routes/TrackSubmission.tsx b/src/routes/TrackSubmission.tsx index 3d7fb9c..876b1e7 100644 --- a/src/routes/TrackSubmission.tsx +++ b/src/routes/TrackSubmission.tsx @@ -19,7 +19,7 @@ export function TrackSubmission() { */} Create a New Draft - Drafts are private versions of Tracks, only available to you. If you want to publish that track, click the "Submit for Review" button on the management page. + Drafts are private versions of Tracks, only available to you. If you want to publish that track, click the "Publish" button on the management page.
Song Name @@ -95,7 +95,7 @@ export function TrackSubmission() { This will play in the background of your song. Make sure it was exported from REAPER. - Cover Image (.jpg, .jpeg, .webp, .png) + Cover Image (.png) Must be a 1:1 ratio. Max: 2048x2048, min: 512x512 @@ -165,16 +165,14 @@ export function TrackSubmission() { return; const MidiRes = await axios.post("/api/drafts/upload/midi", { Data: Buffer.from(await Midi.arrayBuffer()).toString("hex"), TargetSong: SongData.data.ID }); - toast(MidiRes.status === 200 ? "Uploaded MIDI chart successfully." : MidiRes.data.errorMessage, { type: MidiRes.status === 200 ? "success" : "error" }); + toast(MidiRes.status === 200 ? "Uploaded MIDI chart successfully." : MidiRes.data, { type: MidiRes.status === 200 ? "success" : "error" }); const AudioRes = await axios.post("/api/drafts/upload/audio", { Data: Buffer.from(await Music.arrayBuffer()).toString("hex"), TargetSong: SongData.data.ID }); - toast(AudioRes.status === 200 ? "Uploaded audio for processing successfully." : AudioRes.data.errorMessage, { type: AudioRes.status === 200 ? "success" : "error" }); + toast(AudioRes.status === 200 ? "Uploaded audio for processing successfully." : AudioRes.data, { type: AudioRes.status === 200 ? "success" : "error" }); const CoverRes = await axios.post("/api/drafts/upload/cover", { Data: Buffer.from(await Cover.arrayBuffer()).toString("hex"), TargetSong: SongData.data.ID }); - toast(MidiRes.status === 200 ? "Uploaded cover image successfully." : MidiRes.data.errorMessage, { type: CoverRes.status === 200 ? "success" : "error" }); + toast(CoverRes.status === 200 ? "Uploaded cover image successfully." : CoverRes.data, { type: CoverRes.status === 200 ? "success" : "error" }); }}>Create - -
)