From 8e77c047bd6ae7cc43851f06786aad316849f6ef Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Tue, 19 Dec 2023 14:25:55 +0800 Subject: [PATCH] add endpoint to prune items from folder older than some days --- packages/api/src/routers/svc/links.ts | 61 +++++++++++++++++++++++ packages/api/src/services/library_item.ts | 13 ++++- 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/packages/api/src/routers/svc/links.ts b/packages/api/src/routers/svc/links.ts index fcf06d34d..7d6e1d0db 100644 --- a/packages/api/src/routers/svc/links.ts +++ b/packages/api/src/routers/svc/links.ts @@ -2,8 +2,11 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import express from 'express' +import { LessThan } from 'typeorm' +import { LibraryItemState } from '../../entity/library_item' import { readPushSubscription } from '../../pubsub' import { createPageSaveRequest } from '../../services/create_page_save_request' +import { deleteLibraryItemsByAdmin } from '../../services/library_item' import { logger } from '../../utils/logger' interface CreateLinkRequestMessage { @@ -11,6 +14,31 @@ interface CreateLinkRequestMessage { userId: string } +type PruneMessage = { + expireInDays: number + folder?: string + state?: LibraryItemState +} + +const isPruneMessage = (obj: any): obj is PruneMessage => 'expireInDays' in obj + +const getPruneMessage = (msgStr: string): PruneMessage => { + try { + const obj = JSON.parse(msgStr) as unknown + if (isPruneMessage(obj)) { + return obj + } + } catch (err) { + console.log('error deserializing event: ', { msgStr, err }) + } + + // default to prune following folder items older than 30 days + return { + folder: 'following', + expireInDays: 30, + } +} + export function linkServiceRouter() { const router = express.Router() @@ -52,5 +80,38 @@ export function linkServiceRouter() { } }) + router.post('/prune', async (req, res) => { + logger.info('prune expired items in folder') + + const { message: msgStr, expired } = readPushSubscription(req) + + if (!msgStr) { + return res.status(200).send('Bad Request') + } + + if (expired) { + logger.info('discarding expired message') + return res.status(200).send('Expired') + } + + const pruneMessage = getPruneMessage(msgStr) + const expireTime = pruneMessage.expireInDays * 1000 * 60 * 60 * 24 // convert days to milliseconds + + try { + const result = await deleteLibraryItemsByAdmin({ + folder: pruneMessage.folder, + state: pruneMessage.state, + updatedAt: LessThan(new Date(Date.now() - expireTime)), + }) + logger.info('prune result', result) + + return res.sendStatus(200) + } catch (error) { + logger.error('error prune items', error) + + return res.sendStatus(500) + } + }) + return router } diff --git a/packages/api/src/services/library_item.ts b/packages/api/src/services/library_item.ts index 4864055da..6f9eafa80 100644 --- a/packages/api/src/services/library_item.ts +++ b/packages/api/src/services/library_item.ts @@ -1,6 +1,6 @@ import { ExpressionToken, LiqeQuery } from '@omnivore/liqe' import { DateTime } from 'luxon' -import { DeepPartial, ObjectLiteral } from 'typeorm' +import { DeepPartial, FindOptionsWhere, ObjectLiteral } from 'typeorm' import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity' import { EntityLabel } from '../entity/entity_label' import { Highlight } from '../entity/highlight' @@ -1062,3 +1062,14 @@ export const deleteLibraryItemsByUserId = async (userId: string) => { userId ) } + +export const deleteLibraryItemsByAdmin = async ( + criteria: FindOptionsWhere +) => { + return authTrx( + async (tx) => tx.withRepository(libraryItemRepository).delete(criteria), + undefined, + undefined, + 'admin' + ) +}