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 fetchNotifications(reqUser, options = {}) { const notifications = await knex('notifications') .select('notifications.*', 'alerts.id as alert_id', 'scenes.title as scene_title') .leftJoin('releases as scenes', 'scenes.id', 'notifications.scene_id') .leftJoin('alerts', 'alerts.id', 'notifications.alert_id') .where('notifications.user_id', reqUser.id) .limit(options.limit || 10) .orderBy('created_at', 'desc'); return notifications.map((notification) => ({ id: notification.id, sceneId: notification.scene_id, scene: { id: notification.scene_id, title: notification.scene_title, }, alertId: notification.alert_id, isSeen: notification.seen, createdAt: notification.created_at, })); } 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, }); }