diff --git a/pages/entities/@entitySlug/+Page.vue b/pages/entities/@entitySlug/+Page.vue index 3d8e720..f72a736 100644 --- a/pages/entities/@entitySlug/+Page.vue +++ b/pages/entities/@entitySlug/+Page.vue @@ -44,7 +44,7 @@ { // no need to wait for this }); + + return fetchUser(storedKey.user_id); } export async function createKey(reqUser) { diff --git a/src/web/server.js b/src/web/server.js index 4c1085e..4eeaf0a 100644 --- a/src/web/server.js +++ b/src/web/server.js @@ -16,6 +16,7 @@ import initRestrictionHandler from './restrictions.js'; import { scenesRouter } from './scenes.js'; import { actorsRouter } from './actors.js'; +import { syncRouter } from './sync.js'; import { fetchMoviesApi } from './movies.js'; import { fetchEntitiesApi } from './entities.js'; @@ -122,11 +123,7 @@ export default async function initServer() { router.use('/api/*', async (req, _res, next) => { if (req.headers['api-user']) { - await verifyKey(req.headers['api-user'], req.headers['api-key'], req); - - req.user = { // eslint-disable-line no-param-reassign - id: Number(req.headers['api-user']), - }; + req.user = await verifyKey(req.headers['api-user'], req.headers['api-key'], req); } next(); @@ -150,6 +147,7 @@ export default async function initServer() { router.use(alertsRouter); router.use(scenesRouter); router.use(actorsRouter); + router.use(syncRouter); // MOVIES router.get('/api/movies', fetchMoviesApi); diff --git a/src/web/sync.js b/src/web/sync.js new file mode 100644 index 0000000..b406794 --- /dev/null +++ b/src/web/sync.js @@ -0,0 +1,49 @@ +import Router from 'express-promise-router'; + +import { + syncScenes, + syncMovies, + syncActors, + syncStashes, +} from '../sync.js'; + +import verifyAbility from '../../utils/verify-ability.js'; + +export const syncRouter = Router(); + +async function syncScenesApi(req, res) { + verifyAbility(req.user, 'sync', null, { throwError: true }); + + await syncScenes(req.body.sceneIds); + + res.status(204).send(); +} + +async function syncMoviesApi(req, res) { + verifyAbility(req.user, 'sync', null, { throwError: true }); + + await syncMovies(req.body.movieIds); + + res.status(204).send(); +} + +async function syncActorsApi(req, res) { + verifyAbility(req.user, 'sync', null, { throwError: true }); + + await syncActors(req.body.actorIds); + + res.status(204).send(); +} + +async function syncStashesApi(req, res) { + verifyAbility(req.user, 'sync', null, { throwError: true }); + + await syncStashes(req.body.stashIds); + + res.status(204).send(); +} + +syncRouter.post('/api/sync/scenes', syncScenesApi); +syncRouter.post('/api/sync/movies', syncMoviesApi); +syncRouter.post('/api/sync/actors', syncActorsApi); +syncRouter.post('/api/sync/stashes', syncStashesApi); diff --git a/utils/verify-ability.js b/utils/verify-ability.js index 36b47f9..fa712cb 100644 --- a/utils/verify-ability.js +++ b/utils/verify-ability.js @@ -1,4 +1,6 @@ -export default function verifyAbility(user, subject, action) { +import { HttpError } from '../src/errors.js'; + +function checkAbility(user, subject, action) { if (!user?.abilities) { return null; } @@ -8,8 +10,18 @@ export default function verifyAbility(user, subject, action) { } if (subject) { - return user.abilities.some((ability) => ability[subject] === true); + return user.abilities.some((ability) => ability[subject] === true || (ability.subject === subject && !ability.action)); } return false; } + +export default function verifyAbility(user, subject, action, options = {}) { + const isAble = checkAbility(user, subject, action); + + if (!isAble && options.throwError) { + throw new HttpError(`Insufficient privileges for ${[subject, action].filter(Boolean).join()}`, 403); + } + + return isAble; +}