import crypto from 'crypto' import bodyParser from 'body-parser' import cookieParser from 'cookie-parser' import express, { Request, Response, Router } from 'express' import { createStatusRouter } from './middlewares' import { createDatabase, createProblem, createSolution, getProblem, getProblems, getSolution, getSolutions, getUser, getUsers, updateSolution, } from './db/database' import { isAdministrator, isStudent, Problem, ProblemId, UserId } from '../shared/model' import { initialDatabaseValue } from './db/example-data' export async function createApiRouter() { type SessionId = string const sessions = { store: {}, createSession(userId: UserId) { const sid = crypto.randomBytes(10).toString('hex') this.store[sid] = userId return sid }, getUserForSession(sid: SessionId) { return this.store[sid] ?? null }, } const db = createDatabase('./db.local.json', initialDatabaseValue) async function getRequestUser(req: Request) { const userId = sessions.getUserForSession(req.cookies.sid) if (!userId) { return null } console.log(userId) return await getUser(db, userId) } const r: Router = express.Router() r.use(bodyParser.json()) r.use(cookieParser()) r.use('/api/status', createStatusRouter()) // r.use('/api/ping', new PingRouter()) r.get('/api/current-user', async (req, res) => { res.json(await getRequestUser(req)) }) r.post('/api/login', async (req, res) => { const { id } = req.body const user = await getUser(db, id) if (!user) { res.sendStatus(403) return } res.cookie('sid', sessions.createSession(id), { maxAge: 1000 * 60 * 60 * 24 * 7 }) res.json({ status: 'ok' }) }) r.post('/api/logout', (req, res) => { res.cookie('sid', '', { expires: new Date() }) res.json({ status: 'ok' }) }) r.get('/api/users', async (req, res) => { const requestUser = await getRequestUser(req) if (requestUser.role !== 'admin' && requestUser.role !== 'moderator') { res.sendStatus(401) return } const users = await getUsers(db) res.json(users) }) r.get('/api/problems', async (req, res) => { type ProblemWithSolutionsCount = Problem & { solutionsCount?: number } const problems: ProblemWithSolutionsCount[] = await getProblems(db) const solutions = await getSolutions(db) const solutionCounts: Record = {} for (const s of solutions) { solutionCounts[s.forProblem] ||= 0 solutionCounts[s.forProblem]++ } for (const p of problems) { p.solutionsCount = solutionCounts[p.id] || 0 } res.json(problems) }) r.get('/api/problem/:id', async (req, res) => { res.json(await getProblem(db, req.params.id)) }) r.post('/api/problem', async (req, res) => { const user = await getRequestUser(req) if (!user) { res.sendStatus(401) return } if (user.role !== 'admin' && user.role !== 'moderator') { res.sendStatus(401) return } const id = await createProblem(db, { content: req.body.content, createdBy: user.id, }) res.json(id) }) r.get('/api/solution/:id', async (req, res) => { const user = await getRequestUser(req) // l'utente deve essere loggato if (!user) { res.sendStatus(401) return } const solution = await getSolution(db, req.params.id) // uno studente che prova a ottenere la soluzione di un altro utente if (!isAdministrator(user.role) && solution.sentBy !== user.id) { res.sendStatus(401) return } res.json(solution) }) r.get('/api/solutions', async (req, res) => { let queryUserId = req.query.user as string let queryProblemId = req.query.problem as string const requestUser = await getRequestUser(req) if (!requestUser) { res.sendStatus(401) return } // if current user is not an administrator then force the user query to current user if (!isAdministrator(requestUser.role)) { queryUserId = requestUser.id } res.json( await getSolutions(db, { sentBy: queryUserId as UserId, forProblem: queryProblemId as ProblemId, }) ) }) r.post('/api/solution', async (req, res) => { const user = await getRequestUser(req) if (!user) { res.sendStatus(401) return } await createSolution(db, { sentBy: user.id, forProblem: req.body.problemId, content: req.body.content, status: 'pending', }) res.send({ status: 'ok' }) }) r.post('/api/solution/:id', async (req, res) => { const user = await getRequestUser(req) // l'utente deve essere loggato if (!user) { res.sendStatus(401) return } const solutionId = req.params.id const solution = await getSolution(db, solutionId) // uno studente non può modificare una soluzione di un altro utente if (isStudent(user.role) && solution.sentBy !== user.id) { res.sendStatus(401) return } // modifico la soluzione con il json mandato dal client nel body della richiesta await updateSolution(db, solutionId, req.body) res.json({ status: 'ok' }) }) r.get('/api/user/:id', async (req, res) => { const user = await getRequestUser(req) // intanto l'utente deve essere loggato if (!user) { res.sendStatus(401) return } // solo gli amministratori possono usare questa route if (!isAdministrator(user.role)) { res.sendStatus(401) return } const requestedUser = await getUser(db, req.params.id) // l'utente richiesto magari deve esistere if (!requestedUser) { res.sendStatus(404) return } res.json(requestedUser) }) return r }