Source: routers/member.js

  1. const express = require('express');
  2. const auth = require('../middleware/authenticator.js');
  3. const mysql = require("../db/mysql.js");
  4. const router = express.Router();
  5. const verify = require('../middleware/verifier.js');
  6. /**
  7. * @typedef {object} MemberGetGetsFeedbackPackage
  8. * @property {boolean} gets_feedback Whether the user gets feedback or not
  9. */
  10. /**
  11. * Route for checking if this user gets feedback on sheet music if they are verified to be in the choir attached to the sheet music
  12. * @name member/get/gets-feedback
  13. * @function
  14. * @inner
  15. * @param {*} req
  16. * @param {string} req.body.choirId The choir ID to be accessed. NOTE: Only need to pass choirId in body or query not both
  17. * @param {string} req.query.choirId The choir ID to be accessed. NOTE: Only need to pass choirId in body or query not both
  18. * @param {*} res
  19. * @returns {MemberGetGetsFeedbackPackage|string} On success, returns whether or not the user should get feedback. Otherwise, an error message.
  20. * @async
  21. */
  22. router.get('/gets-feedback', auth.token, async(req,res) => {
  23. verify.userIsVerified(req, res).then((isVerified) => {
  24. if (!isVerified) {
  25. res.status(403).send('Unauthorized');
  26. } else {
  27. const choirId = req.query.choirId;
  28. if (!choirId) {
  29. choirId = req.body.choirId;
  30. }
  31. const errorMessage = "Unable to get if gets feedback";
  32. if (choirId) {
  33. mysql.getClient().then((client) => {
  34. client.query("SELECT gets_feedback FROM tma.member WHERE person_id=? AND choir_id=unhex(?)", [res.locals.uid, choirId], function(error, results, fields) {
  35. if (error) {
  36. res.status(503).send(errorMessage);
  37. } else if (results.length === 0) {
  38. res.status(204).send(errorMessage);
  39. } else {
  40. let sum = 0;
  41. for (let i = 0; i < results.length; i++) {
  42. sum += results[i]["gets_feedback"];
  43. }
  44. let gets_feedback = sum > 0 ? true : false;
  45. res.status(200).json({ gets_feedback });
  46. }
  47. });
  48. });
  49. } else {
  50. res.status(400).send(errorMessage);
  51. }
  52. }
  53. }).catch((error) => {
  54. res.status(403).send('Unauthorized');
  55. });
  56. });
  57. /**
  58. * @typedef {MemberGetPendingReturnPackage[]} MemberGetPendingPackage List of information about the pending members
  59. */
  60. /**
  61. * @typedef {object} MemberGetPendingReturnPackage
  62. * @property {string} member_id The ID of the member
  63. * @property {string} first_name The first name of the member
  64. * @property {string} email The email of the member
  65. * @property {string} member_type The type of the member
  66. * @property {string} member_role The role of the member
  67. * @property {boolean} has_picture Whether the person attached to the member has a profile picture
  68. */
  69. /**
  70. * Route for getting all members which are still pending to be added to the choir which this user is an admin to
  71. * @name member/get/pending
  72. * @function
  73. * @inner
  74. * @param {*} req
  75. * @param {string} req.query.choirId The choir ID to be accessed.
  76. * @param {*} res
  77. * @returns {MemberGetPendingPackage|string} On success then information about the members who are pending. Otherwise, an error message.
  78. * @async
  79. */
  80. router.get('/pending', auth.token, async(req,res) => {
  81. verify.admin(req,res).then((isVerified) => {
  82. if (!isVerified) {
  83. res.status(403).send('Unauthorized');
  84. } else {
  85. let choirId = req.query.choirId;
  86. const errorMessage = "Unable to get pending users";
  87. if (choirId) {
  88. mysql.getClient().then((client) => {
  89. client.query("SELECT member_id, first_name, last_name, email, member_type, member_role, has_picture FROM tma.member INNER JOIN tma.person ON person.person_id = member.person_id WHERE member_status = 'pending' AND choir_id=unhex(?)", [choirId], function(error, results, fields) {
  90. if (error) {
  91. res.status(503).send(errorMessage);
  92. } else {
  93. res.status(200).json(results);
  94. }
  95. });
  96. });
  97. } else {
  98. res.status(400).send(errorMessage);
  99. }
  100. }
  101. }).catch((error) => {
  102. res.status(403).send('Unauthorized');
  103. });
  104. });
  105. /**
  106. * Route for a non-admin attempting to join a choir. They are added as a pending member until an admin approves them
  107. * @name member/post
  108. * @function
  109. * @inner
  110. * @param {*} req
  111. * @param {string} req.body.memberType The type of the member to be created
  112. * @param {string} req.body.memberRole The role of the member to be created
  113. * @param {string} req.body.accessCode The access code of the choir to which to add the member
  114. * @param {*} res
  115. * @returns {string} An error message if an error occured
  116. * @async
  117. */
  118. router.post('/', auth.token, async(req,res) => {
  119. let memberType = req.body.memberType;
  120. let memberRole = req.body.memberRole;
  121. let accessCode = req.body.accessCode;
  122. const errorMessage = "Unable to add member";
  123. if (memberType && memberRole && accessCode) {
  124. let client = await mysql.getClient();
  125. client.query("SELECT hex(choir_id) FROM tma.choir WHERE access_code=?", [accessCode], function(error, results, fields) {
  126. if (error) {
  127. res.status(503).send(errorMessage);
  128. } else if (results.length === 0) {
  129. res.status(400).send(errorMessage);
  130. } else {
  131. let choirId = results[0]["hex(choir_id)"];
  132. client.query("SELECT COUNT(*) FROM tma.member WHERE choir_id= unhex(?) AND person_id=? AND member_type=? AND member_role=?", [choirId, res.locals.uid, memberType, memberRole], function(error, results, fields) {
  133. if (error) {
  134. res.status(503).send(errorMessage);
  135. } else if (results.length === 0) {
  136. res.status(204).send(errorMessage);
  137. } else if (parseInt(results[0]["COUNT(*)"],10) > 0) {
  138. res.status(208).send(errorMessage);
  139. } else {
  140. client.query("INSERT INTO tma.member (member_id, member_type, member_role, choir_id, person_id, member_status) values(unhex(replace(uuid(),'-','')), ?, ?, unhex(?), ?, 'pending')", [memberType, memberRole, choirId, res.locals.uid], function(error, results, fields) {
  141. if (error) {
  142. res.status(503).send(errorMessage);
  143. } else {
  144. res.status(200).send();
  145. }
  146. });
  147. }
  148. });
  149. }
  150. });
  151. } else {
  152. res.status(400).send(errorMessage);
  153. }
  154. });
  155. /**
  156. * Route for an admin approving a pending member to joining a choir
  157. * @name member/put/accept
  158. * @function
  159. * @inner
  160. * @param {*} req
  161. * @param {string} req.body.choirId The choir ID to be accessed.
  162. * @param {string} req.body.memberId The member ID to be accepted.
  163. * @param {*} res
  164. * @returns {string} An error message if an error occured
  165. * @async
  166. */
  167. router.put('/accept', auth.token, async(req, res) => {
  168. verify.admin(req,res).then((isVerified) => {
  169. if (!isVerified) {
  170. res.status(403).send('Unauthorized');
  171. } else {
  172. let choirId = req.body.choirId;
  173. let memberId = req.body.memberId;
  174. const errorMessage = "Unable to accept user";
  175. if (choirId && memberId) {
  176. mysql.getClient().then((client) => {
  177. client.query("UPDATE tma.member SET member_status='verified' WHERE member_id=unhex(?) AND choir_id=unhex(?) AND member_status='pending'", [memberId, choirId], function(error, results, fields) {
  178. if (error) {
  179. res.status(503).send(errorMessage);
  180. } else {
  181. res.status(200).send();
  182. }
  183. });
  184. });
  185. } else {
  186. res.status(400).send(errorMessage);
  187. }
  188. }
  189. }).catch((error) => {
  190. res.status(403).send('Unauthorized');
  191. });
  192. });
  193. /**
  194. * Route for an admin rejecting a pending member to joining a choir. This doesn't remove them from the list of members
  195. * @name member/put/reject
  196. * @function
  197. * @inner
  198. * @param {*} req
  199. * @param {string} req.body.choirId The choir ID to be accessed.
  200. * @param {string} req.body.memberId The member ID to be rejected.
  201. * @param {*} res
  202. * @returns {string} An error message if an error occured
  203. * @async
  204. */
  205. router.put('/reject', auth.token, async(req, res) => {
  206. verify.admin(req,res).then((isVerified) => {
  207. if (!isVerified) {
  208. res.status(403).send('Unauthorized');
  209. } else {
  210. let choirId = req.body.choirId;
  211. let memberId = req.body.memberId;
  212. const errorMessage = "Unable to reject user";
  213. if (choirId && memberId) {
  214. mysql.getClient().then((client) => {
  215. client.query("UPDATE tma.member SET member_status='rejected' WHERE member_id=unhex(?) AND choir_id=unhex(?) AND member_status='pending'", [memberId, choirId], function(error, results, fields) {
  216. if (error) {
  217. res.status(503).send(errorMessage);
  218. } else {
  219. res.status(200).send();
  220. }
  221. });
  222. });
  223. } else {
  224. res.status(400).send(errorMessage);
  225. }
  226. }
  227. }).catch((error) => {
  228. res.status(403).send('Unauthorized');
  229. });
  230. });
  231. /**
  232. * Route for updating a member's information by an admin
  233. * @name member/put/update
  234. * @function
  235. * @inner
  236. * @param {*} req
  237. * @param {string} req.body.memberId The member ID to be accessed.
  238. * @param {string} req.body.memberType The new member type for the member
  239. * @param {string} req.body.memberRole The new member role for the member
  240. * @param {*} res
  241. * @returns {string} An error message if an error occured
  242. * @async
  243. */
  244. router.put('/update', auth.token, async(req, res) => {
  245. verify.adminOfMember(req,res).then((choirId) => {
  246. if (choirId === null) {
  247. res.status(403).send('Unauthorized');
  248. } else {
  249. let memberId = req.body.memberId;
  250. let memberType = req.body.memberType;
  251. let memberRole = req.body.memberRole;
  252. const errorMessage = "Unable to update user";
  253. if (memberId && memberType, memberRole) {
  254. mysql.getClient().then((client) => {
  255. client.query("UPDATE tma.member SET member_type=?, member_role=? WHERE member_id=unhex(?)", [memberType, memberRole, memberId], function(error, results, fields) {
  256. if (error) {
  257. res.status(503).send(errorMessage);
  258. } else {
  259. res.status(200).send();
  260. }
  261. });
  262. });
  263. } else {
  264. res.status(400).send(errorMessage);
  265. }
  266. }
  267. }).catch((error) => {
  268. res.status(403).send('Unauthorized');
  269. });
  270. });
  271. /**
  272. * Route for deleting a member from a choir by an admin
  273. * @name member/delete
  274. * @function
  275. * @inner
  276. * @param {*} req
  277. * @param {string} req.body.memberId The member ID to be deleted.
  278. * @param {*} res
  279. * @returns {string} An error message if an error occured
  280. * @async
  281. */
  282. router.delete('/', auth.token, async(req, res) => {
  283. verify.adminOfMember(req,res).then((choirId) => {
  284. if (choirId === null) {
  285. res.status(403).send('Unauthorized');
  286. } else {
  287. let memberId = req.body.memberId;
  288. const errorMessage = "Unable to delete user";
  289. if (memberId) {
  290. mysql.getClient().then((client) => {
  291. client.query("UPDATE tma.member SET member_status='admin_removed' WHERE member_id=unhex(?)", [memberId], function(error, results, fields) {
  292. if (error) {
  293. res.status(503).send(errorMessage);
  294. } else {
  295. res.status(200).send();
  296. }
  297. });
  298. });
  299. } else {
  300. res.status(400).send(errorMessage);
  301. }
  302. }
  303. }).catch((error) => {
  304. res.status(403).send('Unauthorized');
  305. });
  306. });
  307. module.exports = router;