-
Notifications
You must be signed in to change notification settings - Fork 35
Change ui on led sign page to have a tab, if the user is an officer or admin #1988
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: Request-Button4LEDSIGN
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,10 @@ | ||
| const express = require('express'); | ||
| const router = express.Router(); | ||
| const PermissionRequest = require('../models/PermissionRequest'); | ||
| const { OK, UNAUTHORIZED, SERVER_ERROR, NOT_FOUND, BAD_REQUEST, CONFLICT } = require('../../util/constants').STATUS_CODES; | ||
| const { | ||
| STATUS_CODES: { OK, UNAUTHORIZED, SERVER_ERROR, NOT_FOUND, BAD_REQUEST, CONFLICT }, | ||
| PERMISSION_REQUEST_STATUS: PermissionRequestStatus, | ||
| } = require('../../util/constants'); | ||
| const membershipState = require('../../util/constants.js').MEMBERSHIP_STATE; | ||
| const { decodeToken } = require('../util/token-functions.js'); | ||
| const logger = require('../../util/logger'); | ||
|
|
@@ -17,11 +20,23 @@ router.post('/create', async (req, res) => { | |
| } | ||
|
|
||
| try { | ||
| await PermissionRequest.create({ | ||
| const existingRequest = await PermissionRequest.findOne({ | ||
| userId: decoded.token._id, | ||
| type, | ||
| }); | ||
| res.sendStatus(OK); | ||
|
|
||
| if (existingRequest) { | ||
| if (existingRequest.status === PermissionRequestStatus.PENDING) { | ||
| return res.sendStatus(CONFLICT); | ||
| } | ||
| existingRequest.status = PermissionRequestStatus.PENDING; | ||
| existingRequest.createdAt = new Date(); | ||
| await existingRequest.save(); | ||
| return res.sendStatus(OK); | ||
| } | ||
|
|
||
| await PermissionRequest.create({ userId: decoded.token._id, type }); | ||
| return res.sendStatus(OK); | ||
| } catch (error) { | ||
| if (error.code === 11000) return res.sendStatus(CONFLICT); | ||
| logger.error('Failed to create permission request:', error); | ||
|
|
@@ -37,13 +52,17 @@ router.get('/', async (req, res) => { | |
| const isOfficer = decoded.token.accessLevel >= membershipState.OFFICER; | ||
|
|
||
| try { | ||
| const query = { deletedAt: null }; | ||
| const query = { status: { $ne: PermissionRequestStatus.DENIED } }; | ||
|
|
||
| if (queryUserId) { | ||
| query.userId = queryUserId; | ||
| // For member's own request, return it regardless of approval status | ||
| } | ||
| if (!isOfficer) { | ||
| query.userId = decoded.token._id.toString(); | ||
| } else if (!queryUserId) { | ||
| // Officers viewing the list should only see pending requests | ||
| query.status = PermissionRequestStatus.PENDING; | ||
| } | ||
|
|
||
| // If there is a type, filter by it | ||
|
|
@@ -62,6 +81,36 @@ router.get('/', async (req, res) => { | |
| } | ||
| }); | ||
|
|
||
| router.post('/approve', async (req, res) => { | ||
| const decoded = await decodeToken(req, membershipState.OFFICER); | ||
| if (decoded.status !== OK) return res.sendStatus(decoded.status); | ||
|
|
||
| const { type, _id } = req.body; | ||
| if (!type || !Object.keys(PermissionRequestTypes).includes(type)) { | ||
| return res.status(BAD_REQUEST).send({ error: 'Invalid type' }); | ||
| } | ||
|
Comment on lines
+89
to
+91
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same thing like we did before can we do `type ${type} is invalid, valid types are ${Object.keys(PermissionRequestTypes)}` |
||
|
|
||
| if (!_id) { | ||
| return res.status(BAD_REQUEST).send({ error: '_id is required' }); | ||
| } | ||
|
|
||
| try { | ||
| const request = await PermissionRequest.findOne({ | ||
| _id, | ||
| type, | ||
| status: PermissionRequestStatus.PENDING, | ||
| }); | ||
|
|
||
| if (!request) return res.sendStatus(NOT_FOUND); | ||
| request.status = PermissionRequestStatus.APPROVED; | ||
| await request.save(); | ||
| res.sendStatus(OK); | ||
| } catch (error) { | ||
| logger.error('Failed to approve permission request:', error); | ||
| res.sendStatus(SERVER_ERROR); | ||
| } | ||
| }); | ||
|
|
||
| router.post('/delete', async (req, res) => { | ||
| const decoded = await decodeToken(req, membershipState.MEMBER); | ||
| if (decoded.status !== OK) return res.sendStatus(decoded.status); | ||
|
|
@@ -86,13 +135,13 @@ router.post('/delete', async (req, res) => { | |
| const query = { | ||
| _id: idToUse, | ||
| type, | ||
| deletedAt: null, | ||
| status: { $ne: PermissionRequestStatus.DENIED }, | ||
| }; | ||
|
|
||
| const request = await PermissionRequest.findOne(query); | ||
|
|
||
| if (!request) return res.sendStatus(NOT_FOUND); | ||
| request.deletedAt = new Date(); | ||
| request.status = PermissionRequestStatus.DENIED; | ||
| await request.save(); | ||
| res.sendStatus(OK); | ||
| } catch (error) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,10 +1,13 @@ | ||||||||||||||||||||||||||||||||
| import { ApiResponse } from './ApiResponses'; | ||||||||||||||||||||||||||||||||
| import { BASE_API_URL } from '../Enums'; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| export async function getPermissionRequest(type, token) { | ||||||||||||||||||||||||||||||||
| export async function getPermissionRequest(type, userId, token) { | ||||||||||||||||||||||||||||||||
| const status = new ApiResponse(); | ||||||||||||||||||||||||||||||||
| const url = new URL('/api/PermissionRequest/', BASE_API_URL); | ||||||||||||||||||||||||||||||||
| url.searchParams.append('type', type); | ||||||||||||||||||||||||||||||||
| if (userId) { | ||||||||||||||||||||||||||||||||
| url.searchParams.append('userId', userId); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||
| const res = await fetch(url.toString(), { | ||||||||||||||||||||||||||||||||
|
|
@@ -13,10 +16,12 @@ export async function getPermissionRequest(type, token) { | |||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| status.error = !!res.ok; | ||||||||||||||||||||||||||||||||
| status.error = !res.ok; | ||||||||||||||||||||||||||||||||
| if (res.ok) { | ||||||||||||||||||||||||||||||||
| const data = await res.json(); | ||||||||||||||||||||||||||||||||
| status.responseData = Array.isArray(data) && data.length > 0 ? data[0] : null; | ||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||
| status.responseData = null; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||||
| status.responseData = err; | ||||||||||||||||||||||||||||||||
|
|
@@ -40,11 +45,97 @@ export async function createPermissionRequest(type, token) { | |||||||||||||||||||||||||||||||
| body: JSON.stringify({ type }), | ||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| status.error = !!res.ok; | ||||||||||||||||||||||||||||||||
| if (res.ok || res.status === 409) { | ||||||||||||||||||||||||||||||||
| // Backend sends 200 with no body on success, so fetch the created request | ||||||||||||||||||||||||||||||||
| const existingRequest = await getPermissionRequest(type, token); | ||||||||||||||||||||||||||||||||
| status.responseData = existingRequest.responseData; | ||||||||||||||||||||||||||||||||
| if (res.ok) { | ||||||||||||||||||||||||||||||||
| // API returns 200 with no body, so we just mark success | ||||||||||||||||||||||||||||||||
| status.responseData = true; | ||||||||||||||||||||||||||||||||
| } else if (res.status === 409) { | ||||||||||||||||||||||||||||||||
| // CONFLICT - duplicate request | ||||||||||||||||||||||||||||||||
| status.error = true; | ||||||||||||||||||||||||||||||||
| status.responseData = 'Request already exists'; | ||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||
| status.error = true; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
Comment on lines
+48
to
+57
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||||
| status.responseData = err; | ||||||||||||||||||||||||||||||||
| status.error = true; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| return status; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| export async function getAllPermissionRequests(type, token) { | ||||||||||||||||||||||||||||||||
| const status = new ApiResponse(); | ||||||||||||||||||||||||||||||||
| const url = new URL('/api/PermissionRequest/', BASE_API_URL); | ||||||||||||||||||||||||||||||||
| if (type) { | ||||||||||||||||||||||||||||||||
| url.searchParams.append('type', type); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||
| const res = await fetch(url.toString(), { | ||||||||||||||||||||||||||||||||
| headers: { | ||||||||||||||||||||||||||||||||
| Authorization: `Bearer ${token}`, | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if (res.ok) { | ||||||||||||||||||||||||||||||||
| const data = await res.json(); | ||||||||||||||||||||||||||||||||
| status.responseData = data; | ||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||
| status.error = true; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||||
| status.responseData = err; | ||||||||||||||||||||||||||||||||
| status.error = true; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| return status; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| export async function approvePermissionRequest(type, id, token) { | ||||||||||||||||||||||||||||||||
| const status = new ApiResponse(); | ||||||||||||||||||||||||||||||||
| const url = new URL('/api/PermissionRequest/approve', BASE_API_URL); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||
| const res = await fetch(url.toString(), { | ||||||||||||||||||||||||||||||||
| method: 'POST', | ||||||||||||||||||||||||||||||||
| headers: { | ||||||||||||||||||||||||||||||||
| 'Content-Type': 'application/json', | ||||||||||||||||||||||||||||||||
| Authorization: `Bearer ${token}`, | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| body: JSON.stringify({ type, _id: id }), | ||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if (res.ok) { | ||||||||||||||||||||||||||||||||
| status.responseData = true; | ||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||
| status.error = true; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||||
| status.responseData = err; | ||||||||||||||||||||||||||||||||
| status.error = true; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| return status; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| export async function deletePermissionRequest(type, id, token) { | ||||||||||||||||||||||||||||||||
| const status = new ApiResponse(); | ||||||||||||||||||||||||||||||||
| const url = new URL('/api/PermissionRequest/delete', BASE_API_URL); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||
| const res = await fetch(url.toString(), { | ||||||||||||||||||||||||||||||||
| method: 'POST', | ||||||||||||||||||||||||||||||||
| headers: { | ||||||||||||||||||||||||||||||||
| 'Content-Type': 'application/json', | ||||||||||||||||||||||||||||||||
| Authorization: `Bearer ${token}`, | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| body: JSON.stringify({ type, _id: id }), | ||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if (res.ok) { | ||||||||||||||||||||||||||||||||
| status.responseData = true; | ||||||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lets not even set responseData for these create/delete requests we can just rely on if error is true/false for checking if it worked |
||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||
| status.error = true; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||||
| status.responseData = err; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think if it exists we just return conflict no matter what, what do you think
if its denied, we can set the status to pending