add a pubsub service endpoint for cleanup soft deleted users
This commit is contained in:
74
packages/api/src/routers/svc/user.ts
Normal file
74
packages/api/src/routers/svc/user.ts
Normal file
@ -0,0 +1,74 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
import cors from 'cors'
|
||||
import express from 'express'
|
||||
import { LessThan } from 'typeorm'
|
||||
import { StatusType } from '../../entity/user'
|
||||
import { readPushSubscription } from '../../pubsub'
|
||||
import { deleteUsers } from '../../services/user'
|
||||
import { corsConfig } from '../../utils/corsConfig'
|
||||
import { logger } from '../../utils/logger'
|
||||
|
||||
type CleanupMessage = {
|
||||
subDays: number
|
||||
}
|
||||
|
||||
const isCleanupMessage = (obj: any): obj is CleanupMessage =>
|
||||
'subDays' in obj && !isNaN(obj.subDays)
|
||||
|
||||
const getCleanupMessage = (msgStr: string): CleanupMessage => {
|
||||
try {
|
||||
const obj = JSON.parse(msgStr) as unknown
|
||||
if (isCleanupMessage(obj)) {
|
||||
return obj
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('error deserializing event: ', { msgStr, err })
|
||||
}
|
||||
|
||||
return {
|
||||
subDays: 0, // default to 0
|
||||
}
|
||||
}
|
||||
|
||||
export function userServiceRouter() {
|
||||
const router = express.Router()
|
||||
|
||||
router.post(
|
||||
'/cleanup',
|
||||
cors<express.Request>(corsConfig),
|
||||
async (req, res) => {
|
||||
logger.info('cleanup soft deleted users')
|
||||
|
||||
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 cleanupMessage = getCleanupMessage(msgStr)
|
||||
const subTime = cleanupMessage.subDays * 1000 * 60 * 60 * 24 // convert days to milliseconds
|
||||
|
||||
try {
|
||||
const result = await deleteUsers({
|
||||
status: StatusType.Deleted,
|
||||
updatedAt: LessThan(new Date(Date.now() - subTime)), // subDays ago
|
||||
})
|
||||
logger.info('cleanup result', result)
|
||||
|
||||
return res.sendStatus(200)
|
||||
} catch (error) {
|
||||
logger.error('error cleaning up users', error)
|
||||
|
||||
return res.sendStatus(500)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return router
|
||||
}
|
||||
@ -31,6 +31,7 @@ import { newsletterServiceRouter } from './routers/svc/newsletters'
|
||||
// import { remindersServiceRouter } from './routers/svc/reminders'
|
||||
import { rssFeedRouter } from './routers/svc/rss_feed'
|
||||
import { uploadServiceRouter } from './routers/svc/upload'
|
||||
import { userServiceRouter } from './routers/svc/user'
|
||||
import { webhooksServiceRouter } from './routers/svc/webhooks'
|
||||
import { textToSpeechRouter } from './routers/text_to_speech'
|
||||
import { userRouter } from './routers/user_router'
|
||||
@ -121,6 +122,7 @@ export const createApp = (): {
|
||||
app.use('/svc/pubsub/webhooks', webhooksServiceRouter())
|
||||
app.use('/svc/pubsub/integrations', integrationsServiceRouter())
|
||||
app.use('/svc/pubsub/rss-feed', rssFeedRouter())
|
||||
app.use('/svc/pubsub/user', userServiceRouter())
|
||||
// app.use('/svc/reminders', remindersServiceRouter())
|
||||
app.use('/svc/email-attachment', emailAttachmentRouter())
|
||||
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import { DeepPartial, FindOptionsWhere, In } from 'typeorm'
|
||||
import { StatusType, User } from '../entity/user'
|
||||
import { authTrx } from '../repository'
|
||||
import { userRepository } from '../repository/user'
|
||||
import { SetClaimsRole } from '../utils/dictionary'
|
||||
|
||||
export const deleteUser = async (userId: string) => {
|
||||
await authTrx(
|
||||
@ -20,6 +22,28 @@ export const updateUser = async (userId: string, update: Partial<User>) => {
|
||||
)
|
||||
}
|
||||
|
||||
export const findUser = async (id: string): Promise<User | null> => {
|
||||
export const findActiveUser = async (id: string): Promise<User | null> => {
|
||||
return userRepository.findOneBy({ id, status: StatusType.Active })
|
||||
}
|
||||
|
||||
export const findUsersById = async (ids: string[]): Promise<User[]> => {
|
||||
return userRepository.findBy({ id: In(ids) })
|
||||
}
|
||||
|
||||
export const deleteUsers = async (criteria: FindOptionsWhere<User>) => {
|
||||
return authTrx(
|
||||
async (t) => t.getRepository(User).delete(criteria),
|
||||
undefined,
|
||||
undefined,
|
||||
SetClaimsRole.ADMIN
|
||||
)
|
||||
}
|
||||
|
||||
export const createUsers = async (users: DeepPartial<User>[]) => {
|
||||
return authTrx(
|
||||
async (t) => t.getRepository(User).save(users),
|
||||
undefined,
|
||||
undefined,
|
||||
SetClaimsRole.ADMIN
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user