Spaces:
Sleeping
Sleeping
Upload 54 files
Browse files- server.js +7 -7
- services/api.ts +15 -6
server.js
CHANGED
|
@@ -3,7 +3,7 @@ const {
|
|
| 3 |
School, User, Student, Course, Score, ClassModel, SubjectModel, ExamModel, ScheduleModel,
|
| 4 |
ConfigModel, NotificationModel, GameSessionModel, StudentRewardModel, LuckyDrawConfigModel, GameMonsterConfigModel, GameZenConfigModel,
|
| 5 |
AchievementConfigModel, TeacherExchangeConfigModel, StudentAchievementModel, AttendanceModel, LeaveRequestModel, SchoolCalendarModel,
|
| 6 |
-
WishModel, FeedbackModel, TodoModel
|
| 7 |
} = require('./models');
|
| 8 |
|
| 9 |
// Import AI Routes
|
|
@@ -113,8 +113,6 @@ const generateStudentNo = async () => {
|
|
| 113 |
// MOUNT AI ROUTES
|
| 114 |
app.use('/api/ai', aiRoutes);
|
| 115 |
|
| 116 |
-
// ... (Rest of Existing Routes) ...
|
| 117 |
-
|
| 118 |
// --- TODO LIST ENDPOINTS ---
|
| 119 |
app.get('/api/todos', async (req, res) => {
|
| 120 |
const username = req.headers['x-user-username'];
|
|
@@ -201,7 +199,6 @@ app.put('/api/users/:id/menu-order', async (req, res) => {
|
|
| 201 |
res.json({ success: true });
|
| 202 |
});
|
| 203 |
|
| 204 |
-
// ... (Rest of existing routes unchanged) ...
|
| 205 |
app.get('/api/classes/:className/teachers', async (req, res) => {
|
| 206 |
const { className } = req.params;
|
| 207 |
const schoolId = req.headers['x-school-id'];
|
|
@@ -384,10 +381,13 @@ app.get('/api/auth/me', async (req, res) => {
|
|
| 384 |
const token = authHeader.split(' ')[1]; // Bearer <token>
|
| 385 |
if (token && token.startsWith('mock-token-')) {
|
| 386 |
const userId = token.replace('mock-token-', '');
|
| 387 |
-
|
| 388 |
-
|
| 389 |
const user = await User.findById(userId);
|
| 390 |
if (user) return res.json(user);
|
|
|
|
|
|
|
|
|
|
| 391 |
}
|
| 392 |
}
|
| 393 |
}
|
|
@@ -531,7 +531,7 @@ app.post('/api/auth/login', async (req, res) => {
|
|
| 531 |
const user = await User.findOne({ username, password });
|
| 532 |
if (!user) return res.status(401).json({ message: 'Error' });
|
| 533 |
if (user.status !== 'active') return res.status(403).json({ error: 'PENDING_APPROVAL' });
|
| 534 |
-
res.json({ token: 'mock-token-' + user._id, user });
|
| 535 |
});
|
| 536 |
|
| 537 |
app.get('/api/schools', async (req, res) => { res.json(await School.find()); });
|
|
|
|
| 3 |
School, User, Student, Course, Score, ClassModel, SubjectModel, ExamModel, ScheduleModel,
|
| 4 |
ConfigModel, NotificationModel, GameSessionModel, StudentRewardModel, LuckyDrawConfigModel, GameMonsterConfigModel, GameZenConfigModel,
|
| 5 |
AchievementConfigModel, TeacherExchangeConfigModel, StudentAchievementModel, AttendanceModel, LeaveRequestModel, SchoolCalendarModel,
|
| 6 |
+
WishModel, FeedbackModel, TodoModel, AIUsageModel
|
| 7 |
} = require('./models');
|
| 8 |
|
| 9 |
// Import AI Routes
|
|
|
|
| 113 |
// MOUNT AI ROUTES
|
| 114 |
app.use('/api/ai', aiRoutes);
|
| 115 |
|
|
|
|
|
|
|
| 116 |
// --- TODO LIST ENDPOINTS ---
|
| 117 |
app.get('/api/todos', async (req, res) => {
|
| 118 |
const username = req.headers['x-user-username'];
|
|
|
|
| 199 |
res.json({ success: true });
|
| 200 |
});
|
| 201 |
|
|
|
|
| 202 |
app.get('/api/classes/:className/teachers', async (req, res) => {
|
| 203 |
const { className } = req.params;
|
| 204 |
const schoolId = req.headers['x-school-id'];
|
|
|
|
| 381 |
const token = authHeader.split(' ')[1]; // Bearer <token>
|
| 382 |
if (token && token.startsWith('mock-token-')) {
|
| 383 |
const userId = token.replace('mock-token-', '');
|
| 384 |
+
try {
|
| 385 |
+
// Simplified lookup without strict regex to support migrated/mock data
|
| 386 |
const user = await User.findById(userId);
|
| 387 |
if (user) return res.json(user);
|
| 388 |
+
} catch (e) {
|
| 389 |
+
// Ignore cast errors or invalid ID formats
|
| 390 |
+
console.log('Session lookup error:', e.message);
|
| 391 |
}
|
| 392 |
}
|
| 393 |
}
|
|
|
|
| 531 |
const user = await User.findOne({ username, password });
|
| 532 |
if (!user) return res.status(401).json({ message: 'Error' });
|
| 533 |
if (user.status !== 'active') return res.status(403).json({ error: 'PENDING_APPROVAL' });
|
| 534 |
+
res.json({ token: 'mock-token-' + user._id.toString(), user });
|
| 535 |
});
|
| 536 |
|
| 537 |
app.get('/api/schools', async (req, res) => { res.json(await School.find()); });
|
services/api.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { User } from '../types';
|
|
| 3 |
|
| 4 |
const API_BASE = '/api';
|
| 5 |
|
| 6 |
-
const request = async (endpoint: string, options?: RequestInit) => {
|
| 7 |
const token = localStorage.getItem('token');
|
| 8 |
const headers = {
|
| 9 |
'Content-Type': 'application/json',
|
|
@@ -24,9 +24,11 @@ const request = async (endpoint: string, options?: RequestInit) => {
|
|
| 24 |
});
|
| 25 |
|
| 26 |
if (response.status === 401) {
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
|
|
|
|
|
|
| 30 |
throw new Error('Unauthorized');
|
| 31 |
}
|
| 32 |
|
|
@@ -77,10 +79,17 @@ export const api = {
|
|
| 77 |
},
|
| 78 |
refreshSession: async () => {
|
| 79 |
try {
|
| 80 |
-
|
|
|
|
|
|
|
| 81 |
localStorage.setItem('user', JSON.stringify(user));
|
| 82 |
return user;
|
| 83 |
-
} catch (e) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
},
|
| 85 |
updateProfile: (data: any) => request('/auth/profile', { method: 'PUT', body: JSON.stringify(data) }),
|
| 86 |
},
|
|
|
|
| 3 |
|
| 4 |
const API_BASE = '/api';
|
| 5 |
|
| 6 |
+
const request = async (endpoint: string, options?: RequestInit & { skipRedirect?: boolean }) => {
|
| 7 |
const token = localStorage.getItem('token');
|
| 8 |
const headers = {
|
| 9 |
'Content-Type': 'application/json',
|
|
|
|
| 24 |
});
|
| 25 |
|
| 26 |
if (response.status === 401) {
|
| 27 |
+
if (!options?.skipRedirect) {
|
| 28 |
+
localStorage.removeItem('token');
|
| 29 |
+
localStorage.removeItem('user');
|
| 30 |
+
window.location.href = '/';
|
| 31 |
+
}
|
| 32 |
throw new Error('Unauthorized');
|
| 33 |
}
|
| 34 |
|
|
|
|
| 79 |
},
|
| 80 |
refreshSession: async () => {
|
| 81 |
try {
|
| 82 |
+
// Pass skipRedirect: true to prevent infinite reload loop if server returns 401
|
| 83 |
+
// @ts-ignore
|
| 84 |
+
const user = await request('/auth/me', { skipRedirect: true });
|
| 85 |
localStorage.setItem('user', JSON.stringify(user));
|
| 86 |
return user;
|
| 87 |
+
} catch (e) {
|
| 88 |
+
// If refresh fails (e.g. 401), clear local user to force re-login on next check, but don't hard reload
|
| 89 |
+
localStorage.removeItem('user');
|
| 90 |
+
localStorage.removeItem('token');
|
| 91 |
+
return null;
|
| 92 |
+
}
|
| 93 |
},
|
| 94 |
updateProfile: (data: any) => request('/auth/profile', { method: 'PUT', body: JSON.stringify(data) }),
|
| 95 |
},
|