Files
Webklar-Kundenbereich/server/routes/admin/funktionenAdmin.js
T

87 lines
3.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Router } from 'express'
import { config } from '../../config.js'
import { getDocument } from '../../services/appwriteAdmin.js'
import { withLocks } from '../../services/keyedLock.js'
import { adminActivate, assertOwnProject, disable, getFeatureDoc, listCustomerFeatures } from '../../services/siteFeatures.js'
/**
* Funktionen (News-Pinnwand) je Kundenprojekt im Ticketsystem (Kunden-Reiter "Funktionen").
* Eingehaengt in routes/team/index.js unter /api/team/funktionen, dort hinter requireStaffJwt.
*/
export const funktionenAdminRouter = Router()
const NOTE_MAX = 300
const adminName = (req) => req.session.adminName || req.session.name || 'Admin'
function sendError(res, err) {
const status = err.status >= 400 && err.status < 600 ? err.status : 500
if (status >= 500) console.error('[funktionen-admin]', err.message)
return res.status(status).json({ error: err.message || 'Interner Serverfehler' })
}
/** Neuester Eintrag zuerst; aeltere Eintraege fallen vorn heraus, sobald 300 Zeichen voll sind. */
export function composeAdminNote(previous, { by, action, note }, now = new Date()) {
const entry = `${now.toISOString()} ${by}: ${action}${note ? ` – ${note}` : ''}`.slice(0, NOTE_MAX)
const older = String(previous || '')
return older ? `${entry} | ${older}`.slice(0, NOTE_MAX) : entry
}
async function ticketExists(ticketId) {
if (!ticketId) return false
try {
await getDocument(config.collections.workorders, ticketId)
return true
} catch (err) {
if (err.status === 404) return false
throw err
}
}
async function adminView(customerId) {
const projects = await listCustomerFeatures(customerId)
for (const p of projects) {
const news = p.features.news
const doc = await getFeatureDoc(customerId, p.projectId, 'news')
news.adminNote = doc?.adminNote || ''
news.ticketMissing = news.state === 'setup' && !(doc?.ticketId && (await ticketExists(doc.ticketId)))
}
return projects
}
funktionenAdminRouter.get('/:customerId', async (req, res) => {
try {
res.json({ projects: await adminView(req.params.customerId) })
} catch (err) {
sendError(res, err)
}
})
funktionenAdminRouter.post('/:customerId/news', async (req, res) => {
try {
const { customerId } = req.params
const { projectId, action } = req.body || {}
const note = typeof req.body?.note === 'string' ? req.body.note.trim().slice(0, 120) : ''
if (action !== 'activate' && action !== 'disable') {
return res.status(400).json({ error: 'Unbekannte Aktion' })
}
if (typeof projectId !== 'string' || !projectId) {
return res.status(400).json({ error: 'Projekt fehlt' })
}
await assertOwnProject(customerId, projectId)
const by = adminName(req)
await withLocks([`feature-admin:${projectId}`], async () => {
const doc = await getFeatureDoc(customerId, projectId, 'news')
const adminNote = composeAdminNote(doc?.adminNote, { by, action, note })
if (action === 'activate') {
await adminActivate(customerId, projectId, 'news', { by, adminNote })
} else {
if (!doc) throw Object.assign(new Error('Für dieses Projekt gibt es noch keine News-Pinnwand'), { status: 409 })
await disable(doc.$id, { by, adminNote })
}
})
res.json({ projects: (await adminView(customerId)).filter((p) => p.projectId === projectId) })
} catch (err) {
sendError(res, err)
}
})