151 lines
4.3 KiB
JavaScript
151 lines
4.3 KiB
JavaScript
const userDb = require("../db/user.db")
|
|
const userNoteDb = require("../db/userNote.db")
|
|
const refreshTokenDb = require("../db/refreshToken.db")
|
|
const { queueUserUpdate, queueUserNoteCreate } = require("./redis/dbSync.service")
|
|
const {
|
|
getOrCacheUserModeration,
|
|
setUserModerationInRedis,
|
|
} = require("./redis/userModerationCache.service")
|
|
|
|
function assertUserId(userId) {
|
|
const id = Number(userId)
|
|
if (!Number.isInteger(id) || id <= 0) {
|
|
const err = new Error("INVALID_USER_ID")
|
|
err.statusCode = 400
|
|
throw err
|
|
}
|
|
return id
|
|
}
|
|
|
|
function computeUntil(durationDays = 7) {
|
|
const days = Number(durationDays)
|
|
const safeDays = Number.isFinite(days) && days > 0 ? days : 7
|
|
return new Date(Date.now() + safeDays * 24 * 60 * 60 * 1000)
|
|
}
|
|
|
|
async function ensureUserExists(userId) {
|
|
const id = assertUserId(userId)
|
|
const existing = await getOrCacheUserModeration(id)
|
|
if (!existing) {
|
|
const err = new Error("USER_NOT_FOUND")
|
|
err.statusCode = 404
|
|
throw err
|
|
}
|
|
return { id, existing }
|
|
}
|
|
|
|
async function updateUserModeration(id, patch) {
|
|
const updatedAt = new Date()
|
|
const payload = { id, ...patch }
|
|
await setUserModerationInRedis(payload, { ttlSeconds: 60 * 60 })
|
|
queueUserUpdate({
|
|
userId: id,
|
|
data: patch,
|
|
updatedAt: updatedAt.toISOString(),
|
|
}).catch((err) => console.error("DB sync user update failed:", err?.message || err))
|
|
return { id, ...patch }
|
|
}
|
|
|
|
async function muteUser(userId, { durationDays = 7 } = {}) {
|
|
const { id } = await ensureUserExists(userId)
|
|
const mutedUntil = computeUntil(durationDays)
|
|
return updateUserModeration(id, { mutedUntil })
|
|
}
|
|
|
|
async function clearMute(userId) {
|
|
const { id } = await ensureUserExists(userId)
|
|
return updateUserModeration(id, { mutedUntil: null })
|
|
}
|
|
|
|
async function suspendUser(userId, { durationDays = 7 } = {}) {
|
|
const { id } = await ensureUserExists(userId)
|
|
const suspendedUntil = computeUntil(durationDays)
|
|
return updateUserModeration(id, { suspendedUntil })
|
|
}
|
|
|
|
async function clearSuspend(userId) {
|
|
const { id } = await ensureUserExists(userId)
|
|
return updateUserModeration(id, { suspendedUntil: null })
|
|
}
|
|
|
|
async function disableUser(userId) {
|
|
const { id } = await ensureUserExists(userId)
|
|
const disabledAt = new Date()
|
|
await refreshTokenDb.revokeAllUserRefreshTokens(id)
|
|
return updateUserModeration(id, { disabledAt })
|
|
}
|
|
|
|
async function enableUser(userId) {
|
|
const { id } = await ensureUserExists(userId)
|
|
return updateUserModeration(id, { disabledAt: null })
|
|
}
|
|
|
|
async function updateUserRole(userId, role) {
|
|
const { id } = await ensureUserExists(userId)
|
|
const normalized = String(role || "").toUpperCase()
|
|
if (!["USER", "MOD"].includes(normalized)) {
|
|
const err = new Error("INVALID_ROLE")
|
|
err.statusCode = 400
|
|
throw err
|
|
}
|
|
return updateUserModeration(id, { role: normalized })
|
|
}
|
|
|
|
async function addUserNote({ userId, createdById, note }) {
|
|
const uid = assertUserId(userId)
|
|
const cid = assertUserId(createdById)
|
|
const text = String(note || "").trim()
|
|
if (!text) {
|
|
const err = new Error("NOTE_REQUIRED")
|
|
err.statusCode = 400
|
|
throw err
|
|
}
|
|
|
|
queueUserNoteCreate({
|
|
userId: uid,
|
|
createdById: cid,
|
|
note: text.slice(0, 1000),
|
|
createdAt: new Date().toISOString(),
|
|
}).catch((err) => console.error("DB sync user note failed:", err?.message || err))
|
|
|
|
return { userId: uid, createdById: cid, note: text.slice(0, 1000) }
|
|
}
|
|
|
|
async function listUserNotes({ userId, page = 1, limit = 20 }) {
|
|
const uid = assertUserId(userId)
|
|
const safePage = Number.isInteger(Number(page)) && Number(page) > 0 ? Number(page) : 1
|
|
const safeLimit = Number.isInteger(Number(limit)) && Number(limit) > 0 ? Number(limit) : 20
|
|
const skip = (safePage - 1) * safeLimit
|
|
|
|
const [notes, total, userExists] = await Promise.all([
|
|
userNoteDb.listUserNotes({ userId: uid, skip, take: safeLimit }),
|
|
userNoteDb.countUserNotes({ userId: uid }),
|
|
userDb.findUser({ id: uid }, { select: { id: true } }).then((u) => Boolean(u)),
|
|
])
|
|
|
|
if (!userExists) {
|
|
const err = new Error("USER_NOT_FOUND")
|
|
err.statusCode = 404
|
|
throw err
|
|
}
|
|
|
|
return {
|
|
page: safePage,
|
|
total,
|
|
totalPages: total ? Math.ceil(total / safeLimit) : 0,
|
|
results: notes,
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
muteUser,
|
|
clearMute,
|
|
suspendUser,
|
|
clearSuspend,
|
|
disableUser,
|
|
enableUser,
|
|
updateUserRole,
|
|
addUserNote,
|
|
listUserNotes,
|
|
}
|