Compare commits

..

2 Commits

  1. 1
      .idea/modules.xml
  2. 1
      .idea/natsuki-api.iml
  3. 72
      api/ani/v1/routes/series/add.js
  4. 46
      api/ani/v1/routes/series/edits.js
  5. 7
      api/ani/v1/routes/series/queue.js
  6. 3
      api/util/auth/authorize.js
  7. 2
      api/util/startup/cache/series.js

@ -3,6 +3,7 @@
<component name="ProjectModuleManager"> <component name="ProjectModuleManager">
<modules> <modules>
<module fileurl="file://$PROJECT_DIR$/.idea/natsuki-api.iml" filepath="$PROJECT_DIR$/.idea/natsuki-api.iml" /> <module fileurl="file://$PROJECT_DIR$/.idea/natsuki-api.iml" filepath="$PROJECT_DIR$/.idea/natsuki-api.iml" />
<module fileurl="file://$PROJECT_DIR$/../wubzy-api/.idea/wubzy-api.iml" filepath="$PROJECT_DIR$/../wubzy-api/.idea/wubzy-api.iml" />
</modules> </modules>
</component> </component>
</project> </project>

@ -8,5 +8,6 @@
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="wubzy-api" />
</component> </component>
</module> </module>

@ -28,7 +28,7 @@ module.exports = (app, router) => {
|| !req.body.name || !req.body.romaji || !req.params.id || !req.authenticatedUser || !req.authenticatedUser.id || !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.name.match(/^[\w_\- ]+$/gm) || req.body.name.length > 150
|| req.body.romaji.length > 150 || req.body.romaji.length > 150
|| !req.params.id.match(/^[\w_\-]+$/gm) || req.params.id.length > 25 || !req.params.id.match(/^(?=.*[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.");} ) {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({ let series = new Anime({
@ -56,49 +56,35 @@ module.exports = (app, router) => {
//VALIDATION //VALIDATION
const badReq = msg => {res.status(400).send(msg); return null;}; const badReq = msg => {res.status(400).send(msg); return null;};
const validateStringList = (maxLength, list, listName, regex) => { const validateStringList = (maxLength, list, listName, maxListLength, regex) => {
let bad = false; let good = false;
list.forEach(item => {bad = !bad && typeof item === 'string' && item.length < maxLength && (!regex || item.match(regex))}); if (maxListLength !== -1 && list.length > maxListLength) {return badReq(`Your ${listName} had too many items.`);}
if (bad) {return badReq();} 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; return list;
}; };
let options = req.body; let options = req.body;
if (options.altNames && Array.isArray(options.altNames) && options.altNames.length <= 10) { if (options.altNames) {series.altNames = validateStringList(150, options.altNames, 'altNames'); if (!series.altNames) {return;}}
let bad = false; if (options.tags) {series.tags = validateStringList(25, options.tags, 'tags', 25, /^[\w-]+$/gm);if (!series.tags) {return;}}
options.altNames.forEach(name => bad = !bad && typeof name === 'string' && name.length < 150);
if (bad) {return badReq("Your altNames did not contain purely strings, or one of them was too long.");}
series.altNames = options.altNames;
}
if (options.tags && Array.isArray(options.tags) && options.tags.length <= 10) {
let bad = false;
options.tags.forEach(tag => bad = !bad && typeof tag === 'string' && tag.length < 25 && tag.match(/^[\w-]+$/gm));
if (bad) {return badReq("Your tags did not contain purely strings, or one of them was too long, or contained invalud characters");}
series.tags = options.tags.map(tag => tag.toLowerCase());
}
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.");} 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) { else if (options.nsfw) {
series.nsfw = options.nsfw; series.nsfw = options.nsfw;
series.nsfwReason = options.nsfwReason; series.nsfwReason = options.nsfwReason;
} }
if (options.genres && Array.isArray(options.genres) && options.genres.length <= 10) { if (options.genres) {series.genres = validateStringList(25, options.genres, 'genres', 10, /^[\w- ]+$/gm); if (!series.genres) {return;}} //TODO genres as DB
let bad = false; if (options.streamAt) {series.streamAt = validateStringList(25, options.streamAt, 'streamAt locations', 10, /^[\w- ]+$/gm);if (!series.streamAt) {return;}}
options.genres.forEach(tag => bad = !bad && typeof tag === 'string' && tag.length < 25 && tag.match(/^[\w- ]+$/gm)); if (options.publishers) {series.publishers = validateStringList(25, options.publishers, 'publishers', 5, /^[\w- ]+$/gm);if (!series.publishers) {return;}}
if (bad) {return badReq("Your genres did not contain purely strings, or one of them was too long.");} //well this is hell. never again will i make a schema so fat // i have no idea what im doing here
series.genres = options.genres; //TODO validate pt. 2, the requiem
} //TODO genres as DB if (options.air) {
if (options.streamAt && Array.isArray(options.streamAt) && options.streamAt.length <= 10) { if (options.air.from) {series.air.from = options.air.from;}
let bad = false; if (options.air.to) {series.air.to = options.air.to;} //it's 9am send help
options.streamAt.forEach(tag => bad = !bad && typeof tag === 'string' && tag.length < 25 && tag.match(/^[\w- ]+$/gm));
if (bad) {return badReq("Your streamAt locations did not contain purely strings, or one of them was too long.");}
series.streamAt = options.streamAt;
}
if (options.publishers && Array.isArray(options.publishers) && options.publishers.length <= 10) {
let bad = false;
options.publishers.forEach(tag => bad = !bad && typeof tag === 'string' && tag.length < 25 && tag.match(/^[\w- ]+$/gm));
if (bad) {return badReq("Your publishers did not contain purely strings, or one of them was too long.");}
series.publishers = options.publishers;
} }
//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 () => { return series.save().then(async () => {
app.cache.series[series.id] = { app.cache.series[series.id] = {
@ -110,8 +96,22 @@ module.exports = (app, router) => {
genres: series.genres, genres: series.genres,
tags: series.tags 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"}.`); 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.");}); }).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 //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

@ -0,0 +1,46 @@
module.exports = (app, router) => {
const Anime = app.db.models.ani.series;
const editCheck = (series, req, res) => {
if (!series) {res.status(400).send("A series with that ID doesn't exist!"); return 0;}
if (series.meta.locked) {res.status(401).send("This series has been locked. Nobody can edit it unless it has been unlocked. Please contact API admin to have it unlocked if you believe there is erroneous information."); return 0;}
if (series.meta.approved && !series.meta.locked && req.unauthorized) {res.status(401).send("This series has been approved, so you must have series approval permissions in order to do that."); return 0;}
if (series.meta.creator !== req.authenticatedUser && req.unauthorized) {res.status(401).send("You must have series approval permissions or be the series' creator to do that !"); return 0;}
if (!req.authenticatedUser || !req.authenticatedUser.id) {res.status(401).send("You are not authorized to do that!"); return 0;}
return true;
}
router.route('/:id/synopsis') //completed i think?
.patch(app.auth.token, app.auth.perms('series-submit'), async (req, res, next) => {
const series = await Anime.findOne({id: req.params.id.toLowerCase()});
if (!editCheck(series, req, res)) {return;}
if (!req.body.synopsis) {return res.status(400).send("You did not provide a new synopsis in your body.");}
if (req.body.synopsis.length > 1000) {return res.status(400).send("Your new synopsis is too long!");}
try {
series.synopsis.synopsis = req.body.synopsis;
if (!series.synopsis.by) {series.synopsis.by = req.authenticatedUser.id;}
if (req.body.updateAuthor === true && series.synopsis.by !== req.authenticatedUser.id) {series.synopsis.by = req.authenticatedUser.id;}
}
catch {return res.status(500).send("There was an error trying to update your synopsis. Please try again.");}
series.meta.edits.push({
user: req.authenticatedUser.id,
timestamp: new Date().getTime(),
action: "Updated synopsis"
});
series.markModified('synopsis.synopsis');
series.markModified('meta.edits');
return series.save()
.then(() => res.send("Synopsis updated."))
.catch(() => res.status(500).send("There was an error trying to update your synopsis. Please try again."));
})
.get(app.auth.tokenPass, app.auth.permsPass('series-approve'), async (req, res) => { //working
const series = await Anime.findOne({id: req.params.id.toLowerCase()});
if (!series || (series && !series.meta.completed && req.unauthorized)) {return res.status(400).send("A series with that ID doesn't exist!");}
return res.send({synopsis: series.synopsis.synopsis, by: series.synopsis.by});
});
//router.route()
router.use('/:id/altnames', app.auth.tokenPass, app.auth.permsPass('series-approve'))
};

@ -0,0 +1,7 @@
module.exports = (app, router) => {
router.use('/queue', app.auth.token, app.auth.perms('series-approve'),
(req, res, next) => {req.listData = app.cache.seriesQueue; next();},
app.util.list(router, '/queue')
);
//i dont know why i made a whole file for this but oh well here we are
};

@ -2,7 +2,8 @@ module.exports = (app, passAuth) => {
return requiredPermissions => { return requiredPermissions => {
if (!Array.isArray(requiredPermissions)) {requiredPermissions = [requiredPermissions];} if (!Array.isArray(requiredPermissions)) {requiredPermissions = [requiredPermissions];}
return (req, res, next) => { return (req, res, next) => {
if (!req.authenticatedUser) {return next();} if (!req.authenticatedUser && passAuth) {return next();}
if (!req.authenticatedUser) {return res.status(401).send("You are not authenticated!");}
if (req.authenticatedUser.permissions.includes("admin")) {return next();} if (req.authenticatedUser.permissions.includes("admin")) {return next();}
else { else {
let hasAllPerms = true; let hasAllPerms = true;

@ -12,6 +12,8 @@ module.exports = async (app, spinner) => {
let {id, name, romaji, kanji, altNames, genres, tags} = series; let {id, name, romaji, kanji, altNames, genres, tags} = series;
app.cache.series[series.id] = {id, name, romaji, kanji, altNames, genres, tags}; //keep an in-memory index of series' searchable items app.cache.series[series.id] = {id, name, romaji, kanji, altNames, genres, tags}; //keep an in-memory index of series' searchable items
app.cache.series[series.id].synopsis = series.synopsis.synopsis; app.cache.series[series.id].synopsis = series.synopsis.synopsis;
app.cache.seriesQueue = [];
if (series.meta.submitted) {app.cache.seriesQueue.push({id: series.id, submitter: series.meta.submitted});}
spinner.update({text: `${chalk.gray('[PROC]')} >> ${chalk.blueBright(`Cached`)} ${chalk.white(`${amount}`)} ${chalk.blueBright(`ani DB series.`)}`}); spinner.update({text: `${chalk.gray('[PROC]')} >> ${chalk.blueBright(`Cached`)} ${chalk.white(`${amount}`)} ${chalk.blueBright(`ani DB series.`)}`});
app.cache.seriesCount++; app.cache.seriesCount++;
amount++; amount++;

Loading…
Cancel
Save