@fdm-monster/server 2.0.11 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/.yarn/install-state.gz +0 -0
  2. package/.yarn/releases/{yarn-4.13.0.cjs → yarn-4.14.1.cjs} +288 -288
  3. package/.yarnrc.yml +5 -1
  4. package/README.md +2 -1
  5. package/RELEASE_NOTES.MD +153 -2
  6. package/dist/_virtual/{_@oxc-project_runtime@0.127.0 → _@oxc-project_runtime@0.129.0}/helpers/decorate.js +1 -1
  7. package/dist/_virtual/{_@oxc-project_runtime@0.127.0 → _@oxc-project_runtime@0.129.0}/helpers/decorateMetadata.js +1 -1
  8. package/dist/_virtual/_virtual_controllers.js +2 -0
  9. package/dist/consoles/typeorm-create.js.map +1 -1
  10. package/dist/consoles/typeorm-generate.js.map +1 -1
  11. package/dist/consoles/typeorm-migrate.js.map +1 -1
  12. package/dist/constants/authorization.constants.js.map +1 -1
  13. package/dist/container.js +2 -0
  14. package/dist/container.js.map +1 -1
  15. package/dist/container.tokens.js +1 -0
  16. package/dist/container.tokens.js.map +1 -1
  17. package/dist/controllers/api-key.controller.js +72 -0
  18. package/dist/controllers/api-key.controller.js.map +1 -0
  19. package/dist/controllers/auth.controller.js +6 -4
  20. package/dist/controllers/auth.controller.js.map +1 -1
  21. package/dist/controllers/batch-call.controller.js +2 -2
  22. package/dist/controllers/batch-call.controller.js.map +1 -1
  23. package/dist/controllers/camera-stream.controller.js +2 -2
  24. package/dist/controllers/camera-stream.controller.js.map +1 -1
  25. package/dist/controllers/file-storage.controller.js +2 -2
  26. package/dist/controllers/file-storage.controller.js.map +1 -1
  27. package/dist/controllers/first-time-setup.controller.js +2 -2
  28. package/dist/controllers/first-time-setup.controller.js.map +1 -1
  29. package/dist/controllers/floor.controller.js +2 -2
  30. package/dist/controllers/floor.controller.js.map +1 -1
  31. package/dist/controllers/metrics.controller.js +2 -2
  32. package/dist/controllers/metrics.controller.js.map +1 -1
  33. package/dist/controllers/print-job.controller.js +2 -2
  34. package/dist/controllers/print-job.controller.js.map +1 -1
  35. package/dist/controllers/print-queue.controller.js +2 -2
  36. package/dist/controllers/print-queue.controller.js.map +1 -1
  37. package/dist/controllers/printer-files.controller.js +2 -2
  38. package/dist/controllers/printer-files.controller.js.map +1 -1
  39. package/dist/controllers/printer-maintenance-log.controller.js +2 -2
  40. package/dist/controllers/printer-maintenance-log.controller.js.map +1 -1
  41. package/dist/controllers/printer-settings.controller.js +2 -2
  42. package/dist/controllers/printer-settings.controller.js.map +1 -1
  43. package/dist/controllers/printer-tag.controller.js +2 -2
  44. package/dist/controllers/printer-tag.controller.js.map +1 -1
  45. package/dist/controllers/printer.controller.js +2 -2
  46. package/dist/controllers/printer.controller.js.map +1 -1
  47. package/dist/controllers/server-private.controller.js +2 -2
  48. package/dist/controllers/server-private.controller.js.map +1 -1
  49. package/dist/controllers/server-public.controller.js +2 -2
  50. package/dist/controllers/server-public.controller.js.map +1 -1
  51. package/dist/controllers/settings.controller.js +2 -2
  52. package/dist/controllers/settings.controller.js.map +1 -1
  53. package/dist/controllers/slicer-compat.controller.js +2 -2
  54. package/dist/controllers/slicer-compat.controller.js.map +1 -1
  55. package/dist/controllers/user.controller.js +2 -2
  56. package/dist/controllers/user.controller.js.map +1 -1
  57. package/dist/controllers/validation/api-key-controller.validation.js +11 -0
  58. package/dist/controllers/validation/api-key-controller.validation.js.map +1 -0
  59. package/dist/data-source.js +6 -2
  60. package/dist/data-source.js.map +1 -1
  61. package/dist/entities/api-key.entity.js +60 -0
  62. package/dist/entities/api-key.entity.js.map +1 -0
  63. package/dist/entities/camera-stream.entity.js +2 -2
  64. package/dist/entities/floor-position.entity.js +2 -2
  65. package/dist/entities/floor.entity.js +2 -2
  66. package/dist/entities/index.js +2 -1
  67. package/dist/entities/print-job.entity.js +2 -2
  68. package/dist/entities/printer-maintenance-log.entity.js +2 -2
  69. package/dist/entities/printer-tag.entity.js +2 -2
  70. package/dist/entities/printer.entity.js +2 -2
  71. package/dist/entities/refresh-token.entity.js +2 -2
  72. package/dist/entities/role.entity.js +2 -2
  73. package/dist/entities/settings.entity.js +2 -2
  74. package/dist/entities/tag.entity.js +2 -2
  75. package/dist/entities/user-role.entity.js +2 -2
  76. package/dist/entities/user.entity.js +2 -2
  77. package/dist/exceptions/failed-dependency.exception.js.map +1 -1
  78. package/dist/exceptions/job.exceptions.js.map +1 -1
  79. package/dist/exceptions/runtime.exceptions.js.map +1 -1
  80. package/dist/handlers/event-emitter.js.map +1 -1
  81. package/dist/handlers/logger-factory.js.map +1 -1
  82. package/dist/handlers/logger.js.map +1 -1
  83. package/dist/handlers/logging/file-logging.transport.js +1 -2
  84. package/dist/handlers/logging/file-logging.transport.js.map +1 -1
  85. package/dist/handlers/logging/loki-logging.transport.js.map +1 -1
  86. package/dist/handlers/logging/static.logger.js.map +1 -1
  87. package/dist/handlers/validators.js.map +1 -1
  88. package/dist/index.js.map +1 -1
  89. package/dist/middleware/api-key.strategy.js +45 -0
  90. package/dist/middleware/api-key.strategy.js.map +1 -0
  91. package/dist/middleware/authenticate.js.map +1 -1
  92. package/dist/middleware/database.js.map +1 -1
  93. package/dist/middleware/demo.middleware.js.map +1 -1
  94. package/dist/middleware/exception.filter.js.map +1 -1
  95. package/dist/middleware/global.middleware.js.map +1 -1
  96. package/dist/middleware/param-converter.middleware.js.map +1 -1
  97. package/dist/middleware/passport.js +3 -0
  98. package/dist/middleware/passport.js.map +1 -1
  99. package/dist/middleware/printer-resolver.js.map +1 -1
  100. package/dist/middleware/printer.js.map +1 -1
  101. package/dist/middleware/slicer-api-key.middleware.js.map +1 -1
  102. package/dist/middleware/socketio.middleware.js.map +1 -1
  103. package/dist/migrations/1706829146617-InitSqlite.js.map +1 -1
  104. package/dist/migrations/1707494762198-PrinterGroup.js.map +1 -1
  105. package/dist/migrations/1708465930665-ChangePrintCompletionDeletePrinterCascade.js.map +1 -1
  106. package/dist/migrations/1713300747465-ChangeRoleNameUnique.js.map +1 -1
  107. package/dist/migrations/1713897879622-AddPrinterType.js.map +1 -1
  108. package/dist/migrations/1720338804844-RemovePrinterFile.js.map +1 -1
  109. package/dist/migrations/1745141688926-AddPrinterUsernamePassword.js.map +1 -1
  110. package/dist/migrations/1766576698569-DropPermissions.js.map +1 -1
  111. package/dist/migrations/1767278216516-ChangeCameraPrinterOnDeleteSetNull.js.map +1 -1
  112. package/dist/migrations/1767279607392-DropCustomGcode.js.map +1 -1
  113. package/dist/migrations/1767291804417-DropPrintCompletions.js.map +1 -1
  114. package/dist/migrations/1767352862576-DropSettingsFileClean.js.map +1 -1
  115. package/dist/migrations/1767355639023-ChangeFloorLevelToOrder.js.map +1 -1
  116. package/dist/migrations/1767370191762-ChangeFloorNonUniqueOrder.js.map +1 -1
  117. package/dist/migrations/1767432108916-RenameGroupToTag.js.map +1 -1
  118. package/dist/migrations/1767451444137-AddPrintJob.js.map +1 -1
  119. package/dist/migrations/1767909428129-AddPrinterMaintenanceLog.js.map +1 -1
  120. package/dist/migrations/1778446203015-AddApiKey.js +49 -0
  121. package/dist/migrations/1778446203015-AddApiKey.js.map +1 -0
  122. package/dist/plugins/controllers-plugin.js.map +1 -1
  123. package/dist/server.constants.js +2 -1
  124. package/dist/server.constants.js.map +1 -1
  125. package/dist/server.core.js +6 -2
  126. package/dist/server.core.js.map +1 -1
  127. package/dist/server.env.js.map +1 -1
  128. package/dist/server.host.js.map +1 -1
  129. package/dist/services/authentication/auth.service.js.map +1 -1
  130. package/dist/services/authentication/jwt.service.js.map +1 -1
  131. package/dist/services/bambu/bambu-ftp.adapter.js.map +1 -1
  132. package/dist/services/bambu/bambu-mqtt.adapter.js.map +1 -1
  133. package/dist/services/bambu/bambu.client.js.map +1 -1
  134. package/dist/services/bambu.api.js.map +1 -1
  135. package/dist/services/core/batch-call.service.js.map +1 -1
  136. package/dist/services/core/client-bundle.service.js.map +1 -1
  137. package/dist/services/core/config.service.js +4 -0
  138. package/dist/services/core/config.service.js.map +1 -1
  139. package/dist/services/core/cradle.service.js.map +1 -1
  140. package/dist/services/core/github.service.js.map +1 -1
  141. package/dist/services/core/http-client.factory.js.map +1 -1
  142. package/dist/services/core/logs-manager.service.js.map +1 -1
  143. package/dist/services/core/monsterpi.service.js.map +1 -1
  144. package/dist/services/core/multer.service.js.map +1 -1
  145. package/dist/services/core/server-release.service.js.map +1 -1
  146. package/dist/services/core/yaml.service.js.map +1 -1
  147. package/dist/services/file-analysis.service.js.map +1 -1
  148. package/dist/services/file-storage.service.js.map +1 -1
  149. package/dist/services/interfaces/api-key.dto.js +19 -0
  150. package/dist/services/interfaces/api-key.dto.js.map +1 -0
  151. package/dist/services/interfaces/api-key.service.interface.js +1 -0
  152. package/dist/services/interfaces/user.dto.js +2 -0
  153. package/dist/services/interfaces/user.dto.js.map +1 -1
  154. package/dist/services/moonraker/moonraker-websocket.adapter.js.map +1 -1
  155. package/dist/services/moonraker/moonraker.client.js.map +1 -1
  156. package/dist/services/moonraker.api.js.map +1 -1
  157. package/dist/services/octoprint/octoprint-api.routes.js.map +1 -1
  158. package/dist/services/octoprint/octoprint-websocket.adapter.js.map +1 -1
  159. package/dist/services/octoprint/octoprint.client.js.map +1 -1
  160. package/dist/services/octoprint/utils/api.utils.js.map +1 -1
  161. package/dist/services/octoprint/utils/file.utils.js.map +1 -1
  162. package/dist/services/octoprint/utils/octoprint-http-client.builder.js.map +1 -1
  163. package/dist/services/octoprint.api.js.map +1 -1
  164. package/dist/services/orm/api-key.service.js +90 -0
  165. package/dist/services/orm/api-key.service.js.map +1 -0
  166. package/dist/services/orm/base.service.js.map +1 -1
  167. package/dist/services/orm/camera-stream.service.js.map +1 -1
  168. package/dist/services/orm/floor-position.service.js.map +1 -1
  169. package/dist/services/orm/floor.service.js.map +1 -1
  170. package/dist/services/orm/permission.service.js.map +1 -1
  171. package/dist/services/orm/print-job.service.js.map +1 -1
  172. package/dist/services/orm/printer-maintenance-log.service.js.map +1 -1
  173. package/dist/services/orm/printer-tag.service.js.map +1 -1
  174. package/dist/services/orm/printer.service.js.map +1 -1
  175. package/dist/services/orm/refresh-token.service.js.map +1 -1
  176. package/dist/services/orm/role.service.js.map +1 -1
  177. package/dist/services/orm/settings.service.js.map +1 -1
  178. package/dist/services/orm/user-role.service.js.map +1 -1
  179. package/dist/services/orm/user.service.js.map +1 -1
  180. package/dist/services/print-file-downloader.service.js.map +1 -1
  181. package/dist/services/print-queue.service.js.map +1 -1
  182. package/dist/services/printer-api.factory.js.map +1 -1
  183. package/dist/services/printer-api.interface.js.map +1 -1
  184. package/dist/services/prusa-link/prusa-link-http-polling.adapter.js.map +1 -1
  185. package/dist/services/prusa-link/prusa-link.api.js.map +1 -1
  186. package/dist/services/prusa-link/utils/digest-auth.util.js +19 -12
  187. package/dist/services/prusa-link/utils/digest-auth.util.js.map +1 -1
  188. package/dist/services/prusa-link/utils/prusa-link-http-client.builder.js +45 -11
  189. package/dist/services/prusa-link/utils/prusa-link-http-client.builder.js.map +1 -1
  190. package/dist/services/socket.factory.js.map +1 -1
  191. package/dist/services/task-manager.service.js.map +1 -1
  192. package/dist/services/typeorm/typeorm.service.js.map +1 -1
  193. package/dist/services/validators/printer-service.validation.js.map +1 -1
  194. package/dist/shared/default-http-client.builder.js.map +1 -1
  195. package/dist/shared/load-controllers.js.map +1 -1
  196. package/dist/shared/runtime-settings.migration.js.map +1 -1
  197. package/dist/shared/websocket-rpc-extended.adapter.js.map +1 -1
  198. package/dist/shared/websocket.adapter.js.map +1 -1
  199. package/dist/state/file-upload-tracker.cache.js.map +1 -1
  200. package/dist/state/floor.store.js.map +1 -1
  201. package/dist/state/printer-events.cache.js.map +1 -1
  202. package/dist/state/printer-socket.store.js.map +1 -1
  203. package/dist/state/printer-thumbnail.cache.js.map +1 -1
  204. package/dist/state/printer.cache.js.map +1 -1
  205. package/dist/state/settings.store.js.map +1 -1
  206. package/dist/state/socket-io.gateway.js.map +1 -1
  207. package/dist/state/test-printer-socket.store.js.map +1 -1
  208. package/dist/tasks/boot.task.js.map +1 -1
  209. package/dist/tasks/client-bundle.task.js.map +1 -1
  210. package/dist/tasks/print-job-analysis.task.js.map +1 -1
  211. package/dist/tasks/printer-websocket-restore.task.js.map +1 -1
  212. package/dist/tasks/printer-websocket.task.js.map +1 -1
  213. package/dist/tasks/socketio.task.js.map +1 -1
  214. package/dist/tasks/software-update.task.js.map +1 -1
  215. package/dist/tasks.js.map +1 -1
  216. package/dist/utils/array.util.js.map +1 -1
  217. package/dist/utils/bgcode/bgcode-thumbnail.parser.js.map +1 -1
  218. package/dist/utils/bgcode/bgcode.utils.js.map +1 -1
  219. package/dist/utils/bgcode/heatshrink-decoder.js.map +1 -1
  220. package/dist/utils/bgcode/png-encoder.js.map +1 -1
  221. package/dist/utils/bgcode/qoi-decoder.js.map +1 -1
  222. package/dist/utils/cache/key-diff.cache.js.map +1 -1
  223. package/dist/utils/correlation-token.util.js.map +1 -1
  224. package/dist/utils/crypto.utils.js.map +1 -1
  225. package/dist/utils/env.utils.js.map +1 -1
  226. package/dist/utils/error.utils.js.map +1 -1
  227. package/dist/utils/fs.utils.js.map +1 -1
  228. package/dist/utils/gcode.utils.js.map +1 -1
  229. package/dist/utils/image-dimensions.js.map +1 -1
  230. package/dist/utils/job-stats.util.js.map +1 -1
  231. package/dist/utils/normalize-url.js.map +1 -1
  232. package/dist/utils/parsers/3mf.parser.js.map +1 -1
  233. package/dist/utils/parsers/bgcode.parser.js.map +1 -1
  234. package/dist/utils/parsers/gcode.parser.js.map +1 -1
  235. package/dist/utils/pretty-print.utils.js.map +1 -1
  236. package/dist/utils/semver.utils.js.map +1 -1
  237. package/dist/utils/swagger/decorators.js.map +1 -1
  238. package/dist/utils/swagger/generator.js.map +1 -1
  239. package/dist/utils/swagger/swagger.js.map +1 -1
  240. package/dist/utils/thumbnail.util.js.map +1 -1
  241. package/dist/utils/time.utils.js.map +1 -1
  242. package/dist/utils/url.utils.js.map +1 -1
  243. package/package.json +17 -14
  244. package/packages/consoles/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"user.controller.js","names":[],"sources":["../../src/controllers/user.controller.ts"],"sourcesContent":["import type { Request, Response } from \"express\";\nimport { AppConstants } from \"@/server.constants\";\nimport { authenticate, authorizeRoles } from \"@/middleware/authenticate\";\nimport { ROLES } from \"@/constants/authorization.constants\";\nimport { validateInput, validateMiddleware } from \"@/handlers/validators\";\nimport { BadRequestException, ForbiddenError } from \"@/exceptions/runtime.exceptions\";\nimport type { IConfigService } from \"@/services/core/config.service\";\nimport type { IUserService } from \"@/services/interfaces/user-service.interface\";\nimport { demoUserNotAllowed } from \"@/middleware/demo.middleware\";\nimport type { IRoleService } from \"@/services/interfaces/role-service.interface\";\nimport type { IAuthService } from \"@/services/interfaces/auth.service.interface\";\nimport { LoggerService } from \"@/handlers/logger\";\nimport type { ILoggerFactory } from \"@/handlers/logger-factory\";\nimport { errorSummary } from \"@/utils/error.utils\";\nimport { SettingsStore } from \"@/state/settings.store\";\nimport { before, DELETE, GET, POST, route } from \"awilix-express\";\nimport {\n changePasswordSchema,\n isRootUserSchema,\n isVerifiedSchema,\n registerUserWithRolesSchema,\n setUserRolesSchema,\n usernameSchema,\n} from \"@/controllers/validation/user-controller.validation\";\nimport { ParamId } from \"@/middleware/param-converter.middleware\";\n\n@route(AppConstants.apiRoute + \"/user\")\n@before([authenticate()])\nexport class UserController {\n logger: LoggerService;\n\n constructor(\n loggerFactory: ILoggerFactory,\n private readonly userService: IUserService,\n private readonly configService: IConfigService,\n private readonly roleService: IRoleService,\n private readonly authService: IAuthService,\n private readonly settingsStore: SettingsStore,\n ) {\n this.logger = loggerFactory(UserController.name);\n }\n\n @GET()\n @route(\"/\")\n @before([authorizeRoles([ROLES.ADMIN])])\n async list(req: Request, res: Response) {\n const users = await this.userService.listUsers();\n res.send(users.map((u) => this.userService.toDto(u)));\n }\n\n @POST()\n @route(\"/\")\n @before([authorizeRoles([ROLES.ADMIN])])\n async create(req: Request, res: Response) {\n const { username, password, roles } = await validateMiddleware(req, registerUserWithRolesSchema);\n if (\n username.toLowerCase().includes(\"admin\") ||\n username.toLowerCase().includes(\"root\") ||\n username.toLowerCase() === \"demo\"\n ) {\n throw new BadRequestException(\"Username is not allowed\");\n }\n\n await this.userService.register({\n username,\n password,\n roles,\n needsPasswordChange: false,\n isDemoUser: false,\n isRootUser: false,\n // An admin needs to verify the user first\n isVerified: true,\n });\n res.send();\n }\n\n @GET()\n @route(\"/roles\")\n async listRoles(req: Request, res: Response) {\n const roleDtos = this.roleService.roles.map((r) => this.roleService.toDto(r));\n res.send(roleDtos);\n }\n\n @GET()\n @route(\"/profile\")\n async profile(req: Request, res: Response) {\n if (!req.user?.id) {\n res.send({});\n return;\n }\n\n const user = await this.userService.getUser(req.user?.id);\n res.send(this.userService.toDto(user));\n }\n\n @GET()\n @route(\"/:id\")\n @before([authorizeRoles([ROLES.ADMIN]), ParamId(\"id\")])\n async get(req: Request, res: Response) {\n const user = await this.userService.getUser(req.local.id);\n res.send(this.userService.toDto(user));\n }\n\n @DELETE()\n @route(\"/:id\")\n @before([authorizeRoles([ROLES.ADMIN]), demoUserNotAllowed, ParamId(\"id\")])\n async delete(req: Request, res: Response) {\n const deletedUserId = req.local.id;\n const ownUserId = req.user?.id;\n if (ownUserId == deletedUserId) {\n throw new ForbiddenError(\"Not allowed to delete own account\");\n }\n\n const isRootUser = await this.userService.isUserRootUser(deletedUserId);\n if (isRootUser) {\n throw new ForbiddenError(\"Not allowed to delete root user\");\n }\n\n if (this.configService.isDemoMode()) {\n const demoUserId = await this.userService.getDemoUserId();\n if (deletedUserId === demoUserId) {\n this.throwIfDemoMode();\n }\n }\n\n await this.userService.deleteUser(deletedUserId);\n\n try {\n await this.authService.logoutUserId(deletedUserId);\n } catch (e) {\n this.logger.error(errorSummary(e));\n }\n\n res.send();\n }\n\n @POST()\n @route(\"/:id/change-username\")\n @before([demoUserNotAllowed, ParamId(\"id\")])\n async changeUsername(req: Request, res: Response) {\n const changedUserId = req.local.id;\n\n if (req.user?.id != changedUserId && (await this.settingsStore.getLoginRequired())) {\n throw new ForbiddenError(\"Not allowed to change username of other users\");\n }\n\n const { username } = await validateInput(req.body, usernameSchema);\n await this.userService.updateUsernameById(changedUserId, username);\n res.send();\n }\n\n @POST()\n @route(\"/:id/change-password\")\n @before([demoUserNotAllowed, ParamId(\"id\")])\n async changePassword(req: Request, res: Response) {\n const changedUserId = req.local.id;\n\n if (req.user?.id != changedUserId && (await this.settingsStore.getLoginRequired())) {\n throw new ForbiddenError(\"Not allowed to change password of other users\");\n }\n\n const { oldPassword, newPassword } = await validateInput(req.body, changePasswordSchema);\n await this.userService.updatePasswordById(changedUserId, oldPassword, newPassword);\n res.send();\n }\n\n @POST()\n @route(\"/:id/set-user-roles\")\n @before([authorizeRoles([ROLES.ADMIN]), demoUserNotAllowed, ParamId(\"id\")])\n async setUserRoles(req: Request, res: Response) {\n const changedUserId = req.local.id;\n\n const ownUserId = req.user?.id;\n if (!ownUserId) {\n throw new ForbiddenError(\"Need to be logged in, in order to set user roles\");\n }\n\n const ownUser = await this.userService.getUser(ownUserId);\n const mappedUser = this.userService.toDto(ownUser);\n\n const ownUserRoles = mappedUser.roles;\n\n if (ownUserId == changedUserId && !ownUserRoles.includes(ROLES.ADMIN) && !mappedUser.isRootUser) {\n throw new ForbiddenError(\"Only an ADMIN or OWNER user is allowed to change its own roles\");\n }\n\n const { roles } = await validateInput(req.body, setUserRolesSchema);\n\n if (ownUserId == changedUserId && !roles.includes(ROLES.ADMIN)) {\n if (mappedUser.isRootUser) {\n throw new BadRequestException(\"It does not make sense to remove ADMIN role from an OWNER user.\");\n } else {\n throw new BadRequestException(\"An ADMIN user cannot remove its ADMIN role.\");\n }\n }\n\n await this.userService.setUserRoles(changedUserId, roles);\n res.send();\n }\n\n @POST()\n @route(\"/:id/set-verified\")\n @before([authorizeRoles([ROLES.ADMIN]), demoUserNotAllowed, ParamId(\"id\")])\n async setVerified(req: Request, res: Response) {\n const changedUserId = req.local.id;\n\n const ownUserId = req.user?.id;\n if (ownUserId == changedUserId) {\n throw new ForbiddenError(\"Not allowed to change own verified status\");\n }\n\n const isRootUser = await this.userService.isUserRootUser(changedUserId);\n if (isRootUser) {\n throw new ForbiddenError(\"Not allowed to change root user to unverified\");\n }\n\n const { isVerified } = await validateInput(req.body, isVerifiedSchema);\n await this.userService.setVerifiedById(changedUserId, isVerified);\n\n res.send();\n }\n\n @POST()\n @route(\"/:id/set-root-user\")\n @before([demoUserNotAllowed, ParamId(\"id\")])\n async setRootUser(req: Request, res: Response) {\n const changedUserId = req.local.id;\n\n const userId = req.user?.id;\n if (userId) {\n const isRootUser = await this.userService.isUserRootUser(userId);\n if (!isRootUser) {\n throw new ForbiddenError(\"Not allowed to change owner without being owner yourself\");\n }\n }\n const { isRootUser } = await validateInput(req.body, isRootUserSchema);\n await this.userService.setIsRootUserById(changedUserId, isRootUser);\n res.send();\n }\n\n throwIfDemoMode() {\n const isDemoMode = this.configService.isDemoMode();\n if (isDemoMode) {\n throw new ForbiddenError(\"Not allowed in demo mode\");\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"user.controller.js","names":[],"sources":["../../src/controllers/user.controller.ts"],"sourcesContent":["import type { Request, Response } from \"express\";\nimport { AppConstants } from \"@/server.constants\";\nimport { authenticate, authorizeRoles } from \"@/middleware/authenticate\";\nimport { ROLES } from \"@/constants/authorization.constants\";\nimport { validateInput, validateMiddleware } from \"@/handlers/validators\";\nimport { BadRequestException, ForbiddenError } from \"@/exceptions/runtime.exceptions\";\nimport type { IConfigService } from \"@/services/core/config.service\";\nimport type { IUserService } from \"@/services/interfaces/user-service.interface\";\nimport { demoUserNotAllowed } from \"@/middleware/demo.middleware\";\nimport type { IRoleService } from \"@/services/interfaces/role-service.interface\";\nimport type { IAuthService } from \"@/services/interfaces/auth.service.interface\";\nimport { LoggerService } from \"@/handlers/logger\";\nimport type { ILoggerFactory } from \"@/handlers/logger-factory\";\nimport { errorSummary } from \"@/utils/error.utils\";\nimport { SettingsStore } from \"@/state/settings.store\";\nimport { before, DELETE, GET, POST, route } from \"awilix-express\";\nimport {\n changePasswordSchema,\n isRootUserSchema,\n isVerifiedSchema,\n registerUserWithRolesSchema,\n setUserRolesSchema,\n usernameSchema,\n} from \"@/controllers/validation/user-controller.validation\";\nimport { ParamId } from \"@/middleware/param-converter.middleware\";\n\n@route(AppConstants.apiRoute + \"/user\")\n@before([authenticate()])\nexport class UserController {\n logger: LoggerService;\n\n constructor(\n loggerFactory: ILoggerFactory,\n private readonly userService: IUserService,\n private readonly configService: IConfigService,\n private readonly roleService: IRoleService,\n private readonly authService: IAuthService,\n private readonly settingsStore: SettingsStore,\n ) {\n this.logger = loggerFactory(UserController.name);\n }\n\n @GET()\n @route(\"/\")\n @before([authorizeRoles([ROLES.ADMIN])])\n async list(req: Request, res: Response) {\n const users = await this.userService.listUsers();\n res.send(users.map((u) => this.userService.toDto(u)));\n }\n\n @POST()\n @route(\"/\")\n @before([authorizeRoles([ROLES.ADMIN])])\n async create(req: Request, res: Response) {\n const { username, password, roles } = await validateMiddleware(req, registerUserWithRolesSchema);\n if (\n username.toLowerCase().includes(\"admin\") ||\n username.toLowerCase().includes(\"root\") ||\n username.toLowerCase() === \"demo\"\n ) {\n throw new BadRequestException(\"Username is not allowed\");\n }\n\n await this.userService.register({\n username,\n password,\n roles,\n needsPasswordChange: false,\n isDemoUser: false,\n isRootUser: false,\n // An admin needs to verify the user first\n isVerified: true,\n });\n res.send();\n }\n\n @GET()\n @route(\"/roles\")\n async listRoles(req: Request, res: Response) {\n const roleDtos = this.roleService.roles.map((r) => this.roleService.toDto(r));\n res.send(roleDtos);\n }\n\n @GET()\n @route(\"/profile\")\n async profile(req: Request, res: Response) {\n if (!req.user?.id) {\n res.send({});\n return;\n }\n\n const user = await this.userService.getUser(req.user?.id);\n res.send(this.userService.toDto(user));\n }\n\n @GET()\n @route(\"/:id\")\n @before([authorizeRoles([ROLES.ADMIN]), ParamId(\"id\")])\n async get(req: Request, res: Response) {\n const user = await this.userService.getUser(req.local.id);\n res.send(this.userService.toDto(user));\n }\n\n @DELETE()\n @route(\"/:id\")\n @before([authorizeRoles([ROLES.ADMIN]), demoUserNotAllowed, ParamId(\"id\")])\n async delete(req: Request, res: Response) {\n const deletedUserId = req.local.id;\n const ownUserId = req.user?.id;\n if (ownUserId == deletedUserId) {\n throw new ForbiddenError(\"Not allowed to delete own account\");\n }\n\n const isRootUser = await this.userService.isUserRootUser(deletedUserId);\n if (isRootUser) {\n throw new ForbiddenError(\"Not allowed to delete root user\");\n }\n\n if (this.configService.isDemoMode()) {\n const demoUserId = await this.userService.getDemoUserId();\n if (deletedUserId === demoUserId) {\n this.throwIfDemoMode();\n }\n }\n\n await this.userService.deleteUser(deletedUserId);\n\n try {\n await this.authService.logoutUserId(deletedUserId);\n } catch (e) {\n this.logger.error(errorSummary(e));\n }\n\n res.send();\n }\n\n @POST()\n @route(\"/:id/change-username\")\n @before([demoUserNotAllowed, ParamId(\"id\")])\n async changeUsername(req: Request, res: Response) {\n const changedUserId = req.local.id;\n\n if (req.user?.id != changedUserId && (await this.settingsStore.getLoginRequired())) {\n throw new ForbiddenError(\"Not allowed to change username of other users\");\n }\n\n const { username } = await validateInput(req.body, usernameSchema);\n await this.userService.updateUsernameById(changedUserId, username);\n res.send();\n }\n\n @POST()\n @route(\"/:id/change-password\")\n @before([demoUserNotAllowed, ParamId(\"id\")])\n async changePassword(req: Request, res: Response) {\n const changedUserId = req.local.id;\n\n if (req.user?.id != changedUserId && (await this.settingsStore.getLoginRequired())) {\n throw new ForbiddenError(\"Not allowed to change password of other users\");\n }\n\n const { oldPassword, newPassword } = await validateInput(req.body, changePasswordSchema);\n await this.userService.updatePasswordById(changedUserId, oldPassword, newPassword);\n res.send();\n }\n\n @POST()\n @route(\"/:id/set-user-roles\")\n @before([authorizeRoles([ROLES.ADMIN]), demoUserNotAllowed, ParamId(\"id\")])\n async setUserRoles(req: Request, res: Response) {\n const changedUserId = req.local.id;\n\n const ownUserId = req.user?.id;\n if (!ownUserId) {\n throw new ForbiddenError(\"Need to be logged in, in order to set user roles\");\n }\n\n const ownUser = await this.userService.getUser(ownUserId);\n const mappedUser = this.userService.toDto(ownUser);\n\n const ownUserRoles = mappedUser.roles;\n\n if (ownUserId == changedUserId && !ownUserRoles.includes(ROLES.ADMIN) && !mappedUser.isRootUser) {\n throw new ForbiddenError(\"Only an ADMIN or OWNER user is allowed to change its own roles\");\n }\n\n const { roles } = await validateInput(req.body, setUserRolesSchema);\n\n if (ownUserId == changedUserId && !roles.includes(ROLES.ADMIN)) {\n if (mappedUser.isRootUser) {\n throw new BadRequestException(\"It does not make sense to remove ADMIN role from an OWNER user.\");\n } else {\n throw new BadRequestException(\"An ADMIN user cannot remove its ADMIN role.\");\n }\n }\n\n await this.userService.setUserRoles(changedUserId, roles);\n res.send();\n }\n\n @POST()\n @route(\"/:id/set-verified\")\n @before([authorizeRoles([ROLES.ADMIN]), demoUserNotAllowed, ParamId(\"id\")])\n async setVerified(req: Request, res: Response) {\n const changedUserId = req.local.id;\n\n const ownUserId = req.user?.id;\n if (ownUserId == changedUserId) {\n throw new ForbiddenError(\"Not allowed to change own verified status\");\n }\n\n const isRootUser = await this.userService.isUserRootUser(changedUserId);\n if (isRootUser) {\n throw new ForbiddenError(\"Not allowed to change root user to unverified\");\n }\n\n const { isVerified } = await validateInput(req.body, isVerifiedSchema);\n await this.userService.setVerifiedById(changedUserId, isVerified);\n\n res.send();\n }\n\n @POST()\n @route(\"/:id/set-root-user\")\n @before([demoUserNotAllowed, ParamId(\"id\")])\n async setRootUser(req: Request, res: Response) {\n const changedUserId = req.local.id;\n\n const userId = req.user?.id;\n if (userId) {\n const isRootUser = await this.userService.isUserRootUser(userId);\n if (!isRootUser) {\n throw new ForbiddenError(\"Not allowed to change owner without being owner yourself\");\n }\n }\n const { isRootUser } = await validateInput(req.body, isRootUserSchema);\n await this.userService.setIsRootUserById(changedUserId, isRootUser);\n res.send();\n }\n\n throwIfDemoMode() {\n const isDemoMode = this.configService.isDemoMode();\n if (isDemoMode) {\n throw new ForbiddenError(\"Not allowed in demo mode\");\n }\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { z } from "zod";
2
+ //#region src/controllers/validation/api-key-controller.validation.ts
3
+ const createApiKeySchema = z.object({
4
+ label: z.string().trim().min(1, "label is required").max(80, "label too long (max 80 chars)"),
5
+ roleIds: z.array(z.number().int().positive()).min(1, "at least one role is required")
6
+ });
7
+ const apiKeyIdParamSchema = z.object({ id: z.coerce.number().int().positive() });
8
+ //#endregion
9
+ export { apiKeyIdParamSchema, createApiKeySchema };
10
+
11
+ //# sourceMappingURL=api-key-controller.validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-key-controller.validation.js","names":[],"sources":["../../../src/controllers/validation/api-key-controller.validation.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const createApiKeySchema = z.object({\n label: z.string().trim().min(1, \"label is required\").max(80, \"label too long (max 80 chars)\"),\n roleIds: z.array(z.number().int().positive()).min(1, \"at least one role is required\"),\n});\n\nexport const apiKeyIdParamSchema = z.object({\n id: z.coerce.number().int().positive(),\n});\n"],"mappings":";;AAEA,MAAa,qBAAqB,EAAE,OAAO;CACzC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,GAAG,oBAAoB,CAAC,IAAI,IAAI,gCAAgC;CAC7F,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,IAAI,GAAG,gCAAgC;CACtF,CAAC;AAEF,MAAa,sBAAsB,EAAE,OAAO,EAC1C,IAAI,EAAE,OAAO,QAAQ,CAAC,KAAK,CAAC,UAAU,EACvC,CAAC"}
@@ -12,6 +12,7 @@ import { CameraStream } from "./entities/camera-stream.entity.js";
12
12
  import { FloorPosition } from "./entities/floor-position.entity.js";
13
13
  import { Floor } from "./entities/floor.entity.js";
14
14
  import { Settings } from "./entities/settings.entity.js";
15
+ import { ApiKey } from "./entities/api-key.entity.js";
15
16
  import "./entities/index.js";
16
17
  import { InitSqlite1706829146617 } from "./migrations/1706829146617-InitSqlite.js";
17
18
  import { PrinterGroup1707494762198 } from "./migrations/1707494762198-PrinterGroup.js";
@@ -29,6 +30,7 @@ import { ChangeFloorNonUniqueOrder1767370191762 } from "./migrations/17673701917
29
30
  import { RenameGroupToTag1767432108916 } from "./migrations/1767432108916-RenameGroupToTag.js";
30
31
  import { AddPrintJob1767451444137 } from "./migrations/1767451444137-AddPrintJob.js";
31
32
  import { AddPrinterMaintenanceLog1767909428129 } from "./migrations/1767909428129-AddPrinterMaintenanceLog.js";
33
+ import { AddApiKey1778446203015 } from "./migrations/1778446203015-AddApiKey.js";
32
34
  import { DataSource } from "typeorm";
33
35
  import "reflect-metadata";
34
36
  const AppDataSource = new DataSource({
@@ -49,7 +51,8 @@ const AppDataSource = new DataSource({
49
51
  Tag,
50
52
  PrinterTag,
51
53
  PrintJob,
52
- PrinterMaintenanceLog
54
+ PrinterMaintenanceLog,
55
+ ApiKey
53
56
  ],
54
57
  migrations: [
55
58
  InitSqlite1706829146617,
@@ -67,7 +70,8 @@ const AppDataSource = new DataSource({
67
70
  ChangeFloorNonUniqueOrder1767370191762,
68
71
  RenameGroupToTag1767432108916,
69
72
  AddPrintJob1767451444137,
70
- AddPrinterMaintenanceLog1767909428129
73
+ AddPrinterMaintenanceLog1767909428129,
74
+ AddApiKey1778446203015
71
75
  ],
72
76
  subscribers: []
73
77
  });
@@ -1 +1 @@
1
- {"version":3,"file":"data-source.js","names":[],"sources":["../src/data-source.ts"],"sourcesContent":["import \"reflect-metadata\";\nimport { DataSource } from \"typeorm\";\nimport { getDatabaseFilePath } from \"@/utils/fs.utils\";\nimport { Floor } from \"@/entities/floor.entity\";\nimport { FloorPosition } from \"@/entities/floor-position.entity\";\nimport { Printer } from \"@/entities/printer.entity\";\nimport { Settings } from \"@/entities/settings.entity\";\nimport { PrintJob, RefreshToken, User, PrinterMaintenanceLog } from \"@/entities\";\nimport { CameraStream } from \"@/entities/camera-stream.entity\";\nimport { Role } from \"@/entities/role.entity\";\nimport { UserRole } from \"@/entities/user-role.entity\";\nimport { InitSqlite1706829146617 } from \"@/migrations/1706829146617-InitSqlite\";\nimport { PrinterTag } from \"@/entities/printer-tag.entity\";\nimport { Tag } from \"@/entities/tag.entity\";\nimport { PrinterGroup1707494762198 } from \"@/migrations/1707494762198-PrinterGroup\";\nimport { ChangePrintCompletionDeletePrinterCascade1708465930665 } from \"@/migrations/1708465930665-ChangePrintCompletionDeletePrinterCascade\";\nimport { ChangeRoleNameUnique1713300747465 } from \"@/migrations/1713300747465-ChangeRoleNameUnique\";\nimport { RemovePrinterFile1720338804844 } from \"@/migrations/1720338804844-RemovePrinterFile\";\nimport { AddPrinterType1713897879622 } from \"@/migrations/1713897879622-AddPrinterType\";\nimport { AddPrinterUsernamePassword1745141688926 } from \"@/migrations/1745141688926-AddPrinterUsernamePassword\";\nimport { DropPermissions1766576698569 } from \"@/migrations/1766576698569-DropPermissions\";\nimport { ChangeCameraPrinterOnDeleteSetNull1767278216516 } from \"@/migrations/1767278216516-ChangeCameraPrinterOnDeleteSetNull\";\nimport { DropCustomGcode1767279607392 } from \"@/migrations/1767279607392-DropCustomGcode\";\nimport { DropPrintCompletions1767291804417 } from \"@/migrations/1767291804417-DropPrintCompletions\";\nimport { DropSettingsFileClean1767352862576 } from \"@/migrations/1767352862576-DropSettingsFileClean\";\nimport { ChangeFloorNonUniqueOrder1767370191762 } from \"@/migrations/1767370191762-ChangeFloorNonUniqueOrder\";\nimport { RenameGroupToTag1767432108916 } from \"@/migrations/1767432108916-RenameGroupToTag\";\nimport { AddPrintJob1767451444137 } from \"@/migrations/1767451444137-AddPrintJob\";\nimport { AddPrinterMaintenanceLog1767909428129 } from \"@/migrations/1767909428129-AddPrinterMaintenanceLog\";\n\nconst databaseFilePath = getDatabaseFilePath();\n\nexport const AppDataSource = new DataSource({\n type: \"better-sqlite3\",\n database: databaseFilePath,\n synchronize: false,\n logging: false,\n entities: [\n Floor,\n FloorPosition,\n Printer,\n Settings,\n User,\n CameraStream,\n Role,\n RefreshToken,\n UserRole,\n Tag,\n PrinterTag,\n PrintJob,\n PrinterMaintenanceLog,\n ],\n migrations: [\n InitSqlite1706829146617,\n PrinterGroup1707494762198,\n ChangePrintCompletionDeletePrinterCascade1708465930665,\n ChangeRoleNameUnique1713300747465,\n RemovePrinterFile1720338804844,\n AddPrinterType1713897879622,\n AddPrinterUsernamePassword1745141688926,\n DropPermissions1766576698569,\n ChangeCameraPrinterOnDeleteSetNull1767278216516,\n DropCustomGcode1767279607392,\n DropPrintCompletions1767291804417,\n DropSettingsFileClean1767352862576,\n ChangeFloorNonUniqueOrder1767370191762,\n RenameGroupToTag1767432108916,\n AddPrintJob1767451444137,\n AddPrinterMaintenanceLog1767909428129,\n ],\n subscribers: [],\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,MAAa,gBAAgB,IAAI,WAAW;CAC1C,MAAM;CACN,UAJuB,qBAIb;CACV,aAAa;CACb,SAAS;CACT,UAAU;EACR;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;CACD,YAAY;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;CACD,aAAa,EAAE;CAChB,CAAC"}
1
+ {"version":3,"file":"data-source.js","names":[],"sources":["../src/data-source.ts"],"sourcesContent":["import \"reflect-metadata\";\nimport { DataSource } from \"typeorm\";\nimport { getDatabaseFilePath } from \"@/utils/fs.utils\";\nimport { Floor } from \"@/entities/floor.entity\";\nimport { FloorPosition } from \"@/entities/floor-position.entity\";\nimport { Printer } from \"@/entities/printer.entity\";\nimport { Settings } from \"@/entities/settings.entity\";\nimport { PrintJob, RefreshToken, User, PrinterMaintenanceLog, ApiKey } from \"@/entities\";\nimport { CameraStream } from \"@/entities/camera-stream.entity\";\nimport { Role } from \"@/entities/role.entity\";\nimport { UserRole } from \"@/entities/user-role.entity\";\nimport { InitSqlite1706829146617 } from \"@/migrations/1706829146617-InitSqlite\";\nimport { PrinterTag } from \"@/entities/printer-tag.entity\";\nimport { Tag } from \"@/entities/tag.entity\";\nimport { PrinterGroup1707494762198 } from \"@/migrations/1707494762198-PrinterGroup\";\nimport { ChangePrintCompletionDeletePrinterCascade1708465930665 } from \"@/migrations/1708465930665-ChangePrintCompletionDeletePrinterCascade\";\nimport { ChangeRoleNameUnique1713300747465 } from \"@/migrations/1713300747465-ChangeRoleNameUnique\";\nimport { RemovePrinterFile1720338804844 } from \"@/migrations/1720338804844-RemovePrinterFile\";\nimport { AddPrinterType1713897879622 } from \"@/migrations/1713897879622-AddPrinterType\";\nimport { AddPrinterUsernamePassword1745141688926 } from \"@/migrations/1745141688926-AddPrinterUsernamePassword\";\nimport { DropPermissions1766576698569 } from \"@/migrations/1766576698569-DropPermissions\";\nimport { ChangeCameraPrinterOnDeleteSetNull1767278216516 } from \"@/migrations/1767278216516-ChangeCameraPrinterOnDeleteSetNull\";\nimport { DropCustomGcode1767279607392 } from \"@/migrations/1767279607392-DropCustomGcode\";\nimport { DropPrintCompletions1767291804417 } from \"@/migrations/1767291804417-DropPrintCompletions\";\nimport { DropSettingsFileClean1767352862576 } from \"@/migrations/1767352862576-DropSettingsFileClean\";\nimport { ChangeFloorNonUniqueOrder1767370191762 } from \"@/migrations/1767370191762-ChangeFloorNonUniqueOrder\";\nimport { RenameGroupToTag1767432108916 } from \"@/migrations/1767432108916-RenameGroupToTag\";\nimport { AddPrintJob1767451444137 } from \"@/migrations/1767451444137-AddPrintJob\";\nimport { AddPrinterMaintenanceLog1767909428129 } from \"@/migrations/1767909428129-AddPrinterMaintenanceLog\";\nimport { AddApiKey1778446203015 } from \"@/migrations/1778446203015-AddApiKey\";\n\nconst databaseFilePath = getDatabaseFilePath();\n\nexport const AppDataSource = new DataSource({\n type: \"better-sqlite3\",\n database: databaseFilePath,\n synchronize: false,\n logging: false,\n entities: [\n Floor,\n FloorPosition,\n Printer,\n Settings,\n User,\n CameraStream,\n Role,\n RefreshToken,\n UserRole,\n Tag,\n PrinterTag,\n PrintJob,\n PrinterMaintenanceLog,\n ApiKey,\n ],\n migrations: [\n InitSqlite1706829146617,\n PrinterGroup1707494762198,\n ChangePrintCompletionDeletePrinterCascade1708465930665,\n ChangeRoleNameUnique1713300747465,\n RemovePrinterFile1720338804844,\n AddPrinterType1713897879622,\n AddPrinterUsernamePassword1745141688926,\n DropPermissions1766576698569,\n ChangeCameraPrinterOnDeleteSetNull1767278216516,\n DropCustomGcode1767279607392,\n DropPrintCompletions1767291804417,\n DropSettingsFileClean1767352862576,\n ChangeFloorNonUniqueOrder1767370191762,\n RenameGroupToTag1767432108916,\n AddPrintJob1767451444137,\n AddPrinterMaintenanceLog1767909428129,\n AddApiKey1778446203015,\n ],\n subscribers: [],\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,MAAa,gBAAgB,IAAI,WAAW;CAC1C,MAAM;CACN,UAJuB,qBAIb;CACV,aAAa;CACb,SAAS;CACT,UAAU;EACR;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;CACD,YAAY;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;CACD,aAAa,EAAE;CAChB,CAAC"}
@@ -0,0 +1,60 @@
1
+ import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorateMetadata.js";
2
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorate.js";
3
+ import { Role } from "./role.entity.js";
4
+ import { User } from "./user.entity.js";
5
+ import { Column, CreateDateColumn, Entity, Index, JoinColumn, JoinTable, ManyToMany, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
6
+ //#region src/entities/api-key.entity.ts
7
+ var _ref;
8
+ let ApiKey = class ApiKey {
9
+ id;
10
+ createdByUser;
11
+ createdByUserId;
12
+ label;
13
+ prefix;
14
+ hashedSecret;
15
+ createdAt;
16
+ lastUsedAt;
17
+ roles;
18
+ };
19
+ __decorate([PrimaryGeneratedColumn(), __decorateMetadata("design:type", Number)], ApiKey.prototype, "id", void 0);
20
+ __decorate([
21
+ ManyToOne(() => User, {
22
+ nullable: false,
23
+ onDelete: "CASCADE"
24
+ }),
25
+ JoinColumn({ name: "createdByUserId" }),
26
+ __decorateMetadata("design:type", Object)
27
+ ], ApiKey.prototype, "createdByUser", void 0);
28
+ __decorate([Column(), __decorateMetadata("design:type", Number)], ApiKey.prototype, "createdByUserId", void 0);
29
+ __decorate([Column(), __decorateMetadata("design:type", String)], ApiKey.prototype, "label", void 0);
30
+ __decorate([
31
+ Index(),
32
+ Column({ unique: true }),
33
+ __decorateMetadata("design:type", String)
34
+ ], ApiKey.prototype, "prefix", void 0);
35
+ __decorate([Column(), __decorateMetadata("design:type", String)], ApiKey.prototype, "hashedSecret", void 0);
36
+ __decorate([CreateDateColumn(), __decorateMetadata("design:type", typeof (_ref = typeof Date !== "undefined" && Date) === "function" ? _ref : Object)], ApiKey.prototype, "createdAt", void 0);
37
+ __decorate([Column({
38
+ type: "datetime",
39
+ nullable: true
40
+ }), __decorateMetadata("design:type", Object)], ApiKey.prototype, "lastUsedAt", void 0);
41
+ __decorate([
42
+ ManyToMany(() => Role, { eager: true }),
43
+ JoinTable({
44
+ name: "api_key_role",
45
+ joinColumn: {
46
+ name: "apiKeyId",
47
+ referencedColumnName: "id"
48
+ },
49
+ inverseJoinColumn: {
50
+ name: "roleId",
51
+ referencedColumnName: "id"
52
+ }
53
+ }),
54
+ __decorateMetadata("design:type", Array)
55
+ ], ApiKey.prototype, "roles", void 0);
56
+ ApiKey = __decorate([Entity()], ApiKey);
57
+ //#endregion
58
+ export { ApiKey };
59
+
60
+ //# sourceMappingURL=api-key.entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-key.entity.js","names":[],"sources":["../../src/entities/api-key.entity.ts"],"sourcesContent":["import {\n Column,\n CreateDateColumn,\n Entity,\n Index,\n JoinColumn,\n JoinTable,\n ManyToMany,\n ManyToOne,\n PrimaryGeneratedColumn,\n type Relation,\n} from \"typeorm\";\nimport { User } from \"@/entities/user.entity\";\nimport { Role } from \"@/entities/role.entity\";\n\n/**\n * Long-lived bearer credential for programmatic API access.\n *\n * Token format: `fdmm_api_<base64url>` (high-entropy CSPRNG). The `prefix`\n * is the indexable lookup column; `hashedSecret` is sha256 of the full\n * token, verified with timingSafeEqual.\n *\n * Permissions are derived from `roles` (via the `api_key_role` join table),\n * NOT from the bound user's roles. `createdByUserId` is audit-only — it\n * records who minted the key and does not drive authorisation.\n */\n@Entity()\nexport class ApiKey {\n @PrimaryGeneratedColumn()\n id: number;\n\n @ManyToOne(() => User, { nullable: false, onDelete: \"CASCADE\" })\n @JoinColumn({ name: \"createdByUserId\" })\n createdByUser: Relation<User>;\n\n @Column()\n createdByUserId: number;\n\n @Column()\n label: string;\n\n @Index()\n @Column({ unique: true })\n prefix: string;\n\n @Column()\n hashedSecret: string;\n\n @CreateDateColumn()\n createdAt: Date;\n\n @Column({ type: \"datetime\", nullable: true })\n lastUsedAt: Date | null;\n\n @ManyToMany(() => Role, { eager: true })\n @JoinTable({\n name: \"api_key_role\",\n joinColumn: { name: \"apiKeyId\", referencedColumnName: \"id\" },\n inverseJoinColumn: { name: \"roleId\", referencedColumnName: \"id\" },\n })\n roles: Relation<Role>[];\n}\n"],"mappings":";;;;;;;AA2BO,IAAA,SAAA,MAAM,OAAO;CAClB;CAGA;CAIA;CAGA;CAGA;CAIA;CAGA;CAGA;CAGA;;YA1BC,wBAAwB,EAAA,mBAAA,eAAA,OAAA,CAAA,EAAA,OAAA,WAAA,MAAA,KAAA,EAAA;;CAGxB,gBAAgB,MAAM;EAAE,UAAU;EAAO,UAAU;EAAW,CAAC;CAC/D,WAAW,EAAE,MAAM,mBAAmB,CAAC;;;YAGvC,QAAQ,EAAA,mBAAA,eAAA,OAAA,CAAA,EAAA,OAAA,WAAA,mBAAA,KAAA,EAAA;YAGR,QAAQ,EAAA,mBAAA,eAAA,OAAA,CAAA,EAAA,OAAA,WAAA,SAAA,KAAA,EAAA;;CAGR,OAAO;CACP,OAAO,EAAE,QAAQ,MAAM,CAAC;;;YAGxB,QAAQ,EAAA,mBAAA,eAAA,OAAA,CAAA,EAAA,OAAA,WAAA,gBAAA,KAAA,EAAA;YAGR,kBAAkB,EAAA,mBAAA,eAAA,QAAA,OAAA,OAAA,SAAA,eAAA,UAAA,aAAA,OAAA,OAAA,CAAA,EAAA,OAAA,WAAA,aAAA,KAAA,EAAA;YAGlB,OAAO;CAAE,MAAM;CAAY,UAAU;CAAM,CAAC,EAAA,mBAAA,eAAA,OAAA,CAAA,EAAA,OAAA,WAAA,cAAA,KAAA,EAAA;;CAG5C,iBAAiB,MAAM,EAAE,OAAO,MAAM,CAAC;CACvC,UAAU;EACT,MAAM;EACN,YAAY;GAAE,MAAM;GAAY,sBAAsB;GAAM;EAC5D,mBAAmB;GAAE,MAAM;GAAU,sBAAsB;GAAM;EAClE,CAAC;;;qBAjCH,QAAQ,CAAA,EAAA,OAAA"}
@@ -1,5 +1,5 @@
1
- import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.js";
2
- import { __decorate } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.js";
1
+ import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorateMetadata.js";
2
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorate.js";
3
3
  import { Printer } from "./printer.entity.js";
4
4
  import { Column, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn } from "typeorm";
5
5
  //#region src/entities/camera-stream.entity.ts
@@ -1,5 +1,5 @@
1
- import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.js";
2
- import { __decorate } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.js";
1
+ import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorateMetadata.js";
2
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorate.js";
3
3
  import { Printer } from "./printer.entity.js";
4
4
  import { Floor } from "./floor.entity.js";
5
5
  import { Column, Entity, JoinColumn, ManyToOne, OneToOne, PrimaryGeneratedColumn, Unique } from "typeorm";
@@ -1,5 +1,5 @@
1
- import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.js";
2
- import { __decorate } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.js";
1
+ import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorateMetadata.js";
2
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorate.js";
3
3
  import { FloorPosition } from "./floor-position.entity.js";
4
4
  import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm";
5
5
  //#region src/entities/floor.entity.ts
@@ -11,4 +11,5 @@ import { CameraStream } from "./camera-stream.entity.js";
11
11
  import { FloorPosition } from "./floor-position.entity.js";
12
12
  import { Floor } from "./floor.entity.js";
13
13
  import { Settings } from "./settings.entity.js";
14
- export { CameraStream, Floor, FloorPosition, PrintJob, Printer, PrinterMaintenanceLog, PrinterTag, RefreshToken, Role, Settings, Tag, User, UserRole };
14
+ import { ApiKey } from "./api-key.entity.js";
15
+ export { ApiKey, CameraStream, Floor, FloorPosition, PrintJob, Printer, PrinterMaintenanceLog, PrinterTag, RefreshToken, Role, Settings, Tag, User, UserRole };
@@ -1,5 +1,5 @@
1
- import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.js";
2
- import { __decorate } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.js";
1
+ import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorateMetadata.js";
2
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorate.js";
3
3
  import { Printer } from "./printer.entity.js";
4
4
  import { Column, CreateDateColumn, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn } from "typeorm";
5
5
  //#region src/entities/print-job.entity.ts
@@ -1,5 +1,5 @@
1
- import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.js";
2
- import { __decorate } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.js";
1
+ import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorateMetadata.js";
2
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorate.js";
3
3
  import { Printer } from "./printer.entity.js";
4
4
  import { User } from "./user.entity.js";
5
5
  import { Column, CreateDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
@@ -1,5 +1,5 @@
1
- import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.js";
2
- import { __decorate } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.js";
1
+ import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorateMetadata.js";
2
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorate.js";
3
3
  import { Tag } from "./tag.entity.js";
4
4
  import { Printer } from "./printer.entity.js";
5
5
  import { Column, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn, Unique } from "typeorm";
@@ -1,5 +1,5 @@
1
- import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.js";
2
- import { __decorate } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.js";
1
+ import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorateMetadata.js";
2
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorate.js";
3
3
  import { PrinterTag } from "./printer-tag.entity.js";
4
4
  import "../services/printer-api.interface.js";
5
5
  import { IsAlphanumeric } from "class-validator";
@@ -1,5 +1,5 @@
1
- import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.js";
2
- import { __decorate } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.js";
1
+ import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorateMetadata.js";
2
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorate.js";
3
3
  import { User } from "./user.entity.js";
4
4
  import { Column, CreateDateColumn, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
5
5
  //#region src/entities/refresh-token.entity.ts
@@ -1,5 +1,5 @@
1
- import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.js";
2
- import { __decorate } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.js";
1
+ import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorateMetadata.js";
2
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorate.js";
3
3
  import { UserRole } from "./user-role.entity.js";
4
4
  import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm";
5
5
  //#region src/entities/role.entity.ts
@@ -1,5 +1,5 @@
1
- import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.js";
2
- import { __decorate } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.js";
1
+ import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorateMetadata.js";
2
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorate.js";
3
3
  import { credentialSettingsKey, frontendSettingKey, serverSettingsKey, timeoutSettingKey, wizardSettingKey } from "../constants/server-settings.constants.js";
4
4
  import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
5
5
  //#region src/entities/settings.entity.ts
@@ -1,5 +1,5 @@
1
- import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.js";
2
- import { __decorate } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.js";
1
+ import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorateMetadata.js";
2
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorate.js";
3
3
  import { Length } from "class-validator";
4
4
  import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
5
5
  //#region src/entities/tag.entity.ts
@@ -1,5 +1,5 @@
1
- import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.js";
2
- import { __decorate } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.js";
1
+ import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorateMetadata.js";
2
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorate.js";
3
3
  import { Role } from "./role.entity.js";
4
4
  import { User } from "./user.entity.js";
5
5
  import { Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn, Unique } from "typeorm";
@@ -1,5 +1,5 @@
1
- import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorateMetadata.js";
2
- import { __decorate } from "../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.js";
1
+ import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorateMetadata.js";
2
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorate.js";
3
3
  import { RefreshToken } from "./refresh-token.entity.js";
4
4
  import { UserRole } from "./user-role.entity.js";
5
5
  import { Column, CreateDateColumn, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm";
@@ -1 +1 @@
1
- {"version":3,"file":"failed-dependency.exception.js","names":[],"sources":["../../src/exceptions/failed-dependency.exception.ts"],"sourcesContent":["export class FailedDependencyException extends Error {\n serviceCode?: number;\n constructor(message: string, serviceCode?: number) {\n super(message);\n this.name = FailedDependencyException.name;\n this.serviceCode = serviceCode;\n }\n}\n"],"mappings":";AAAA,IAAa,4BAAb,MAAa,kCAAkC,MAAM;CACnD;CACA,YAAY,SAAiB,aAAsB;AACjD,QAAM,QAAQ;AACd,OAAK,OAAO,0BAA0B;AACtC,OAAK,cAAc"}
1
+ {"version":3,"file":"failed-dependency.exception.js","names":[],"sources":["../../src/exceptions/failed-dependency.exception.ts"],"sourcesContent":["export class FailedDependencyException extends Error {\n serviceCode?: number;\n constructor(message: string, serviceCode?: number) {\n super(message);\n this.name = FailedDependencyException.name;\n this.serviceCode = serviceCode;\n }\n}\n"],"mappings":";AAAA,IAAa,4BAAb,MAAa,kCAAkC,MAAM;CACnD;CACA,YAAY,SAAiB,aAAsB;EACjD,MAAM,QAAQ;EACd,KAAK,OAAO,0BAA0B;EACtC,KAAK,cAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"job.exceptions.js","names":[],"sources":["../../src/exceptions/job.exceptions.ts"],"sourcesContent":["export class JobValidationException extends Error {\n constructor(message: string, taskId: string) {\n super(message);\n this.name = `JobValidationError [${taskId || \"anonymous\"}]`;\n }\n}\n"],"mappings":";AAAA,IAAa,yBAAb,cAA4C,MAAM;CAChD,YAAY,SAAiB,QAAgB;AAC3C,QAAM,QAAQ;AACd,OAAK,OAAO,uBAAuB,UAAU,YAAY"}
1
+ {"version":3,"file":"job.exceptions.js","names":[],"sources":["../../src/exceptions/job.exceptions.ts"],"sourcesContent":["export class JobValidationException extends Error {\n constructor(message: string, taskId: string) {\n super(message);\n this.name = `JobValidationError [${taskId || \"anonymous\"}]`;\n }\n}\n"],"mappings":";AAAA,IAAa,yBAAb,cAA4C,MAAM;CAChD,YAAY,SAAiB,QAAgB;EAC3C,MAAM,QAAQ;EACd,KAAK,OAAO,uBAAuB,UAAU,YAAY"}
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.exceptions.js","names":[],"sources":["../../src/exceptions/runtime.exceptions.ts"],"sourcesContent":["export class NotImplementedException extends Error {\n constructor(message?: string) {\n super(message);\n this.name = NotImplementedException.name;\n }\n}\n\nexport class AuthenticationError extends Error {\n reasonCode: string;\n\n constructor(error?: string, reasonCode = \"\") {\n super(error);\n this.name = AuthenticationError.name;\n this.reasonCode = reasonCode;\n }\n}\n\nexport class ForbiddenError extends Error {\n constructor(error?: string) {\n super(error);\n this.name = ForbiddenError.name;\n }\n}\n\nexport class AuthorizationError extends Error {\n permissions?: string[] = [];\n roles?: string[] = [];\n reason?: string;\n\n constructor({ permissions, roles, reason }: { permissions?: string[]; roles?: string[]; reason?: string }) {\n super(\"Authorization failed\");\n this.name = AuthorizationError.name;\n this.reason = reason;\n this.permissions = permissions;\n this.roles = roles;\n }\n}\n\nexport class BadRequestException extends Error {\n constructor(message: string) {\n super(message);\n this.name = BadRequestException.name;\n }\n}\n\nexport class ConflictException extends Error {\n existingResourceId?: string;\n\n constructor(message: string, existingResourceId?: string) {\n super(message);\n this.name = ConflictException.name;\n this.existingResourceId = existingResourceId;\n }\n}\n\nexport class NotFoundException extends Error {\n path?: string;\n\n constructor(message: string, path?: string) {\n super(message);\n this.name = NotFoundException.name;\n this.path = path;\n }\n}\n\nexport class ValidationException<T = any> extends Error {\n errors: T;\n\n constructor(validationObject: T) {\n super(JSON.stringify(validationObject));\n this.name = ValidationException.name;\n this.errors = validationObject;\n }\n}\n\nexport class ExternalServiceError extends Error {\n error: any;\n serviceType?: string;\n\n constructor(responseObject: any, serviceType?: string) {\n super(JSON.stringify(responseObject));\n this.name = ExternalServiceError.name;\n this.error = responseObject;\n this.serviceType = serviceType;\n }\n}\n\nexport class InternalServerException extends Error {\n constructor(message: string, stack?: any) {\n super(message);\n this.name = InternalServerException.name;\n this.stack = stack;\n }\n}\n"],"mappings":";AAAA,IAAa,0BAAb,MAAa,gCAAgC,MAAM;CACjD,YAAY,SAAkB;AAC5B,QAAM,QAAQ;AACd,OAAK,OAAO,wBAAwB;;;AAIxC,IAAa,sBAAb,MAAa,4BAA4B,MAAM;CAC7C;CAEA,YAAY,OAAgB,aAAa,IAAI;AAC3C,QAAM,MAAM;AACZ,OAAK,OAAO,oBAAoB;AAChC,OAAK,aAAa;;;AAItB,IAAa,iBAAb,MAAa,uBAAuB,MAAM;CACxC,YAAY,OAAgB;AAC1B,QAAM,MAAM;AACZ,OAAK,OAAO,eAAe;;;AAI/B,IAAa,qBAAb,MAAa,2BAA2B,MAAM;CAC5C,cAAyB,EAAE;CAC3B,QAAmB,EAAE;CACrB;CAEA,YAAY,EAAE,aAAa,OAAO,UAAyE;AACzG,QAAM,uBAAuB;AAC7B,OAAK,OAAO,mBAAmB;AAC/B,OAAK,SAAS;AACd,OAAK,cAAc;AACnB,OAAK,QAAQ;;;AAIjB,IAAa,sBAAb,MAAa,4BAA4B,MAAM;CAC7C,YAAY,SAAiB;AAC3B,QAAM,QAAQ;AACd,OAAK,OAAO,oBAAoB;;;AAIpC,IAAa,oBAAb,MAAa,0BAA0B,MAAM;CAC3C;CAEA,YAAY,SAAiB,oBAA6B;AACxD,QAAM,QAAQ;AACd,OAAK,OAAO,kBAAkB;AAC9B,OAAK,qBAAqB;;;AAI9B,IAAa,oBAAb,MAAa,0BAA0B,MAAM;CAC3C;CAEA,YAAY,SAAiB,MAAe;AAC1C,QAAM,QAAQ;AACd,OAAK,OAAO,kBAAkB;AAC9B,OAAK,OAAO;;;AAIhB,IAAa,sBAAb,MAAa,4BAAqC,MAAM;CACtD;CAEA,YAAY,kBAAqB;AAC/B,QAAM,KAAK,UAAU,iBAAiB,CAAC;AACvC,OAAK,OAAO,oBAAoB;AAChC,OAAK,SAAS;;;AAIlB,IAAa,uBAAb,MAAa,6BAA6B,MAAM;CAC9C;CACA;CAEA,YAAY,gBAAqB,aAAsB;AACrD,QAAM,KAAK,UAAU,eAAe,CAAC;AACrC,OAAK,OAAO,qBAAqB;AACjC,OAAK,QAAQ;AACb,OAAK,cAAc;;;AAIvB,IAAa,0BAAb,MAAa,gCAAgC,MAAM;CACjD,YAAY,SAAiB,OAAa;AACxC,QAAM,QAAQ;AACd,OAAK,OAAO,wBAAwB;AACpC,OAAK,QAAQ"}
1
+ {"version":3,"file":"runtime.exceptions.js","names":[],"sources":["../../src/exceptions/runtime.exceptions.ts"],"sourcesContent":["export class NotImplementedException extends Error {\n constructor(message?: string) {\n super(message);\n this.name = NotImplementedException.name;\n }\n}\n\nexport class AuthenticationError extends Error {\n reasonCode: string;\n\n constructor(error?: string, reasonCode = \"\") {\n super(error);\n this.name = AuthenticationError.name;\n this.reasonCode = reasonCode;\n }\n}\n\nexport class ForbiddenError extends Error {\n constructor(error?: string) {\n super(error);\n this.name = ForbiddenError.name;\n }\n}\n\nexport class AuthorizationError extends Error {\n permissions?: string[] = [];\n roles?: string[] = [];\n reason?: string;\n\n constructor({ permissions, roles, reason }: { permissions?: string[]; roles?: string[]; reason?: string }) {\n super(\"Authorization failed\");\n this.name = AuthorizationError.name;\n this.reason = reason;\n this.permissions = permissions;\n this.roles = roles;\n }\n}\n\nexport class BadRequestException extends Error {\n constructor(message: string) {\n super(message);\n this.name = BadRequestException.name;\n }\n}\n\nexport class ConflictException extends Error {\n existingResourceId?: string;\n\n constructor(message: string, existingResourceId?: string) {\n super(message);\n this.name = ConflictException.name;\n this.existingResourceId = existingResourceId;\n }\n}\n\nexport class NotFoundException extends Error {\n path?: string;\n\n constructor(message: string, path?: string) {\n super(message);\n this.name = NotFoundException.name;\n this.path = path;\n }\n}\n\nexport class ValidationException<T = any> extends Error {\n errors: T;\n\n constructor(validationObject: T) {\n super(JSON.stringify(validationObject));\n this.name = ValidationException.name;\n this.errors = validationObject;\n }\n}\n\nexport class ExternalServiceError extends Error {\n error: any;\n serviceType?: string;\n\n constructor(responseObject: any, serviceType?: string) {\n super(JSON.stringify(responseObject));\n this.name = ExternalServiceError.name;\n this.error = responseObject;\n this.serviceType = serviceType;\n }\n}\n\nexport class InternalServerException extends Error {\n constructor(message: string, stack?: any) {\n super(message);\n this.name = InternalServerException.name;\n this.stack = stack;\n }\n}\n"],"mappings":";AAAA,IAAa,0BAAb,MAAa,gCAAgC,MAAM;CACjD,YAAY,SAAkB;EAC5B,MAAM,QAAQ;EACd,KAAK,OAAO,wBAAwB;;;AAIxC,IAAa,sBAAb,MAAa,4BAA4B,MAAM;CAC7C;CAEA,YAAY,OAAgB,aAAa,IAAI;EAC3C,MAAM,MAAM;EACZ,KAAK,OAAO,oBAAoB;EAChC,KAAK,aAAa;;;AAItB,IAAa,iBAAb,MAAa,uBAAuB,MAAM;CACxC,YAAY,OAAgB;EAC1B,MAAM,MAAM;EACZ,KAAK,OAAO,eAAe;;;AAI/B,IAAa,qBAAb,MAAa,2BAA2B,MAAM;CAC5C,cAAyB,EAAE;CAC3B,QAAmB,EAAE;CACrB;CAEA,YAAY,EAAE,aAAa,OAAO,UAAyE;EACzG,MAAM,uBAAuB;EAC7B,KAAK,OAAO,mBAAmB;EAC/B,KAAK,SAAS;EACd,KAAK,cAAc;EACnB,KAAK,QAAQ;;;AAIjB,IAAa,sBAAb,MAAa,4BAA4B,MAAM;CAC7C,YAAY,SAAiB;EAC3B,MAAM,QAAQ;EACd,KAAK,OAAO,oBAAoB;;;AAIpC,IAAa,oBAAb,MAAa,0BAA0B,MAAM;CAC3C;CAEA,YAAY,SAAiB,oBAA6B;EACxD,MAAM,QAAQ;EACd,KAAK,OAAO,kBAAkB;EAC9B,KAAK,qBAAqB;;;AAI9B,IAAa,oBAAb,MAAa,0BAA0B,MAAM;CAC3C;CAEA,YAAY,SAAiB,MAAe;EAC1C,MAAM,QAAQ;EACd,KAAK,OAAO,kBAAkB;EAC9B,KAAK,OAAO;;;AAIhB,IAAa,sBAAb,MAAa,4BAAqC,MAAM;CACtD;CAEA,YAAY,kBAAqB;EAC/B,MAAM,KAAK,UAAU,iBAAiB,CAAC;EACvC,KAAK,OAAO,oBAAoB;EAChC,KAAK,SAAS;;;AAIlB,IAAa,uBAAb,MAAa,6BAA6B,MAAM;CAC9C;CACA;CAEA,YAAY,gBAAqB,aAAsB;EACrD,MAAM,KAAK,UAAU,eAAe,CAAC;EACrC,KAAK,OAAO,qBAAqB;EACjC,KAAK,QAAQ;EACb,KAAK,cAAc;;;AAIvB,IAAa,0BAAb,MAAa,gCAAgC,MAAM;CACjD,YAAY,SAAiB,OAAa;EACxC,MAAM,QAAQ;EACd,KAAK,OAAO,wBAAwB;EACpC,KAAK,QAAQ"}
@@ -1 +1 @@
1
- {"version":3,"file":"event-emitter.js","names":[],"sources":["../../src/handlers/event-emitter.ts"],"sourcesContent":["import EventEmitter2 from \"eventemitter2\";\n\nexport function configureEventEmitter() {\n return new EventEmitter2({\n // set this to `true` to use wildcards\n wildcard: true,\n\n // the delimiter used to segment namespaces\n delimiter: \".\",\n\n // set this to `true` if you want to emit the newListener event\n newListener: false,\n\n // set this to `true` if you want to emit the removeListener event\n removeListener: false,\n\n // the maximum amount of listeners that can be assigned to an event\n maxListeners: 10,\n\n // show event name in memory leak message when more than maximum amount of listeners is assigned\n verboseMemoryLeak: true,\n\n // disable throwing uncaughtException if an error event is emitted and it has no listeners\n ignoreErrors: false,\n });\n}\n"],"mappings":";;AAEA,SAAgB,wBAAwB;AACtC,QAAO,IAAI,cAAc;EAEvB,UAAU;EAGV,WAAW;EAGX,aAAa;EAGb,gBAAgB;EAGhB,cAAc;EAGd,mBAAmB;EAGnB,cAAc;EACf,CAAC"}
1
+ {"version":3,"file":"event-emitter.js","names":[],"sources":["../../src/handlers/event-emitter.ts"],"sourcesContent":["import EventEmitter2 from \"eventemitter2\";\n\nexport function configureEventEmitter() {\n return new EventEmitter2({\n // set this to `true` to use wildcards\n wildcard: true,\n\n // the delimiter used to segment namespaces\n delimiter: \".\",\n\n // set this to `true` if you want to emit the newListener event\n newListener: false,\n\n // set this to `true` if you want to emit the removeListener event\n removeListener: false,\n\n // the maximum amount of listeners that can be assigned to an event\n maxListeners: 10,\n\n // show event name in memory leak message when more than maximum amount of listeners is assigned\n verboseMemoryLeak: true,\n\n // disable throwing uncaughtException if an error event is emitted and it has no listeners\n ignoreErrors: false,\n });\n}\n"],"mappings":";;AAEA,SAAgB,wBAAwB;CACtC,OAAO,IAAI,cAAc;EAEvB,UAAU;EAGV,WAAW;EAGX,aAAa;EAGb,gBAAgB;EAGhB,cAAc;EAGd,mBAAmB;EAGnB,cAAc;EACf,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"logger-factory.js","names":[],"sources":["../../src/handlers/logger-factory.ts"],"sourcesContent":["import { LoggerService } from \"./logger\";\n\nexport type ILoggerFactory = (name: string) => LoggerService;\n\nexport function LoggerFactory(): ILoggerFactory {\n return (name: string) => {\n return new LoggerService(name);\n };\n}\n"],"mappings":";;AAIA,SAAgB,gBAAgC;AAC9C,SAAQ,SAAiB;AACvB,SAAO,IAAI,cAAc,KAAK"}
1
+ {"version":3,"file":"logger-factory.js","names":[],"sources":["../../src/handlers/logger-factory.ts"],"sourcesContent":["import { LoggerService } from \"./logger\";\n\nexport type ILoggerFactory = (name: string) => LoggerService;\n\nexport function LoggerFactory(): ILoggerFactory {\n return (name: string) => {\n return new LoggerService(name);\n };\n}\n"],"mappings":";;AAIA,SAAgB,gBAAgC;CAC9C,QAAQ,SAAiB;EACvB,OAAO,IAAI,cAAc,KAAK"}
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","names":[],"sources":["../../src/handlers/logger.ts"],"sourcesContent":["import winston from \"winston\";\nimport { getStaticLogger, logContextClassProperty } from \"@/handlers/logging/static.logger\";\n\nexport class LoggerService {\n logger: winston.Logger;\n\n constructor(private readonly name: string) {\n this.logger = getStaticLogger().child({ [logContextClassProperty]: this.name });\n }\n\n newDebug(object: any) {\n this.logger.debug(object);\n }\n\n log(message: string, meta?: any) {\n this.logger.log(\"info\", message, meta);\n }\n\n warn(message: string, meta?: any) {\n this.logger.log(\"warn\", message, meta);\n }\n\n debug(message: string, meta?: any) {\n this.logger.log(\"debug\", message, meta);\n }\n\n error(message: string, meta?: any) {\n this.logger.log(\"error\", message, meta);\n }\n}\n"],"mappings":";;AAGA,IAAa,gBAAb,MAA2B;CACzB;CAEA,YAAY,MAA+B;AAAd,OAAA,OAAA;AAC3B,OAAK,SAAS,iBAAiB,CAAC,MAAM,GAAG,0BAA0B,KAAK,MAAM,CAAC;;CAGjF,SAAS,QAAa;AACpB,OAAK,OAAO,MAAM,OAAO;;CAG3B,IAAI,SAAiB,MAAY;AAC/B,OAAK,OAAO,IAAI,QAAQ,SAAS,KAAK;;CAGxC,KAAK,SAAiB,MAAY;AAChC,OAAK,OAAO,IAAI,QAAQ,SAAS,KAAK;;CAGxC,MAAM,SAAiB,MAAY;AACjC,OAAK,OAAO,IAAI,SAAS,SAAS,KAAK;;CAGzC,MAAM,SAAiB,MAAY;AACjC,OAAK,OAAO,IAAI,SAAS,SAAS,KAAK"}
1
+ {"version":3,"file":"logger.js","names":[],"sources":["../../src/handlers/logger.ts"],"sourcesContent":["import winston from \"winston\";\nimport { getStaticLogger, logContextClassProperty } from \"@/handlers/logging/static.logger\";\n\nexport class LoggerService {\n logger: winston.Logger;\n\n constructor(private readonly name: string) {\n this.logger = getStaticLogger().child({ [logContextClassProperty]: this.name });\n }\n\n newDebug(object: any) {\n this.logger.debug(object);\n }\n\n log(message: string, meta?: any) {\n this.logger.log(\"info\", message, meta);\n }\n\n warn(message: string, meta?: any) {\n this.logger.log(\"warn\", message, meta);\n }\n\n debug(message: string, meta?: any) {\n this.logger.log(\"debug\", message, meta);\n }\n\n error(message: string, meta?: any) {\n this.logger.log(\"error\", message, meta);\n }\n}\n"],"mappings":";;AAGA,IAAa,gBAAb,MAA2B;CACzB;CAEA,YAAY,MAA+B;EAAd,KAAA,OAAA;EAC3B,KAAK,SAAS,iBAAiB,CAAC,MAAM,GAAG,0BAA0B,KAAK,MAAM,CAAC;;CAGjF,SAAS,QAAa;EACpB,KAAK,OAAO,MAAM,OAAO;;CAG3B,IAAI,SAAiB,MAAY;EAC/B,KAAK,OAAO,IAAI,QAAQ,SAAS,KAAK;;CAGxC,KAAK,SAAiB,MAAY;EAChC,KAAK,OAAO,IAAI,QAAQ,SAAS,KAAK;;CAGxC,MAAM,SAAiB,MAAY;EACjC,KAAK,OAAO,IAAI,SAAS,SAAS,KAAK;;CAGzC,MAAM,SAAiB,MAAY;EACjC,KAAK,OAAO,IAAI,SAAS,SAAS,KAAK"}
@@ -2,11 +2,10 @@ import { AppConstants } from "../../server.constants.js";
2
2
  import { getMediaPath } from "../../utils/fs.utils.js";
3
3
  import { join } from "node:path";
4
4
  import winston from "winston";
5
- import { DateTime } from "luxon";
6
5
  //#region src/handlers/logging/file-logging.transport.ts
7
6
  function createFileLoggingTransport(options) {
8
7
  if (!options.enabled) return;
9
- const date = DateTime.now().toISODate();
8
+ const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
10
9
  const logFilePath = join(getMediaPath(), AppConstants.defaultLogsFolder, `${AppConstants.logAppName}-${date}.log`);
11
10
  return new winston.transports.File({
12
11
  level: options.isTest ? "warn" : "info",
@@ -1 +1 @@
1
- {"version":3,"file":"file-logging.transport.js","names":[],"sources":["../../../src/handlers/logging/file-logging.transport.ts"],"sourcesContent":["import { DateTime } from \"luxon\";\nimport { join } from \"node:path\";\nimport { getMediaPath } from \"@/utils/fs.utils\";\nimport { AppConstants } from \"@/server.constants\";\nimport winston from \"winston\";\n\nexport interface FileLoggerOptions {\n enabled: boolean;\n isTest: boolean;\n}\n\nexport function createFileLoggingTransport(options: FileLoggerOptions): winston.transport | undefined {\n if (!options.enabled) {\n return;\n }\n\n const date = DateTime.now().toISODate();\n const logFilePath = join(getMediaPath(), AppConstants.defaultLogsFolder, `${AppConstants.logAppName}-${date}.log`);\n\n return new winston.transports.File({\n level: options.isTest ? \"warn\" : \"info\",\n filename: logFilePath,\n maxsize: 5000000,\n maxFiles: 5,\n });\n}\n"],"mappings":";;;;;;AAWA,SAAgB,2BAA2B,SAA2D;AACpG,KAAI,CAAC,QAAQ,QACX;CAGF,MAAM,OAAO,SAAS,KAAK,CAAC,WAAW;CACvC,MAAM,cAAc,KAAK,cAAc,EAAE,aAAa,mBAAmB,GAAG,aAAa,WAAW,GAAG,KAAK,MAAM;AAElH,QAAO,IAAI,QAAQ,WAAW,KAAK;EACjC,OAAO,QAAQ,SAAS,SAAS;EACjC,UAAU;EACV,SAAS;EACT,UAAU;EACX,CAAC"}
1
+ {"version":3,"file":"file-logging.transport.js","names":[],"sources":["../../../src/handlers/logging/file-logging.transport.ts"],"sourcesContent":["import { join } from \"node:path\";\nimport { getMediaPath } from \"@/utils/fs.utils\";\nimport { AppConstants } from \"@/server.constants\";\nimport winston from \"winston\";\n\nexport interface FileLoggerOptions {\n enabled: boolean;\n isTest: boolean;\n}\n\nexport function createFileLoggingTransport(options: FileLoggerOptions): winston.transport | undefined {\n if (!options.enabled) {\n return;\n }\n const date = new Date().toISOString().slice(0, 10);\n const logFilePath = join(getMediaPath(), AppConstants.defaultLogsFolder, `${AppConstants.logAppName}-${date}.log`);\n\n return new winston.transports.File({\n level: options.isTest ? \"warn\" : \"info\",\n filename: logFilePath,\n maxsize: 5000000,\n maxFiles: 5,\n });\n}\n"],"mappings":";;;;;AAUA,SAAgB,2BAA2B,SAA2D;CACpG,IAAI,CAAC,QAAQ,SACX;CAEF,MAAM,wBAAO,IAAI,MAAM,EAAC,aAAa,CAAC,MAAM,GAAG,GAAG;CAClD,MAAM,cAAc,KAAK,cAAc,EAAE,aAAa,mBAAmB,GAAG,aAAa,WAAW,GAAG,KAAK,MAAM;CAElH,OAAO,IAAI,QAAQ,WAAW,KAAK;EACjC,OAAO,QAAQ,SAAS,SAAS;EACjC,UAAU;EACV,SAAS;EACT,UAAU;EACX,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"loki-logging.transport.js","names":[],"sources":["../../../src/handlers/logging/loki-logging.transport.ts"],"sourcesContent":["import LokiTransport from \"winston-loki\";\nimport winston from \"winston\";\nimport process from \"node:process\";\nimport { AppConstants } from \"@/server.constants\";\nimport { z } from \"zod\";\n\nexport interface LokiLoggerOptions {\n logLevel: string;\n}\n\nconst lokiValidationSchema = z.object({\n lokiEnabled: z.boolean(),\n lokiAddress: z.string().url(),\n lokiTimeoutSeconds: z.coerce.number().positive().default(30),\n lokiInterval: z.coerce.number().positive().default(15),\n});\n\nexport function createLokiLoggingTransport(options: LokiLoggerOptions) {\n const lokiConfigValidationResult = lokiValidationSchema.safeParse({\n lokiEnabled: process.env[AppConstants.ENABLE_LOKI_LOGGING] === \"true\",\n lokiTimeoutSeconds: process.env[AppConstants.LOKI_TIMEOUT_SECONDS],\n lokiAddress: process.env[AppConstants.LOKI_ADDRESS],\n lokiInterval: process.env[AppConstants.LOKI_INTERVAL],\n });\n\n if (!lokiConfigValidationResult.success || !lokiConfigValidationResult.data.lokiEnabled) {\n return;\n }\n\n return new LokiTransport({\n level: options.logLevel ?? \"info\",\n host: lokiConfigValidationResult.data.lokiAddress,\n interval: lokiConfigValidationResult.data.lokiInterval,\n timeout: lokiConfigValidationResult.data.lokiTimeoutSeconds,\n handleExceptions: true,\n onConnectionError(error: unknown) {\n console.debug(`Loki logger enabled, but connection failed. ${error}`);\n },\n // The labels,json, useWinstonMetaAsLabels, format settings plays well with Loki + Grafana\n labels: {\n app: \"fdm-monster-server\",\n },\n // When set to false, uses protobuf\n json: false,\n // When set to false, the labels column cardinality is kept low (better for performance)\n useWinstonMetaAsLabels: false,\n // Other formats like simple cause string + json, and are thus harder to work with\n format: winston.format.json(),\n });\n}\n"],"mappings":";;;;;;AAUA,MAAM,uBAAuB,EAAE,OAAO;CACpC,aAAa,EAAE,SAAS;CACxB,aAAa,EAAE,QAAQ,CAAC,KAAK;CAC7B,oBAAoB,EAAE,OAAO,QAAQ,CAAC,UAAU,CAAC,QAAQ,GAAG;CAC5D,cAAc,EAAE,OAAO,QAAQ,CAAC,UAAU,CAAC,QAAQ,GAAG;CACvD,CAAC;AAEF,SAAgB,2BAA2B,SAA4B;CACrE,MAAM,6BAA6B,qBAAqB,UAAU;EAChE,aAAa,QAAQ,IAAI,aAAa,yBAAyB;EAC/D,oBAAoB,QAAQ,IAAI,aAAa;EAC7C,aAAa,QAAQ,IAAI,aAAa;EACtC,cAAc,QAAQ,IAAI,aAAa;EACxC,CAAC;AAEF,KAAI,CAAC,2BAA2B,WAAW,CAAC,2BAA2B,KAAK,YAC1E;AAGF,QAAO,IAAI,cAAc;EACvB,OAAO,QAAQ,YAAY;EAC3B,MAAM,2BAA2B,KAAK;EACtC,UAAU,2BAA2B,KAAK;EAC1C,SAAS,2BAA2B,KAAK;EACzC,kBAAkB;EAClB,kBAAkB,OAAgB;AAChC,WAAQ,MAAM,+CAA+C,QAAQ;;EAGvE,QAAQ,EACN,KAAK,sBACN;EAED,MAAM;EAEN,wBAAwB;EAExB,QAAQ,QAAQ,OAAO,MAAM;EAC9B,CAAC"}
1
+ {"version":3,"file":"loki-logging.transport.js","names":[],"sources":["../../../src/handlers/logging/loki-logging.transport.ts"],"sourcesContent":["import LokiTransport from \"winston-loki\";\nimport winston from \"winston\";\nimport process from \"node:process\";\nimport { AppConstants } from \"@/server.constants\";\nimport { z } from \"zod\";\n\nexport interface LokiLoggerOptions {\n logLevel: string;\n}\n\nconst lokiValidationSchema = z.object({\n lokiEnabled: z.boolean(),\n lokiAddress: z.string().url(),\n lokiTimeoutSeconds: z.coerce.number().positive().default(30),\n lokiInterval: z.coerce.number().positive().default(15),\n});\n\nexport function createLokiLoggingTransport(options: LokiLoggerOptions) {\n const lokiConfigValidationResult = lokiValidationSchema.safeParse({\n lokiEnabled: process.env[AppConstants.ENABLE_LOKI_LOGGING] === \"true\",\n lokiTimeoutSeconds: process.env[AppConstants.LOKI_TIMEOUT_SECONDS],\n lokiAddress: process.env[AppConstants.LOKI_ADDRESS],\n lokiInterval: process.env[AppConstants.LOKI_INTERVAL],\n });\n\n if (!lokiConfigValidationResult.success || !lokiConfigValidationResult.data.lokiEnabled) {\n return;\n }\n\n return new LokiTransport({\n level: options.logLevel ?? \"info\",\n host: lokiConfigValidationResult.data.lokiAddress,\n interval: lokiConfigValidationResult.data.lokiInterval,\n timeout: lokiConfigValidationResult.data.lokiTimeoutSeconds,\n handleExceptions: true,\n onConnectionError(error: unknown) {\n console.debug(`Loki logger enabled, but connection failed. ${error}`);\n },\n // The labels,json, useWinstonMetaAsLabels, format settings plays well with Loki + Grafana\n labels: {\n app: \"fdm-monster-server\",\n },\n // When set to false, uses protobuf\n json: false,\n // When set to false, the labels column cardinality is kept low (better for performance)\n useWinstonMetaAsLabels: false,\n // Other formats like simple cause string + json, and are thus harder to work with\n format: winston.format.json(),\n });\n}\n"],"mappings":";;;;;;AAUA,MAAM,uBAAuB,EAAE,OAAO;CACpC,aAAa,EAAE,SAAS;CACxB,aAAa,EAAE,QAAQ,CAAC,KAAK;CAC7B,oBAAoB,EAAE,OAAO,QAAQ,CAAC,UAAU,CAAC,QAAQ,GAAG;CAC5D,cAAc,EAAE,OAAO,QAAQ,CAAC,UAAU,CAAC,QAAQ,GAAG;CACvD,CAAC;AAEF,SAAgB,2BAA2B,SAA4B;CACrE,MAAM,6BAA6B,qBAAqB,UAAU;EAChE,aAAa,QAAQ,IAAI,aAAa,yBAAyB;EAC/D,oBAAoB,QAAQ,IAAI,aAAa;EAC7C,aAAa,QAAQ,IAAI,aAAa;EACtC,cAAc,QAAQ,IAAI,aAAa;EACxC,CAAC;CAEF,IAAI,CAAC,2BAA2B,WAAW,CAAC,2BAA2B,KAAK,aAC1E;CAGF,OAAO,IAAI,cAAc;EACvB,OAAO,QAAQ,YAAY;EAC3B,MAAM,2BAA2B,KAAK;EACtC,UAAU,2BAA2B,KAAK;EAC1C,SAAS,2BAA2B,KAAK;EACzC,kBAAkB;EAClB,kBAAkB,OAAgB;GAChC,QAAQ,MAAM,+CAA+C,QAAQ;;EAGvE,QAAQ,EACN,KAAK,sBACN;EAED,MAAM;EAEN,wBAAwB;EAExB,QAAQ,QAAQ,OAAO,MAAM;EAC9B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"static.logger.js","names":[],"sources":["../../../src/handlers/logging/static.logger.ts"],"sourcesContent":["import winston from \"winston\";\nimport process from \"node:process\";\nimport { AppConstants } from \"@/server.constants\";\nimport { createLokiLoggingTransport } from \"@/handlers/logging/loki-logging.transport\";\nimport { createFileLoggingTransport } from \"@/handlers/logging/file-logging.transport\";\nimport { isDevelopmentEnvironment } from \"@/utils/env.utils\";\n\nlet staticLogger: winston.Logger | null = null;\n\nexport interface StaticLoggerConfig {\n enableFileLogs: boolean;\n}\n\nconst levelMap: Record<string, string> = {\n error: \"ERR\",\n warn: \"WRN\",\n info: \"INF\",\n debug: \"DBG\",\n http: \"HTT\",\n verbose: \"VRB\",\n silly: \"SLY\",\n};\n\nexport const logContextClassProperty = \"class\";\n\nexport function getStaticLogger() {\n if (!staticLogger) {\n throw new Error(\"Logger not yet initialized.\");\n }\n\n return staticLogger;\n}\n\nexport function createStaticLogger(config: StaticLoggerConfig) {\n if (staticLogger) {\n return;\n }\n\n const isProd = process.env[AppConstants.NODE_ENV_KEY] === AppConstants.defaultProductionEnv;\n const isTest = process.env[AppConstants.NODE_ENV_KEY] === AppConstants.defaultTestEnv;\n\n const effectiveLogLevel = isProd || isTest ? \"warn\" : \"debug\";\n\n const lokiTransport = createLokiLoggingTransport({\n logLevel: effectiveLogLevel,\n });\n\n const extraWinstonTransports: winston.transport[] = [];\n if (lokiTransport) {\n extraWinstonTransports.push(lokiTransport);\n }\n\n const fileLoggerTransport = createFileLoggingTransport({\n enabled: config.enableFileLogs,\n isTest,\n });\n if (fileLoggerTransport) {\n extraWinstonTransports.push(fileLoggerTransport);\n }\n\n staticLogger = winston.createLogger({\n transports: [\n ...extraWinstonTransports,\n // Always include console transport\n new winston.transports.Console({\n level: effectiveLogLevel,\n format: winston.format.combine(\n ...(isDevelopmentEnvironment() && process.env[AppConstants.ENABLE_COLORED_LOGS_KEY] == \"true\"\n ? [\n // Store the original level before colorization\n winston.format((info) => {\n info.rawLevel = info.level;\n return info;\n })(),\n winston.format.colorize({\n colors: {\n error: \"red\",\n warn: \"yellow\",\n info: \"white\",\n debug: \"gray\",\n http: \"magenta\",\n verbose: \"cyan\",\n silly: \"gray\",\n },\n level: true,\n message: true, // Don't colorize the whole message\n all: false,\n }),\n winston.format.printf((info) => {\n // Format timestamp similar to Serilog (ISO with milliseconds)\n const now = new Date();\n const timestamp = `${now.toISOString().split(\"T\")[0]} ${now.toTimeString().split(\" \")[0]}.${now.getMilliseconds().toString().padStart(3, \"0\")}`;\n\n // Get colored level from winston\n // @ts-ignore\n const levelAbbr = levelMap[info.rawLevel] ?? info.rawLevel.substring(0, 3).toUpperCase();\n\n // Apply custom coloring using ANSI color codes\n const gray = \"\\x1b[90m\"; // Dim/gray\n const reset = (info.message as string).substring(0, 5) ?? \"\\x1b[0m\"; // Reset\n const numberRegex = /\\b\\d+\\b/g;\n\n // Apply purple color to numbers in the message\n const coloredMessage = (info.message as string).replace(\n numberRegex,\n (match) => `\\x1b[35m${match}${reset}`,\n );\n\n const serviceName = info[logContextClassProperty] ?? \"unknown\";\n\n // Format the log entry with gray timestamp and brackets, colored level, and message with purple numbers\n let logEntry = `${gray}[${timestamp} ${reset}${levelAbbr}${reset}${gray}]${reset} ${gray}[${reset}${serviceName}${gray}]${reset} ${coloredMessage}`;\n\n // Add metadata if present\n if (info.meta) {\n // Add metadata with numbers colorized in purple\n const metaString = JSON.stringify(info.meta);\n const coloredMeta = metaString.replace(numberRegex, (match) => `\\x1b[35m${match}${reset}`);\n logEntry += ` ${coloredMeta}`;\n }\n\n return logEntry;\n }),\n ]\n : []),\n ),\n }),\n ],\n format: winston.format.printf((info) => {\n // Format timestamp similar to Serilog (ISO with milliseconds)\n const now = new Date();\n const timestamp = `${now.toISOString().split(\"T\")[0]} ${now.toTimeString().split(\" \")[0]}.${now.getMilliseconds().toString().padStart(3, \"0\")}`;\n\n const levelAbbr = levelMap[info.level] || `[${info.level.substring(0, 3).toUpperCase()}]`;\n\n const serviceName = info[logContextClassProperty] ?? \"unknown\";\n let message = `[${timestamp} ${levelAbbr}] [${serviceName}] ${info.message}`;\n\n // Add metadata if present, without dash separator\n if (info.meta) {\n // Convert camelCase to PascalCase for C# style\n const pascalCaseMeta = Object.entries(info.meta).reduce(\n (acc, [key, value]) => {\n const pascalKey = key.charAt(0).toUpperCase() + key.slice(1);\n acc[pascalKey] = value;\n return acc;\n },\n {} as Record<string, any>,\n );\n\n message += ` ${JSON.stringify(pascalCaseMeta)}`;\n }\n\n return message;\n }),\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"static.logger.js","names":[],"sources":["../../../src/handlers/logging/static.logger.ts"],"sourcesContent":["import winston from \"winston\";\nimport process from \"node:process\";\nimport { AppConstants } from \"@/server.constants\";\nimport { createLokiLoggingTransport } from \"@/handlers/logging/loki-logging.transport\";\nimport { createFileLoggingTransport } from \"@/handlers/logging/file-logging.transport\";\nimport { isDevelopmentEnvironment } from \"@/utils/env.utils\";\n\nlet staticLogger: winston.Logger | null = null;\n\nexport interface StaticLoggerConfig {\n enableFileLogs: boolean;\n}\n\nconst levelMap: Record<string, string> = {\n error: \"ERR\",\n warn: \"WRN\",\n info: \"INF\",\n debug: \"DBG\",\n http: \"HTT\",\n verbose: \"VRB\",\n silly: \"SLY\",\n};\n\nexport const logContextClassProperty = \"class\";\n\nexport function getStaticLogger() {\n if (!staticLogger) {\n throw new Error(\"Logger not yet initialized.\");\n }\n\n return staticLogger;\n}\n\nexport function createStaticLogger(config: StaticLoggerConfig) {\n if (staticLogger) {\n return;\n }\n\n const isProd = process.env[AppConstants.NODE_ENV_KEY] === AppConstants.defaultProductionEnv;\n const isTest = process.env[AppConstants.NODE_ENV_KEY] === AppConstants.defaultTestEnv;\n\n const effectiveLogLevel = isProd || isTest ? \"warn\" : \"debug\";\n\n const lokiTransport = createLokiLoggingTransport({\n logLevel: effectiveLogLevel,\n });\n\n const extraWinstonTransports: winston.transport[] = [];\n if (lokiTransport) {\n extraWinstonTransports.push(lokiTransport);\n }\n\n const fileLoggerTransport = createFileLoggingTransport({\n enabled: config.enableFileLogs,\n isTest,\n });\n if (fileLoggerTransport) {\n extraWinstonTransports.push(fileLoggerTransport);\n }\n\n staticLogger = winston.createLogger({\n transports: [\n ...extraWinstonTransports,\n // Always include console transport\n new winston.transports.Console({\n level: effectiveLogLevel,\n format: winston.format.combine(\n ...(isDevelopmentEnvironment() && process.env[AppConstants.ENABLE_COLORED_LOGS_KEY] == \"true\"\n ? [\n // Store the original level before colorization\n winston.format((info) => {\n info.rawLevel = info.level;\n return info;\n })(),\n winston.format.colorize({\n colors: {\n error: \"red\",\n warn: \"yellow\",\n info: \"white\",\n debug: \"gray\",\n http: \"magenta\",\n verbose: \"cyan\",\n silly: \"gray\",\n },\n level: true,\n message: true, // Don't colorize the whole message\n all: false,\n }),\n winston.format.printf((info) => {\n // Format timestamp similar to Serilog (ISO with milliseconds)\n const now = new Date();\n const timestamp = `${now.toISOString().split(\"T\")[0]} ${now.toTimeString().split(\" \")[0]}.${now.getMilliseconds().toString().padStart(3, \"0\")}`;\n\n // Get colored level from winston\n // @ts-ignore\n const levelAbbr = levelMap[info.rawLevel] ?? info.rawLevel.substring(0, 3).toUpperCase();\n\n // Apply custom coloring using ANSI color codes\n const gray = \"\\x1b[90m\"; // Dim/gray\n const reset = (info.message as string).substring(0, 5) ?? \"\\x1b[0m\"; // Reset\n const numberRegex = /\\b\\d+\\b/g;\n\n // Apply purple color to numbers in the message\n const coloredMessage = (info.message as string).replace(\n numberRegex,\n (match) => `\\x1b[35m${match}${reset}`,\n );\n\n const serviceName = info[logContextClassProperty] ?? \"unknown\";\n\n // Format the log entry with gray timestamp and brackets, colored level, and message with purple numbers\n let logEntry = `${gray}[${timestamp} ${reset}${levelAbbr}${reset}${gray}]${reset} ${gray}[${reset}${serviceName}${gray}]${reset} ${coloredMessage}`;\n\n // Add metadata if present\n if (info.meta) {\n // Add metadata with numbers colorized in purple\n const metaString = JSON.stringify(info.meta);\n const coloredMeta = metaString.replace(numberRegex, (match) => `\\x1b[35m${match}${reset}`);\n logEntry += ` ${coloredMeta}`;\n }\n\n return logEntry;\n }),\n ]\n : []),\n ),\n }),\n ],\n format: winston.format.printf((info) => {\n // Format timestamp similar to Serilog (ISO with milliseconds)\n const now = new Date();\n const timestamp = `${now.toISOString().split(\"T\")[0]} ${now.toTimeString().split(\" \")[0]}.${now.getMilliseconds().toString().padStart(3, \"0\")}`;\n\n const levelAbbr = levelMap[info.level] || `[${info.level.substring(0, 3).toUpperCase()}]`;\n\n const serviceName = info[logContextClassProperty] ?? \"unknown\";\n let message = `[${timestamp} ${levelAbbr}] [${serviceName}] ${info.message}`;\n\n // Add metadata if present, without dash separator\n if (info.meta) {\n // Convert camelCase to PascalCase for C# style\n const pascalCaseMeta = Object.entries(info.meta).reduce(\n (acc, [key, value]) => {\n const pascalKey = key.charAt(0).toUpperCase() + key.slice(1);\n acc[pascalKey] = value;\n return acc;\n },\n {} as Record<string, any>,\n );\n\n message += ` ${JSON.stringify(pascalCaseMeta)}`;\n }\n\n return message;\n }),\n });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"validators.js","names":[],"sources":["../../src/handlers/validators.ts"],"sourcesContent":["import type { Request } from \"express\";\nimport { ValidationException } from \"@/exceptions/runtime.exceptions\";\nimport { ZodSchema } from \"zod\";\n\nexport async function validateInput<I, S>(data: I, zodSchema: ZodSchema<S>): Promise<S> {\n const result = await zodSchema.safeParseAsync(data);\n\n if (!result.success) {\n throw new ValidationException(result.error);\n }\n return result.data;\n}\n\nexport async function validateMiddleware<I, S>(req: Request<I>, zodSchema: ZodSchema<S>): Promise<S> {\n return validateInput(req.body, zodSchema);\n}\n"],"mappings":";;AAIA,eAAsB,cAAoB,MAAS,WAAqC;CACtF,MAAM,SAAS,MAAM,UAAU,eAAe,KAAK;AAEnD,KAAI,CAAC,OAAO,QACV,OAAM,IAAI,oBAAoB,OAAO,MAAM;AAE7C,QAAO,OAAO;;AAGhB,eAAsB,mBAAyB,KAAiB,WAAqC;AACnG,QAAO,cAAc,IAAI,MAAM,UAAU"}
1
+ {"version":3,"file":"validators.js","names":[],"sources":["../../src/handlers/validators.ts"],"sourcesContent":["import type { Request } from \"express\";\nimport { ValidationException } from \"@/exceptions/runtime.exceptions\";\nimport { ZodSchema } from \"zod\";\n\nexport async function validateInput<I, S>(data: I, zodSchema: ZodSchema<S>): Promise<S> {\n const result = await zodSchema.safeParseAsync(data);\n\n if (!result.success) {\n throw new ValidationException(result.error);\n }\n return result.data;\n}\n\nexport async function validateMiddleware<I, S>(req: Request<I>, zodSchema: ZodSchema<S>): Promise<S> {\n return validateInput(req.body, zodSchema);\n}\n"],"mappings":";;AAIA,eAAsB,cAAoB,MAAS,WAAqC;CACtF,MAAM,SAAS,MAAM,UAAU,eAAe,KAAK;CAEnD,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,oBAAoB,OAAO,MAAM;CAE7C,OAAO,OAAO;;AAGhB,eAAsB,mBAAyB,KAAiB,WAAqC;CACnG,OAAO,cAAc,IAAI,MAAM,UAAU"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["Logger"],"sources":["../src/index.ts"],"sourcesContent":["import { captureException, flush } from \"@sentry/node\";\nimport { setupEnvConfig } from \"./server.env\";\nimport { setupServer } from \"./server.core\";\nimport { DITokens } from \"./container.tokens\";\nimport { ServerHost } from \"@/server.host\";\nimport { LoggerService as Logger } from \"@/handlers/logger\";\nimport { createStaticLogger } from \"@/handlers/logging/static.logger\";\n\ncreateStaticLogger({ enableFileLogs: true });\nconst logger = new Logger(\"FDM-Environment\");\nlogger.log(\"✓ Parsed environment with (optional) .env file, created static logger\");\n\nsetupEnvConfig();\n\nprocess.on(\"uncaughtException\", (err) => {\n logger.error(\"Uncaught exception\", err);\n});\n\nprocess.on(\"unhandledRejection\", (reason) => {\n logger.error(\"Unhandled promise rejection\", reason);\n});\n\nsetupServer().then(({ httpServer, container }) => {\n container\n .resolve<ServerHost>(DITokens.serverHost)\n .boot(httpServer)\n .catch(async (e: Error) => {\n console.error(\"Server has crashed unintentionally - please report this\", e);\n\n captureException(e);\n await flush(0);\n process.exit(1);\n });\n});\n"],"mappings":";;;;;;;AAQA,mBAAmB,EAAE,gBAAgB,MAAM,CAAC;AAC5C,MAAM,SAAS,IAAIA,cAAO,kBAAkB;AAC5C,OAAO,IAAI,wEAAwE;AAEnF,gBAAgB;AAEhB,QAAQ,GAAG,sBAAsB,QAAQ;AACvC,QAAO,MAAM,sBAAsB,IAAI;EACvC;AAEF,QAAQ,GAAG,uBAAuB,WAAW;AAC3C,QAAO,MAAM,+BAA+B,OAAO;EACnD;AAEF,aAAa,CAAC,MAAM,EAAE,YAAY,gBAAgB;AAChD,WACG,QAAoB,SAAS,WAAW,CACxC,KAAK,WAAW,CAChB,MAAM,OAAO,MAAa;AACzB,UAAQ,MAAM,2DAA2D,EAAE;AAE3E,mBAAiB,EAAE;AACnB,QAAM,MAAM,EAAE;AACd,UAAQ,KAAK,EAAE;GACf;EACJ"}
1
+ {"version":3,"file":"index.js","names":["Logger"],"sources":["../src/index.ts"],"sourcesContent":["import { captureException, flush } from \"@sentry/node\";\nimport { setupEnvConfig } from \"./server.env\";\nimport { setupServer } from \"./server.core\";\nimport { DITokens } from \"./container.tokens\";\nimport { ServerHost } from \"@/server.host\";\nimport { LoggerService as Logger } from \"@/handlers/logger\";\nimport { createStaticLogger } from \"@/handlers/logging/static.logger\";\n\ncreateStaticLogger({ enableFileLogs: true });\nconst logger = new Logger(\"FDM-Environment\");\nlogger.log(\"✓ Parsed environment with (optional) .env file, created static logger\");\n\nsetupEnvConfig();\n\nprocess.on(\"uncaughtException\", (err) => {\n logger.error(\"Uncaught exception\", err);\n});\n\nprocess.on(\"unhandledRejection\", (reason) => {\n logger.error(\"Unhandled promise rejection\", reason);\n});\n\nsetupServer().then(({ httpServer, container }) => {\n container\n .resolve<ServerHost>(DITokens.serverHost)\n .boot(httpServer)\n .catch(async (e: Error) => {\n console.error(\"Server has crashed unintentionally - please report this\", e);\n\n captureException(e);\n await flush(0);\n process.exit(1);\n });\n});\n"],"mappings":";;;;;;;AAQA,mBAAmB,EAAE,gBAAgB,MAAM,CAAC;AAC5C,MAAM,SAAS,IAAIA,cAAO,kBAAkB;AAC5C,OAAO,IAAI,wEAAwE;AAEnF,gBAAgB;AAEhB,QAAQ,GAAG,sBAAsB,QAAQ;CACvC,OAAO,MAAM,sBAAsB,IAAI;EACvC;AAEF,QAAQ,GAAG,uBAAuB,WAAW;CAC3C,OAAO,MAAM,+BAA+B,OAAO;EACnD;AAEF,aAAa,CAAC,MAAM,EAAE,YAAY,gBAAgB;CAChD,UACG,QAAoB,SAAS,WAAW,CACxC,KAAK,WAAW,CAChB,MAAM,OAAO,MAAa;EACzB,QAAQ,MAAM,2DAA2D,EAAE;EAE3E,iBAAiB,EAAE;EACnB,MAAM,MAAM,EAAE;EACd,QAAQ,KAAK,EAAE;GACf;EACJ"}
@@ -0,0 +1,45 @@
1
+ import { Strategy } from "passport";
2
+ //#region src/middleware/api-key.strategy.ts
3
+ /**
4
+ * Passport strategy for API-key bearer auth. Slotted between JWT and Anonymous
5
+ * so a request with no auth header still falls through to anonymous.
6
+ *
7
+ * Important: we do NOT look up the bound user. The api_key_role join is the
8
+ * sole permission source for the request — keys are self-contained credentials,
9
+ * not user impersonation. `req.user.isApiKey === true` and `req.user.id = -1`
10
+ * are how downstream audit/branching code can detect an api-key principal.
11
+ */
12
+ var ApiKeyStrategy = class extends Strategy {
13
+ name = "api-key";
14
+ constructor(apiKeyService) {
15
+ super();
16
+ this.apiKeyService = apiKeyService;
17
+ }
18
+ async authenticate(req, _options) {
19
+ const header = req.headers.authorization;
20
+ const token = header?.startsWith("Bearer ") ? header.slice(7) : void 0;
21
+ if (!token || !this.apiKeyService.looksLikeApiKey(token)) return this.pass();
22
+ try {
23
+ const apiKey = await this.apiKeyService.verify(token);
24
+ if (!apiKey) return this.fail({ message: "Invalid API key" }, 401);
25
+ const principal = {
26
+ id: -1,
27
+ username: `api-key:${apiKey.id}`,
28
+ isDemoUser: false,
29
+ isRootUser: false,
30
+ isVerified: true,
31
+ needsPasswordChange: false,
32
+ createdAt: apiKey.createdAt,
33
+ roles: (apiKey.roles ?? []).map((r) => r.name),
34
+ isApiKey: true
35
+ };
36
+ return this.success(principal);
37
+ } catch (err) {
38
+ return this.error(err instanceof Error ? err : new Error(String(err)));
39
+ }
40
+ }
41
+ };
42
+ //#endregion
43
+ export { ApiKeyStrategy };
44
+
45
+ //# sourceMappingURL=api-key.strategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-key.strategy.js","names":[],"sources":["../../src/middleware/api-key.strategy.ts"],"sourcesContent":["import { Strategy } from \"passport\";\nimport type { Request } from \"express\";\nimport type { IApiKeyService } from \"@/services/interfaces/api-key.service.interface\";\nimport type { UserDto } from \"@/services/interfaces/user.dto\";\nimport type { RoleName } from \"@/constants/authorization.constants\";\n\n/**\n * Passport strategy for API-key bearer auth. Slotted between JWT and Anonymous\n * so a request with no auth header still falls through to anonymous.\n *\n * Important: we do NOT look up the bound user. The api_key_role join is the\n * sole permission source for the request — keys are self-contained credentials,\n * not user impersonation. `req.user.isApiKey === true` and `req.user.id = -1`\n * are how downstream audit/branching code can detect an api-key principal.\n */\nexport class ApiKeyStrategy extends Strategy {\n name = \"api-key\";\n\n constructor(private readonly apiKeyService: IApiKeyService) {\n super();\n }\n\n async authenticate(req: Request, _options?: any): Promise<void> {\n const header = req.headers.authorization;\n const token = header?.startsWith(\"Bearer \") ? header.slice(\"Bearer \".length) : undefined;\n if (!token || !this.apiKeyService.looksLikeApiKey(token)) {\n return this.pass();\n }\n\n try {\n const apiKey = await this.apiKeyService.verify(token);\n if (!apiKey) {\n return this.fail({ message: \"Invalid API key\" }, 401);\n }\n const principal: UserDto = {\n id: -1,\n username: `api-key:${apiKey.id}`,\n isDemoUser: false,\n isRootUser: false,\n isVerified: true,\n needsPasswordChange: false,\n createdAt: apiKey.createdAt,\n roles: (apiKey.roles ?? []).map((r) => r.name as RoleName),\n isApiKey: true,\n };\n return this.success(principal);\n } catch (err) {\n return this.error(err instanceof Error ? err : new Error(String(err)));\n }\n }\n}\n"],"mappings":";;;;;;;;;;;AAeA,IAAa,iBAAb,cAAoC,SAAS;CAC3C,OAAO;CAEP,YAAY,eAAgD;EAC1D,OAAO;EADoB,KAAA,gBAAA;;CAI7B,MAAM,aAAa,KAAc,UAA+B;EAC9D,MAAM,SAAS,IAAI,QAAQ;EAC3B,MAAM,QAAQ,QAAQ,WAAW,UAAU,GAAG,OAAO,MAAM,EAAiB,GAAG,KAAA;EAC/E,IAAI,CAAC,SAAS,CAAC,KAAK,cAAc,gBAAgB,MAAM,EACtD,OAAO,KAAK,MAAM;EAGpB,IAAI;GACF,MAAM,SAAS,MAAM,KAAK,cAAc,OAAO,MAAM;GACrD,IAAI,CAAC,QACH,OAAO,KAAK,KAAK,EAAE,SAAS,mBAAmB,EAAE,IAAI;GAEvD,MAAM,YAAqB;IACzB,IAAI;IACJ,UAAU,WAAW,OAAO;IAC5B,YAAY;IACZ,YAAY;IACZ,YAAY;IACZ,qBAAqB;IACrB,WAAW,OAAO;IAClB,QAAQ,OAAO,SAAS,EAAE,EAAE,KAAK,MAAM,EAAE,KAAiB;IAC1D,UAAU;IACX;GACD,OAAO,KAAK,QAAQ,UAAU;WACvB,KAAK;GACZ,OAAO,KAAK,MAAM,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,IAAI,CAAC,CAAC"}