Add more robust error handling to server

Changes:
- Use the proper RESTful HTTP status codes for all endpoints (e.g. 204 rather than 200 when the response body is empty)
- Add consistent and more helpful error messages
- Handle invalid route parameters by checking if jamid returns empty objects (e.g. invalid contact IDs)
- Use res.send() rather than res.json() for consistency
- Handle three new signals

GitLab: #111
Change-Id: I1d48dc4629995ab9a96bb2086a9aa91f81889598
diff --git a/server/src/routers/setup-router.ts b/server/src/routers/setup-router.ts
index d155ef5..7dfa24f 100644
--- a/server/src/routers/setup-router.ts
+++ b/server/src/routers/setup-router.ts
@@ -40,19 +40,25 @@
 setupRouter.post(
   '/admin/create',
   asyncHandler(async (req: Request<ParamsDictionary, string, { password?: string }>, res, _next) => {
-    const isAdminCreated = adminConfig.get() !== undefined;
-    if (isAdminCreated) {
-      res.sendStatus(HttpStatusCode.BadRequest);
+    const { password } = req.body;
+    if (password === undefined) {
+      res.status(HttpStatusCode.BadRequest).send('Missing password in body');
       return;
     }
 
-    const { password } = req.body;
-    if (!password) {
-      res.status(HttpStatusCode.BadRequest).send('Missing password');
+    if (password === '') {
+      res.status(HttpStatusCode.BadRequest).send('Password may not be empty');
+      return;
+    }
+
+    const isAdminCreated = adminConfig.get() !== undefined;
+    if (isAdminCreated) {
+      res.status(HttpStatusCode.Conflict).send('Admin already exists');
       return;
     }
 
     const hashedPassword = await argon2.hash(password, { type: argon2.argon2id });
+
     adminConfig.set(hashedPassword);
     await adminConfig.save();
 
@@ -62,7 +68,7 @@
 
 // Every request handler after this line will be submitted to this middleware
 // in order to ensure that the admin account is set up before proceeding with
-// setup related requests.
+// setup related requests
 setupRouter.use(checkAdminSetup);
 
 setupRouter.post(
@@ -70,15 +76,16 @@
   asyncHandler(
     async (req: Request<ParamsDictionary, { accessToken: string } | string, { password: string }>, res, _next) => {
       const { password } = req.body;
-      if (!password) {
-        res.status(HttpStatusCode.BadRequest).send('Missing password');
+      if (password === undefined) {
+        res.status(HttpStatusCode.BadRequest).send('Missing password in body');
         return;
       }
 
       const hashedPassword = adminConfig.get();
+
       const isPasswordVerified = await argon2.verify(hashedPassword, password);
       if (!isPasswordVerified) {
-        res.sendStatus(HttpStatusCode.Forbidden);
+        res.status(HttpStatusCode.Forbidden).send('Incorrect password');
         return;
       }