Discussion only open while a study is active; v2.11.1
Publish Container / build (push) Has been cancelled

- Community posts, replies, likes and lesson comments are refused (403) for finished and planned studies
- Reading a closed study's community/comments returns an empty list with closed:true
- Discussion hub lists only active studies; community page shows a closed notice
- Lesson page hides Lesson Discussion, 'Discuss this lesson' and 'Go to Community' for inactive studies; Community buttons hidden on study/dashboard pages
- Tests for isStudyDiscussionOpen

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-09 11:49:03 -04:00
1 parent bb83f3e8c5
commit f1d4909acb
7 files changed
+98 -14

No files matched your search

+9 -1
View File
@@ -1,6 +1,6 @@
import { randomUUID } from 'node:crypto'
import { requireAdminAuth } from '../auth.js'
import { requireStudyAuth, normalizeStudySlug, normalizeLessonSectionId, hasStudyAccess, findStudyUserById, getCommunityDisplayName } from '../study-helpers.js'
import { requireStudyAuth, normalizeStudySlug, normalizeLessonSectionId, hasStudyAccess, findStudyUserById, getCommunityDisplayName, isStudyDiscussionOpen, DISCUSSION_CLOSED_MESSAGE } from '../study-helpers.js'
import { state } from '../state.js'
import { queueStudyCommentsWrite } from '../data.js'
import { MAX_STUDY_COMMENTS, MAX_STUDY_COMMENT_LENGTH } from '../config.js'
@@ -14,6 +14,10 @@ export function register(app) {
res.status(400).json({ message: 'Invalid study slug or section id.' }); return
}
if (!isStudyDiscussionOpen(studySlug)) {
res.json({ comments: [], closed: true, message: DISCUSSION_CLOSED_MESSAGE }); return
}
const comments = state.studyComments
.filter(c => c.studySlug === studySlug && c.sectionId === sectionId && c.isApproved === true)
.sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt))
@@ -44,6 +48,10 @@ export function register(app) {
res.status(403).json({ message: 'Please enroll in this study to leave a comment.' }); return
}
if (!isStudyDiscussionOpen(studySlug)) {
res.status(403).json({ message: DISCUSSION_CLOSED_MESSAGE, closed: true }); return
}
const text = typeof req.body?.text === 'string' ? req.body.text.trim() : ''
if (!text || text.length < 2 || text.length > MAX_STUDY_COMMENT_LENGTH) {
res.status(400).json({ message: `Comment must be between 2 and ${MAX_STUDY_COMMENT_LENGTH} characters.` }); return