Feedback-Window
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 50s

This commit is contained in:
Torsten Schulz (local)
2026-06-09 07:57:36 +02:00
parent f0142d5682
commit 16465fafc8
9 changed files with 208 additions and 3 deletions

View File

@@ -0,0 +1,29 @@
import User from '../models/User.js';
import { sendMobileFeedbackEmail } from '../services/emailService.js';
const clean = (value, max = 4000) => String(value ?? '').trim().slice(0, max);
export const sendMobileFeedback = async (req, res) => {
try {
const message = clean(req.body?.message, 5000);
if (!message) {
return res.status(400).json({ error: 'message_required' });
}
const user = req.user?.id ? await User.findByPk(req.user.id) : null;
await sendMobileFeedbackEmail({
message,
screen: clean(req.body?.screen, 200),
clubId: req.body?.clubId ?? null,
appVersion: clean(req.body?.appVersion, 80),
platform: clean(req.body?.platform, 80) || 'Android',
backendBaseUrl: clean(req.body?.backendBaseUrl, 300),
user: user ? { id: user.id, username: user.username, email: user.email } : { id: req.user?.id },
});
return res.status(200).json({ success: true });
} catch (error) {
console.error('[sendMobileFeedback] - error:', error);
return res.status(500).json({ error: 'internalerror' });
}
};

View File

@@ -0,0 +1,9 @@
import express from 'express';
import { authenticate } from '../middleware/authMiddleware.js';
import { sendMobileFeedback } from '../controllers/mobileFeedbackController.js';
const router = express.Router();
router.post('/', authenticate, sendMobileFeedback);
export default router;

View File

@@ -67,6 +67,7 @@ import friendlyMatchSharedRoutes from './routes/friendlyMatchSharedRoutes.js';
import friendlyMatchInvitationRoutes from './routes/friendlyMatchInvitationRoutes.js';
import calendarRoutes from './routes/calendarRoutes.js';
import calendarEventRoutes from './routes/calendarEventRoutes.js';
import mobileFeedbackRoutes from './routes/mobileFeedbackRoutes.js';
import schedulerService from './services/schedulerService.js';
import { requestLoggingMiddleware } from './middleware/requestLoggingMiddleware.js';
import HttpError from './exceptions/HttpError.js';
@@ -366,6 +367,7 @@ app.use('/api/friendly-matches', friendlyMatchRoutes);
app.use('/api/friendly-match-invitations', friendlyMatchInvitationRoutes);
app.use('/api/calendar', calendarRoutes);
app.use('/api/calendar-events', calendarEventRoutes);
app.use('/api/mobile-feedback', mobileFeedbackRoutes);
// Middleware für dynamischen kanonischen Tag (vor express.static)
const setCanonicalTag = (req, res, next) => {

View File

@@ -95,4 +95,43 @@ const sendFriendlyMatchInvitationEmail = async ({
await transporter.sendMail(mailOptions);
};
export { sendActivationEmail, sendPasswordResetEmail, sendFriendlyMatchInvitationEmail };
const escapeHtml = (value) => String(value ?? '')
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
const sendMobileFeedbackEmail = async ({
message,
screen,
clubId,
appVersion,
platform,
backendBaseUrl,
user,
}) => {
const mailOptions = {
from: process.env.EMAIL_USER,
to: 'tsschulz2001@gmail.com',
subject: `Android Feedback${screen ? ` - ${screen}` : ''}`,
html: `
<div style="font-family: Arial, sans-serif; max-width: 760px; margin: 0 auto;">
<h2 style="color:#1f2937;">Android Feedback</h2>
<p><strong>Seite:</strong> ${escapeHtml(screen || '-')}</p>
<p><strong>Verein-ID:</strong> ${escapeHtml(clubId ?? '-')}</p>
<p><strong>User:</strong> ${escapeHtml(user?.username || user?.email || user?.id || '-')}</p>
<p><strong>User-ID:</strong> ${escapeHtml(user?.id || '-')}</p>
<p><strong>App-Version:</strong> ${escapeHtml(appVersion || '-')}</p>
<p><strong>Plattform:</strong> ${escapeHtml(platform || 'Android')}</p>
<p><strong>Backend:</strong> ${escapeHtml(backendBaseUrl || '-')}</p>
<hr style="border:none;border-top:1px solid #e5e7eb;margin:16px 0;">
<h3 style="color:#1f2937;">Meldung</h3>
<div style="white-space:pre-wrap;background:#f9fafb;border:1px solid #e5e7eb;border-radius:8px;padding:12px;">${escapeHtml(message)}</div>
</div>
`,
};
await transporter.sendMail(mailOptions);
};
export { sendActivationEmail, sendPasswordResetEmail, sendFriendlyMatchInvitationEmail, sendMobileFeedbackEmail };