diff --git a/backend/internal/user.js b/backend/internal/user.js index b9aa60e9..5ae0c373 100644 --- a/backend/internal/user.js +++ b/backend/internal/user.js @@ -9,7 +9,7 @@ import internalAuditLog from "./audit-log.js"; import internalToken from "./token.js"; const omissions = () => { - return ["is_deleted"]; + return ["is_deleted", "permissions.id", "permissions.user_id", "permissions.created_on", "permissions.modified_on"]; }; const DEFAULT_AVATAR = gravatar.url("admin@example.com", { default: "mm" }); @@ -250,6 +250,14 @@ const internalUser = { }); }, + deleteAll: async () => { + await userModel + .query() + .patch({ + is_deleted: 1, + }); + }, + /** * This will only count the users * diff --git a/backend/lib/config.js b/backend/lib/config.js index 2176246a..ff3f2e12 100644 --- a/backend/lib/config.js +++ b/backend/lib/config.js @@ -199,6 +199,13 @@ const isPostgres = () => { */ const isDebugMode = () => !!process.env.DEBUG; +/** + * Are we running in CI? + * + * @returns {boolean} + */ +const isCI = () => process.env.CI === 'true' && process.env.DEBUG === 'true'; + /** * Returns a public key * @@ -234,4 +241,4 @@ const useLetsencryptServer = () => { return null; }; -export { configHas, configGet, isSqlite, isMysql, isPostgres, isDebugMode, getPrivateKey, getPublicKey, useLetsencryptStaging, useLetsencryptServer }; +export { isCI, configHas, configGet, isSqlite, isMysql, isPostgres, isDebugMode, getPrivateKey, getPublicKey, useLetsencryptStaging, useLetsencryptServer }; diff --git a/backend/lib/error.js b/backend/lib/error.js index 00e1df23..d7dbf0c9 100644 --- a/backend/lib/error.js +++ b/backend/lib/error.js @@ -14,7 +14,10 @@ const errs = { Error.captureStackTrace(this, this.constructor); this.name = this.constructor.name; this.previous = previous; - this.message = `Item Not Found - ${id}`; + this.message = "Not Found"; + if (id) { + this.message = `Not Found - ${id}`; + } this.public = true; this.status = 404; }, diff --git a/backend/routes/audit-log.js b/backend/routes/audit-log.js index 80aabe22..0c51e38a 100644 --- a/backend/routes/audit-log.js +++ b/backend/routes/audit-log.js @@ -2,6 +2,7 @@ import express from "express"; import internalAuditLog from "../internal/audit-log.js"; import jwtdecode from "../lib/express/jwt-decode.js"; import validator from "../lib/validator/index.js"; +import { express as logger } from "../logger.js"; const router = express.Router({ caseSensitive: true, @@ -24,31 +25,31 @@ router * * Retrieve all logs */ - .get((req, res, next) => { - validator( - { - additionalProperties: false, - properties: { - expand: { - $ref: "common#/properties/expand", - }, - query: { - $ref: "common#/properties/query", + .get(async (req, res, next) => { + try { + const data = await validator( + { + additionalProperties: false, + properties: { + expand: { + $ref: "common#/properties/expand", + }, + query: { + $ref: "common#/properties/query", + }, }, }, - }, - { - expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, - query: typeof req.query.query === "string" ? req.query.query : null, - }, - ) - .then((data) => { - return internalAuditLog.getAll(res.locals.access, data.expand, data.query); - }) - .then((rows) => { - res.status(200).send(rows); - }) - .catch(next); + { + expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, + query: typeof req.query.query === "string" ? req.query.query : null, + }, + ); + const rows = await internalAuditLog.getAll(res.locals.access, data.expand, data.query); + res.status(200).send(rows); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); export default router; diff --git a/backend/routes/nginx/access_lists.js b/backend/routes/nginx/access_lists.js index 6f066e36..a1c75f82 100644 --- a/backend/routes/nginx/access_lists.js +++ b/backend/routes/nginx/access_lists.js @@ -3,6 +3,7 @@ import internalAccessList from "../../internal/access-list.js"; import jwtdecode from "../../lib/express/jwt-decode.js"; import apiValidator from "../../lib/validator/api.js"; import validator from "../../lib/validator/index.js"; +import { express as logger } from "../../logger.js"; import { getValidationSchema } from "../../schema/index.js"; const router = express.Router({ @@ -26,31 +27,31 @@ router * * Retrieve all access-lists */ - .get((req, res, next) => { - validator( - { - additionalProperties: false, - properties: { - expand: { - $ref: "common#/properties/expand", - }, - query: { - $ref: "common#/properties/query", + .get(async (req, res, next) => { + try { + const data = await validator( + { + additionalProperties: false, + properties: { + expand: { + $ref: "common#/properties/expand", + }, + query: { + $ref: "common#/properties/query", + }, }, }, - }, - { - expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, - query: typeof req.query.query === "string" ? req.query.query : null, - }, - ) - .then((data) => { - return internalAccessList.getAll(res.locals.access, data.expand, data.query); - }) - .then((rows) => { - res.status(200).send(rows); - }) - .catch(next); + { + expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, + query: typeof req.query.query === "string" ? req.query.query : null, + }, + ); + const rows = await internalAccessList.getAll(res.locals.access, data.expand, data.query); + res.status(200).send(rows); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }) /** @@ -58,15 +59,15 @@ router * * Create a new access-list */ - .post((req, res, next) => { - apiValidator(getValidationSchema("/nginx/access-lists", "post"), req.body) - .then((payload) => { - return internalAccessList.create(res.locals.access, payload); - }) - .then((result) => { - res.status(201).send(result); - }) - .catch(next); + .post(async (req, res, next) => { + try { + const payload = await apiValidator(getValidationSchema("/nginx/access-lists", "post"), req.body); + const result = await internalAccessList.create(res.locals.access, payload); + res.status(201).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); /** @@ -86,35 +87,35 @@ router * * Retrieve a specific access-list */ - .get((req, res, next) => { - validator( - { - required: ["list_id"], - additionalProperties: false, - properties: { - list_id: { - $ref: "common#/properties/id", - }, - expand: { - $ref: "common#/properties/expand", + .get(async (req, res, next) => { + try { + const data = await validator( + { + required: ["list_id"], + additionalProperties: false, + properties: { + list_id: { + $ref: "common#/properties/id", + }, + expand: { + $ref: "common#/properties/expand", + }, }, }, - }, - { - list_id: req.params.list_id, - expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, - }, - ) - .then((data) => { - return internalAccessList.get(res.locals.access, { - id: Number.parseInt(data.list_id, 10), - expand: data.expand, - }); - }) - .then((row) => { - res.status(200).send(row); - }) - .catch(next); + { + list_id: req.params.list_id, + expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, + }, + ); + const row = await internalAccessList.get(res.locals.access, { + id: Number.parseInt(data.list_id, 10), + expand: data.expand, + }); + res.status(200).send(row); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }) /** @@ -122,16 +123,16 @@ router * * Update and existing access-list */ - .put((req, res, next) => { - apiValidator(getValidationSchema("/nginx/access-lists/{listID}", "put"), req.body) - .then((payload) => { - payload.id = Number.parseInt(req.params.list_id, 10); - return internalAccessList.update(res.locals.access, payload); - }) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + .put(async (req, res, next) => { + try { + const payload = await apiValidator(getValidationSchema("/nginx/access-lists/{listID}", "put"), req.body); + payload.id = Number.parseInt(req.params.list_id, 10); + const result = await internalAccessList.update(res.locals.access, payload); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }) /** @@ -139,13 +140,16 @@ router * * Delete and existing access-list */ - .delete((req, res, next) => { - internalAccessList - .delete(res.locals.access, { id: Number.parseInt(req.params.list_id, 10) }) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + .delete(async (req, res, next) => { + try { + const result = await internalAccessList.delete(res.locals.access, { + id: Number.parseInt(req.params.list_id, 10), + }); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); export default router; diff --git a/backend/routes/nginx/certificates.js b/backend/routes/nginx/certificates.js index 7f9d4227..16d04f38 100644 --- a/backend/routes/nginx/certificates.js +++ b/backend/routes/nginx/certificates.js @@ -4,6 +4,7 @@ import errs from "../../lib/error.js"; import jwtdecode from "../../lib/express/jwt-decode.js"; import apiValidator from "../../lib/validator/api.js"; import validator from "../../lib/validator/index.js"; +import { express as logger } from "../../logger.js"; import { getValidationSchema } from "../../schema/index.js"; const router = express.Router({ @@ -27,31 +28,31 @@ router * * Retrieve all certificates */ - .get((req, res, next) => { - validator( - { - additionalProperties: false, - properties: { - expand: { - $ref: "common#/properties/expand", - }, - query: { - $ref: "common#/properties/query", + .get(async (req, res, next) => { + try { + const data = await validator( + { + additionalProperties: false, + properties: { + expand: { + $ref: "common#/properties/expand", + }, + query: { + $ref: "common#/properties/query", + }, }, }, - }, - { - expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, - query: typeof req.query.query === "string" ? req.query.query : null, - }, - ) - .then((data) => { - return internalCertificate.getAll(res.locals.access, data.expand, data.query); - }) - .then((rows) => { - res.status(200).send(rows); - }) - .catch(next); + { + expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, + query: typeof req.query.query === "string" ? req.query.query : null, + }, + ); + const rows = await internalCertificate.getAll(res.locals.access, data.expand, data.query); + res.status(200).send(rows); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }) /** @@ -59,16 +60,16 @@ router * * Create a new certificate */ - .post((req, res, next) => { - apiValidator(getValidationSchema("/nginx/certificates", "post"), req.body) - .then((payload) => { - req.setTimeout(900000); // 15 minutes timeout - return internalCertificate.create(res.locals.access, payload); - }) - .then((result) => { - res.status(201).send(result); - }) - .catch(next); + .post(async (req, res, next) => { + try { + const payload = await apiValidator(getValidationSchema("/nginx/certificates", "post"), req.body); + req.setTimeout(900000); // 15 minutes timeout + const result = await internalCertificate.create(res.locals.access, payload); + res.status(201).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); /** @@ -88,18 +89,22 @@ router * * Test HTTP challenge for domains */ - .get((req, res, next) => { + .get(async (req, res, next) => { if (req.query.domains === undefined) { next(new errs.ValidationError("Domains are required as query parameters")); return; } - internalCertificate - .testHttpsChallenge(res.locals.access, JSON.parse(req.query.domains)) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + try { + const result = await internalCertificate.testHttpsChallenge( + res.locals.access, + JSON.parse(req.query.domains), + ); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); /** @@ -119,35 +124,35 @@ router * * Retrieve a specific certificate */ - .get((req, res, next) => { - validator( - { - required: ["certificate_id"], - additionalProperties: false, - properties: { - certificate_id: { - $ref: "common#/properties/id", - }, - expand: { - $ref: "common#/properties/expand", + .get(async (req, res, next) => { + try { + const data = await validator( + { + required: ["certificate_id"], + additionalProperties: false, + properties: { + certificate_id: { + $ref: "common#/properties/id", + }, + expand: { + $ref: "common#/properties/expand", + }, }, }, - }, - { - certificate_id: req.params.certificate_id, - expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, - }, - ) - .then((data) => { - return internalCertificate.get(res.locals.access, { - id: Number.parseInt(data.certificate_id, 10), - expand: data.expand, - }); - }) - .then((row) => { - res.status(200).send(row); - }) - .catch(next); + { + certificate_id: req.params.certificate_id, + expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, + }, + ); + const row = await internalCertificate.get(res.locals.access, { + id: Number.parseInt(data.certificate_id, 10), + expand: data.expand, + }); + res.status(200).send(row); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }) /** @@ -155,13 +160,16 @@ router * * Update and existing certificate */ - .delete((req, res, next) => { - internalCertificate - .delete(res.locals.access, { id: Number.parseInt(req.params.certificate_id, 10) }) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + .delete(async (req, res, next) => { + try { + const result = await internalCertificate.delete(res.locals.access, { + id: Number.parseInt(req.params.certificate_id, 10), + }); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); /** @@ -181,19 +189,21 @@ router * * Upload certificates */ - .post((req, res, next) => { + .post(async (req, res, next) => { if (!req.files) { res.status(400).send({ error: "No files were uploaded" }); - } else { - internalCertificate - .upload(res.locals.access, { - id: Number.parseInt(req.params.certificate_id, 10), - files: req.files, - }) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + return; + } + + try { + const result = await internalCertificate.upload(res.locals.access, { + id: Number.parseInt(req.params.certificate_id, 10), + files: req.files, + }); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); } }); @@ -214,16 +224,17 @@ router * * Renew certificate */ - .post((req, res, next) => { + .post(async (req, res, next) => { req.setTimeout(900000); // 15 minutes timeout - internalCertificate - .renew(res.locals.access, { + try { + const result = await internalCertificate.renew(res.locals.access, { id: Number.parseInt(req.params.certificate_id, 10), - }) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + }); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); /** @@ -243,15 +254,16 @@ router * * Renew certificate */ - .get((req, res, next) => { - internalCertificate - .download(res.locals.access, { + .get(async (req, res, next) => { + try { + const result = await internalCertificate.download(res.locals.access, { id: Number.parseInt(req.params.certificate_id, 10), - }) - .then((result) => { - res.status(200).download(result.fileName); - }) - .catch(next); + }); + res.status(200).download(result.fileName); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); /** @@ -271,18 +283,20 @@ router * * Validate certificates */ - .post((req, res, next) => { + .post(async (req, res, next) => { if (!req.files) { res.status(400).send({ error: "No files were uploaded" }); - } else { - internalCertificate - .validate({ - files: req.files, - }) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + return; + } + + try { + const result = await internalCertificate.validate({ + files: req.files, + }); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); } }); diff --git a/backend/routes/nginx/dead_hosts.js b/backend/routes/nginx/dead_hosts.js index 49a6cfed..7fc64a07 100644 --- a/backend/routes/nginx/dead_hosts.js +++ b/backend/routes/nginx/dead_hosts.js @@ -3,6 +3,7 @@ import internalDeadHost from "../../internal/dead-host.js"; import jwtdecode from "../../lib/express/jwt-decode.js"; import apiValidator from "../../lib/validator/api.js"; import validator from "../../lib/validator/index.js"; +import { express as logger } from "../../logger.js"; import { getValidationSchema } from "../../schema/index.js"; const router = express.Router({ @@ -26,31 +27,31 @@ router * * Retrieve all dead-hosts */ - .get((req, res, next) => { - validator( - { - additionalProperties: false, - properties: { - expand: { - $ref: "common#/properties/expand", - }, - query: { - $ref: "common#/properties/query", + .get(async (req, res, next) => { + try { + const data = await validator( + { + additionalProperties: false, + properties: { + expand: { + $ref: "common#/properties/expand", + }, + query: { + $ref: "common#/properties/query", + }, }, }, - }, - { - expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, - query: typeof req.query.query === "string" ? req.query.query : null, - }, - ) - .then((data) => { - return internalDeadHost.getAll(res.locals.access, data.expand, data.query); - }) - .then((rows) => { - res.status(200).send(rows); - }) - .catch(next); + { + expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, + query: typeof req.query.query === "string" ? req.query.query : null, + }, + ); + const rows = await internalDeadHost.getAll(res.locals.access, data.expand, data.query); + res.status(200).send(rows); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }) /** @@ -58,15 +59,15 @@ router * * Create a new dead-host */ - .post((req, res, next) => { - apiValidator(getValidationSchema("/nginx/dead-hosts", "post"), req.body) - .then((payload) => { - return internalDeadHost.create(res.locals.access, payload); - }) - .then((result) => { - res.status(201).send(result); - }) - .catch(next); + .post(async (req, res, next) => { + try { + const payload = await apiValidator(getValidationSchema("/nginx/dead-hosts", "post"), req.body); + const result = await internalDeadHost.create(res.locals.access, payload); + res.status(201).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); /** @@ -86,35 +87,35 @@ router * * Retrieve a specific dead-host */ - .get((req, res, next) => { - validator( - { - required: ["host_id"], - additionalProperties: false, - properties: { - host_id: { - $ref: "common#/properties/id", - }, - expand: { - $ref: "common#/properties/expand", + .get(async (req, res, next) => { + try { + const data = await validator( + { + required: ["host_id"], + additionalProperties: false, + properties: { + host_id: { + $ref: "common#/properties/id", + }, + expand: { + $ref: "common#/properties/expand", + }, }, }, - }, - { - host_id: req.params.host_id, - expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, - }, - ) - .then((data) => { - return internalDeadHost.get(res.locals.access, { - id: Number.parseInt(data.host_id, 10), - expand: data.expand, - }); - }) - .then((row) => { - res.status(200).send(row); - }) - .catch(next); + { + host_id: req.params.host_id, + expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, + }, + ); + const row = await internalDeadHost.get(res.locals.access, { + id: Number.parseInt(data.host_id, 10), + expand: data.expand, + }); + res.status(200).send(row); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }) /** @@ -122,16 +123,16 @@ router * * Update and existing dead-host */ - .put((req, res, next) => { - apiValidator(getValidationSchema("/nginx/dead-hosts/{hostID}", "put"), req.body) - .then((payload) => { - payload.id = Number.parseInt(req.params.host_id, 10); - return internalDeadHost.update(res.locals.access, payload); - }) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + .put(async (req, res, next) => { + try { + const payload = await apiValidator(getValidationSchema("/nginx/dead-hosts/{hostID}", "put"), req.body); + payload.id = Number.parseInt(req.params.host_id, 10); + const result = await internalDeadHost.update(res.locals.access, payload); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }) /** @@ -139,13 +140,16 @@ router * * Update and existing dead-host */ - .delete((req, res, next) => { - internalDeadHost - .delete(res.locals.access, { id: Number.parseInt(req.params.host_id, 10) }) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + .delete(async (req, res, next) => { + try { + const result = await internalDeadHost.delete(res.locals.access, { + id: Number.parseInt(req.params.host_id, 10), + }); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); /** @@ -163,13 +167,16 @@ router /** * POST /api/nginx/dead-hosts/123/enable */ - .post((req, res, next) => { - internalDeadHost - .enable(res.locals.access, { id: Number.parseInt(req.params.host_id, 10) }) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + .post(async (req, res, next) => { + try { + const result = await internalDeadHost.enable(res.locals.access, { + id: Number.parseInt(req.params.host_id, 10), + }); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); /** @@ -188,12 +195,13 @@ router * POST /api/nginx/dead-hosts/123/disable */ .post((req, res, next) => { - internalDeadHost - .disable(res.locals.access, { id: Number.parseInt(req.params.host_id, 10) }) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + try { + const result = internalDeadHost.disable(res.locals.access, { id: Number.parseInt(req.params.host_id, 10) }); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); export default router; diff --git a/backend/routes/nginx/proxy_hosts.js b/backend/routes/nginx/proxy_hosts.js index 226938a4..5b3ef057 100644 --- a/backend/routes/nginx/proxy_hosts.js +++ b/backend/routes/nginx/proxy_hosts.js @@ -3,6 +3,7 @@ import internalProxyHost from "../../internal/proxy-host.js"; import jwtdecode from "../../lib/express/jwt-decode.js"; import apiValidator from "../../lib/validator/api.js"; import validator from "../../lib/validator/index.js"; +import { express as logger } from "../../logger.js"; import { getValidationSchema } from "../../schema/index.js"; const router = express.Router({ @@ -26,31 +27,31 @@ router * * Retrieve all proxy-hosts */ - .get((req, res, next) => { - validator( - { - additionalProperties: false, - properties: { - expand: { - $ref: "common#/properties/expand", - }, - query: { - $ref: "common#/properties/query", + .get(async (req, res, next) => { + try { + const data = await validator( + { + additionalProperties: false, + properties: { + expand: { + $ref: "common#/properties/expand", + }, + query: { + $ref: "common#/properties/query", + }, }, }, - }, - { - expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, - query: typeof req.query.query === "string" ? req.query.query : null, - }, - ) - .then((data) => { - return internalProxyHost.getAll(res.locals.access, data.expand, data.query); - }) - .then((rows) => { - res.status(200).send(rows); - }) - .catch(next); + { + expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, + query: typeof req.query.query === "string" ? req.query.query : null, + }, + ); + const rows = await internalProxyHost.getAll(res.locals.access, data.expand, data.query); + res.status(200).send(rows); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }) /** @@ -58,15 +59,15 @@ router * * Create a new proxy-host */ - .post((req, res, next) => { - apiValidator(getValidationSchema("/nginx/proxy-hosts", "post"), req.body) - .then((payload) => { - return internalProxyHost.create(res.locals.access, payload); - }) - .then((result) => { - res.status(201).send(result); - }) - .catch(next); + .post(async (req, res, next) => { + try { + const payload = await apiValidator(getValidationSchema("/nginx/proxy-hosts", "post"), req.body); + const result = await internalProxyHost.create(res.locals.access, payload); + res.status(201).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); /** @@ -86,35 +87,35 @@ router * * Retrieve a specific proxy-host */ - .get((req, res, next) => { - validator( - { - required: ["host_id"], - additionalProperties: false, - properties: { - host_id: { - $ref: "common#/properties/id", - }, - expand: { - $ref: "common#/properties/expand", + .get(async (req, res, next) => { + try { + const data = await validator( + { + required: ["host_id"], + additionalProperties: false, + properties: { + host_id: { + $ref: "common#/properties/id", + }, + expand: { + $ref: "common#/properties/expand", + }, }, }, - }, - { - host_id: req.params.host_id, - expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, - }, - ) - .then((data) => { - return internalProxyHost.get(res.locals.access, { - id: Number.parseInt(data.host_id, 10), - expand: data.expand, - }); - }) - .then((row) => { - res.status(200).send(row); - }) - .catch(next); + { + host_id: req.params.host_id, + expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, + }, + ); + const row = await internalProxyHost.get(res.locals.access, { + id: Number.parseInt(data.host_id, 10), + expand: data.expand, + }); + res.status(200).send(row); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }) /** @@ -122,16 +123,16 @@ router * * Update and existing proxy-host */ - .put((req, res, next) => { - apiValidator(getValidationSchema("/nginx/proxy-hosts/{hostID}", "put"), req.body) - .then((payload) => { - payload.id = Number.parseInt(req.params.host_id, 10); - return internalProxyHost.update(res.locals.access, payload); - }) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + .put(async (req, res, next) => { + try { + const payload = await apiValidator(getValidationSchema("/nginx/proxy-hosts/{hostID}", "put"), req.body); + payload.id = Number.parseInt(req.params.host_id, 10); + const result = await internalProxyHost.update(res.locals.access, payload); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }) /** @@ -139,13 +140,16 @@ router * * Update and existing proxy-host */ - .delete((req, res, next) => { - internalProxyHost - .delete(res.locals.access, { id: Number.parseInt(req.params.host_id, 10) }) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + .delete(async (req, res, next) => { + try { + const result = await internalProxyHost.delete(res.locals.access, { + id: Number.parseInt(req.params.host_id, 10), + }); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); /** @@ -163,13 +167,16 @@ router /** * POST /api/nginx/proxy-hosts/123/enable */ - .post((req, res, next) => { - internalProxyHost - .enable(res.locals.access, { id: Number.parseInt(req.params.host_id, 10) }) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + .post(async (req, res, next) => { + try { + const result = await internalProxyHost.enable(res.locals.access, { + id: Number.parseInt(req.params.host_id, 10), + }); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); /** @@ -187,13 +194,16 @@ router /** * POST /api/nginx/proxy-hosts/123/disable */ - .post((req, res, next) => { - internalProxyHost - .disable(res.locals.access, { id: Number.parseInt(req.params.host_id, 10) }) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + .post(async (req, res, next) => { + try { + const result = await internalProxyHost.disable(res.locals.access, { + id: Number.parseInt(req.params.host_id, 10), + }); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); export default router; diff --git a/backend/routes/nginx/redirection_hosts.js b/backend/routes/nginx/redirection_hosts.js index 9be6deb2..bbeccf6e 100644 --- a/backend/routes/nginx/redirection_hosts.js +++ b/backend/routes/nginx/redirection_hosts.js @@ -3,6 +3,7 @@ import internalRedirectionHost from "../../internal/redirection-host.js"; import jwtdecode from "../../lib/express/jwt-decode.js"; import apiValidator from "../../lib/validator/api.js"; import validator from "../../lib/validator/index.js"; +import { express as logger } from "../../logger.js"; import { getValidationSchema } from "../../schema/index.js"; const router = express.Router({ @@ -26,31 +27,31 @@ router * * Retrieve all redirection-hosts */ - .get((req, res, next) => { - validator( - { - additionalProperties: false, - properties: { - expand: { - $ref: "common#/properties/expand", - }, - query: { - $ref: "common#/properties/query", + .get(async (req, res, next) => { + try { + const data = await validator( + { + additionalProperties: false, + properties: { + expand: { + $ref: "common#/properties/expand", + }, + query: { + $ref: "common#/properties/query", + }, }, }, - }, - { - expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, - query: typeof req.query.query === "string" ? req.query.query : null, - }, - ) - .then((data) => { - return internalRedirectionHost.getAll(res.locals.access, data.expand, data.query); - }) - .then((rows) => { - res.status(200).send(rows); - }) - .catch(next); + { + expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, + query: typeof req.query.query === "string" ? req.query.query : null, + }, + ); + const rows = await internalRedirectionHost.getAll(res.locals.access, data.expand, data.query); + res.status(200).send(rows); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }) /** @@ -58,15 +59,15 @@ router * * Create a new redirection-host */ - .post((req, res, next) => { - apiValidator(getValidationSchema("/nginx/redirection-hosts", "post"), req.body) - .then((payload) => { - return internalRedirectionHost.create(res.locals.access, payload); - }) - .then((result) => { - res.status(201).send(result); - }) - .catch(next); + .post(async (req, res, next) => { + try { + const payload = await apiValidator(getValidationSchema("/nginx/redirection-hosts", "post"), req.body); + const result = await internalRedirectionHost.create(res.locals.access, payload); + res.status(201).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); /** @@ -86,35 +87,35 @@ router * * Retrieve a specific redirection-host */ - .get((req, res, next) => { - validator( - { - required: ["host_id"], - additionalProperties: false, - properties: { - host_id: { - $ref: "common#/properties/id", - }, - expand: { - $ref: "common#/properties/expand", + .get(async (req, res, next) => { + try { + const data = await validator( + { + required: ["host_id"], + additionalProperties: false, + properties: { + host_id: { + $ref: "common#/properties/id", + }, + expand: { + $ref: "common#/properties/expand", + }, }, }, - }, - { - host_id: req.params.host_id, - expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, - }, - ) - .then((data) => { - return internalRedirectionHost.get(res.locals.access, { - id: Number.parseInt(data.host_id, 10), - expand: data.expand, - }); - }) - .then((row) => { - res.status(200).send(row); - }) - .catch(next); + { + host_id: req.params.host_id, + expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, + }, + ); + const row = await internalRedirectionHost.get(res.locals.access, { + id: Number.parseInt(data.host_id, 10), + expand: data.expand, + }); + res.status(200).send(row); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }) /** @@ -122,16 +123,19 @@ router * * Update and existing redirection-host */ - .put((req, res, next) => { - apiValidator(getValidationSchema("/nginx/redirection-hosts/{hostID}", "put"), req.body) - .then((payload) => { - payload.id = Number.parseInt(req.params.host_id, 10); - return internalRedirectionHost.update(res.locals.access, payload); - }) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + .put(async (req, res, next) => { + try { + const payload = await apiValidator( + getValidationSchema("/nginx/redirection-hosts/{hostID}", "put"), + req.body, + ); + payload.id = Number.parseInt(req.params.host_id, 10); + const result = await internalRedirectionHost.update(res.locals.access, payload); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }) /** @@ -139,13 +143,16 @@ router * * Update and existing redirection-host */ - .delete((req, res, next) => { - internalRedirectionHost - .delete(res.locals.access, { id: Number.parseInt(req.params.host_id, 10) }) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + .delete(async (req, res, next) => { + try { + const result = await internalRedirectionHost.delete(res.locals.access, { + id: Number.parseInt(req.params.host_id, 10), + }); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); /** @@ -163,13 +170,16 @@ router /** * POST /api/nginx/redirection-hosts/123/enable */ - .post((req, res, next) => { - internalRedirectionHost - .enable(res.locals.access, { id: Number.parseInt(req.params.host_id, 10) }) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + .post(async (req, res, next) => { + try { + const result = await internalRedirectionHost.enable(res.locals.access, { + id: Number.parseInt(req.params.host_id, 10), + }); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); /** @@ -187,13 +197,16 @@ router /** * POST /api/nginx/redirection-hosts/123/disable */ - .post((req, res, next) => { - internalRedirectionHost - .disable(res.locals.access, { id: Number.parseInt(req.params.host_id, 10) }) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + .post(async (req, res, next) => { + try { + const result = await internalRedirectionHost.disable(res.locals.access, { + id: Number.parseInt(req.params.host_id, 10), + }); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); export default router; diff --git a/backend/routes/nginx/streams.js b/backend/routes/nginx/streams.js index 45dd6d93..878dd148 100644 --- a/backend/routes/nginx/streams.js +++ b/backend/routes/nginx/streams.js @@ -3,6 +3,7 @@ import internalStream from "../../internal/stream.js"; import jwtdecode from "../../lib/express/jwt-decode.js"; import apiValidator from "../../lib/validator/api.js"; import validator from "../../lib/validator/index.js"; +import { express as logger } from "../../logger.js"; import { getValidationSchema } from "../../schema/index.js"; const router = express.Router({ @@ -26,31 +27,31 @@ router * * Retrieve all streams */ - .get((req, res, next) => { - validator( - { - additionalProperties: false, - properties: { - expand: { - $ref: "common#/properties/expand", - }, - query: { - $ref: "common#/properties/query", + .get(async (req, res, next) => { + try { + const data = await validator( + { + additionalProperties: false, + properties: { + expand: { + $ref: "common#/properties/expand", + }, + query: { + $ref: "common#/properties/query", + }, }, }, - }, - { - expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, - query: typeof req.query.query === "string" ? req.query.query : null, - }, - ) - .then((data) => { - return internalStream.getAll(res.locals.access, data.expand, data.query); - }) - .then((rows) => { - res.status(200).send(rows); - }) - .catch(next); + { + expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, + query: typeof req.query.query === "string" ? req.query.query : null, + }, + ); + const rows = await internalStream.getAll(res.locals.access, data.expand, data.query); + res.status(200).send(rows); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }) /** @@ -58,15 +59,15 @@ router * * Create a new stream */ - .post((req, res, next) => { - apiValidator(getValidationSchema("/nginx/streams", "post"), req.body) - .then((payload) => { - return internalStream.create(res.locals.access, payload); - }) - .then((result) => { - res.status(201).send(result); - }) - .catch(next); + .post(async (req, res, next) => { + try { + const payload = await apiValidator(getValidationSchema("/nginx/streams", "post"), req.body); + const result = await internalStream.create(res.locals.access, payload); + res.status(201).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); /** @@ -86,35 +87,35 @@ router * * Retrieve a specific stream */ - .get((req, res, next) => { - validator( - { - required: ["stream_id"], - additionalProperties: false, - properties: { - stream_id: { - $ref: "common#/properties/id", - }, - expand: { - $ref: "common#/properties/expand", + .get(async (req, res, next) => { + try { + const data = await validator( + { + required: ["stream_id"], + additionalProperties: false, + properties: { + stream_id: { + $ref: "common#/properties/id", + }, + expand: { + $ref: "common#/properties/expand", + }, }, }, - }, - { - stream_id: req.params.stream_id, - expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, - }, - ) - .then((data) => { - return internalStream.get(res.locals.access, { - id: Number.parseInt(data.stream_id, 10), - expand: data.expand, - }); - }) - .then((row) => { - res.status(200).send(row); - }) - .catch(next); + { + stream_id: req.params.stream_id, + expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, + }, + ); + const row = await internalStream.get(res.locals.access, { + id: Number.parseInt(data.stream_id, 10), + expand: data.expand, + }); + res.status(200).send(row); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }) /** @@ -122,16 +123,16 @@ router * * Update and existing stream */ - .put((req, res, next) => { - apiValidator(getValidationSchema("/nginx/streams/{streamID}", "put"), req.body) - .then((payload) => { - payload.id = Number.parseInt(req.params.stream_id, 10); - return internalStream.update(res.locals.access, payload); - }) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + .put(async (req, res, next) => { + try { + const payload = await apiValidator(getValidationSchema("/nginx/streams/{streamID}", "put"), req.body); + payload.id = Number.parseInt(req.params.stream_id, 10); + const result = await internalStream.update(res.locals.access, payload); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }) /** @@ -139,13 +140,16 @@ router * * Update and existing stream */ - .delete((req, res, next) => { - internalStream - .delete(res.locals.access, { id: Number.parseInt(req.params.stream_id, 10) }) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + .delete(async (req, res, next) => { + try { + const result = await internalStream.delete(res.locals.access, { + id: Number.parseInt(req.params.stream_id, 10), + }); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); /** @@ -163,13 +167,16 @@ router /** * POST /api/nginx/streams/123/enable */ - .post((req, res, next) => { - internalStream - .enable(res.locals.access, { id: Number.parseInt(req.params.host_id, 10) }) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + .post(async (req, res, next) => { + try { + const result = await internalStream.enable(res.locals.access, { + id: Number.parseInt(req.params.host_id, 10), + }); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); /** @@ -187,13 +194,16 @@ router /** * POST /api/nginx/streams/123/disable */ - .post((req, res, next) => { - internalStream - .disable(res.locals.access, { id: Number.parseInt(req.params.host_id, 10) }) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + .post(async (req, res, next) => { + try { + const result = await internalStream.disable(res.locals.access, { + id: Number.parseInt(req.params.host_id, 10), + }); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); export default router; diff --git a/backend/routes/reports.js b/backend/routes/reports.js index 3c83075f..a5ac3de7 100644 --- a/backend/routes/reports.js +++ b/backend/routes/reports.js @@ -1,6 +1,7 @@ import express from "express"; import internalReport from "../internal/report.js"; import jwtdecode from "../lib/express/jwt-decode.js"; +import { express as logger } from "../logger.js"; const router = express.Router({ caseSensitive: true, @@ -17,13 +18,14 @@ router /** * GET /reports/hosts */ - .get(jwtdecode(), (_, res, next) => { - internalReport - .getHostsReport(res.locals.access) - .then((data) => { - res.status(200).send(data); - }) - .catch(next); + .get(jwtdecode(), async (req, res, next) => { + try { + const data = await internalReport.getHostsReport(res.locals.access); + res.status(200).send(data); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); export default router; diff --git a/backend/routes/schema.js b/backend/routes/schema.js index d0561cfc..71003f21 100644 --- a/backend/routes/schema.js +++ b/backend/routes/schema.js @@ -1,4 +1,5 @@ import express from "express"; +import { express as logger } from "../logger.js"; import PACKAGE from "../package.json" with { type: "json" }; import { getCompiledSchema } from "../schema/index.js"; @@ -18,21 +19,26 @@ router * GET /schema */ .get(async (req, res) => { - const swaggerJSON = await getCompiledSchema(); + try { + const swaggerJSON = await getCompiledSchema(); - let proto = req.protocol; - if (typeof req.headers["x-forwarded-proto"] !== "undefined" && req.headers["x-forwarded-proto"]) { - proto = req.headers["x-forwarded-proto"]; + let proto = req.protocol; + if (typeof req.headers["x-forwarded-proto"] !== "undefined" && req.headers["x-forwarded-proto"]) { + proto = req.headers["x-forwarded-proto"]; + } + + let origin = `${proto}://${req.hostname}`; + if (typeof req.headers.origin !== "undefined" && req.headers.origin) { + origin = req.headers.origin; + } + + swaggerJSON.info.version = PACKAGE.version; + swaggerJSON.servers[0].url = `${origin}/api`; + res.status(200).send(swaggerJSON); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); } - - let origin = `${proto}://${req.hostname}`; - if (typeof req.headers.origin !== "undefined" && req.headers.origin) { - origin = req.headers.origin; - } - - swaggerJSON.info.version = PACKAGE.version; - swaggerJSON.servers[0].url = `${origin}/api`; - res.status(200).send(swaggerJSON); }); export default router; diff --git a/backend/routes/settings.js b/backend/routes/settings.js index 1d8cc949..0439992c 100644 --- a/backend/routes/settings.js +++ b/backend/routes/settings.js @@ -3,6 +3,7 @@ import internalSetting from "../internal/setting.js"; import jwtdecode from "../lib/express/jwt-decode.js"; import apiValidator from "../lib/validator/api.js"; import validator from "../lib/validator/index.js"; +import { express as logger } from "../logger.js"; import { getValidationSchema } from "../schema/index.js"; const router = express.Router({ @@ -26,13 +27,14 @@ router * * Retrieve all settings */ - .get((_, res, next) => { - internalSetting - .getAll(res.locals.access) - .then((rows) => { - res.status(200).send(rows); - }) - .catch(next); + .get(async (req, res, next) => { + try { + const rows = await internalSetting.getAll(res.locals.access); + res.status(200).send(rows); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); /** @@ -52,31 +54,31 @@ router * * Retrieve a specific setting */ - .get((req, res, next) => { - validator( - { - required: ["setting_id"], - additionalProperties: false, - properties: { - setting_id: { - type: "string", - minLength: 1, + .get(async (req, res, next) => { + try { + const data = await validator( + { + required: ["setting_id"], + additionalProperties: false, + properties: { + setting_id: { + type: "string", + minLength: 1, + }, }, }, - }, - { - setting_id: req.params.setting_id, - }, - ) - .then((data) => { - return internalSetting.get(res.locals.access, { - id: data.setting_id, - }); - }) - .then((row) => { - res.status(200).send(row); - }) - .catch(next); + { + setting_id: req.params.setting_id, + }, + ); + const row = await internalSetting.get(res.locals.access, { + id: data.setting_id, + }); + res.status(200).send(row); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }) /** @@ -84,16 +86,16 @@ router * * Update and existing setting */ - .put((req, res, next) => { - apiValidator(getValidationSchema("/settings/{settingID}", "put"), req.body) - .then((payload) => { - payload.id = req.params.setting_id; - return internalSetting.update(res.locals.access, payload); - }) - .then((result) => { - res.status(200).send(result); - }) - .catch(next); + .put(async (req, res, next) => { + try { + const payload = await apiValidator(getValidationSchema("/settings/{settingID}", "put"), req.body); + payload.id = req.params.setting_id; + const result = await internalSetting.update(res.locals.access, payload); + res.status(200).send(result); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } }); export default router; diff --git a/backend/routes/users.js b/backend/routes/users.js index ef0b3120..db6656d5 100644 --- a/backend/routes/users.js +++ b/backend/routes/users.js @@ -1,6 +1,8 @@ import express from "express"; import internalUser from "../internal/user.js"; import Access from "../lib/access.js"; +import { isCI } from "../lib/config.js"; +import errs from "../lib/error.js"; import jwtdecode from "../lib/express/jwt-decode.js"; import userIdFromMe from "../lib/express/user-id-from-me.js"; import apiValidator from "../lib/validator/api.js"; @@ -45,11 +47,18 @@ router }, }, { - expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, + expand: + typeof req.query.expand === "string" + ? req.query.expand.split(",") + : null, query: typeof req.query.query === "string" ? req.query.query : null, }, ); - const users = await internalUser.getAll(res.locals.access, data.expand, data.query); + const users = await internalUser.getAll( + res.locals.access, + data.expand, + data.query, + ); res.status(200).send(users); } catch (err) { logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); @@ -85,13 +94,43 @@ router } } - const payload = await apiValidator(getValidationSchema("/users", "post"), body); + const payload = await apiValidator( + getValidationSchema("/users", "post"), + body, + ); const user = await internalUser.create(res.locals.access, payload); res.status(201).send(user); } catch (err) { logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); next(err); } + }) + + /** + * DELETE /api/users + * + * Deletes ALL users. This is NOT GENERALLY AVAILABLE! + * (!) It is NOT an authenticated endpoint. + * (!) Only CI should be able to call this endpoint. As a result, + * + * it will only work when the env vars DEBUG=true and CI=true + * + * Do NOT set those env vars in a production environment! + */ + .delete(async (_, res, next) => { + if (isCI()) { + try { + logger.warn("Deleting all users - CI environment detected, allowing this operation"); + await internalUser.deleteAll(); + res.status(200).send(true); + } catch (err) { + logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); + next(err); + } + return; + } + + next(new errs.ItemNotFoundError()); }); /** @@ -129,14 +168,20 @@ router }, { user_id: req.params.user_id, - expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, + expand: + typeof req.query.expand === "string" + ? req.query.expand.split(",") + : null, }, ); const user = await internalUser.get(res.locals.access, { id: data.user_id, expand: data.expand, - omit: internalUser.getUserOmisionsByAccess(res.locals.access, data.user_id), + omit: internalUser.getUserOmisionsByAccess( + res.locals.access, + data.user_id, + ), }); res.status(200).send(user); } catch (err) { @@ -152,7 +197,10 @@ router */ .put(async (req, res, next) => { try { - const payload = await apiValidator(getValidationSchema("/users/{userID}", "put"), req.body); + const payload = await apiValidator( + getValidationSchema("/users/{userID}", "put"), + req.body, + ); payload.id = req.params.user_id; const result = await internalUser.update(res.locals.access, payload); res.status(200).send(result); @@ -169,7 +217,9 @@ router */ .delete(async (req, res, next) => { try { - const result = await internalUser.delete(res.locals.access, { id: req.params.user_id }); + const result = await internalUser.delete(res.locals.access, { + id: req.params.user_id, + }); res.status(200).send(result); } catch (err) { logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); @@ -197,7 +247,10 @@ router */ .put(async (req, res, next) => { try { - const payload = await apiValidator(getValidationSchema("/users/{userID}/auth", "put"), req.body); + const payload = await apiValidator( + getValidationSchema("/users/{userID}/auth", "put"), + req.body, + ); payload.id = req.params.user_id; const result = await internalUser.setPassword(res.locals.access, payload); res.status(200).send(result); @@ -227,9 +280,15 @@ router */ .put(async (req, res, next) => { try { - const payload = await apiValidator(getValidationSchema("/users/{userID}/permissions", "put"), req.body); + const payload = await apiValidator( + getValidationSchema("/users/{userID}/permissions", "put"), + req.body, + ); payload.id = req.params.user_id; - const result = await internalUser.setPermissions(res.locals.access, payload); + const result = await internalUser.setPermissions( + res.locals.access, + payload, + ); res.status(200).send(result); } catch (err) { logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); diff --git a/backend/schema/components/health-object.json b/backend/schema/components/health-object.json index 8d223417..f6398094 100644 --- a/backend/schema/components/health-object.json +++ b/backend/schema/components/health-object.json @@ -9,6 +9,11 @@ "description": "Healthy", "example": "OK" }, + "setup": { + "type": "boolean", + "description": "Whether the initial setup has been completed", + "example": true + }, "version": { "type": "object", "description": "The version object", diff --git a/backend/schema/components/user-object.json b/backend/schema/components/user-object.json index 180e8f19..eec02a86 100644 --- a/backend/schema/components/user-object.json +++ b/backend/schema/components/user-object.json @@ -54,6 +54,63 @@ "items": { "type": "string" } + }, + "permissions": { + "type": "object", + "description": "Permissions if expanded in request", + "required": [ + "visibility", + "proxy_hosts", + "redirection_hosts", + "dead_hosts", + "streams", + "access_lists", + "certificates" + ], + "properties": { + "visibility": { + "type": "string", + "description": "Visibility level", + "example": "all", + "pattern": "^(all|user)$" + }, + "proxy_hosts": { + "type": "string", + "description": "Proxy Hosts access level", + "example": "all", + "pattern": "^(manage|view|hidden)$" + }, + "redirection_hosts": { + "type": "string", + "description": "Redirection Hosts access level", + "example": "all", + "pattern": "^(manage|view|hidden)$" + }, + "dead_hosts": { + "type": "string", + "description": "Dead Hosts access level", + "example": "all", + "pattern": "^(manage|view|hidden)$" + }, + "streams": { + "type": "string", + "description": "Streams access level", + "example": "all", + "pattern": "^(manage|view|hidden)$" + }, + "access_lists": { + "type": "string", + "description": "Access Lists access level", + "example": "all", + "pattern": "^(manage|view|hidden)$" + }, + "certificates": { + "type": "string", + "description": "Certificates access level", + "example": "all", + "pattern": "^(manage|view|hidden)$" + } + } } } } diff --git a/backend/schema/paths/get.json b/backend/schema/paths/get.json index 8c3a4e02..9ca1b8d3 100644 --- a/backend/schema/paths/get.json +++ b/backend/schema/paths/get.json @@ -11,6 +11,7 @@ "default": { "value": { "status": "OK", + "setup": true, "version": { "major": 2, "minor": 1, diff --git a/docker/docker-compose.ci.yml b/docker/docker-compose.ci.yml index e656c926..8070aa42 100644 --- a/docker/docker-compose.ci.yml +++ b/docker/docker-compose.ci.yml @@ -9,6 +9,7 @@ services: environment: TZ: "${TZ:-Australia/Brisbane}" DEBUG: 'true' + CI: 'true' FORCE_COLOR: 1 # Required for DNS Certificate provisioning in CI LE_SERVER: 'https://ca.internal/acme/acme/directory' diff --git a/test/cypress/e2e/api/Certificates.cy.js b/test/cypress/e2e/api/Certificates.cy.js index 837bde96..35e9d1bb 100644 --- a/test/cypress/e2e/api/Certificates.cy.js +++ b/test/cypress/e2e/api/Certificates.cy.js @@ -5,6 +5,7 @@ describe('Certificates endpoints', () => { let certID; before(() => { + cy.resetUsers(); cy.getToken().then((tok) => { token = tok; }); diff --git a/test/cypress/e2e/api/Dashboard.cy.js b/test/cypress/e2e/api/Dashboard.cy.js index 62cb40e4..8da332ab 100644 --- a/test/cypress/e2e/api/Dashboard.cy.js +++ b/test/cypress/e2e/api/Dashboard.cy.js @@ -4,6 +4,7 @@ describe('Dashboard endpoints', () => { let token; before(() => { + cy.resetUsers(); cy.getToken().then((tok) => { token = tok; }); diff --git a/test/cypress/e2e/api/FullCertProvision.cy.js b/test/cypress/e2e/api/FullCertProvision.cy.js index 9c6a7d2d..b82d7298 100644 --- a/test/cypress/e2e/api/FullCertProvision.cy.js +++ b/test/cypress/e2e/api/FullCertProvision.cy.js @@ -4,6 +4,7 @@ describe('Full Certificate Provisions', () => { let token; before(() => { + cy.resetUsers(); cy.getToken().then((tok) => { token = tok; }); diff --git a/test/cypress/e2e/api/Ldap.cy.js b/test/cypress/e2e/api/Ldap.cy.js index 715c793b..64e17730 100644 --- a/test/cypress/e2e/api/Ldap.cy.js +++ b/test/cypress/e2e/api/Ldap.cy.js @@ -5,6 +5,7 @@ describe('LDAP with Authentik', () => { if (Cypress.env('skipStackCheck') === 'true' || Cypress.env('stack') === 'postgres') { before(() => { + cy.resetUsers(); cy.getToken().then((tok) => { _token = tok; diff --git a/test/cypress/e2e/api/ProxyHosts.cy.js b/test/cypress/e2e/api/ProxyHosts.cy.js index 476945e7..4ca52279 100644 --- a/test/cypress/e2e/api/ProxyHosts.cy.js +++ b/test/cypress/e2e/api/ProxyHosts.cy.js @@ -4,6 +4,7 @@ describe('Proxy Hosts endpoints', () => { let token; before(() => { + cy.resetUsers(); cy.getToken().then((tok) => { token = tok; }); diff --git a/test/cypress/e2e/api/Settings.cy.js b/test/cypress/e2e/api/Settings.cy.js index fcaa0628..a925b2d3 100644 --- a/test/cypress/e2e/api/Settings.cy.js +++ b/test/cypress/e2e/api/Settings.cy.js @@ -4,6 +4,7 @@ describe('Settings endpoints', () => { let token; before(() => { + cy.resetUsers(); cy.getToken().then((tok) => { token = tok; }); diff --git a/test/cypress/e2e/api/Streams.cy.js b/test/cypress/e2e/api/Streams.cy.js index e38f9dba..eb56892b 100644 --- a/test/cypress/e2e/api/Streams.cy.js +++ b/test/cypress/e2e/api/Streams.cy.js @@ -4,6 +4,7 @@ describe('Streams', () => { let token; before(() => { + cy.resetUsers(); cy.getToken().then((tok) => { token = tok; // Set default site content diff --git a/test/cypress/e2e/api/Users.cy.js b/test/cypress/e2e/api/Users.cy.js index 7307f87d..044dcdee 100644 --- a/test/cypress/e2e/api/Users.cy.js +++ b/test/cypress/e2e/api/Users.cy.js @@ -4,6 +4,7 @@ describe('Users endpoints', () => { let token; before(() => { + cy.resetUsers(); cy.getToken().then((tok) => { token = tok; }); diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js index 7d602ecb..621d0d77 100644 --- a/test/cypress/support/commands.js +++ b/test/cypress/support/commands.js @@ -12,10 +12,10 @@ import 'cypress-wait-until'; Cypress.Commands.add('randomString', (length) => { - var result = ''; - var characters = 'ABCDEFGHIJK LMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - var charactersLength = characters.length; - for (var i = 0; i < length; i++) { + let result = ''; + const characters = 'ABCDEFGHIJK LMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + const charactersLength = characters.length; + for (let i = 0; i < length; i++) { result += characters.charAt(Math.floor(Math.random() * charactersLength)); } return result; @@ -40,16 +40,81 @@ Cypress.Commands.add('validateSwaggerSchema', (method, code, path, data) => { }).should('equal', null); }); -Cypress.Commands.add('getToken', () => { - // login with existing user - cy.task('backendApiPost', { - path: '/api/tokens', - data: { - identity: 'admin@example.com', - secret: 'changeme' +Cypress.Commands.add('createInitialUser', (defaultUser) => { + let user = { + name: 'Cypress McGee', + nickname: 'Cypress', + email: 'cypress@example.com', + auth: { + type: 'password', + secret: 'changeme' + }, + }; + + if (typeof defaultUser === 'object' && defaultUser) { + user = Object.assign({}, user, defaultUser); + } + + return cy.task('backendApiPost', { + path: '/api/users', + data: user, + }).then((data) => { + // Check the swagger schema: + cy.validateSwaggerSchema('post', 201, '/users', data); + expect(data).to.have.property('id'); + expect(data.id).to.be.greaterThan(0); + cy.wrap(data); + }); +}); + +Cypress.Commands.add('getToken', (defaultUser, defaultAuth) => { + if (typeof defaultAuth === 'object' && defaultAuth) { + if (!defaultUser) { + defaultUser = {}; } - }).then(res => { - cy.wrap(res.token); + defaultUser.auth = defaultAuth; + } + + cy.task('backendApiGet', { + path: '/api/', + }).then((data) => { + // Check the swagger schema: + cy.validateSwaggerSchema('get', 200, '/', data); + + if (!data.setup) { + cy.log('Setup = false'); + // create a new user + cy.createInitialUser(defaultUser).then(() => { + return cy.getToken(defaultUser); + }); + } else { + let auth = { + identity: 'cypress@example.com', + secret: 'changeme', + }; + + if (typeof defaultUser === 'object' && defaultUser && typeof defaultUser.auth === 'object' && defaultUser.auth) { + auth = Object.assign({}, auth, defaultUser.auth); + } + + cy.log('Setup = true'); + // login with existing user + cy.task('backendApiPost', { + path: '/api/tokens', + data: auth, + }).then((res) => { + cy.wrap(res.token); + }); + } + }); +}); + +Cypress.Commands.add('resetUsers', () => { + cy.task('backendApiDelete', { + path: '/api/users' + }).then((data) => { + expect(data).to.be.equal(true); + cy.wrap(data); }); });