Natsuki's API!
https://api.natsuki.app
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
119 lines
7.9 KiB
119 lines
7.9 KiB
// how many hours have i spent banging my head at the wall now?
|
|
|
|
// i literally dont have internet i wrote all of this forever ago im doing this out of sheer lack of other options WHY DID I WRITE THIS
|
|
|
|
module.exports = (app, router) => {
|
|
const Anime = app.db.models.ani.series;
|
|
|
|
router.route('/:id')
|
|
.post(app.auth.token, app.auth.perms('series-submit'), app.auth.permsPass('series-approve'), async (req, res) => {
|
|
let submitting = req.unauthorized; //if user doesn't have series-approve, they still have perms to submit by this point
|
|
|
|
/**REQUIRED ITEMS
|
|
*
|
|
*!Submissions must include a name and id (romaji included)
|
|
* Only altNames, tags, and streaming locations can be omitted
|
|
*
|
|
* If a request has submissible criteria but not completable
|
|
* criteria, it will be treated as a submission even if the user
|
|
* is authorized to approve series.
|
|
*/
|
|
|
|
//TODO submit anyways if incomplete but user has approval permissions
|
|
//TODO un-submitted (incomplete) but curated route
|
|
|
|
|
|
if (!req.params.id) {return res.status(400).send("You didn't include an anime ID in your request!");}
|
|
if (await Anime.findOne({id: req.params.id})) {return res.status(400).send("An anime already exists with that ID!");}
|
|
if (
|
|
!req.body //i just ate dinner and i can't even think straight
|
|
|| !req.body.name || !req.body.romaji || !req.params.id || !req.authenticatedUser || !req.authenticatedUser.id
|
|
|| !req.body.name.match(/^[\w_\-!?.:; ]+$/gm) || req.body.name.length > 150
|
|
|| req.body.romaji.length > 150
|
|
|| !req.params.id.match(/^[0-9_-]*[a-zA-Z][\w-]*$/gm) || req.params.id.length > 25
|
|
) {return res.status(400).send("The server cannot accept your request as your body is missing fields or is malformed. Ensure fields aren't too long and that they don't contain illegal characters.");}
|
|
|
|
let series = new Anime({
|
|
id: req.params.id,
|
|
numericalId: app.cache.seriesCount + 1,
|
|
name: req.body.name,
|
|
romaji: req.body.romaji,
|
|
synopsis: {
|
|
by: req.body.synopsis ? req.authenticatedUser.id : null,
|
|
synopsis: req.body.synopsis || "A synopsis is not yet available for this series..."
|
|
},
|
|
meta: {
|
|
submitted: submitting ? req.authenticatedUser.id : false, //TODO make sure to update submitted status
|
|
creator: req.authenticatedUser.id,
|
|
edits: [{
|
|
user: req.authenticatedUser.id,
|
|
action: 'Submitted series',
|
|
timestamp: new Date().getTime()
|
|
}]
|
|
},
|
|
genres: req.body.genres && Array.isArray(req.body.genres) && req.body.genres.length ? req.body.genres : []
|
|
});
|
|
if (!req.body.synopsis || !req.body.genres || !Array.isArray(req.body.genres) || !req.body.genres.length) {series.meta.submitted = req.authenticatedUser.id;}
|
|
|
|
//VALIDATION
|
|
|
|
const badReq = msg => {res.status(400).send(msg); return null;};
|
|
const validateStringList = (maxLength, list, listName, maxListLength, regex) => {
|
|
let good = false;
|
|
if (maxListLength !== -1 && list.length > maxListLength) {return badReq(`Your ${listName} had too many items.`);}
|
|
list.forEach(item => {good = !good && typeof item === 'string' && item.length < maxLength && (!regex || item.match(regex));});
|
|
if (!good) {return badReq(`Your ${listName} did not contain purely strings, or one of them was too long.`);}
|
|
return list;
|
|
};
|
|
let options = req.body;
|
|
if (options.altNames) {series.altNames = validateStringList(150, options.altNames, 'altNames'); if (!series.altNames) {return;}}
|
|
if (options.tags) {series.tags = validateStringList(25, options.tags, 'tags', 25, /^[\w-]+$/gm);if (!series.tags) {return;}}
|
|
if (options.nsfw === true && !(options.nsfwReason || ['gore', 'language', 'nudity', 'themes'].includes(options.nsfwReason))) {return badReq("You marked this series as nsfw, but did not provide a reason.");}
|
|
else if (options.nsfw) {
|
|
series.nsfw = options.nsfw;
|
|
series.nsfwReason = options.nsfwReason;
|
|
}
|
|
if (options.genres) {series.genres = validateStringList(25, options.genres, 'genres', 10, /^[\w- ]+$/gm); if (!series.genres) {return;}} //TODO genres as DB
|
|
if (options.streamAt) {series.streamAt = validateStringList(25, options.streamAt, 'streamAt locations', 10, /^[\w- ]+$/gm);if (!series.streamAt) {return;}}
|
|
if (options.publishers) {series.publishers = validateStringList(25, options.publishers, 'publishers', 5, /^[\w- ]+$/gm);if (!series.publishers) {return;}}
|
|
//well this is hell. never again will i make a schema so fat // i have no idea what im doing here
|
|
//TODO validate pt. 2, the requiem
|
|
if (options.air) {
|
|
if (options.air.from) {series.air.from = options.air.from;}
|
|
if (options.air.to) {series.air.to = options.air.to;} //it's 9am send help
|
|
}
|
|
//unvalidated BS at this point fuck it
|
|
//get rotated idiot
|
|
if (options.art) {series.art = options.art;}
|
|
if (options.officialSite) {series.officialSite = options.officialSite;}
|
|
if (options.videos) {series.videos = options.videos;} //it probably works. good enough yeah?
|
|
|
|
return series.save().then(async () => {
|
|
app.cache.series[series.id] = {
|
|
id: series.id,
|
|
name: series.name,
|
|
romaji: series.romaji,
|
|
kanji: series.kanji,
|
|
altNames: series.altNames,
|
|
genres: series.genres,
|
|
tags: series.tags
|
|
};
|
|
app.cache.seriesCount++;
|
|
if (series.meta.submitted) {app.cache.seriesQueue.push({id: series.id, submitter: series.submitted});}
|
|
return res.send(`Your series was successfully ${series.meta.submitted ? 'submitted' : "added"}.`);
|
|
}).catch((e) => {console.error(e); res.status(500).send("There was an error trying to process your request. It's likely that our database found something wrong with your body fields, and the server didn't realize. Check your request and try again.");});
|
|
//TODO remove console error
|
|
})
|
|
.get(app.auth.tokenPass, app.auth.permsPass('series-approve'), async (req, res, next) => {
|
|
const series = await Anime.findOne(/*{$or: [*/{id: req.params.id.toLowerCase()}/*, {numericalId: req.params.id}]}*/); //TODO make sure all ID calls are lowercased
|
|
if (!series || (series && !series.meta.completed && req.unauthorized)) {
|
|
if (req.params.id.toLowerCase() === 'queue') {return next();}
|
|
return res.status(400).send("A series with that ID doesn't exist!");
|
|
}
|
|
let {name, id, romaji, kanji, air, rating, likes, seasons, characters, altNames, genres, tags, nsfw, nsfwReason, synopsis, numericalId, completed, publishers, studios} = series;
|
|
return res.json({
|
|
name, id, romaji, kanji, air, rating, likes, seasons, characters, altNames, genres, tags, nsfw, nsfwReason, synopsis, numericalId, completed, publishers, studios,
|
|
meta: {completed: series.meta.completed, creator: series.meta.creator, locked: series.meta.locked}
|
|
});
|
|
});
|
|
}; //TODO check that series can be submitted but not completed. consider making this not allowed, or figure out what to do about the fact that editing all the fields is a complete pain
|