certificates.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. import express from "express";
  2. import internalCertificate from "../../internal/certificate.js";
  3. import errs from "../../lib/error.js";
  4. import jwtdecode from "../../lib/express/jwt-decode.js";
  5. import apiValidator from "../../lib/validator/api.js";
  6. import validator from "../../lib/validator/index.js";
  7. import { express as logger } from "../../logger.js";
  8. import { getValidationSchema } from "../../schema/index.js";
  9. const router = express.Router({
  10. caseSensitive: true,
  11. strict: true,
  12. mergeParams: true,
  13. });
  14. /**
  15. * /api/nginx/certificates
  16. */
  17. router
  18. .route("/")
  19. .options((_, res) => {
  20. res.sendStatus(204);
  21. })
  22. .all(jwtdecode())
  23. /**
  24. * GET /api/nginx/certificates
  25. *
  26. * Retrieve all certificates
  27. */
  28. .get(async (req, res, next) => {
  29. try {
  30. const data = await validator(
  31. {
  32. additionalProperties: false,
  33. properties: {
  34. expand: {
  35. $ref: "common#/properties/expand",
  36. },
  37. query: {
  38. $ref: "common#/properties/query",
  39. },
  40. },
  41. },
  42. {
  43. expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null,
  44. query: typeof req.query.query === "string" ? req.query.query : null,
  45. },
  46. );
  47. const rows = await internalCertificate.getAll(res.locals.access, data.expand, data.query);
  48. res.status(200).send(rows);
  49. } catch (err) {
  50. logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
  51. next(err);
  52. }
  53. })
  54. /**
  55. * POST /api/nginx/certificates
  56. *
  57. * Create a new certificate
  58. */
  59. .post(async (req, res, next) => {
  60. try {
  61. const payload = await apiValidator(getValidationSchema("/nginx/certificates", "post"), req.body);
  62. req.setTimeout(900000); // 15 minutes timeout
  63. const result = await internalCertificate.create(res.locals.access, payload);
  64. res.status(201).send(result);
  65. } catch (err) {
  66. logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
  67. next(err);
  68. }
  69. });
  70. /**
  71. * Test HTTP challenge for domains
  72. *
  73. * /api/nginx/certificates/test-http
  74. */
  75. router
  76. .route("/test-http")
  77. .options((_, res) => {
  78. res.sendStatus(204);
  79. })
  80. .all(jwtdecode())
  81. /**
  82. * GET /api/nginx/certificates/test-http
  83. *
  84. * Test HTTP challenge for domains
  85. */
  86. .get(async (req, res, next) => {
  87. if (req.query.domains === undefined) {
  88. next(new errs.ValidationError("Domains are required as query parameters"));
  89. return;
  90. }
  91. try {
  92. const result = await internalCertificate.testHttpsChallenge(
  93. res.locals.access,
  94. JSON.parse(req.query.domains),
  95. );
  96. res.status(200).send(result);
  97. } catch (err) {
  98. logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
  99. next(err);
  100. }
  101. });
  102. /**
  103. * Specific certificate
  104. *
  105. * /api/nginx/certificates/123
  106. */
  107. router
  108. .route("/:certificate_id")
  109. .options((_, res) => {
  110. res.sendStatus(204);
  111. })
  112. .all(jwtdecode())
  113. /**
  114. * GET /api/nginx/certificates/123
  115. *
  116. * Retrieve a specific certificate
  117. */
  118. .get(async (req, res, next) => {
  119. try {
  120. const data = await validator(
  121. {
  122. required: ["certificate_id"],
  123. additionalProperties: false,
  124. properties: {
  125. certificate_id: {
  126. $ref: "common#/properties/id",
  127. },
  128. expand: {
  129. $ref: "common#/properties/expand",
  130. },
  131. },
  132. },
  133. {
  134. certificate_id: req.params.certificate_id,
  135. expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null,
  136. },
  137. );
  138. const row = await internalCertificate.get(res.locals.access, {
  139. id: Number.parseInt(data.certificate_id, 10),
  140. expand: data.expand,
  141. });
  142. res.status(200).send(row);
  143. } catch (err) {
  144. logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
  145. next(err);
  146. }
  147. })
  148. /**
  149. * DELETE /api/nginx/certificates/123
  150. *
  151. * Update and existing certificate
  152. */
  153. .delete(async (req, res, next) => {
  154. try {
  155. const result = await internalCertificate.delete(res.locals.access, {
  156. id: Number.parseInt(req.params.certificate_id, 10),
  157. });
  158. res.status(200).send(result);
  159. } catch (err) {
  160. logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
  161. next(err);
  162. }
  163. });
  164. /**
  165. * Upload Certs
  166. *
  167. * /api/nginx/certificates/123/upload
  168. */
  169. router
  170. .route("/:certificate_id/upload")
  171. .options((_, res) => {
  172. res.sendStatus(204);
  173. })
  174. .all(jwtdecode())
  175. /**
  176. * POST /api/nginx/certificates/123/upload
  177. *
  178. * Upload certificates
  179. */
  180. .post(async (req, res, next) => {
  181. if (!req.files) {
  182. res.status(400).send({ error: "No files were uploaded" });
  183. return;
  184. }
  185. try {
  186. const result = await internalCertificate.upload(res.locals.access, {
  187. id: Number.parseInt(req.params.certificate_id, 10),
  188. files: req.files,
  189. });
  190. res.status(200).send(result);
  191. } catch (err) {
  192. logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
  193. next(err);
  194. }
  195. });
  196. /**
  197. * Renew LE Certs
  198. *
  199. * /api/nginx/certificates/123/renew
  200. */
  201. router
  202. .route("/:certificate_id/renew")
  203. .options((_, res) => {
  204. res.sendStatus(204);
  205. })
  206. .all(jwtdecode())
  207. /**
  208. * POST /api/nginx/certificates/123/renew
  209. *
  210. * Renew certificate
  211. */
  212. .post(async (req, res, next) => {
  213. req.setTimeout(900000); // 15 minutes timeout
  214. try {
  215. const result = await internalCertificate.renew(res.locals.access, {
  216. id: Number.parseInt(req.params.certificate_id, 10),
  217. });
  218. res.status(200).send(result);
  219. } catch (err) {
  220. logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
  221. next(err);
  222. }
  223. });
  224. /**
  225. * Download LE Certs
  226. *
  227. * /api/nginx/certificates/123/download
  228. */
  229. router
  230. .route("/:certificate_id/download")
  231. .options((_req, res) => {
  232. res.sendStatus(204);
  233. })
  234. .all(jwtdecode())
  235. /**
  236. * GET /api/nginx/certificates/123/download
  237. *
  238. * Renew certificate
  239. */
  240. .get(async (req, res, next) => {
  241. try {
  242. const result = await internalCertificate.download(res.locals.access, {
  243. id: Number.parseInt(req.params.certificate_id, 10),
  244. });
  245. res.status(200).download(result.fileName);
  246. } catch (err) {
  247. logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
  248. next(err);
  249. }
  250. });
  251. /**
  252. * Validate Certs before saving
  253. *
  254. * /api/nginx/certificates/validate
  255. */
  256. router
  257. .route("/validate")
  258. .options((_, res) => {
  259. res.sendStatus(204);
  260. })
  261. .all(jwtdecode())
  262. /**
  263. * POST /api/nginx/certificates/validate
  264. *
  265. * Validate certificates
  266. */
  267. .post(async (req, res, next) => {
  268. if (!req.files) {
  269. res.status(400).send({ error: "No files were uploaded" });
  270. return;
  271. }
  272. try {
  273. const result = await internalCertificate.validate({
  274. files: req.files,
  275. });
  276. res.status(200).send(result);
  277. } catch (err) {
  278. logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
  279. next(err);
  280. }
  281. });
  282. export default router;