const express = require('express'); const router = express.Router(); import pool from '../db'; import { approveApplication, createApplication, denyApplication, getAllMemberApplications, getApplicationByID, getApplicationComments, getApplicationList, getMemberApplication } from '../services/db/applicationService'; import { setUserState } from '../services/db/memberService'; import { MemberState } from '@app/shared/types/member'; import { getRankByName, insertMemberRank } from '../services/db/rankService'; import { ApplicationFull, CommentRow } from "@app/shared/types/application" import { assignUserToStatus } from '../services/db/statusService'; import { Request, response, Response } from 'express'; import { getUserRoles } from '../services/db/rolesService'; import { requireLogin, requireRole } from '../middleware/auth'; import { logger } from '../services/logging/logger'; import { bus } from '../services/events/eventBus'; //get CoC router.get('/coc', async (req: Request, res: Response) => { try { const response = await fetch(`${process.env.DOC_HOST}/api/pages/714`, { headers: { Authorization: `Token ${process.env.DOC_TOKEN_ID}:${process.env.DOC_TOKEN_SECRET}`, }, }); if (!response.ok) { const text = await response.text(); logger.error('app', 'Failed to fetch LOA policy from Bookstack', { status: response.status, statusText: response.statusText, body: text, }); return res.sendStatus(500); } const out = await response.json(); res.status(200).json(out.html); } catch (error) { logger.error('app', 'Error fetching LOA policy from Bookstack', { error: error instanceof Error ? error.message : String(error), }); res.sendStatus(500); } }); // POST /application router.post('/', [requireLogin], async (req: Request, res: Response) => { const memberID = req.user.id; const App = req.body?.App || {}; const appVersion = 1; try { let appID = await createApplication(memberID, appVersion, JSON.stringify(App)); await setUserState(memberID, MemberState.Applicant); res.sendStatus(201); bus.emit("application.create", { application: appID, member_name: req.user.name, member_discord_id: req.user.discord_id || null }) logger.info('app', 'Application Posted', { user: memberID, app: appID }) } catch (err) { logger.error( 'app', 'Failed to create application', { memberID, error: err instanceof Error ? err.message : String(err), stack: err instanceof Error ? err.stack : undefined, } ); res.status(500).json({ error: 'Failed to create application' }); } }); // GET /application/all router.get('/all', [requireLogin, requireRole("Recruiter")], async (req, res) => { try { const rows = await getApplicationList(); res.status(200).json(rows); } catch (err) { logger.error( 'app', 'Failed to get applications', { error: err instanceof Error ? err.message : String(err), stack: err instanceof Error ? err.stack : undefined, } ); res.status(500); } }); router.get('/meList', async (req, res) => { let userID = req.user.id; try { let application = await getAllMemberApplications(userID); return res.status(200).json(application); } catch (error) { logger.error( 'app', 'Failed to get applications for user', { user: userID, error: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, } ); return res.status(500); } }) router.get('/me', [requireLogin], async (req, res) => { let userID = req.user.id; try { let application = await getMemberApplication(userID); if (application === undefined) { res.sendStatus(204); return; } const comments: CommentRow[] = await getApplicationComments(application.id); const output: ApplicationFull = { application, comments, } return res.status(200).json(output); } catch (error) { logger.error( 'app', 'Failed to load application', { user: userID, error: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, } ); return res.status(500).json(error); } }) // GET /me/:id router.get('/me/:id', [requireLogin], async (req: Request, res: Response) => { let appID = Number(req.params.id); let member = req.user.id; try { const application = await getApplicationByID(appID); if (application === undefined) return res.sendStatus(204); if (application.member_id != member) { return res.sendStatus(403); } const comments: CommentRow[] = await getApplicationComments(appID); const output: ApplicationFull = { application, comments, } return res.status(200).json(output); } catch (error) { logger.error( 'app', 'Failed to load application', { application: appID, user: member, error: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, } ); return res.status(500); } }); // GET /application/:id router.get('/:id', [requireLogin, requireRole("Recruiter")], async (req: Request, res: Response) => { let appID = Number(req.params.id); let asAdmin = !!req.query.admin || false; try { const application = await getApplicationByID(appID); if (application === undefined) return res.sendStatus(204); const comments: CommentRow[] = await getApplicationComments(appID, asAdmin); const output: ApplicationFull = { application, comments, } return res.status(200).json(output); } catch (error) { logger.error( 'app', 'Failed to load application', { application: appID, error: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, } ); return res.status(500); } }); // POST /application/approve/:id router.post('/approve/:id', [requireLogin, requireRole("Recruiter")], async (req: Request, res: Response) => { const appID = Number(req.params.id); const approved_by = req.user.id; try { const app = await getApplicationByID(appID); await approveApplication(appID, approved_by); //update user profile await setUserState(app.member_id, MemberState.Member); await pool.query('CALL sp_accept_new_recruit_validation(?, ?, ?, ?)', [Number(process.env.CONFIG_ID), app.member_id, approved_by, approved_by]) logger.info('app', "Member application approved", { application: app.id, applicant: app.member_id, approver: approved_by }) res.sendStatus(200); } catch (error) { logger.error( 'app', 'Failed to approve application', { application: appID, error: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, } ); res.status(500).json({ error: 'Failed to approve application' }); } }); // POST /application/deny/:id router.post('/deny/:id', [requireLogin, requireRole("Recruiter")], async (req: Request, res: Response) => { const appID = Number(req.params.id); const approver = Number(req.user.id); try { const app = await getApplicationByID(appID); await denyApplication(appID, approver); await setUserState(app.member_id, MemberState.Denied); logger.info('app', "Member application approved", { application: app.id, applicant: app.member_id, approver: approver }) res.sendStatus(200); } catch (error) { logger.error( 'app', 'Failed to deny application', { application: appID, error: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, } ); res.status(500).json({ error: 'Failed to deny application' }); } }); // POST /application/:id/comment router.post('/:id/comment', [requireLogin], async (req: Request, res: Response) => { const appID = req.params.id; const data = req.body.message; const user = req.user; const sql = `INSERT INTO application_comments( application_id, poster_id, post_content ) VALUES(?, ?, ?);` try { var conn = await pool.getConnection(); const result = await conn.query(sql, [appID, user.id, data]) if (result.affectedRows !== 1) { conn.release(); throw new Error("Insert Failure") } const getSQL = `SELECT app.id AS comment_id, app.post_content, app.poster_id, app.post_time, app.last_modified, member.name AS poster_name FROM application_comments AS app INNER JOIN members AS member ON member.id = app.poster_id WHERE app.id = ?; `; const comment = await conn.query(getSQL, [result.insertId]) logger.info('app', "Application comment posted", { application: appID, poster: user.id, comment: result.insertId, }) res.status(201).json(comment[0]); } catch (error) { logger.error( 'app', 'Failed to post comment', { application: appID, error: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, } ); res.status(500).json({ error: 'Could not post comment' }); } finally { conn.release(); } }); // POST /application/:id/comment router.post('/:id/adminComment', [requireLogin, requireRole("Recruiter")], async (req: Request, res: Response) => { const appID = req.params.id; const data = req.body.message; const user = req.user; const sql = `INSERT INTO application_comments( application_id, poster_id, post_content, admin_only ) VALUES(?, ?, ?, 1);` try { var conn = await pool.getConnection(); const result = await conn.query(sql, [appID, user.id, data]) if (result.affectedRows !== 1) { conn.release(); throw new Error("Insert Failure") } const getSQL = `SELECT app.id AS comment_id, app.post_content, app.poster_id, app.post_time, app.last_modified, app.admin_only, member.name AS poster_name FROM application_comments AS app INNER JOIN members AS member ON member.id = app.poster_id WHERE app.id = ?; `; const comment = await conn.query(getSQL, [result.insertId]) logger.info('app', "Admin application comment posted", { application: appID, poster: user.id, comment: result.insertId, }) res.status(201).json(comment[0]); } catch (error) { logger.error( 'app', 'Failed to post comment', { application: appID, error: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, } ); res.status(500).json({ error: 'Could not post comment' }); } finally { conn.release(); } }); router.post('/restart', async (req: Request, res: Response) => { const user = req.user.id; try { await setUserState(user, MemberState.Guest); logger.info('app', "Member restarted application", { user: user }) res.sendStatus(200); } catch (error) { logger.error( 'app', 'Failed to restart application', { user: user, error: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, } ); res.status(500).json({ error: 'Could not rester application' }); } }) export const applicationRouter = router;