s
This commit is contained in:
parent
017b619766
commit
2bd4203827
|
@ -53,9 +53,13 @@ App.post("/upload/midi",
|
||||||
if ((await fromBuffer(Decoded))?.ext !== "mid")
|
if ((await fromBuffer(Decoded))?.ext !== "mid")
|
||||||
return res.status(400).send("Uploaded MIDI file is not a valid MIDI.");
|
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.");
|
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);
|
writeFileSync(`./Saved/Songs/${req.body.TargetSong}/Data.mid`, Decoded);
|
||||||
res.send(`${FULL_SERVER_ROOT}/song/download/${req.body.TargetSong}/midi.mid`);
|
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))
|
if (!["mp3", "m4a", "ogg", "wav"].includes(ext))
|
||||||
return res.status(404).send("Invalid audio file. (supported: mp3, m4a, ogg, wav)");
|
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.");
|
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);
|
await writeFileSync(`./Saved/Songs/${req.body.TargetSong}/Audio.${ext}`, Decoded);
|
||||||
ffmpeg()
|
ffmpeg()
|
||||||
.input(`./Saved/Songs/${req.body.TargetSong}/Audio.${ext}`)
|
.input(`./Saved/Songs/${req.body.TargetSong}/Audio.${ext}`)
|
||||||
|
@ -142,9 +150,28 @@ App.post("/upload/audio",
|
||||||
|
|
||||||
App.post("/submit",
|
App.post("/submit",
|
||||||
RequireAuthentication(),
|
RequireAuthentication(),
|
||||||
ValidateBody(j.object({})),
|
ValidateBody(j.object({
|
||||||
|
TargetSong: j.string().uuid().required()
|
||||||
|
})),
|
||||||
async (req, res) => {
|
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 {
|
export default {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { RequireAuthentication, ValidateBody } from "../Modules/Middleware";
|
||||||
import { Song } from "../Schemas/Song";
|
import { Song } from "../Schemas/Song";
|
||||||
import { OriginalSparks } from "../Modules/FNUtil";
|
import { OriginalSparks } from "../Modules/FNUtil";
|
||||||
import j from "joi";
|
import j from "joi";
|
||||||
|
import { UserPermissions } from "../Schemas/User";
|
||||||
|
|
||||||
const App = Router();
|
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)
|
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 } }))
|
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." });
|
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!.Library.push({ SongID: req.body.SongID.toLowerCase(), Overriding: req.body.ToOverride.toLowerCase() });
|
||||||
req.user!.save();
|
req.user!.save();
|
||||||
|
|
||||||
|
@ -65,7 +70,7 @@ async (req, res) => {
|
||||||
if (!SongData)
|
if (!SongData)
|
||||||
return res.status(404).json({ errorMessage: "Provided song doesn't exist." });
|
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." });
|
return res.status(403).json({ errorMessage: "You cannot subscribe to this track, because it's a draft." });
|
||||||
|
|
||||||
req.user?.BookmarkedSongs.push(SongData);
|
req.user?.BookmarkedSongs.push(SongData);
|
||||||
|
@ -93,12 +98,12 @@ async (req, res) => {
|
||||||
App.get("/song/data/:InternalID",
|
App.get("/song/data/:InternalID",
|
||||||
RequireAuthentication(),
|
RequireAuthentication(),
|
||||||
async (req, res) => {
|
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)
|
if (!SongData)
|
||||||
return res.status(404).json({ errorMessage: "Provided song doesn't exist." });
|
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 use this track, because it's a draft." });
|
return res.status(403).json({ errorMessage: "You cannot subscribe to this track, because it's a draft." });
|
||||||
|
|
||||||
res.json(SongData.Package());
|
res.json(SongData.Package());
|
||||||
})
|
})
|
||||||
|
|
|
@ -68,6 +68,9 @@ export class Song extends BaseEntity {
|
||||||
@Column({ default: false })
|
@Column({ default: false })
|
||||||
DraftAwaitingReview: boolean;
|
DraftAwaitingReview: boolean;
|
||||||
|
|
||||||
|
@Column({ nullable: true })
|
||||||
|
DraftReviewSubmittedAt?: Date;
|
||||||
|
|
||||||
@Column()
|
@Column()
|
||||||
CreationDate: Date;
|
CreationDate: Date;
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,11 @@ export function Song({ data, children }: { data: any, children?: JSX.Element[] |
|
||||||
<Text sx={{ display: "block", textOverflow: "ellipsis", overflow: "hidden", whiteSpace: "nowrap" }}>{data.ArtistName}</Text>
|
<Text sx={{ display: "block", textOverflow: "ellipsis", overflow: "hidden", whiteSpace: "nowrap" }}>{data.ArtistName}</Text>
|
||||||
<Text sx={{ display: "block", textOverflow: "ellipsis", overflow: "hidden", whiteSpace: "nowrap" }}><b>{data.Name}</b></Text>
|
<Text sx={{ display: "block", textOverflow: "ellipsis", overflow: "hidden", whiteSpace: "nowrap" }}><b>{data.Name}</b></Text>
|
||||||
{
|
{
|
||||||
data.IsDraft ? <Label variant="danger">Draft - not published</Label> : <></>
|
data.IsDraft ?
|
||||||
|
data.DraftAwaitingReview ?
|
||||||
|
<Label variant="attention">Draft - awaiting review</Label> :
|
||||||
|
<Label variant="danger">Draft - not published</Label> :
|
||||||
|
<></>
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
children ? <Divider /> : <></>
|
children ? <Divider /> : <></>
|
||||||
|
|
22
src/routes/AdminSubmissions.tsx
Normal file
22
src/routes/AdminSubmissions.tsx
Normal file
|
@ -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 (
|
||||||
|
<>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
|
@ -4,7 +4,6 @@ import { PageHeader } from "@primer/react/drafts";
|
||||||
import { useContext, useEffect, useState } from "react";
|
import { useContext, useEffect, useState } from "react";
|
||||||
import { SiteContext } from "../utils/State";
|
import { SiteContext } from "../utils/State";
|
||||||
import { useCookies } from "react-cookie";
|
import { useCookies } from "react-cookie";
|
||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
import { Song } from "../components/Song";
|
import { Song } from "../components/Song";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
|
@ -18,7 +17,6 @@ export function Profile() {
|
||||||
const [draftsSongs, setDraftsSongs] = useState<unknown[]>([]);
|
const [draftsSongs, setDraftsSongs] = useState<unknown[]>([]);
|
||||||
const [availableOverrides, setAvailableOverrides] = useState<{ Name: string, Template: string }[]>([]);
|
const [availableOverrides, setAvailableOverrides] = useState<{ Name: string, Template: string }[]>([]);
|
||||||
const [overriding, setOverriding] = useState<unknown>({});
|
const [overriding, setOverriding] = useState<unknown>({});
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
|
@ -131,18 +129,27 @@ export function Profile() {
|
||||||
<Box className="songCategory">
|
<Box className="songCategory">
|
||||||
{
|
{
|
||||||
draftsSongs.length >= 1 ?
|
draftsSongs.length >= 1 ?
|
||||||
draftsSongs.map(x => {
|
draftsSongs.map((x, i) => {
|
||||||
return <Song data={x}>
|
return <Song data={x}>
|
||||||
<Button sx={{ width: "100%", marginBottom: 1 }} variant="primary" onClick={() => { setIsActivateDialogOpen(true); setOverriding(x) }} disabled={librarySongs.findIndex(y => y.ID === x.ID) !== -1}>Add to Active</Button>
|
<Button sx={{ width: "100%", marginBottom: 1 }} variant="primary" onClick={() => { setIsActivateDialogOpen(true); setOverriding(x) }} disabled={librarySongs.findIndex(y => y.ID === x.ID) !== -1}>Add to Active</Button>
|
||||||
<Button sx={{ width: "100%", marginBottom: 1 }}>Publish</Button>
|
<Button sx={{ width: "100%", marginBottom: 1 }} onClick={async () => {
|
||||||
|
const Res = await axios.post("/api/drafts/submit", { TargetSong: x.ID });
|
||||||
|
if (Res.status === 200) {
|
||||||
|
x.DraftAwaitingReview = true;
|
||||||
|
draftsSongs[i] = x;
|
||||||
|
setDraftsSongs([...draftsSongs]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
toast(Res.data, { type: "error" });
|
||||||
|
}}>Publish</Button>
|
||||||
<Button sx={{ width: "100%" }} variant="danger" onClick={async () => {
|
<Button sx={{ width: "100%" }} variant="danger" onClick={async () => {
|
||||||
const Res = await axios.post("/api/drafts/delete", { SongID: x.ID });
|
const Res = await axios.post("/api/drafts/delete", { TargetSong: x.ID });
|
||||||
if (Res.status === 200) {
|
if (Res.status === 200) {
|
||||||
draftsSongs.splice(draftsSongs.findIndex(y => y.ID === x.ID), 1);
|
draftsSongs.splice(draftsSongs.findIndex(y => y.ID === x.ID), 1);
|
||||||
setDraftsSongs([...draftsSongs]);
|
setDraftsSongs([...draftsSongs]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
toast(Res.data.errorMessage, { type: "error" })
|
toast(Res.data, { type: "error" });
|
||||||
}}>Unsubscribe</Button>
|
}}>Unsubscribe</Button>
|
||||||
</Song>;
|
</Song>;
|
||||||
})
|
})
|
||||||
|
|
|
@ -19,7 +19,7 @@ export function TrackSubmission() {
|
||||||
<Text></Text>
|
<Text></Text>
|
||||||
</Box>*/}
|
</Box>*/}
|
||||||
<Heading>Create a New Draft</Heading>
|
<Heading>Create a New Draft</Heading>
|
||||||
<Text>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.</Text>
|
<Text>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.</Text>
|
||||||
<form method="GET" action="" ref={formRef}>
|
<form method="GET" action="" ref={formRef}>
|
||||||
<FormControl required={true} sx={formControlStyle}>
|
<FormControl required={true} sx={formControlStyle}>
|
||||||
<FormControl.Label>Song Name</FormControl.Label>
|
<FormControl.Label>Song Name</FormControl.Label>
|
||||||
|
@ -95,7 +95,7 @@ export function TrackSubmission() {
|
||||||
<FormControl.Caption>This will play in the background of your song. Make sure it was exported from REAPER.</FormControl.Caption>
|
<FormControl.Caption>This will play in the background of your song. Make sure it was exported from REAPER.</FormControl.Caption>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl required={true} sx={formControlStyle}>
|
<FormControl required={true} sx={formControlStyle}>
|
||||||
<FormControl.Label>Cover Image (.jpg, .jpeg, .webp, .png)</FormControl.Label>
|
<FormControl.Label>Cover Image (.png)</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>
|
||||||
|
@ -165,16 +165,14 @@ export function TrackSubmission() {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const MidiRes = await axios.post("/api/drafts/upload/midi", { Data: Buffer.from(await Midi.arrayBuffer()).toString("hex"), TargetSong: SongData.data.ID });
|
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 });
|
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 });
|
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</Button>
|
}}>Create</Button>
|
||||||
|
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user