feat: анти-спам блокировка повторной отправки на главной (cooldown по spam_interval_min)

- index.html/index.js: экран подтверждения отправки с обратным отсчётом
- локальный запрет повторной отправки через localStorage (по настройке spam_interval_min)
- server.js: отдаём spam_interval_min в public-settings (default 30)
- Dockerfile: timeout для apk add, чтобы сборка не зависала на недоступном зеркале
- docker-compose.yml: отключаем cloudflared сервис (закомментирован)
This commit is contained in:
dev
2026-09-13 12:27:35 +03:00
parent 0b763e5738
commit 2c8beedc4b
5 changed files with 93 additions and 23 deletions
+3 -2
View File
@@ -872,9 +872,10 @@ app.get('/api/settings', requireAdmin, async (_, res) => {
app.get('/api/public-settings', apiLimiter, async (_, res) => {
const out = await cacheWrap('public-settings', PUBLIC_TTL_MS, async () => {
const keys = ['footer_left', 'footer_right', 'share_show_student_message', 'share_show_entry_date', 'share_show_student_names', 'share_show_group_photos', 'cookie_notice_text'];
const keys = ['footer_left', 'footer_right', 'share_show_student_message', 'share_show_entry_date', 'share_show_student_names', 'share_show_group_photos', 'cookie_notice_text', 'spam_interval_min'];
const defaults = { spam_interval_min: '30' };
const result = {};
for (const k of keys) result[k] = await getSetting(k, '');
for (const k of keys) result[k] = await getSetting(k, defaults[k] || '');
return result;
});
res.json(out);