Spaces:
Sleeping
Sleeping
Upload folder using huggingface_hub
Browse files- index.js +2 -0
- models/WeekBrief.js +6 -0
- routes/admin-weeks.js +34 -0
- routes/search.js +11 -0
index.js
CHANGED
|
@@ -21,6 +21,7 @@ const messagesRoutes = require('./routes/messages');
|
|
| 21 |
const refinityRoutes = require('./routes/refinity');
|
| 22 |
const linksRoutes = require('./routes/links');
|
| 23 |
const docsRoutes = require('./routes/docs');
|
|
|
|
| 24 |
|
| 25 |
dotenv.config();
|
| 26 |
|
|
@@ -128,6 +129,7 @@ app.use('/api/messages', messagesRoutes);
|
|
| 128 |
app.use('/api/links', linksRoutes);
|
| 129 |
app.use('/api/docs', docsRoutes);
|
| 130 |
app.use('/api/refinity', refinityRoutes);
|
|
|
|
| 131 |
|
| 132 |
// Health check endpoint
|
| 133 |
app.get('/api/health', (req, res) => {
|
|
|
|
| 21 |
const refinityRoutes = require('./routes/refinity');
|
| 22 |
const linksRoutes = require('./routes/links');
|
| 23 |
const docsRoutes = require('./routes/docs');
|
| 24 |
+
const adminWeeksRoutes = require('./routes/admin-weeks');
|
| 25 |
|
| 26 |
dotenv.config();
|
| 27 |
|
|
|
|
| 129 |
app.use('/api/links', linksRoutes);
|
| 130 |
app.use('/api/docs', docsRoutes);
|
| 131 |
app.use('/api/refinity', refinityRoutes);
|
| 132 |
+
app.use('/api', adminWeeksRoutes);
|
| 133 |
|
| 134 |
// Health check endpoint
|
| 135 |
app.get('/api/health', (req, res) => {
|
models/WeekBrief.js
CHANGED
|
@@ -15,6 +15,12 @@ const weekBriefSchema = new mongoose.Schema({
|
|
| 15 |
translationBrief: {
|
| 16 |
type: String,
|
| 17 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
}, { timestamps: true, indexes: [{ fields: { category: 1, weekNumber: 1 }, unique: true }] });
|
| 19 |
|
| 20 |
module.exports = mongoose.model('WeekBrief', weekBriefSchema);
|
|
|
|
| 15 |
translationBrief: {
|
| 16 |
type: String,
|
| 17 |
}
|
| 18 |
+
,
|
| 19 |
+
hidden: {
|
| 20 |
+
type: Boolean,
|
| 21 |
+
default: false,
|
| 22 |
+
index: true
|
| 23 |
+
}
|
| 24 |
}, { timestamps: true, indexes: [{ fields: { category: 1, weekNumber: 1 }, unique: true }] });
|
| 25 |
|
| 26 |
module.exports = mongoose.model('WeekBrief', weekBriefSchema);
|
routes/admin-weeks.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const express = require('express');
|
| 2 |
+
const router = express.Router();
|
| 3 |
+
const { authenticateToken } = require('./auth');
|
| 4 |
+
|
| 5 |
+
// Require admin helper
|
| 6 |
+
const requireAdmin = (req, res, next) => {
|
| 7 |
+
const userRole = req.headers['user-role'] || req.body.role;
|
| 8 |
+
if (userRole !== 'admin') {
|
| 9 |
+
return res.status(403).json({ error: 'Admin access required' });
|
| 10 |
+
}
|
| 11 |
+
next();
|
| 12 |
+
};
|
| 13 |
+
|
| 14 |
+
router.put('/admin/weeks/:category/:week/visibility', authenticateToken, requireAdmin, async (req, res) => {
|
| 15 |
+
try {
|
| 16 |
+
const WeekBrief = require('../models/WeekBrief');
|
| 17 |
+
const category = (req.params.category === 'tutorial') ? 'tutorial' : 'weekly-practice';
|
| 18 |
+
const weekNumber = parseInt(req.params.week);
|
| 19 |
+
const { hidden } = req.body || {};
|
| 20 |
+
const updated = await WeekBrief.findOneAndUpdate(
|
| 21 |
+
{ category, weekNumber },
|
| 22 |
+
{ hidden: !!hidden },
|
| 23 |
+
{ upsert: true, new: true }
|
| 24 |
+
);
|
| 25 |
+
res.json({ success: true, week: updated });
|
| 26 |
+
} catch (error) {
|
| 27 |
+
console.error('Update week visibility error:', error);
|
| 28 |
+
res.status(500).json({ error: 'Failed to update week visibility' });
|
| 29 |
+
}
|
| 30 |
+
});
|
| 31 |
+
|
| 32 |
+
module.exports = router;
|
| 33 |
+
|
| 34 |
+
|
routes/search.js
CHANGED
|
@@ -24,10 +24,17 @@ router.get('/tutorial-tasks/:week', authenticateToken, async (req, res) => {
|
|
| 24 |
const SourceText = require('../models/SourceText');
|
| 25 |
const weekNumber = parseInt(req.params.week);
|
| 26 |
|
|
|
|
| 27 |
const tasks = await SourceText.find({
|
| 28 |
category: 'tutorial',
|
| 29 |
weekNumber: weekNumber
|
| 30 |
}).sort({ position: 1, createdAt: 1 });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
|
| 32 |
res.json(tasks);
|
| 33 |
} catch (error) {
|
|
@@ -53,6 +60,10 @@ router.get('/weekly-practice/:week', authenticateToken, async (req, res) => {
|
|
| 53 |
|
| 54 |
// Attach week-level brief to first item if items exist; do not create placeholders
|
| 55 |
const wb = await WeekBrief.findOne({ category: 'weekly-practice', weekNumber });
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
if (wb && wb.translationBrief && practice.length > 0) {
|
| 57 |
if (!practice[0].translationBrief) {
|
| 58 |
practice[0] = { ...practice[0]._doc, translationBrief: wb.translationBrief };
|
|
|
|
| 24 |
const SourceText = require('../models/SourceText');
|
| 25 |
const weekNumber = parseInt(req.params.week);
|
| 26 |
|
| 27 |
+
const WeekBrief = require('../models/WeekBrief');
|
| 28 |
const tasks = await SourceText.find({
|
| 29 |
category: 'tutorial',
|
| 30 |
weekNumber: weekNumber
|
| 31 |
}).sort({ position: 1, createdAt: 1 });
|
| 32 |
+
// Check hidden flag; if hidden and user not admin, return empty
|
| 33 |
+
const wb = await WeekBrief.findOne({ category: 'tutorial', weekNumber });
|
| 34 |
+
const role = (req.headers['user-role'] || 'visitor').toString();
|
| 35 |
+
if (wb?.hidden && role !== 'admin') {
|
| 36 |
+
return res.json([]);
|
| 37 |
+
}
|
| 38 |
|
| 39 |
res.json(tasks);
|
| 40 |
} catch (error) {
|
|
|
|
| 60 |
|
| 61 |
// Attach week-level brief to first item if items exist; do not create placeholders
|
| 62 |
const wb = await WeekBrief.findOne({ category: 'weekly-practice', weekNumber });
|
| 63 |
+
const role = (req.headers['user-role'] || 'visitor').toString();
|
| 64 |
+
if (wb?.hidden && role !== 'admin') {
|
| 65 |
+
return res.json([]);
|
| 66 |
+
}
|
| 67 |
if (wb && wb.translationBrief && practice.length > 0) {
|
| 68 |
if (!practice[0].translationBrief) {
|
| 69 |
practice[0] = { ...practice[0]._doc, translationBrief: wb.translationBrief };
|