fix: data-action buttons, sources enabled, email notify, ticket detail single-page
- app.js: mount page objects via inline script (CSP blocks eval → data-action buttons dead) - app.js: add tickets list entry to sidebar nav (admin could not find claimed tickets) - sources: GET /sources?manage=1 returns enabled field for admin view - mailer: sendNotifyEmail consumes email-type notification_configs (webhook_url = recipients) - tickets: trigger notify on created/claimed/transferred/updated/status-change - notifications: email type shows recipient field, validates email list - ticket-detail: single-page chat flow (player/staff bubbles) + processing log timeline - css: chat bubble styles
This commit is contained in:
@@ -22,7 +22,7 @@ const { v4: uuid } = require('uuid');
|
||||
const { getPool, query, getRow } = require('../db');
|
||||
const { authenticate, requireRole, optionalAuth } = require('../middleware/auth');
|
||||
const { upload, finalizeUpload } = require('../middleware/upload');
|
||||
const { sendEmail } = require('../mailer');
|
||||
const { sendEmail, sendNotifyEmail } = require('../mailer');
|
||||
const { sendWebhook } = require('../webhook');
|
||||
const { validateLengths, ticketAnonLimiter } = require('../middleware/security');
|
||||
|
||||
@@ -221,6 +221,7 @@ router.post('/', ticketAnonLimiter, optionalAuth, upload.array('files', 5), fina
|
||||
|
||||
const emailVars = { reporter_game_name: rgn, reporter_game_uid: rgu, ticket_type: TL[type], ticket_id: ticketId, ticket_title: title, ticket_reason: reason||'', ticket_description: description||'', tracking_link: `${await siteUrl()}#/ticket/${ticketId}`, date: new Date().toLocaleString('zh-CN') };
|
||||
if (req.user?.email) sendEmail(req.user.email, 'ticket_created', emailVars).catch(()=>{});
|
||||
sendNotifyEmail('ticket_created', { ...emailVars, type });
|
||||
sendWebhook('ticket_created', { ...emailVars, type }).catch(()=>{});
|
||||
|
||||
res.setHeader('Set-Cookie', `tracking_token=${trackingToken}; Path=/; SameSite=Lax; Max-Age=${365*24*3600}`);
|
||||
@@ -276,6 +277,7 @@ router.post('/:id/claim', authenticate, requireRole('owner','admin'), async (req
|
||||
|
||||
const user = await getRow('SELECT username, game_name FROM users WHERE id = ?', [req.user.id]);
|
||||
await sendEmailForTicket(t, 'ticket_claimed', { assigned_to: user?.game_name||req.user.username, claim_note: claimNote });
|
||||
sendNotifyEmail('ticket_claimed', { ticket_id: t.id, ticket_title: t.title, assigned_to: user?.game_name||req.user.username, claim_note: claimNote });
|
||||
sendWebhook('ticket_claimed', { ticket_id: t.id, ticket_title: t.title, assigned_to: user?.game_name||req.user.username, claim_note: claimNote }).catch(()=>{});
|
||||
res.json({ message: '认领成功' });
|
||||
});
|
||||
@@ -306,6 +308,7 @@ router.post('/:id/transfer', authenticate, requireRole('owner','admin'), async (
|
||||
|
||||
const fromUser = await getRow('SELECT game_name FROM users WHERE id = ?', [req.user.id]);
|
||||
await sendEmailForTicket(t, 'ticket_transferred', { from_user: fromUser?.game_name||req.user.username, to_user: toUser.game_name||toUser.username, transfer_reason: reason||'', claim_note: t.claim_note||'无' });
|
||||
sendNotifyEmail('ticket_transferred', { ticket_id: t.id, ticket_title: t.title, from_user: fromUser?.game_name, to_user: toUser.game_name, reason: reason||'' });
|
||||
sendWebhook('ticket_transferred', { ticket_id: t.id, ticket_title: t.title, from_user: fromUser?.game_name, to_user: toUser.game_name, reason: reason||'' }).catch(()=>{});
|
||||
res.json({ message: '转交成功' });
|
||||
});
|
||||
@@ -349,6 +352,7 @@ router.post('/:id/response', authenticate, async (req, res) => {
|
||||
|
||||
if (isStaff && t.user_id) {
|
||||
await sendEmailForTicket(t, 'ticket_updated', { update_note: content });
|
||||
sendNotifyEmail('ticket_updated', { ticket_id: t.id, ticket_title: t.title, responder: req.user.username, note: content });
|
||||
sendWebhook('ticket_updated', { ticket_id: t.id, ticket_title: t.title, responder: req.user.username, note: content }).catch(()=>{});
|
||||
}
|
||||
res.status(201).json({ message: '回复成功' });
|
||||
@@ -369,6 +373,7 @@ async function notifyStatusChange(ticket, newStatus) {
|
||||
if (ticket.user_id) {
|
||||
await sendEmailForTicket(ticket, 'ticket_updated', { ticket_status: SL[newStatus]||newStatus, ticket_status_color: SC[newStatus]||'#6b7280' });
|
||||
}
|
||||
sendNotifyEmail('ticket_updated', { ticket_id: ticket.id, ticket_title: ticket.title, new_status: SL[newStatus]||newStatus, type: ticket.type });
|
||||
sendWebhook('ticket_updated', { ticket_id: ticket.id, ticket_title: ticket.title, new_status: SL[newStatus]||newStatus, type: ticket.type }).catch(()=>{});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user