2026-05-27 14:29:58 +05:30

180 lines
6.1 KiB
TypeScript

import express from "express";
import path from "path";
import { createServer as createViteServer } from "vite";
async function startServer() {
const app = express();
const PORT = 3000;
app.use(express.json());
// In-memory "database"
const db = {
users: [
{ id: "admin1", username: "admin", password: "password", role: "admin", name: "Administrator" },
{ id: "teacher1", username: "teacher1", password: "password", role: "teacher", name: "Gokula Krishnan", assignedGrade: "12" },
{ id: "teacher2", username: "teacher2", password: "password", role: "teacher", name: "Arun Kumar", assignedGrade: "10" },
{ id: "teacher3", username: "teacher3", password: "password", role: "teacher", name: "Meera Devi", assignedGrade: "11" },
{ id: "teacher4", username: "teacher4", password: "password", role: "teacher", name: "Senthil Nathan", assignedGrade: "9" }
],
submissions: [] as any[],
students: [] as any[],
studentStats: {} as Record<string, any> // rollNumber -> latest LEI stats
};
// Auth
app.post("/api/login", (req, res) => {
const { username, password } = req.body;
const user = db.users.find(u => u.username === username && u.password === password);
if (user) {
res.json({ id: user.id, username: user.username, role: user.role, name: user.name, assignedGrade: (user as any).assignedGrade });
} else {
res.status(401).json({ success: false, message: "Invalid credentials" });
}
});
// Submissions
app.post("/api/submissions", (req, res) => {
const submission = {
id: Date.now().toString(),
...req.body,
status: "pending",
submittedAt: new Date().toISOString(),
documentationMarks: 0,
vivaScore: 0,
understandingLevel: 0,
explanationAbility: 0,
teacherRemarks: "",
lei: req.body.lei || 0,
leiCategory: req.body.leiCategory || 'Stalled Learner',
leiInsight: req.body.leiInsight || '',
retries: req.body.retries || 0,
streak: req.body.streak || 0,
completionRate: req.body.completionRate || 0
};
db.submissions.push(submission);
res.json({ success: true, submission });
});
app.get("/api/submissions", (req, res) => {
const { grade } = req.query;
let filtered = db.submissions;
if (grade) {
filtered = filtered.filter(s => s.grade.includes(grade as string));
}
res.json(filtered);
});
app.post("/api/student/lei", (req, res) => {
const { rollNumber, lei, leiCategory, leiInsight, studentName, grade } = req.body;
if (!rollNumber) return res.status(400).json({ success: false, message: "Roll number required" });
db.studentStats[rollNumber] = {
lei,
leiCategory,
leiInsight,
studentName,
grade,
updatedAt: new Date().toISOString()
};
res.json({ success: true });
});
app.get("/api/students/lei", (req, res) => {
const { grade } = req.query;
let stats = Object.values(db.studentStats);
if (grade) {
stats = stats.filter(s => s.grade.includes(grade as string));
}
res.json(stats);
});
app.post("/api/student/viva", (req, res) => {
const { rollNumber, session, vivaScore } = req.body;
if (!rollNumber) return res.status(400).json({ success: false, message: "Roll number required" });
if (db.studentStats[rollNumber]) {
if (!db.studentStats[rollNumber].vivaSessions) {
db.studentStats[rollNumber].vivaSessions = [];
}
db.studentStats[rollNumber].vivaSessions.push(session);
db.studentStats[rollNumber].vivaSummary = session.summary;
db.studentStats[rollNumber].vivaScore = vivaScore;
}
res.json({ success: true });
});
app.patch("/api/submissions/:id", (req, res) => {
const { id } = req.params;
const {
documentationMarks, vivaScore, teacherVivaMarks, vivaRequested,
teacherRemarks, status, marks, totalScore
} = req.body;
const index = db.submissions.findIndex(s => s.id === id);
if (index !== -1) {
if (documentationMarks !== undefined) db.submissions[index].documentationMarks = documentationMarks;
if (vivaScore !== undefined) db.submissions[index].vivaScore = vivaScore;
if (teacherVivaMarks !== undefined) db.submissions[index].teacherVivaMarks = teacherVivaMarks;
if (vivaRequested !== undefined) db.submissions[index].vivaRequested = vivaRequested;
if (teacherRemarks !== undefined) db.submissions[index].teacherRemarks = teacherRemarks;
if (marks !== undefined) db.submissions[index].marks = marks;
if (totalScore !== undefined) db.submissions[index].totalScore = totalScore;
db.submissions[index].status = status || "reviewed";
res.json({ success: true, submission: db.submissions[index] });
} else {
res.status(404).json({ success: false, message: "Submission not found" });
}
});
// Leaderboard
app.get("/api/leaderboard", (req, res) => {
const { grade } = req.query;
let filtered = db.submissions;
if (grade) {
filtered = filtered.filter(s => s.grade.includes(grade as string));
}
const leaderboard = filtered
.sort((a, b) => (b.lei || 0) - (a.lei || 0))
.slice(0, 10)
.map(s => {
let label = "Active Learner";
if (s.lei > 100) label = "Top Contributor";
else if (s.lei > 50) label = "Most Consistent";
else if (s.lei > 20) label = "Rising Star";
return {
name: s.studentName,
lei: s.lei || 0,
grade: s.grade,
rollNumber: s.rollNumber,
label: label
};
});
res.json(leaderboard);
});
// Vite middleware for development
if (process.env.NODE_ENV !== "production") {
const vite = await createViteServer({
server: { middlewareMode: true },
appType: "spa",
});
app.use(vite.middlewares);
} else {
const distPath = path.join(process.cwd(), 'dist');
app.use(express.static(distPath));
app.get('*', (req, res) => {
res.sendFile(path.join(distPath, 'index.html'));
});
}
app.listen(PORT, "0.0.0.0", () => {
console.log(`Server running on http://localhost:${PORT}`);
});
}
startServer();