Added remaining elements to alert dialog.
This commit is contained in:
158
src/alerts.js
Executable file
158
src/alerts.js
Executable file
@@ -0,0 +1,158 @@
|
||||
import { knexOwner as knex } from './knex.js';
|
||||
import promiseProps from '../utils/promise-props.js';
|
||||
import { HttpError } from './errors.js';
|
||||
|
||||
function curateAlert(alert, context = {}) {
|
||||
return {
|
||||
id: alert.id,
|
||||
notify: alert.notify,
|
||||
email: alert.email,
|
||||
createdAt: alert.created_at,
|
||||
and: {
|
||||
fields: alert.all,
|
||||
actors: alert.all_actors,
|
||||
tags: alert.all_tags,
|
||||
entities: alert.all_entities,
|
||||
matches: alert.all_tags,
|
||||
},
|
||||
actors: context.actors?.map((actor) => ({
|
||||
id: actor.actor_id,
|
||||
name: actor.actor_name,
|
||||
slug: actor.actor_slug,
|
||||
})) || [],
|
||||
tags: context.tags?.map((tag) => ({
|
||||
id: tag.tag_id,
|
||||
name: tag.tag_name,
|
||||
slug: tag.tag_slug,
|
||||
})) || [],
|
||||
entities: context.entities?.map((entity) => ({
|
||||
id: entity.entity_id,
|
||||
name: entity.entity_name,
|
||||
slug: entity.entity_slug,
|
||||
type: entity.type,
|
||||
})) || [],
|
||||
matches: context.matches?.map((match) => ({
|
||||
id: match.id,
|
||||
property: match.property,
|
||||
expression: match.expression,
|
||||
})) || [],
|
||||
};
|
||||
}
|
||||
|
||||
export async function fetchAlerts(user) {
|
||||
const {
|
||||
alerts,
|
||||
actors,
|
||||
tags,
|
||||
entities,
|
||||
matches,
|
||||
} = await promiseProps({
|
||||
alerts: knex('alerts')
|
||||
.where('user_id', user.id),
|
||||
actors: knex('alerts_actors')
|
||||
.select('alerts_actors.*', 'actors.name as actor_name', 'actors.slug as actor_slug')
|
||||
.leftJoin('alerts', 'alerts.id', 'alerts_actors.alert_id')
|
||||
.leftJoin('actors', 'actors.id', 'alerts_actors.actor_id')
|
||||
.where('alerts.user_id', user.id),
|
||||
tags: knex('alerts_tags')
|
||||
.select('alerts_tags.*', 'tags.name as tag_name', 'tags.slug as tag_slug')
|
||||
.leftJoin('alerts', 'alerts.id', 'alerts_tags.alert_id')
|
||||
.leftJoin('tags', 'tags.id', 'alerts_tags.tag_id')
|
||||
.where('alerts.user_id', user.id),
|
||||
entities: knex('alerts_entities')
|
||||
.select('alerts_entities.*', 'entities.name as entity_name', 'entities.slug as entity_slug', 'entities.type as entity_type')
|
||||
.leftJoin('alerts', 'alerts.id', 'alerts_entities.alert_id')
|
||||
.leftJoin('entities', 'entities.id', 'alerts_entities.entity_id')
|
||||
.where('alerts.user_id', user.id),
|
||||
matches: knex('alerts_matches')
|
||||
.select('alerts_matches.*')
|
||||
.leftJoin('alerts', 'alerts.id', 'alerts_matches.alert_id')
|
||||
.where('alerts.user_id', user.id),
|
||||
});
|
||||
|
||||
const curatedAlerts = alerts.map((alert) => curateAlert(alert, {
|
||||
actors: actors.filter((actor) => actor.alert_id === alert.id),
|
||||
tags: tags.filter((tag) => tag.alert_id === alert.id),
|
||||
entities: entities.filter((entity) => entity.alert_id === alert.id),
|
||||
matches: matches.filter((match) => match.alert_id === alert.id),
|
||||
}));
|
||||
|
||||
return curatedAlerts;
|
||||
}
|
||||
|
||||
export async function createAlert(alert, reqUser) {
|
||||
if (!reqUser) {
|
||||
throw new HttpError('You are not authenthicated', 401);
|
||||
}
|
||||
|
||||
if ((!alert.actors || alert.actors.length === 0) && (!alert.tags || alert.tags.length === 0) && (!alert.entities || alert.entities.length === 0) && (!alert.matches || alert.matches.length === 0)) {
|
||||
throw new HttpError('Alert must contain at least one actor, tag or entity', 400);
|
||||
}
|
||||
|
||||
if (alert.matches?.some((match) => !match.property || !match.expression)) {
|
||||
throw new HttpError('Match must define a property and an expression', 400);
|
||||
}
|
||||
|
||||
const [{ id: alertId }] = await knex('alerts')
|
||||
.insert({
|
||||
user_id: reqUser.id,
|
||||
notify: alert.notify,
|
||||
email: alert.email,
|
||||
all: alert.all,
|
||||
all_actors: alert.allActors,
|
||||
all_entities: alert.allEntities,
|
||||
all_tags: alert.allTags,
|
||||
all_matches: alert.allMatches,
|
||||
})
|
||||
.returning('id');
|
||||
|
||||
await Promise.all([
|
||||
alert.actors?.length > 0 && knex('alerts_actors').insert(alert.actors.map((actorId) => ({
|
||||
alert_id: alertId,
|
||||
actor_id: actorId,
|
||||
}))),
|
||||
alert.tags?.length > 0 && knex('alerts_tags').insert(alert.tags.map((tagId) => ({
|
||||
alert_id: alertId,
|
||||
tag_id: tagId,
|
||||
}))),
|
||||
alert.matches?.length > 0 && knex('alerts_matches').insert(alert.matches.map((match) => ({
|
||||
alert_id: alertId,
|
||||
property: match.property,
|
||||
expression: match.expression,
|
||||
}))),
|
||||
alert.stashes?.length > 0 && knex('alerts_stashes').insert(alert.stashes.map((stashId) => ({
|
||||
alert_id: alertId,
|
||||
stash_id: stashId,
|
||||
}))),
|
||||
alert.entities?.length > 0 && knex('alerts_entities').insert(alert.entities.map((entityId) => ({
|
||||
alert_id: alertId,
|
||||
entity_id: entityId,
|
||||
})).slice(0, alert.allEntities ? 1 : Infinity)), // one scene can never match multiple entities in AND mode
|
||||
]);
|
||||
|
||||
return alertId;
|
||||
}
|
||||
|
||||
export async function removeAlert(alertId, reqUser) {
|
||||
await knex('alerts')
|
||||
.where('id', alertId)
|
||||
.where('user_id', reqUser.id)
|
||||
.delete();
|
||||
}
|
||||
|
||||
export async function updateNotification(notificationId, updatedNotification, reqUser) {
|
||||
await knex('notifications')
|
||||
.where('id', notificationId)
|
||||
.where('user_id', reqUser.id)
|
||||
.update({
|
||||
seen: updatedNotification.seen,
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateNotifications(updatedNotification, reqUser) {
|
||||
await knex('notifications')
|
||||
.where('user_id', reqUser.id)
|
||||
.update({
|
||||
seen: updatedNotification.seen,
|
||||
});
|
||||
}
|
||||
19
src/api.js
19
src/api.js
@@ -20,13 +20,24 @@ function getQuery(data) {
|
||||
}
|
||||
|
||||
function showFeedback(isSuccess, options = {}, errorMessage) {
|
||||
if (!isSuccess && typeof options.errorFeedback) {
|
||||
if (!isSuccess && (typeof options.errorFeedback === 'string' || options.appendErrorMessage)) {
|
||||
events.emit('feedback', {
|
||||
type: 'error',
|
||||
message: options.appendErrorMessage && errorMessage
|
||||
? `${options.errorFeedback}: ${errorMessage}`
|
||||
? `${options.errorFeedback ? `${options.errorFeedback}: ` : ''}${errorMessage}`
|
||||
: options.errorFeedback,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isSuccess) {
|
||||
events.emit('feedback', {
|
||||
type: 'error',
|
||||
message: 'Error, please try again',
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSuccess && options.successFeedback) {
|
||||
@@ -34,6 +45,8 @@ function showFeedback(isSuccess, options = {}, errorMessage) {
|
||||
type: 'success',
|
||||
message: options.successFeedback,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSuccess && options.undoFeedback) {
|
||||
@@ -82,6 +95,8 @@ export async function post(path, data, options = {}) {
|
||||
return body;
|
||||
}
|
||||
|
||||
console.log(body.statusMessage);
|
||||
|
||||
showFeedback(false, options, body.statusMessage);
|
||||
throw new Error(body.statusMessage);
|
||||
} catch (error) {
|
||||
|
||||
@@ -32,11 +32,17 @@ function curateTag(tag, context) {
|
||||
export async function fetchTags(options = {}) {
|
||||
const [tags, posters] = await Promise.all([
|
||||
knex('tags')
|
||||
.select('tags.*', 'tags_posters.media_id as poster_id')
|
||||
.select('tags.*')
|
||||
.modify((builder) => {
|
||||
if (!options.includeAliases) {
|
||||
builder.whereNull('alias_for');
|
||||
}
|
||||
|
||||
if (options.query) {
|
||||
builder
|
||||
.where('name', 'like', `%${options.query}%`)
|
||||
.orWhere('slug', 'like', `%${options.query}%`);
|
||||
}
|
||||
}),
|
||||
knex('tags_posters')
|
||||
.select('tags_posters.tag_id', 'media.*', knex.raw('row_to_json(entities) as entity'), knex.raw('row_to_json(parents) as entity_parent'))
|
||||
|
||||
37
src/web/alerts.js
Executable file
37
src/web/alerts.js
Executable file
@@ -0,0 +1,37 @@
|
||||
import {
|
||||
fetchAlerts,
|
||||
createAlert,
|
||||
removeAlert,
|
||||
updateNotifications,
|
||||
updateNotification,
|
||||
} from '../alerts.js';
|
||||
|
||||
export async function fetchAlertsApi(req, res) {
|
||||
const alerts = await fetchAlerts(req.user);
|
||||
|
||||
res.send(alerts);
|
||||
}
|
||||
|
||||
export async function createAlertApi(req, res) {
|
||||
const alertId = await createAlert(req.body, req.user);
|
||||
|
||||
res.send({ id: alertId });
|
||||
}
|
||||
|
||||
export async function removeAlertApi(req, res) {
|
||||
await removeAlert(req.params.alertId, req.user);
|
||||
|
||||
res.status(204).send();
|
||||
}
|
||||
|
||||
export async function updateNotificationsApi(req, res) {
|
||||
await updateNotifications(req.body, req.user);
|
||||
|
||||
res.status(204).send();
|
||||
}
|
||||
|
||||
export async function updateNotificationApi(req, res) {
|
||||
await updateNotification(req.params.notificationId, req.body, req.user);
|
||||
|
||||
res.status(204).send();
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { fetchEntities } from '../entities.js';
|
||||
|
||||
export async function fetchEntitiesApi(req, res) {
|
||||
const entities = await fetchEntities(req.body);
|
||||
const entities = await fetchEntities(req.query);
|
||||
|
||||
res.send(entities);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import { fetchScenesApi } from './scenes.js';
|
||||
import { fetchActorsApi } from './actors.js';
|
||||
import { fetchMoviesApi } from './movies.js';
|
||||
import { fetchEntitiesApi } from './entities.js';
|
||||
import { fetchTagsApi } from './tags.js';
|
||||
|
||||
import {
|
||||
setUserApi,
|
||||
@@ -42,6 +43,14 @@ import {
|
||||
updateStashApi,
|
||||
} from './stashes.js';
|
||||
|
||||
import {
|
||||
fetchAlertsApi,
|
||||
createAlertApi,
|
||||
removeAlertApi,
|
||||
updateNotificationApi,
|
||||
updateNotificationsApi,
|
||||
} from './alerts.js';
|
||||
|
||||
import initLogger from '../logger.js';
|
||||
|
||||
const logger = initLogger();
|
||||
@@ -118,6 +127,9 @@ export default async function initServer() {
|
||||
router.get('/api/users/:userId', fetchUserApi);
|
||||
router.post('/api/users', signupApi);
|
||||
|
||||
router.patch('/api/users/:userId/notifications', updateNotificationsApi);
|
||||
router.patch('/api/users/:userId/notifications/:notificationId', updateNotificationApi);
|
||||
|
||||
// STASHES
|
||||
router.post('/api/stashes', createStashApi);
|
||||
router.patch('/api/stashes/:stashId', updateStashApi);
|
||||
@@ -131,6 +143,11 @@ export default async function initServer() {
|
||||
router.delete('/api/stashes/:stashId/scenes/:sceneId', unstashSceneApi);
|
||||
router.delete('/api/stashes/:stashId/movies/:movieId', unstashMovieApi);
|
||||
|
||||
// ALERTS
|
||||
router.get('/api/alerts', fetchAlertsApi);
|
||||
router.post('/api/alerts', createAlertApi);
|
||||
router.delete('/api/alerts/:alertId', removeAlertApi);
|
||||
|
||||
// SCENES
|
||||
router.get('/api/scenes', fetchScenesApi);
|
||||
|
||||
@@ -143,6 +160,9 @@ export default async function initServer() {
|
||||
// ENTITIES
|
||||
router.get('/api/entities', fetchEntitiesApi);
|
||||
|
||||
// TAGS
|
||||
router.get('/api/tags', fetchTagsApi);
|
||||
|
||||
router.get('*', async (req, res, next) => {
|
||||
const pageContextInit = {
|
||||
urlOriginal: req.originalUrl,
|
||||
|
||||
9
src/web/tags.js
Normal file
9
src/web/tags.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import { fetchTags } from '../tags.js';
|
||||
|
||||
export async function fetchTagsApi(req, res) {
|
||||
const tags = await fetchTags({
|
||||
query: req.query.query,
|
||||
});
|
||||
|
||||
res.send(tags);
|
||||
}
|
||||
Reference in New Issue
Block a user