@fdm-monster/server 2.1.0 → 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.
- package/.yarn/install-state.gz +0 -0
- package/.yarn/releases/{yarn-4.13.0.cjs → yarn-4.14.1.cjs} +288 -288
- package/.yarnrc.yml +5 -1
- package/RELEASE_NOTES.MD +14 -0
- package/dist/_virtual/{_@oxc-project_runtime@0.127.0 → _@oxc-project_runtime@0.129.0}/helpers/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.127.0 → _@oxc-project_runtime@0.129.0}/helpers/decorateMetadata.js +1 -1
- package/dist/consoles/typeorm-create.js.map +1 -1
- package/dist/consoles/typeorm-generate.js.map +1 -1
- package/dist/consoles/typeorm-migrate.js.map +1 -1
- package/dist/constants/authorization.constants.js.map +1 -1
- package/dist/container.js.map +1 -1
- package/dist/controllers/api-key.controller.js +2 -2
- package/dist/controllers/api-key.controller.js.map +1 -1
- package/dist/controllers/auth.controller.js +5 -3
- package/dist/controllers/auth.controller.js.map +1 -1
- package/dist/controllers/batch-call.controller.js +2 -2
- package/dist/controllers/batch-call.controller.js.map +1 -1
- package/dist/controllers/camera-stream.controller.js +2 -2
- package/dist/controllers/camera-stream.controller.js.map +1 -1
- package/dist/controllers/file-storage.controller.js +2 -2
- package/dist/controllers/file-storage.controller.js.map +1 -1
- package/dist/controllers/first-time-setup.controller.js +2 -2
- package/dist/controllers/first-time-setup.controller.js.map +1 -1
- package/dist/controllers/floor.controller.js +2 -2
- package/dist/controllers/floor.controller.js.map +1 -1
- package/dist/controllers/metrics.controller.js +2 -2
- package/dist/controllers/metrics.controller.js.map +1 -1
- package/dist/controllers/print-job.controller.js +2 -2
- package/dist/controllers/print-job.controller.js.map +1 -1
- package/dist/controllers/print-queue.controller.js +2 -2
- package/dist/controllers/print-queue.controller.js.map +1 -1
- package/dist/controllers/printer-files.controller.js +2 -2
- package/dist/controllers/printer-files.controller.js.map +1 -1
- package/dist/controllers/printer-maintenance-log.controller.js +2 -2
- package/dist/controllers/printer-maintenance-log.controller.js.map +1 -1
- package/dist/controllers/printer-settings.controller.js +2 -2
- package/dist/controllers/printer-settings.controller.js.map +1 -1
- package/dist/controllers/printer-tag.controller.js +2 -2
- package/dist/controllers/printer-tag.controller.js.map +1 -1
- package/dist/controllers/printer.controller.js +2 -2
- package/dist/controllers/printer.controller.js.map +1 -1
- package/dist/controllers/server-private.controller.js +2 -2
- package/dist/controllers/server-private.controller.js.map +1 -1
- package/dist/controllers/server-public.controller.js +2 -2
- package/dist/controllers/server-public.controller.js.map +1 -1
- package/dist/controllers/settings.controller.js +2 -2
- package/dist/controllers/settings.controller.js.map +1 -1
- package/dist/controllers/slicer-compat.controller.js +2 -2
- package/dist/controllers/slicer-compat.controller.js.map +1 -1
- package/dist/controllers/user.controller.js +2 -2
- package/dist/controllers/user.controller.js.map +1 -1
- package/dist/entities/api-key.entity.js +2 -2
- package/dist/entities/camera-stream.entity.js +2 -2
- package/dist/entities/floor-position.entity.js +2 -2
- package/dist/entities/floor.entity.js +2 -2
- package/dist/entities/print-job.entity.js +2 -2
- package/dist/entities/printer-maintenance-log.entity.js +2 -2
- package/dist/entities/printer-tag.entity.js +2 -2
- package/dist/entities/printer.entity.js +2 -2
- package/dist/entities/refresh-token.entity.js +2 -2
- package/dist/entities/role.entity.js +2 -2
- package/dist/entities/settings.entity.js +2 -2
- package/dist/entities/tag.entity.js +2 -2
- package/dist/entities/user-role.entity.js +2 -2
- package/dist/entities/user.entity.js +2 -2
- package/dist/exceptions/failed-dependency.exception.js.map +1 -1
- package/dist/exceptions/job.exceptions.js.map +1 -1
- package/dist/exceptions/runtime.exceptions.js.map +1 -1
- package/dist/handlers/event-emitter.js.map +1 -1
- package/dist/handlers/logger-factory.js.map +1 -1
- package/dist/handlers/logger.js.map +1 -1
- package/dist/handlers/logging/file-logging.transport.js +1 -2
- package/dist/handlers/logging/file-logging.transport.js.map +1 -1
- package/dist/handlers/logging/loki-logging.transport.js.map +1 -1
- package/dist/handlers/logging/static.logger.js.map +1 -1
- package/dist/handlers/validators.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/middleware/api-key.strategy.js.map +1 -1
- package/dist/middleware/authenticate.js.map +1 -1
- package/dist/middleware/database.js.map +1 -1
- package/dist/middleware/demo.middleware.js.map +1 -1
- package/dist/middleware/exception.filter.js.map +1 -1
- package/dist/middleware/global.middleware.js.map +1 -1
- package/dist/middleware/param-converter.middleware.js.map +1 -1
- package/dist/middleware/passport.js.map +1 -1
- package/dist/middleware/printer-resolver.js.map +1 -1
- package/dist/middleware/printer.js.map +1 -1
- package/dist/middleware/slicer-api-key.middleware.js.map +1 -1
- package/dist/middleware/socketio.middleware.js.map +1 -1
- package/dist/migrations/1706829146617-InitSqlite.js.map +1 -1
- package/dist/migrations/1707494762198-PrinterGroup.js.map +1 -1
- package/dist/migrations/1708465930665-ChangePrintCompletionDeletePrinterCascade.js.map +1 -1
- package/dist/migrations/1713300747465-ChangeRoleNameUnique.js.map +1 -1
- package/dist/migrations/1713897879622-AddPrinterType.js.map +1 -1
- package/dist/migrations/1720338804844-RemovePrinterFile.js.map +1 -1
- package/dist/migrations/1745141688926-AddPrinterUsernamePassword.js.map +1 -1
- package/dist/migrations/1766576698569-DropPermissions.js.map +1 -1
- package/dist/migrations/1767278216516-ChangeCameraPrinterOnDeleteSetNull.js.map +1 -1
- package/dist/migrations/1767279607392-DropCustomGcode.js.map +1 -1
- package/dist/migrations/1767291804417-DropPrintCompletions.js.map +1 -1
- package/dist/migrations/1767352862576-DropSettingsFileClean.js.map +1 -1
- package/dist/migrations/1767355639023-ChangeFloorLevelToOrder.js.map +1 -1
- package/dist/migrations/1767370191762-ChangeFloorNonUniqueOrder.js.map +1 -1
- package/dist/migrations/1767432108916-RenameGroupToTag.js.map +1 -1
- package/dist/migrations/1767451444137-AddPrintJob.js.map +1 -1
- package/dist/migrations/1767909428129-AddPrinterMaintenanceLog.js.map +1 -1
- package/dist/migrations/1778446203015-AddApiKey.js.map +1 -1
- package/dist/plugins/controllers-plugin.js.map +1 -1
- package/dist/server.constants.js +2 -1
- package/dist/server.constants.js.map +1 -1
- package/dist/server.core.js.map +1 -1
- package/dist/server.env.js.map +1 -1
- package/dist/server.host.js.map +1 -1
- package/dist/services/authentication/auth.service.js.map +1 -1
- package/dist/services/authentication/jwt.service.js.map +1 -1
- package/dist/services/bambu/bambu-ftp.adapter.js.map +1 -1
- package/dist/services/bambu/bambu-mqtt.adapter.js.map +1 -1
- package/dist/services/bambu/bambu.client.js.map +1 -1
- package/dist/services/bambu.api.js.map +1 -1
- package/dist/services/core/batch-call.service.js.map +1 -1
- package/dist/services/core/client-bundle.service.js.map +1 -1
- package/dist/services/core/config.service.js +4 -0
- package/dist/services/core/config.service.js.map +1 -1
- package/dist/services/core/cradle.service.js.map +1 -1
- package/dist/services/core/github.service.js.map +1 -1
- package/dist/services/core/http-client.factory.js.map +1 -1
- package/dist/services/core/logs-manager.service.js.map +1 -1
- package/dist/services/core/monsterpi.service.js.map +1 -1
- package/dist/services/core/multer.service.js.map +1 -1
- package/dist/services/core/server-release.service.js.map +1 -1
- package/dist/services/core/yaml.service.js.map +1 -1
- package/dist/services/file-analysis.service.js.map +1 -1
- package/dist/services/file-storage.service.js.map +1 -1
- package/dist/services/moonraker/moonraker-websocket.adapter.js.map +1 -1
- package/dist/services/moonraker/moonraker.client.js.map +1 -1
- package/dist/services/moonraker.api.js.map +1 -1
- package/dist/services/octoprint/octoprint-api.routes.js.map +1 -1
- package/dist/services/octoprint/octoprint-websocket.adapter.js.map +1 -1
- package/dist/services/octoprint/octoprint.client.js.map +1 -1
- package/dist/services/octoprint/utils/api.utils.js.map +1 -1
- package/dist/services/octoprint/utils/file.utils.js.map +1 -1
- package/dist/services/octoprint/utils/octoprint-http-client.builder.js.map +1 -1
- package/dist/services/octoprint.api.js.map +1 -1
- package/dist/services/orm/api-key.service.js.map +1 -1
- package/dist/services/orm/base.service.js.map +1 -1
- package/dist/services/orm/camera-stream.service.js.map +1 -1
- package/dist/services/orm/floor-position.service.js.map +1 -1
- package/dist/services/orm/floor.service.js.map +1 -1
- package/dist/services/orm/permission.service.js.map +1 -1
- package/dist/services/orm/print-job.service.js.map +1 -1
- package/dist/services/orm/printer-maintenance-log.service.js.map +1 -1
- package/dist/services/orm/printer-tag.service.js.map +1 -1
- package/dist/services/orm/printer.service.js.map +1 -1
- package/dist/services/orm/refresh-token.service.js.map +1 -1
- package/dist/services/orm/role.service.js.map +1 -1
- package/dist/services/orm/settings.service.js.map +1 -1
- package/dist/services/orm/user-role.service.js.map +1 -1
- package/dist/services/orm/user.service.js.map +1 -1
- package/dist/services/print-file-downloader.service.js.map +1 -1
- package/dist/services/print-queue.service.js.map +1 -1
- package/dist/services/printer-api.factory.js.map +1 -1
- package/dist/services/printer-api.interface.js.map +1 -1
- package/dist/services/prusa-link/prusa-link-http-polling.adapter.js.map +1 -1
- package/dist/services/prusa-link/prusa-link.api.js.map +1 -1
- package/dist/services/prusa-link/utils/digest-auth.util.js +19 -12
- package/dist/services/prusa-link/utils/digest-auth.util.js.map +1 -1
- package/dist/services/prusa-link/utils/prusa-link-http-client.builder.js +45 -11
- package/dist/services/prusa-link/utils/prusa-link-http-client.builder.js.map +1 -1
- package/dist/services/socket.factory.js.map +1 -1
- package/dist/services/task-manager.service.js.map +1 -1
- package/dist/services/typeorm/typeorm.service.js.map +1 -1
- package/dist/services/validators/printer-service.validation.js.map +1 -1
- package/dist/shared/default-http-client.builder.js.map +1 -1
- package/dist/shared/load-controllers.js.map +1 -1
- package/dist/shared/runtime-settings.migration.js.map +1 -1
- package/dist/shared/websocket-rpc-extended.adapter.js.map +1 -1
- package/dist/shared/websocket.adapter.js.map +1 -1
- package/dist/state/file-upload-tracker.cache.js.map +1 -1
- package/dist/state/floor.store.js.map +1 -1
- package/dist/state/printer-events.cache.js.map +1 -1
- package/dist/state/printer-socket.store.js.map +1 -1
- package/dist/state/printer-thumbnail.cache.js.map +1 -1
- package/dist/state/printer.cache.js.map +1 -1
- package/dist/state/settings.store.js.map +1 -1
- package/dist/state/socket-io.gateway.js.map +1 -1
- package/dist/state/test-printer-socket.store.js.map +1 -1
- package/dist/tasks/boot.task.js.map +1 -1
- package/dist/tasks/client-bundle.task.js.map +1 -1
- package/dist/tasks/print-job-analysis.task.js.map +1 -1
- package/dist/tasks/printer-websocket-restore.task.js.map +1 -1
- package/dist/tasks/printer-websocket.task.js.map +1 -1
- package/dist/tasks/socketio.task.js.map +1 -1
- package/dist/tasks/software-update.task.js.map +1 -1
- package/dist/tasks.js.map +1 -1
- package/dist/utils/array.util.js.map +1 -1
- package/dist/utils/bgcode/bgcode-thumbnail.parser.js.map +1 -1
- package/dist/utils/bgcode/bgcode.utils.js.map +1 -1
- package/dist/utils/bgcode/heatshrink-decoder.js.map +1 -1
- package/dist/utils/bgcode/png-encoder.js.map +1 -1
- package/dist/utils/bgcode/qoi-decoder.js.map +1 -1
- package/dist/utils/cache/key-diff.cache.js.map +1 -1
- package/dist/utils/correlation-token.util.js.map +1 -1
- package/dist/utils/crypto.utils.js.map +1 -1
- package/dist/utils/env.utils.js.map +1 -1
- package/dist/utils/error.utils.js.map +1 -1
- package/dist/utils/fs.utils.js.map +1 -1
- package/dist/utils/gcode.utils.js.map +1 -1
- package/dist/utils/image-dimensions.js.map +1 -1
- package/dist/utils/job-stats.util.js.map +1 -1
- package/dist/utils/normalize-url.js.map +1 -1
- package/dist/utils/parsers/3mf.parser.js.map +1 -1
- package/dist/utils/parsers/bgcode.parser.js.map +1 -1
- package/dist/utils/parsers/gcode.parser.js.map +1 -1
- package/dist/utils/pretty-print.utils.js.map +1 -1
- package/dist/utils/semver.utils.js.map +1 -1
- package/dist/utils/swagger/decorators.js.map +1 -1
- package/dist/utils/swagger/generator.js.map +1 -1
- package/dist/utils/swagger/swagger.js.map +1 -1
- package/dist/utils/thumbnail.util.js.map +1 -1
- package/dist/utils/time.utils.js.map +1 -1
- package/dist/utils/url.utils.js.map +1 -1
- package/package.json +10 -7
- package/packages/consoles/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metrics.controller.js","names":[],"sources":["../../src/controllers/metrics.controller.ts"],"sourcesContent":["import { GET, route } from \"awilix-express\";\nimport { register } from \"prom-client\";\nimport type { Request, Response } from \"express\";\nimport type { IConfigService } from \"@/services/core/config.service\";\nimport { AppConstants } from \"@/server.constants\";\n\n@route(\"/metrics\")\nexport class MetricsController {\n constructor(private readonly configService: IConfigService) {}\n\n @GET()\n @route(\"\")\n async getMetrics(req: Request, res: Response) {\n if (this.configService.get<string>(AppConstants.ENABLE_PROMETHEUS_METRICS) !== \"true\") {\n return res.status(404).send(\"Metrics disabled\");\n }\n res.setHeader(\"Content-Type\", register.contentType);\n res.end(await register.metrics());\n }\n}\n"],"mappings":";;;;;;;;AAOO,IAAA,oBAAA,MAAM,kBAAkB;CAC7B,YAAY,eAAgD;
|
|
1
|
+
{"version":3,"file":"metrics.controller.js","names":[],"sources":["../../src/controllers/metrics.controller.ts"],"sourcesContent":["import { GET, route } from \"awilix-express\";\nimport { register } from \"prom-client\";\nimport type { Request, Response } from \"express\";\nimport type { IConfigService } from \"@/services/core/config.service\";\nimport { AppConstants } from \"@/server.constants\";\n\n@route(\"/metrics\")\nexport class MetricsController {\n constructor(private readonly configService: IConfigService) {}\n\n @GET()\n @route(\"\")\n async getMetrics(req: Request, res: Response) {\n if (this.configService.get<string>(AppConstants.ENABLE_PROMETHEUS_METRICS) !== \"true\") {\n return res.status(404).send(\"Metrics disabled\");\n }\n res.setHeader(\"Content-Type\", register.contentType);\n res.end(await register.metrics());\n }\n}\n"],"mappings":";;;;;;;;AAOO,IAAA,oBAAA,MAAM,kBAAkB;CAC7B,YAAY,eAAgD;EAA/B,KAAA,gBAAA;;CAE7B,MAEM,WAAW,KAAc,KAAe;EAC5C,IAAI,KAAK,cAAc,IAAY,aAAa,0BAA0B,KAAK,QAC7E,OAAO,IAAI,OAAO,IAAI,CAAC,KAAK,mBAAmB;EAEjD,IAAI,UAAU,gBAAgB,SAAS,YAAY;EACnD,IAAI,IAAI,MAAM,SAAS,SAAS,CAAC;;;;CAPlC,KAAK;CACL,MAAM,GAAG;;;;;gCALX,MAAM,WAAW,EAAA,mBAAA,qBAAA,CAAA,OAAA,CAAA,CAAA,EAAA,kBAAA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __exportAll } from "../_virtual/_rolldown/runtime.js";
|
|
2
|
-
import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.
|
|
3
|
-
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
+
import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorateMetadata.js";
|
|
3
|
+
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorate.js";
|
|
4
4
|
import { NotFoundException } from "../exceptions/runtime.exceptions.js";
|
|
5
5
|
import { validateInput } from "../handlers/validators.js";
|
|
6
6
|
import { AppConstants } from "../server.constants.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"print-job.controller.js","names":[],"sources":["../../src/controllers/print-job.controller.ts"],"sourcesContent":["import { before, DELETE, GET, POST, route } from \"awilix-express\";\nimport { AppConstants } from \"@/server.constants\";\nimport type { Request, Response } from \"express\";\nimport { authorizeRoles, authenticate } from \"@/middleware/authenticate\";\nimport { ROLES } from \"@/constants/authorization.constants\";\nimport { searchJobsSchema, searchJobsPagedSchema } from \"@/services/validators/print-job.validation\";\nimport { validateInput } from \"@/handlers/validators\";\nimport { PrintJobService } from \"@/services/orm/print-job.service\";\nimport { FileAnalysisService } from \"@/services/file-analysis.service\";\nimport { FileStorageService } from \"@/services/file-storage.service\";\nimport type { ILoggerFactory } from \"@/handlers/logger-factory\";\nimport { LoggerService } from \"@/handlers/logger\";\nimport { ParamId } from \"@/middleware/param-converter.middleware\";\nimport { NotFoundException } from \"@/exceptions/runtime.exceptions\";\nimport { extractThumbnailsFromMetadata } from \"@/utils/thumbnail.util\";\n\n@route(AppConstants.apiRoute + \"/print-jobs\")\n@before([authenticate(), authorizeRoles([ROLES.ADMIN])])\nexport class PrintJobController {\n private readonly logger: LoggerService;\n\n constructor(\n loggerFactory: ILoggerFactory,\n private readonly printJobService: PrintJobService,\n private readonly fileAnalysisService: FileAnalysisService,\n private readonly fileStorageService: FileStorageService,\n ) {\n this.logger = loggerFactory(PrintJobController.name);\n }\n\n @GET()\n @route(\"/search\")\n async searchJobs(req: Request, res: Response) {\n const { searchPrinter, searchFile, startDate, endDate } = await validateInput(req.query, searchJobsSchema);\n\n const endDateObj = endDate ? this.toEndOfDay(new Date(endDate)) : undefined;\n\n const result = await this.printJobService.searchPrintJobs(\n searchPrinter,\n searchFile,\n startDate ? new Date(startDate) : undefined,\n endDateObj,\n );\n res.send(result);\n }\n\n @GET()\n @route(\"/search-paged\")\n async searchJobsPaged(req: Request, res: Response) {\n const { page, pageSize, searchPrinter, searchFile, startDate, endDate } = await validateInput(\n req.query,\n searchJobsPagedSchema,\n );\n\n const endDateObj = endDate ? this.toEndOfDay(new Date(endDate)) : undefined;\n\n const [items, count] = await this.printJobService.searchPrintJobsPaged(\n searchPrinter,\n searchFile,\n startDate ? new Date(startDate) : undefined,\n endDateObj,\n page,\n pageSize,\n );\n\n const itemsWithThumbnails = await Promise.all(\n items.map(async (job) => {\n let thumbnails: any[] = [];\n if (job.fileStorageId) {\n try {\n const metadata = await this.fileStorageService.loadMetadata(job.fileStorageId);\n thumbnails = extractThumbnailsFromMetadata(metadata);\n } catch {}\n }\n return { ...job, thumbnails };\n }),\n );\n\n res.send({ items: itemsWithThumbnails, count, pages: Math.ceil(count / pageSize) });\n }\n\n @GET()\n @route(\"/:id\")\n @before([ParamId(\"id\")])\n async getJob(req: Request, res: Response) {\n const jobId = req.local.id;\n\n const job = await this.printJobService.getJobByIdOrFail(jobId, [\"printer\"]);\n\n try {\n let thumbnails: any[] = [];\n if (job.fileStorageId) {\n const metadata = await this.fileStorageService.loadMetadata(job.fileStorageId);\n thumbnails = extractThumbnailsFromMetadata(metadata);\n }\n\n res.send({\n ...job,\n thumbnails,\n });\n } catch (error) {\n this.logger.error(`Failed to get job ${jobId}: ${error}`);\n res.status(500).send({ error: \"Failed to get job\" });\n }\n }\n\n @POST()\n @route(\"/:id/set-completed\")\n @before([ParamId(\"id\")])\n async setCompleted(req: Request, res: Response) {\n const jobId = req.local.id;\n const job = await this.printJobService.getJobByIdOrFail(jobId);\n\n try {\n if ([\"PENDING\", \"QUEUED\"].includes(job.status)) {\n res.status(400).send({\n error: 'Can only mark jobs which are not \"PENDING\" | \"QUEUED\" as completed',\n currentStatus: job.status,\n suggestion: \"This endpoint is for resolving jobs with unknown state\",\n });\n return;\n }\n\n const previousStatus = job.status;\n await this.printJobService.markAsCompleted(jobId);\n\n res.send({\n message: \"Job marked as completed\",\n jobId,\n previousStatus,\n newStatus: \"COMPLETED\",\n });\n } catch (error) {\n this.logger.error(`Failed to mark job ${jobId} as completed: ${error}`);\n res.status(500).send({\n error: \"Failed to update job status\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n\n @POST()\n @route(\"/:id/set-failed\")\n @before([ParamId(\"id\")])\n async setFailed(req: Request, res: Response) {\n const jobId = req.local.id;\n const job = await this.printJobService.getJobByIdOrFail(jobId);\n\n const previousStatus = job.status;\n\n if (![\"UNKNOWN\", \"PRINTING\", \"CANCELLED\", \"COMPLETED\"].includes(job.status)) {\n res.status(400).send({\n error: \"Can only mark UNKNOWN, PRINTING, CANCELLED, or COMPLETED jobs as failed\",\n currentStatus: job.status,\n });\n return;\n }\n\n try {\n await this.printJobService.markAsFailed(jobId, \"Manually marked as failed by user\");\n\n res.send({\n message: \"Job marked as failed\",\n jobId,\n previousStatus,\n newStatus: \"FAILED\",\n });\n } catch (error) {\n this.logger.error(`Failed to mark job ${jobId} as failed: ${error}`);\n res.status(500).send({\n error: \"Failed to update job status\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n\n @POST()\n @route(\"/:id/set-cancelled\")\n @before([ParamId(\"id\")])\n async setCancelled(req: Request, res: Response) {\n const jobId = req.local.id;\n const job = await this.printJobService.getJobByIdOrFail(jobId);\n\n const previousStatus = job.status;\n\n if (![\"UNKNOWN\", \"PRINTING\", \"PAUSED\"].includes(job.status)) {\n res.status(400).send({\n error: \"Can only mark UNKNOWN, PRINTING, or PAUSED jobs as cancelled\",\n currentStatus: job.status,\n });\n return;\n }\n\n try {\n await this.printJobService.markAsCancelled(jobId, \"Manually marked as cancelled by user\");\n\n res.send({\n message: \"Job marked as cancelled\",\n jobId,\n previousStatus,\n newStatus: \"CANCELLED\",\n });\n } catch (error) {\n this.logger.error(`Failed to mark job ${jobId} as cancelled: ${error}`);\n res.status(500).send({\n error: \"Failed to update job status\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n\n @POST()\n @route(\"/:id/set-unknown\")\n @before([ParamId(\"id\")])\n async setUnknown(req: Request, res: Response) {\n const jobId = req.local.id;\n const job = await this.printJobService.getJobByIdOrFail(jobId);\n\n const previousStatus = job.status;\n\n if (![\"PRINTING\", \"PAUSED\"].includes(job.status)) {\n res.status(400).send({\n error: \"Can only mark PRINTING or PAUSED jobs as unknown\",\n currentStatus: job.status,\n suggestion: \"This endpoint is for marking jobs with uncertain state (e.g., after connection loss)\",\n });\n return;\n }\n\n try {\n await this.printJobService.markAsUnknown(jobId);\n\n res.send({\n message: \"Job marked as unknown\",\n jobId,\n previousStatus,\n newStatus: \"UNKNOWN\",\n });\n } catch (error) {\n this.logger.error(`Failed to mark job ${jobId} as unknown: ${error}`);\n res.status(500).send({\n error: \"Failed to update job status\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n\n @POST()\n @route(\"/:id/re-analyze\")\n @before([ParamId(\"id\")])\n async reAnalyzeJob(req: Request, res: Response) {\n const jobId = req.local.id;\n const job = await this.printJobService.getJobByIdOrFail(jobId);\n\n this.logger.log(`Re-analyzing job ${jobId}`);\n\n try {\n if (!job.fileStorageId) {\n this.logger.log(`Job ${jobId} has no fileStorageId - triggering download and analysis`);\n await this.printJobService.triggerFileAnalysis(jobId);\n\n res.send({\n message: \"File download and analysis triggered (async)\",\n jobId,\n status: \"pending\",\n });\n return;\n }\n\n const filePath = this.fileStorageService.getFilePath(job.fileStorageId);\n const exists = await this.fileAnalysisService.needsAnalysis(filePath);\n if (!exists) {\n throw new NotFoundException(`File not found in storage: ${job.fileStorageId}`);\n }\n\n this.logger.log(`Re-analyzing file for job ${jobId}: ${filePath}`);\n\n job.analysisState = \"ANALYZING\";\n await this.printJobService.updateJob(job);\n\n const { metadata, thumbnails } = await this.fileAnalysisService.analyzeFile(filePath);\n\n let thumbnailMetadata: any[] = [];\n if (thumbnails && thumbnails.length > 0) {\n thumbnailMetadata = await this.fileStorageService.saveThumbnails(job.fileStorageId, thumbnails);\n this.logger.log(`Saved ${thumbnailMetadata.length} thumbnail(s) for job ${jobId}`);\n }\n\n await this.printJobService.handleFileAnalyzed(jobId, metadata, thumbnails);\n\n const fileHash = job.fileHash || undefined;\n await this.fileStorageService.saveMetadata(\n job.fileStorageId,\n metadata,\n fileHash,\n job.fileName,\n thumbnailMetadata,\n );\n\n this.logger.log(`Successfully re-analyzed job ${jobId}`);\n\n res.send({\n message: \"File re-analyzed successfully\",\n jobId,\n status: \"analyzed\",\n metadata,\n thumbnailCount: thumbnails?.length || 0,\n });\n } catch (error) {\n this.logger.error(`Failed to re-analyze job ${jobId}: ${error}`);\n res.status(500).send({\n error: \"Re-analysis failed\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n\n @DELETE()\n @route(\"/:id\")\n @before([ParamId(\"id\")])\n async deleteJob(req: Request, res: Response) {\n const jobId = req.local.id;\n const job = await this.printJobService.getJobByIdOrFail(jobId);\n const deleteFileParam = req.query.deleteFile === \"true\";\n\n if (job.status === \"PRINTING\" || job.status === \"PAUSED\") {\n res.status(400).send({\n error: \"Cannot delete active print job\",\n status: job.status,\n suggestion: \"Wait for print to complete or cancel it first\",\n });\n return;\n }\n\n try {\n const fileStorageId = job.fileStorageId;\n const fileName = job.fileName;\n\n await this.printJobService.deleteJob(job);\n this.logger.log(`Deleted job ${jobId}: ${fileName}`);\n\n if (fileStorageId && deleteFileParam) {\n const otherJobs = await this.printJobService.countJobsReferencingFile(fileStorageId);\n\n if (otherJobs === 0) {\n try {\n await this.fileStorageService.deleteFile(fileStorageId);\n this.logger.log(`Deleted file as requested: ${fileStorageId}`);\n res.send({\n message: \"Job and associated file deleted\",\n jobId,\n fileDeleted: true,\n });\n } catch (error) {\n this.logger.warn(`Failed to delete file ${fileStorageId}: ${error}`);\n res.send({\n message: \"Job deleted, but file deletion failed\",\n jobId,\n fileDeleted: false,\n });\n }\n } else {\n this.logger.log(`File ${fileStorageId} still referenced by ${otherJobs} other job(s) - keeping file`);\n res.send({\n message: \"Job deleted (file kept - still used by other jobs)\",\n jobId,\n fileDeleted: false,\n remainingReferences: otherJobs,\n });\n }\n } else {\n res.send({\n message: \"Job deleted\",\n jobId,\n fileDeleted: false,\n });\n }\n } catch (error) {\n this.logger.error(`Failed to delete job ${jobId}: ${error}`);\n res.status(500).send({\n error: \"Delete failed\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n\n private toEndOfDay(date: Date): Date {\n const endOfDay = new Date(date);\n endOfDay.setHours(23, 59, 59, 999);\n return endOfDay;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"print-job.controller.js","names":[],"sources":["../../src/controllers/print-job.controller.ts"],"sourcesContent":["import { before, DELETE, GET, POST, route } from \"awilix-express\";\nimport { AppConstants } from \"@/server.constants\";\nimport type { Request, Response } from \"express\";\nimport { authorizeRoles, authenticate } from \"@/middleware/authenticate\";\nimport { ROLES } from \"@/constants/authorization.constants\";\nimport { searchJobsSchema, searchJobsPagedSchema } from \"@/services/validators/print-job.validation\";\nimport { validateInput } from \"@/handlers/validators\";\nimport { PrintJobService } from \"@/services/orm/print-job.service\";\nimport { FileAnalysisService } from \"@/services/file-analysis.service\";\nimport { FileStorageService } from \"@/services/file-storage.service\";\nimport type { ILoggerFactory } from \"@/handlers/logger-factory\";\nimport { LoggerService } from \"@/handlers/logger\";\nimport { ParamId } from \"@/middleware/param-converter.middleware\";\nimport { NotFoundException } from \"@/exceptions/runtime.exceptions\";\nimport { extractThumbnailsFromMetadata } from \"@/utils/thumbnail.util\";\n\n@route(AppConstants.apiRoute + \"/print-jobs\")\n@before([authenticate(), authorizeRoles([ROLES.ADMIN])])\nexport class PrintJobController {\n private readonly logger: LoggerService;\n\n constructor(\n loggerFactory: ILoggerFactory,\n private readonly printJobService: PrintJobService,\n private readonly fileAnalysisService: FileAnalysisService,\n private readonly fileStorageService: FileStorageService,\n ) {\n this.logger = loggerFactory(PrintJobController.name);\n }\n\n @GET()\n @route(\"/search\")\n async searchJobs(req: Request, res: Response) {\n const { searchPrinter, searchFile, startDate, endDate } = await validateInput(req.query, searchJobsSchema);\n\n const endDateObj = endDate ? this.toEndOfDay(new Date(endDate)) : undefined;\n\n const result = await this.printJobService.searchPrintJobs(\n searchPrinter,\n searchFile,\n startDate ? new Date(startDate) : undefined,\n endDateObj,\n );\n res.send(result);\n }\n\n @GET()\n @route(\"/search-paged\")\n async searchJobsPaged(req: Request, res: Response) {\n const { page, pageSize, searchPrinter, searchFile, startDate, endDate } = await validateInput(\n req.query,\n searchJobsPagedSchema,\n );\n\n const endDateObj = endDate ? this.toEndOfDay(new Date(endDate)) : undefined;\n\n const [items, count] = await this.printJobService.searchPrintJobsPaged(\n searchPrinter,\n searchFile,\n startDate ? new Date(startDate) : undefined,\n endDateObj,\n page,\n pageSize,\n );\n\n const itemsWithThumbnails = await Promise.all(\n items.map(async (job) => {\n let thumbnails: any[] = [];\n if (job.fileStorageId) {\n try {\n const metadata = await this.fileStorageService.loadMetadata(job.fileStorageId);\n thumbnails = extractThumbnailsFromMetadata(metadata);\n } catch {}\n }\n return { ...job, thumbnails };\n }),\n );\n\n res.send({ items: itemsWithThumbnails, count, pages: Math.ceil(count / pageSize) });\n }\n\n @GET()\n @route(\"/:id\")\n @before([ParamId(\"id\")])\n async getJob(req: Request, res: Response) {\n const jobId = req.local.id;\n\n const job = await this.printJobService.getJobByIdOrFail(jobId, [\"printer\"]);\n\n try {\n let thumbnails: any[] = [];\n if (job.fileStorageId) {\n const metadata = await this.fileStorageService.loadMetadata(job.fileStorageId);\n thumbnails = extractThumbnailsFromMetadata(metadata);\n }\n\n res.send({\n ...job,\n thumbnails,\n });\n } catch (error) {\n this.logger.error(`Failed to get job ${jobId}: ${error}`);\n res.status(500).send({ error: \"Failed to get job\" });\n }\n }\n\n @POST()\n @route(\"/:id/set-completed\")\n @before([ParamId(\"id\")])\n async setCompleted(req: Request, res: Response) {\n const jobId = req.local.id;\n const job = await this.printJobService.getJobByIdOrFail(jobId);\n\n try {\n if ([\"PENDING\", \"QUEUED\"].includes(job.status)) {\n res.status(400).send({\n error: 'Can only mark jobs which are not \"PENDING\" | \"QUEUED\" as completed',\n currentStatus: job.status,\n suggestion: \"This endpoint is for resolving jobs with unknown state\",\n });\n return;\n }\n\n const previousStatus = job.status;\n await this.printJobService.markAsCompleted(jobId);\n\n res.send({\n message: \"Job marked as completed\",\n jobId,\n previousStatus,\n newStatus: \"COMPLETED\",\n });\n } catch (error) {\n this.logger.error(`Failed to mark job ${jobId} as completed: ${error}`);\n res.status(500).send({\n error: \"Failed to update job status\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n\n @POST()\n @route(\"/:id/set-failed\")\n @before([ParamId(\"id\")])\n async setFailed(req: Request, res: Response) {\n const jobId = req.local.id;\n const job = await this.printJobService.getJobByIdOrFail(jobId);\n\n const previousStatus = job.status;\n\n if (![\"UNKNOWN\", \"PRINTING\", \"CANCELLED\", \"COMPLETED\"].includes(job.status)) {\n res.status(400).send({\n error: \"Can only mark UNKNOWN, PRINTING, CANCELLED, or COMPLETED jobs as failed\",\n currentStatus: job.status,\n });\n return;\n }\n\n try {\n await this.printJobService.markAsFailed(jobId, \"Manually marked as failed by user\");\n\n res.send({\n message: \"Job marked as failed\",\n jobId,\n previousStatus,\n newStatus: \"FAILED\",\n });\n } catch (error) {\n this.logger.error(`Failed to mark job ${jobId} as failed: ${error}`);\n res.status(500).send({\n error: \"Failed to update job status\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n\n @POST()\n @route(\"/:id/set-cancelled\")\n @before([ParamId(\"id\")])\n async setCancelled(req: Request, res: Response) {\n const jobId = req.local.id;\n const job = await this.printJobService.getJobByIdOrFail(jobId);\n\n const previousStatus = job.status;\n\n if (![\"UNKNOWN\", \"PRINTING\", \"PAUSED\"].includes(job.status)) {\n res.status(400).send({\n error: \"Can only mark UNKNOWN, PRINTING, or PAUSED jobs as cancelled\",\n currentStatus: job.status,\n });\n return;\n }\n\n try {\n await this.printJobService.markAsCancelled(jobId, \"Manually marked as cancelled by user\");\n\n res.send({\n message: \"Job marked as cancelled\",\n jobId,\n previousStatus,\n newStatus: \"CANCELLED\",\n });\n } catch (error) {\n this.logger.error(`Failed to mark job ${jobId} as cancelled: ${error}`);\n res.status(500).send({\n error: \"Failed to update job status\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n\n @POST()\n @route(\"/:id/set-unknown\")\n @before([ParamId(\"id\")])\n async setUnknown(req: Request, res: Response) {\n const jobId = req.local.id;\n const job = await this.printJobService.getJobByIdOrFail(jobId);\n\n const previousStatus = job.status;\n\n if (![\"PRINTING\", \"PAUSED\"].includes(job.status)) {\n res.status(400).send({\n error: \"Can only mark PRINTING or PAUSED jobs as unknown\",\n currentStatus: job.status,\n suggestion: \"This endpoint is for marking jobs with uncertain state (e.g., after connection loss)\",\n });\n return;\n }\n\n try {\n await this.printJobService.markAsUnknown(jobId);\n\n res.send({\n message: \"Job marked as unknown\",\n jobId,\n previousStatus,\n newStatus: \"UNKNOWN\",\n });\n } catch (error) {\n this.logger.error(`Failed to mark job ${jobId} as unknown: ${error}`);\n res.status(500).send({\n error: \"Failed to update job status\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n\n @POST()\n @route(\"/:id/re-analyze\")\n @before([ParamId(\"id\")])\n async reAnalyzeJob(req: Request, res: Response) {\n const jobId = req.local.id;\n const job = await this.printJobService.getJobByIdOrFail(jobId);\n\n this.logger.log(`Re-analyzing job ${jobId}`);\n\n try {\n if (!job.fileStorageId) {\n this.logger.log(`Job ${jobId} has no fileStorageId - triggering download and analysis`);\n await this.printJobService.triggerFileAnalysis(jobId);\n\n res.send({\n message: \"File download and analysis triggered (async)\",\n jobId,\n status: \"pending\",\n });\n return;\n }\n\n const filePath = this.fileStorageService.getFilePath(job.fileStorageId);\n const exists = await this.fileAnalysisService.needsAnalysis(filePath);\n if (!exists) {\n throw new NotFoundException(`File not found in storage: ${job.fileStorageId}`);\n }\n\n this.logger.log(`Re-analyzing file for job ${jobId}: ${filePath}`);\n\n job.analysisState = \"ANALYZING\";\n await this.printJobService.updateJob(job);\n\n const { metadata, thumbnails } = await this.fileAnalysisService.analyzeFile(filePath);\n\n let thumbnailMetadata: any[] = [];\n if (thumbnails && thumbnails.length > 0) {\n thumbnailMetadata = await this.fileStorageService.saveThumbnails(job.fileStorageId, thumbnails);\n this.logger.log(`Saved ${thumbnailMetadata.length} thumbnail(s) for job ${jobId}`);\n }\n\n await this.printJobService.handleFileAnalyzed(jobId, metadata, thumbnails);\n\n const fileHash = job.fileHash || undefined;\n await this.fileStorageService.saveMetadata(\n job.fileStorageId,\n metadata,\n fileHash,\n job.fileName,\n thumbnailMetadata,\n );\n\n this.logger.log(`Successfully re-analyzed job ${jobId}`);\n\n res.send({\n message: \"File re-analyzed successfully\",\n jobId,\n status: \"analyzed\",\n metadata,\n thumbnailCount: thumbnails?.length || 0,\n });\n } catch (error) {\n this.logger.error(`Failed to re-analyze job ${jobId}: ${error}`);\n res.status(500).send({\n error: \"Re-analysis failed\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n\n @DELETE()\n @route(\"/:id\")\n @before([ParamId(\"id\")])\n async deleteJob(req: Request, res: Response) {\n const jobId = req.local.id;\n const job = await this.printJobService.getJobByIdOrFail(jobId);\n const deleteFileParam = req.query.deleteFile === \"true\";\n\n if (job.status === \"PRINTING\" || job.status === \"PAUSED\") {\n res.status(400).send({\n error: \"Cannot delete active print job\",\n status: job.status,\n suggestion: \"Wait for print to complete or cancel it first\",\n });\n return;\n }\n\n try {\n const fileStorageId = job.fileStorageId;\n const fileName = job.fileName;\n\n await this.printJobService.deleteJob(job);\n this.logger.log(`Deleted job ${jobId}: ${fileName}`);\n\n if (fileStorageId && deleteFileParam) {\n const otherJobs = await this.printJobService.countJobsReferencingFile(fileStorageId);\n\n if (otherJobs === 0) {\n try {\n await this.fileStorageService.deleteFile(fileStorageId);\n this.logger.log(`Deleted file as requested: ${fileStorageId}`);\n res.send({\n message: \"Job and associated file deleted\",\n jobId,\n fileDeleted: true,\n });\n } catch (error) {\n this.logger.warn(`Failed to delete file ${fileStorageId}: ${error}`);\n res.send({\n message: \"Job deleted, but file deletion failed\",\n jobId,\n fileDeleted: false,\n });\n }\n } else {\n this.logger.log(`File ${fileStorageId} still referenced by ${otherJobs} other job(s) - keeping file`);\n res.send({\n message: \"Job deleted (file kept - still used by other jobs)\",\n jobId,\n fileDeleted: false,\n remainingReferences: otherJobs,\n });\n }\n } else {\n res.send({\n message: \"Job deleted\",\n jobId,\n fileDeleted: false,\n });\n }\n } catch (error) {\n this.logger.error(`Failed to delete job ${jobId}: ${error}`);\n res.status(500).send({\n error: \"Delete failed\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n\n private toEndOfDay(date: Date): Date {\n const endOfDay = new Date(date);\n endOfDay.setHours(23, 59, 59, 999);\n return endOfDay;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __exportAll } from "../_virtual/_rolldown/runtime.js";
|
|
2
|
-
import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.
|
|
3
|
-
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
+
import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorateMetadata.js";
|
|
3
|
+
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorate.js";
|
|
4
4
|
import { NotFoundException } from "../exceptions/runtime.exceptions.js";
|
|
5
5
|
import { AppConstants } from "../server.constants.js";
|
|
6
6
|
import { ROLES } from "../constants/authorization.constants.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"print-queue.controller.js","names":[],"sources":["../../src/controllers/print-queue.controller.ts"],"sourcesContent":["import { before, DELETE, GET, POST, PUT, route } from \"awilix-express\";\nimport { AppConstants } from \"@/server.constants\";\nimport type { Request, Response } from \"express\";\nimport { authorizeRoles, authenticate } from \"@/middleware/authenticate\";\nimport { ROLES } from \"@/constants/authorization.constants\";\nimport { PrintQueueService } from \"@/services/print-queue.service\";\nimport { PrintJobService } from \"@/services/orm/print-job.service\";\nimport { FileStorageService } from \"@/services/file-storage.service\";\nimport { PrinterCache } from \"@/state/printer.cache\";\nimport type { ILoggerFactory } from \"@/handlers/logger-factory\";\nimport { LoggerService } from \"@/handlers/logger\";\nimport { ParamId } from \"@/middleware/param-converter.middleware\";\nimport { NotFoundException } from \"@/exceptions/runtime.exceptions\";\n\n@route(AppConstants.apiRoute + \"/print-queue\")\n@before([authenticate(), authorizeRoles([ROLES.ADMIN, ROLES.OPERATOR])])\nexport class PrintQueueController {\n private readonly logger: LoggerService;\n\n constructor(\n loggerFactory: ILoggerFactory,\n private readonly printQueueService: PrintQueueService,\n private readonly printJobService: PrintJobService,\n private readonly fileStorageService: FileStorageService,\n private readonly printerCache: PrinterCache,\n ) {\n this.logger = loggerFactory(PrintQueueController.name);\n }\n\n @GET()\n async getGlobalQueue(req: Request, res: Response) {\n try {\n const page = Number.parseInt(req.query.page as string) || 1;\n const pageSize = Number.parseInt(req.query.pageSize as string) || 50;\n\n if (page < 1 || pageSize < 1 || pageSize > 200) {\n res.status(400).send({ error: \"Invalid page or pageSize parameters\" });\n return;\n }\n\n const [jobs, totalCount] = await this.printQueueService.getGlobalQueuePaged(page, pageSize);\n\n const queueItems = jobs.map((job) => ({\n jobId: job.id,\n fileName: job.fileName,\n printerId: job.printerId,\n printerName: job.printerName || job.printer?.name,\n queuePosition: job.queuePosition,\n status: job.status,\n createdAt: job.createdAt,\n estimatedTimeSeconds: (job.metadata as any)?.gcodePrintTimeSeconds,\n filamentGrams: (job.metadata as any)?.filamentUsedGrams,\n }));\n\n res.send({\n items: queueItems,\n page,\n pageSize,\n totalCount,\n totalPages: Math.ceil(totalCount / pageSize),\n });\n } catch (error) {\n this.logger.error(`Failed to get global queue: ${error}`);\n res.status(500).send({ error: \"Failed to get global queue\" });\n }\n }\n\n @GET()\n @route(\"/:printerId\")\n @before([ParamId(\"printerId\")])\n async getQueue(req: Request, res: Response) {\n const printerId = req.local.printerId;\n\n try {\n const queue = await this.printQueueService.getQueue(printerId);\n res.send({\n printerId,\n queue,\n count: queue.length,\n });\n } catch (error) {\n this.logger.error(`Failed to get queue for printer ${printerId}: ${error}`);\n res.status(500).send({ error: \"Failed to get queue\" });\n }\n }\n\n @POST()\n @route(\"/:printerId/add/:jobId\")\n @before([ParamId(\"printerId\"), ParamId(\"jobId\")])\n async addToQueue(req: Request, res: Response) {\n const printerId = req.local.printerId;\n const jobId = req.local.jobId;\n const position = req.body.position === undefined ? undefined : Number.parseInt(req.body.position);\n\n try {\n await this.printQueueService.addToQueue(printerId, jobId, position);\n const queue = await this.printQueueService.getQueue(printerId);\n\n res.send({\n message: \"Job added to queue\",\n printerId,\n jobId,\n position,\n queue,\n });\n } catch (error) {\n this.logger.error(`Failed to add job ${jobId} to queue: ${error}`);\n res.status(500).send({\n error: \"Failed to add to queue\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n\n @PUT()\n @route(\"/:printerId/reorder\")\n @before([ParamId(\"printerId\")])\n async reorderQueue(req: Request, res: Response) {\n const printerId = req.local.printerId;\n const jobIds = req.body.jobIds;\n\n if (!Array.isArray(jobIds)) {\n res.status(400).send({ error: \"jobIds must be an array\" });\n return;\n }\n\n try {\n await this.printQueueService.reorderQueue(printerId, jobIds);\n const queue = await this.printQueueService.getQueue(printerId);\n\n res.send({\n message: \"Queue reordered\",\n printerId,\n queue,\n });\n } catch (error) {\n this.logger.error(`Failed to reorder queue for printer ${printerId}: ${error}`);\n res.status(500).send({\n error: \"Failed to reorder queue\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n\n @DELETE()\n @route(\"/:printerId/clear\")\n @before([ParamId(\"printerId\")])\n async clearQueue(req: Request, res: Response) {\n const printerId = req.local.printerId;\n\n try {\n await this.printQueueService.clearQueue(printerId);\n\n res.send({\n message: \"Queue cleared\",\n printerId,\n });\n } catch (error) {\n this.logger.error(`Failed to clear queue for printer ${printerId}: ${error}`);\n res.status(500).send({\n error: \"Failed to clear queue\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n\n @DELETE()\n @route(\"/:printerId/:jobId\")\n @before([ParamId(\"printerId\"), ParamId(\"jobId\")])\n async removeFromQueue(req: Request, res: Response) {\n const printerId = req.local.printerId;\n const jobId = req.local.jobId;\n\n try {\n await this.printQueueService.removeFromQueue(jobId);\n const queue = await this.printQueueService.getQueue(printerId);\n\n res.send({\n message: \"Job removed from queue\",\n printerId,\n jobId,\n queue,\n });\n } catch (error) {\n this.logger.error(`Failed to remove job ${jobId} from queue: ${error}`);\n res.status(500).send({\n error: \"Failed to remove from queue\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n\n @GET()\n @route(\"/:printerId/next\")\n @before([ParamId(\"printerId\")])\n async getNextInQueue(req: Request, res: Response) {\n const printerId = req.local.printerId;\n\n try {\n const nextJob = await this.printQueueService.getNextInQueue(printerId);\n\n res.send({\n printerId,\n nextJob,\n });\n } catch (error) {\n this.logger.error(`Failed to get next job for printer ${printerId}: ${error}`);\n res.status(500).send({ error: \"Failed to get next job\" });\n }\n }\n\n @POST()\n @route(\"/:printerId/process\")\n @before([ParamId(\"printerId\")])\n async processQueue(req: Request, res: Response) {\n const printerId = req.local.printerId;\n\n try {\n const nextJob = await this.printQueueService.processQueue(printerId);\n\n if (!nextJob) {\n res.send({\n message: \"Queue is empty\",\n printerId,\n nextJob: null,\n });\n return;\n }\n\n res.send({\n message: \"Processing next job in queue\",\n printerId,\n nextJob,\n });\n } catch (error) {\n this.logger.error(`Failed to process queue for printer ${printerId}: ${error}`);\n res.status(500).send({\n error: \"Failed to process queue\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n\n @POST()\n @route(\"/:printerId/from-file\")\n @before([ParamId(\"printerId\")])\n async createJobFromFile(req: Request, res: Response) {\n const printerId = req.local.printerId;\n const { fileStorageId, addToQueue = true, position } = req.body;\n\n if (!fileStorageId) {\n res.status(400).send({ error: \"fileStorageId is required\" });\n return;\n }\n\n try {\n const fileExists = await this.fileStorageService.fileExists(fileStorageId);\n if (!fileExists) {\n throw new NotFoundException(\"File not found in storage\");\n }\n\n const metadata = await this.fileStorageService.loadMetadata(fileStorageId);\n if (!metadata) {\n res.status(400).send({ error: \"File has no metadata. Please analyze the file first.\" });\n return;\n }\n\n const printer = await this.printerCache.getCachedPrinterOrThrowAsync(printerId);\n\n const job = await this.printJobService.createPendingJob(\n printerId,\n metadata._originalFileName || metadata.fileName || \"Unknown\",\n metadata,\n printer.name,\n );\n\n job.fileStorageId = fileStorageId;\n job.fileHash = metadata._fileHash;\n job.analysisState = \"ANALYZED\";\n job.analyzedAt = new Date();\n\n if (metadata.fileFormat) {\n job.fileFormat = metadata.fileFormat;\n }\n\n await this.printJobService.updateJob(job);\n\n if (addToQueue) {\n await this.printQueueService.addToQueue(printerId, job.id, position);\n }\n\n this.logger.log(\n `Created job ${job.id} from file storage ${fileStorageId} for printer ${printerId}${addToQueue ? \" and added to queue\" : \"\"}`,\n );\n\n res.send({\n id: job.id,\n printerId: job.printerId,\n printerName: job.printerName,\n fileName: job.fileName,\n fileStorageId: job.fileStorageId,\n status: job.status,\n analysisState: job.analysisState,\n createdAt: job.createdAt,\n addedToQueue: addToQueue,\n });\n } catch (error) {\n this.logger.error(`Failed to create job from file ${fileStorageId}: ${error}`);\n res.status(500).send({ error: \"Failed to create job from file\" });\n }\n }\n\n @POST()\n @route(\"/:printerId/submit/:jobId\")\n @before([ParamId(\"printerId\"), ParamId(\"jobId\")])\n async submitToPrinter(req: Request, res: Response) {\n const printerId = req.local.printerId;\n const jobId = req.local.jobId;\n\n try {\n await this.printQueueService.submitToPrinter(printerId, jobId);\n\n res.send({\n message: \"Job submitted to printer for printing\",\n printerId,\n jobId,\n });\n } catch (error) {\n this.logger.error(`Failed to submit job ${jobId} to printer ${printerId}: ${error}`);\n res.status(500).send({\n error: \"Failed to submit job to printer\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"print-queue.controller.js","names":[],"sources":["../../src/controllers/print-queue.controller.ts"],"sourcesContent":["import { before, DELETE, GET, POST, PUT, route } from \"awilix-express\";\nimport { AppConstants } from \"@/server.constants\";\nimport type { Request, Response } from \"express\";\nimport { authorizeRoles, authenticate } from \"@/middleware/authenticate\";\nimport { ROLES } from \"@/constants/authorization.constants\";\nimport { PrintQueueService } from \"@/services/print-queue.service\";\nimport { PrintJobService } from \"@/services/orm/print-job.service\";\nimport { FileStorageService } from \"@/services/file-storage.service\";\nimport { PrinterCache } from \"@/state/printer.cache\";\nimport type { ILoggerFactory } from \"@/handlers/logger-factory\";\nimport { LoggerService } from \"@/handlers/logger\";\nimport { ParamId } from \"@/middleware/param-converter.middleware\";\nimport { NotFoundException } from \"@/exceptions/runtime.exceptions\";\n\n@route(AppConstants.apiRoute + \"/print-queue\")\n@before([authenticate(), authorizeRoles([ROLES.ADMIN, ROLES.OPERATOR])])\nexport class PrintQueueController {\n private readonly logger: LoggerService;\n\n constructor(\n loggerFactory: ILoggerFactory,\n private readonly printQueueService: PrintQueueService,\n private readonly printJobService: PrintJobService,\n private readonly fileStorageService: FileStorageService,\n private readonly printerCache: PrinterCache,\n ) {\n this.logger = loggerFactory(PrintQueueController.name);\n }\n\n @GET()\n async getGlobalQueue(req: Request, res: Response) {\n try {\n const page = Number.parseInt(req.query.page as string) || 1;\n const pageSize = Number.parseInt(req.query.pageSize as string) || 50;\n\n if (page < 1 || pageSize < 1 || pageSize > 200) {\n res.status(400).send({ error: \"Invalid page or pageSize parameters\" });\n return;\n }\n\n const [jobs, totalCount] = await this.printQueueService.getGlobalQueuePaged(page, pageSize);\n\n const queueItems = jobs.map((job) => ({\n jobId: job.id,\n fileName: job.fileName,\n printerId: job.printerId,\n printerName: job.printerName || job.printer?.name,\n queuePosition: job.queuePosition,\n status: job.status,\n createdAt: job.createdAt,\n estimatedTimeSeconds: (job.metadata as any)?.gcodePrintTimeSeconds,\n filamentGrams: (job.metadata as any)?.filamentUsedGrams,\n }));\n\n res.send({\n items: queueItems,\n page,\n pageSize,\n totalCount,\n totalPages: Math.ceil(totalCount / pageSize),\n });\n } catch (error) {\n this.logger.error(`Failed to get global queue: ${error}`);\n res.status(500).send({ error: \"Failed to get global queue\" });\n }\n }\n\n @GET()\n @route(\"/:printerId\")\n @before([ParamId(\"printerId\")])\n async getQueue(req: Request, res: Response) {\n const printerId = req.local.printerId;\n\n try {\n const queue = await this.printQueueService.getQueue(printerId);\n res.send({\n printerId,\n queue,\n count: queue.length,\n });\n } catch (error) {\n this.logger.error(`Failed to get queue for printer ${printerId}: ${error}`);\n res.status(500).send({ error: \"Failed to get queue\" });\n }\n }\n\n @POST()\n @route(\"/:printerId/add/:jobId\")\n @before([ParamId(\"printerId\"), ParamId(\"jobId\")])\n async addToQueue(req: Request, res: Response) {\n const printerId = req.local.printerId;\n const jobId = req.local.jobId;\n const position = req.body.position === undefined ? undefined : Number.parseInt(req.body.position);\n\n try {\n await this.printQueueService.addToQueue(printerId, jobId, position);\n const queue = await this.printQueueService.getQueue(printerId);\n\n res.send({\n message: \"Job added to queue\",\n printerId,\n jobId,\n position,\n queue,\n });\n } catch (error) {\n this.logger.error(`Failed to add job ${jobId} to queue: ${error}`);\n res.status(500).send({\n error: \"Failed to add to queue\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n\n @PUT()\n @route(\"/:printerId/reorder\")\n @before([ParamId(\"printerId\")])\n async reorderQueue(req: Request, res: Response) {\n const printerId = req.local.printerId;\n const jobIds = req.body.jobIds;\n\n if (!Array.isArray(jobIds)) {\n res.status(400).send({ error: \"jobIds must be an array\" });\n return;\n }\n\n try {\n await this.printQueueService.reorderQueue(printerId, jobIds);\n const queue = await this.printQueueService.getQueue(printerId);\n\n res.send({\n message: \"Queue reordered\",\n printerId,\n queue,\n });\n } catch (error) {\n this.logger.error(`Failed to reorder queue for printer ${printerId}: ${error}`);\n res.status(500).send({\n error: \"Failed to reorder queue\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n\n @DELETE()\n @route(\"/:printerId/clear\")\n @before([ParamId(\"printerId\")])\n async clearQueue(req: Request, res: Response) {\n const printerId = req.local.printerId;\n\n try {\n await this.printQueueService.clearQueue(printerId);\n\n res.send({\n message: \"Queue cleared\",\n printerId,\n });\n } catch (error) {\n this.logger.error(`Failed to clear queue for printer ${printerId}: ${error}`);\n res.status(500).send({\n error: \"Failed to clear queue\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n\n @DELETE()\n @route(\"/:printerId/:jobId\")\n @before([ParamId(\"printerId\"), ParamId(\"jobId\")])\n async removeFromQueue(req: Request, res: Response) {\n const printerId = req.local.printerId;\n const jobId = req.local.jobId;\n\n try {\n await this.printQueueService.removeFromQueue(jobId);\n const queue = await this.printQueueService.getQueue(printerId);\n\n res.send({\n message: \"Job removed from queue\",\n printerId,\n jobId,\n queue,\n });\n } catch (error) {\n this.logger.error(`Failed to remove job ${jobId} from queue: ${error}`);\n res.status(500).send({\n error: \"Failed to remove from queue\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n\n @GET()\n @route(\"/:printerId/next\")\n @before([ParamId(\"printerId\")])\n async getNextInQueue(req: Request, res: Response) {\n const printerId = req.local.printerId;\n\n try {\n const nextJob = await this.printQueueService.getNextInQueue(printerId);\n\n res.send({\n printerId,\n nextJob,\n });\n } catch (error) {\n this.logger.error(`Failed to get next job for printer ${printerId}: ${error}`);\n res.status(500).send({ error: \"Failed to get next job\" });\n }\n }\n\n @POST()\n @route(\"/:printerId/process\")\n @before([ParamId(\"printerId\")])\n async processQueue(req: Request, res: Response) {\n const printerId = req.local.printerId;\n\n try {\n const nextJob = await this.printQueueService.processQueue(printerId);\n\n if (!nextJob) {\n res.send({\n message: \"Queue is empty\",\n printerId,\n nextJob: null,\n });\n return;\n }\n\n res.send({\n message: \"Processing next job in queue\",\n printerId,\n nextJob,\n });\n } catch (error) {\n this.logger.error(`Failed to process queue for printer ${printerId}: ${error}`);\n res.status(500).send({\n error: \"Failed to process queue\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n\n @POST()\n @route(\"/:printerId/from-file\")\n @before([ParamId(\"printerId\")])\n async createJobFromFile(req: Request, res: Response) {\n const printerId = req.local.printerId;\n const { fileStorageId, addToQueue = true, position } = req.body;\n\n if (!fileStorageId) {\n res.status(400).send({ error: \"fileStorageId is required\" });\n return;\n }\n\n try {\n const fileExists = await this.fileStorageService.fileExists(fileStorageId);\n if (!fileExists) {\n throw new NotFoundException(\"File not found in storage\");\n }\n\n const metadata = await this.fileStorageService.loadMetadata(fileStorageId);\n if (!metadata) {\n res.status(400).send({ error: \"File has no metadata. Please analyze the file first.\" });\n return;\n }\n\n const printer = await this.printerCache.getCachedPrinterOrThrowAsync(printerId);\n\n const job = await this.printJobService.createPendingJob(\n printerId,\n metadata._originalFileName || metadata.fileName || \"Unknown\",\n metadata,\n printer.name,\n );\n\n job.fileStorageId = fileStorageId;\n job.fileHash = metadata._fileHash;\n job.analysisState = \"ANALYZED\";\n job.analyzedAt = new Date();\n\n if (metadata.fileFormat) {\n job.fileFormat = metadata.fileFormat;\n }\n\n await this.printJobService.updateJob(job);\n\n if (addToQueue) {\n await this.printQueueService.addToQueue(printerId, job.id, position);\n }\n\n this.logger.log(\n `Created job ${job.id} from file storage ${fileStorageId} for printer ${printerId}${addToQueue ? \" and added to queue\" : \"\"}`,\n );\n\n res.send({\n id: job.id,\n printerId: job.printerId,\n printerName: job.printerName,\n fileName: job.fileName,\n fileStorageId: job.fileStorageId,\n status: job.status,\n analysisState: job.analysisState,\n createdAt: job.createdAt,\n addedToQueue: addToQueue,\n });\n } catch (error) {\n this.logger.error(`Failed to create job from file ${fileStorageId}: ${error}`);\n res.status(500).send({ error: \"Failed to create job from file\" });\n }\n }\n\n @POST()\n @route(\"/:printerId/submit/:jobId\")\n @before([ParamId(\"printerId\"), ParamId(\"jobId\")])\n async submitToPrinter(req: Request, res: Response) {\n const printerId = req.local.printerId;\n const jobId = req.local.jobId;\n\n try {\n await this.printQueueService.submitToPrinter(printerId, jobId);\n\n res.send({\n message: \"Job submitted to printer for printing\",\n printerId,\n jobId,\n });\n } catch (error) {\n this.logger.error(`Failed to submit job ${jobId} to printer ${printerId}: ${error}`);\n res.status(500).send({\n error: \"Failed to submit job to printer\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __exportAll } from "../_virtual/_rolldown/runtime.js";
|
|
2
|
-
import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.
|
|
3
|
-
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
+
import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorateMetadata.js";
|
|
3
|
+
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorate.js";
|
|
4
4
|
import "../services/printer-api.interface.js";
|
|
5
5
|
import { NotFoundException, ValidationException } from "../exceptions/runtime.exceptions.js";
|
|
6
6
|
import { validateInput } from "../handlers/validators.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"printer-files.controller.js","names":[],"sources":["../../src/controllers/printer-files.controller.ts"],"sourcesContent":["import { before, DELETE, GET, POST, route } from \"awilix-express\";\nimport { authenticate, authorizeRoles, permission } from \"@/middleware/authenticate\";\nimport { validateInput } from \"@/handlers/validators\";\nimport { AppConstants } from \"@/server.constants\";\nimport {\n downloadFileSchema,\n getFileSchema,\n getFilesSchema,\n startPrintFileSchema,\n uploadFileSchema,\n} from \"./validation/printer-files-controller.validation\";\nimport { NotFoundException, ValidationException } from \"@/exceptions/runtime.exceptions\";\nimport { printerResolveMiddleware } from \"@/middleware/printer\";\nimport { PERMS, ROLES } from \"@/constants/authorization.constants\";\nimport { MulterService } from \"@/services/core/multer.service\";\nimport { LoggerService } from \"@/handlers/logger\";\nimport type { ILoggerFactory } from \"@/handlers/logger-factory\";\nimport type { Request, Response } from \"express\";\nimport { BambuType, type IPrinterApi } from \"@/services/printer-api.interface\";\nimport { PrinterThumbnailCache } from \"@/state/printer-thumbnail.cache\";\nimport { captureException } from \"@sentry/node\";\nimport { errorSummary } from \"@/utils/error.utils\";\nimport { getScopedPrinter } from \"@/middleware/printer-resolver\";\nimport { FileAnalysisService } from \"@/services/file-analysis.service\";\nimport { FileStorageService } from \"@/services/file-storage.service\";\nimport { copyFileSync, createReadStream, existsSync, unlinkSync } from \"node:fs\";\nimport { PrintJobService } from \"@/services/orm/print-job.service\";\nimport { extname } from \"node:path\";\n\n@route(AppConstants.apiRoute + \"/printer-files\")\n@before([authenticate(), authorizeRoles([ROLES.ADMIN, ROLES.OPERATOR]), printerResolveMiddleware()])\nexport class PrinterFilesController {\n private readonly logger: LoggerService;\n\n constructor(\n loggerFactory: ILoggerFactory,\n private readonly printerApi: IPrinterApi,\n private readonly printJobService: PrintJobService,\n private readonly fileAnalysisService: FileAnalysisService,\n private readonly fileStorageService: FileStorageService,\n private readonly multerService: MulterService,\n private readonly printerThumbnailCache: PrinterThumbnailCache,\n ) {\n this.logger = loggerFactory(PrinterFilesController.name);\n }\n\n @GET()\n @route(\"/thumbnails\")\n @before(permission(PERMS.PrinterFiles.Get))\n async getThumbnails(req: Request, res: Response) {\n const thumbnails = await this.printerThumbnailCache.getAllValues();\n res.send(thumbnails);\n }\n\n @GET()\n @route(\"/:id\")\n @before(permission(PERMS.PrinterFiles.Get))\n async getFiles(req: Request, res: Response) {\n const { printerApi } = getScopedPrinter(req);\n const { recursive: recursiveStr, startDir } = await validateInput(req.query, getFilesSchema);\n const recursive = recursiveStr === \"true\";\n\n const files = await printerApi.getFiles(recursive, startDir);\n res.send(files);\n }\n\n @POST()\n /**\n * @obsolete /:id/select, removed in v2\n */\n @route(\"/:id/select\")\n @route(\"/:id/print\")\n @before(permission(PERMS.PrinterFiles.Actions))\n async startPrintFile(req: Request, res: Response) {\n const { currentPrinterId } = getScopedPrinter(req);\n const { filePath } = await validateInput(req.body, startPrintFileSchema);\n const encodedFilePath = filePath.split(\"/\").map(encodeURIComponent).join(\"/\");\n await this.printerApi.startPrint(encodedFilePath);\n\n this.logger.log(`Started print for printer ${currentPrinterId}`);\n\n res.send();\n }\n\n @GET()\n @route(\"/:id/download/:path\")\n @before(permission(PERMS.PrinterFiles.Get))\n async downloadFile(req: Request, res: Response) {\n this.logger.log(`Downloading file ${req.params.path}`);\n const { path } = await validateInput(req.params, downloadFileSchema);\n const encodedFilePath = path.split(\"/\").map(encodeURIComponent).join(\"/\");\n\n const response = await this.printerApi.downloadFile(encodedFilePath);\n res.setHeader(\"Content-Type\", response.headers[\"content-type\"]);\n res.setHeader(\"Content-Length\", response.headers[\"content-length\"]);\n res.setHeader(\"Content-Disposition\", response.headers[\"content-disposition\"]);\n if (response.headers[\"etag\"]?.length) {\n res.setHeader(\"ETag\", response.headers[\"etag\"]);\n }\n response.data.pipe(res);\n }\n\n @DELETE()\n @route(\"/:id\")\n @before(permission(PERMS.PrinterFiles.Delete))\n async deleteFileOrFolder(req: Request, res: Response) {\n const { path } = await validateInput(req.query, getFileSchema);\n const encodedFilePath = path.split(\"/\").map(encodeURIComponent).join(\"/\");\n\n const result = await this.printerApi.deleteFile(encodedFilePath);\n res.send(result);\n }\n\n @GET()\n @route(\"/:id/thumbnail\")\n @before(permission(PERMS.PrinterFiles.Get))\n async getPrinterThumbnail(req: Request, res: Response) {\n const { currentPrinterId } = getScopedPrinter(req);\n const printerThumbnail = await this.printerThumbnailCache.getValue(currentPrinterId);\n res.send(printerThumbnail);\n }\n\n @POST()\n @route(\"/:id/upload\")\n @before(permission(PERMS.PrinterFiles.Upload))\n async uploadPrinterFile(req: Request, res: Response) {\n const { currentPrinterId, currentPrinter } = getScopedPrinter(req);\n\n const acceptedExtensions = this.getAcceptedFileExtensions(currentPrinter.printerType);\n const files = await this.multerService.multerLoadFileAsync(req, res, acceptedExtensions, true);\n\n // FormData has key-values with type string only\n const { startPrint: startPrintString } = await validateInput(req.body, uploadFileSchema);\n const startPrint = startPrintString === \"true\";\n\n if (!files?.length) {\n throw new ValidationException({\n error: `No file was available for upload. Did you upload files with one of these extensions: ${acceptedExtensions.join(\n \", \",\n )}?`,\n });\n }\n if (files.length > 1) {\n throw new ValidationException({\n error: \"Only 1 file can be uploaded at a time\",\n });\n }\n\n const uploadedFile = files[0];\n const token = this.multerService.startTrackingSession(uploadedFile, currentPrinterId);\n\n await this.printerApi\n .uploadFile({\n stream: createReadStream(uploadedFile.path),\n fileName: uploadedFile.originalname,\n contentLength: uploadedFile.size,\n startPrint,\n uploadToken: token,\n })\n .catch((e) => {\n try {\n this.multerService.clearUploadedFile(uploadedFile);\n } catch (e) {\n this.logger.error(`Could not remove uploaded file from temporary storage ${errorSummary(e)}`);\n }\n throw e;\n });\n\n // Process file: analyze, store, create job\n const ext = extname(uploadedFile.originalname);\n const tempPathWithExt = uploadedFile.path + ext;\n\n try {\n this.logger.log(`Processing uploaded file: ${uploadedFile.originalname} (ext: ${ext})`);\n\n // Copy file with proper extension for hash calculation and analysis\n if (!existsSync(uploadedFile.path)) {\n throw new NotFoundException(`Upload file does not exist: ${uploadedFile.path}`);\n }\n\n copyFileSync(uploadedFile.path, tempPathWithExt);\n\n // Calculate hash for deduplication\n const fileHash = await this.fileStorageService.calculateFileHash(tempPathWithExt);\n this.logger.log(`File hash: ${fileHash.substring(0, 12)}...`);\n\n // Check if file already analyzed (by hash)\n const existingJob = await this.fileStorageService.findDuplicateByHash(fileHash);\n\n let metadata;\n let fileStorageId;\n\n if (existingJob && existingJob.fileStorageId) {\n // Found duplicate by hash - REUSE existing storage file\n const cachedMetadata = await this.fileStorageService.loadMetadata(existingJob.fileStorageId);\n\n if (cachedMetadata) {\n // Use cached metadata from JSON file (fastest - no re-analysis, no re-storage)\n this.logger.log(\n `Duplicate file detected (job ${existingJob.id}, hash match) - reusing storage ${existingJob.fileStorageId}`,\n );\n metadata = {\n ...cachedMetadata,\n fileName: uploadedFile.originalname, // Update filename to current upload\n };\n fileStorageId = existingJob.fileStorageId; // REUSE existing storage!\n } else if (existingJob.analysisState === \"ANALYZED\" && existingJob.metadata) {\n // No JSON cache, but have metadata in DB - use it and create JSON\n this.logger.log(\n `Duplicate file with DB metadata (job ${existingJob.id}) - reusing storage ${existingJob.fileStorageId}`,\n );\n metadata = {\n ...existingJob.metadata,\n fileName: uploadedFile.originalname,\n };\n fileStorageId = existingJob.fileStorageId; // REUSE existing storage!\n\n // Save metadata JSON for future deduplication (preserve original filename)\n await this.fileStorageService.saveMetadata(fileStorageId, metadata, fileHash, uploadedFile.originalname);\n } else {\n // Duplicate hash but not analyzed - reuse storage, analyze file\n this.logger.log(`Duplicate file not analyzed - reusing storage ${existingJob.fileStorageId}, analyzing now`);\n\n // Get existing file for analysis\n const existingFilePath = this.fileStorageService.getFilePath(existingJob.fileStorageId);\n const analysisResult = await this.fileAnalysisService.analyzeFile(existingFilePath);\n metadata = analysisResult.metadata;\n\n fileStorageId = existingJob.fileStorageId; // REUSE existing storage!\n await this.fileStorageService.saveMetadata(fileStorageId, metadata, fileHash, uploadedFile.originalname);\n this.logger.log(`Analysis complete and cached: ${fileStorageId}`);\n }\n } else {\n // New file - analyze BEFORE saving (saveFile moves the original!)\n this.logger.log(`Analyzing new file: ${uploadedFile.originalname}`);\n const analysisResult = await this.fileAnalysisService.analyzeFile(tempPathWithExt);\n metadata = analysisResult.metadata;\n const thumbnails = analysisResult.thumbnails;\n this.logger.log(\n `Analysis complete: format=${metadata.fileFormat}, layers=${metadata.totalLayers}, time=${metadata.gcodePrintTimeSeconds}s, filament=${metadata.filamentUsedGrams}g, thumbnails=${thumbnails.length}`,\n );\n\n // Now save file to storage with deterministic ID (moves file from temp to permanent storage)\n fileStorageId = await this.fileStorageService.saveFile(uploadedFile, fileHash);\n this.logger.log(`Saved file to storage: ${fileStorageId} (deterministic from hash+name)`);\n\n // Save thumbnails\n let thumbnailMetadata: any[] = [];\n if (thumbnails.length > 0) {\n thumbnailMetadata = await this.fileStorageService.saveThumbnails(fileStorageId, thumbnails);\n this.logger.log(`Saved ${thumbnailMetadata.length} thumbnail(s) for ${fileStorageId}`);\n }\n\n // Save metadata JSON with thumbnail index\n await this.fileStorageService.saveMetadata(\n fileStorageId,\n metadata,\n fileHash,\n uploadedFile.originalname,\n thumbnailMetadata,\n );\n this.logger.log(`Saved metadata JSON for ${fileStorageId}`);\n }\n\n // Create job with analyzed metadata, hash, and storageId\n const job = await this.printJobService.createPendingJob(\n currentPrinterId,\n uploadedFile.originalname,\n metadata,\n currentPrinter.name,\n );\n\n job.fileStorageId = fileStorageId;\n job.fileHash = fileHash;\n await this.printJobService.updateJob(job);\n\n this.logger.log(\n `Created job ${job.id}: format=${job.fileFormat}, ` +\n `state=${job.analysisState}, ` +\n `storageId=${fileStorageId}, ` +\n `hash=${fileHash.substring(0, 8)}...`,\n );\n\n // Clean up temp analysis file\n if (existsSync(tempPathWithExt)) {\n unlinkSync(tempPathWithExt);\n }\n\n // Thumbnail will be automatically loaded from the analyzed print job\n // by the PrinterThumbnailCache when the job is analyzed\n } catch (error) {\n this.logger.error(`File processing failed: ${errorSummary(error)}`);\n captureException(error);\n // Don't throw - allow upload to succeed even if analysis/storage fails\n } finally {\n // Always clean up temp file\n try {\n this.multerService.clearUploadedFile(uploadedFile);\n } catch (e) {\n this.logger.error(`Could not remove uploaded file from temporary storage ${errorSummary(e)}`);\n }\n }\n\n res.send();\n }\n\n private getAcceptedFileExtensions(printerType: number): string[] {\n if (printerType === BambuType) {\n return AppConstants.defaultAcceptedBambuExtensions;\n }\n return AppConstants.defaultAcceptedGcodeExtensions;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"printer-files.controller.js","names":[],"sources":["../../src/controllers/printer-files.controller.ts"],"sourcesContent":["import { before, DELETE, GET, POST, route } from \"awilix-express\";\nimport { authenticate, authorizeRoles, permission } from \"@/middleware/authenticate\";\nimport { validateInput } from \"@/handlers/validators\";\nimport { AppConstants } from \"@/server.constants\";\nimport {\n downloadFileSchema,\n getFileSchema,\n getFilesSchema,\n startPrintFileSchema,\n uploadFileSchema,\n} from \"./validation/printer-files-controller.validation\";\nimport { NotFoundException, ValidationException } from \"@/exceptions/runtime.exceptions\";\nimport { printerResolveMiddleware } from \"@/middleware/printer\";\nimport { PERMS, ROLES } from \"@/constants/authorization.constants\";\nimport { MulterService } from \"@/services/core/multer.service\";\nimport { LoggerService } from \"@/handlers/logger\";\nimport type { ILoggerFactory } from \"@/handlers/logger-factory\";\nimport type { Request, Response } from \"express\";\nimport { BambuType, type IPrinterApi } from \"@/services/printer-api.interface\";\nimport { PrinterThumbnailCache } from \"@/state/printer-thumbnail.cache\";\nimport { captureException } from \"@sentry/node\";\nimport { errorSummary } from \"@/utils/error.utils\";\nimport { getScopedPrinter } from \"@/middleware/printer-resolver\";\nimport { FileAnalysisService } from \"@/services/file-analysis.service\";\nimport { FileStorageService } from \"@/services/file-storage.service\";\nimport { copyFileSync, createReadStream, existsSync, unlinkSync } from \"node:fs\";\nimport { PrintJobService } from \"@/services/orm/print-job.service\";\nimport { extname } from \"node:path\";\n\n@route(AppConstants.apiRoute + \"/printer-files\")\n@before([authenticate(), authorizeRoles([ROLES.ADMIN, ROLES.OPERATOR]), printerResolveMiddleware()])\nexport class PrinterFilesController {\n private readonly logger: LoggerService;\n\n constructor(\n loggerFactory: ILoggerFactory,\n private readonly printerApi: IPrinterApi,\n private readonly printJobService: PrintJobService,\n private readonly fileAnalysisService: FileAnalysisService,\n private readonly fileStorageService: FileStorageService,\n private readonly multerService: MulterService,\n private readonly printerThumbnailCache: PrinterThumbnailCache,\n ) {\n this.logger = loggerFactory(PrinterFilesController.name);\n }\n\n @GET()\n @route(\"/thumbnails\")\n @before(permission(PERMS.PrinterFiles.Get))\n async getThumbnails(req: Request, res: Response) {\n const thumbnails = await this.printerThumbnailCache.getAllValues();\n res.send(thumbnails);\n }\n\n @GET()\n @route(\"/:id\")\n @before(permission(PERMS.PrinterFiles.Get))\n async getFiles(req: Request, res: Response) {\n const { printerApi } = getScopedPrinter(req);\n const { recursive: recursiveStr, startDir } = await validateInput(req.query, getFilesSchema);\n const recursive = recursiveStr === \"true\";\n\n const files = await printerApi.getFiles(recursive, startDir);\n res.send(files);\n }\n\n @POST()\n /**\n * @obsolete /:id/select, removed in v2\n */\n @route(\"/:id/select\")\n @route(\"/:id/print\")\n @before(permission(PERMS.PrinterFiles.Actions))\n async startPrintFile(req: Request, res: Response) {\n const { currentPrinterId } = getScopedPrinter(req);\n const { filePath } = await validateInput(req.body, startPrintFileSchema);\n const encodedFilePath = filePath.split(\"/\").map(encodeURIComponent).join(\"/\");\n await this.printerApi.startPrint(encodedFilePath);\n\n this.logger.log(`Started print for printer ${currentPrinterId}`);\n\n res.send();\n }\n\n @GET()\n @route(\"/:id/download/:path\")\n @before(permission(PERMS.PrinterFiles.Get))\n async downloadFile(req: Request, res: Response) {\n this.logger.log(`Downloading file ${req.params.path}`);\n const { path } = await validateInput(req.params, downloadFileSchema);\n const encodedFilePath = path.split(\"/\").map(encodeURIComponent).join(\"/\");\n\n const response = await this.printerApi.downloadFile(encodedFilePath);\n res.setHeader(\"Content-Type\", response.headers[\"content-type\"]);\n res.setHeader(\"Content-Length\", response.headers[\"content-length\"]);\n res.setHeader(\"Content-Disposition\", response.headers[\"content-disposition\"]);\n if (response.headers[\"etag\"]?.length) {\n res.setHeader(\"ETag\", response.headers[\"etag\"]);\n }\n response.data.pipe(res);\n }\n\n @DELETE()\n @route(\"/:id\")\n @before(permission(PERMS.PrinterFiles.Delete))\n async deleteFileOrFolder(req: Request, res: Response) {\n const { path } = await validateInput(req.query, getFileSchema);\n const encodedFilePath = path.split(\"/\").map(encodeURIComponent).join(\"/\");\n\n const result = await this.printerApi.deleteFile(encodedFilePath);\n res.send(result);\n }\n\n @GET()\n @route(\"/:id/thumbnail\")\n @before(permission(PERMS.PrinterFiles.Get))\n async getPrinterThumbnail(req: Request, res: Response) {\n const { currentPrinterId } = getScopedPrinter(req);\n const printerThumbnail = await this.printerThumbnailCache.getValue(currentPrinterId);\n res.send(printerThumbnail);\n }\n\n @POST()\n @route(\"/:id/upload\")\n @before(permission(PERMS.PrinterFiles.Upload))\n async uploadPrinterFile(req: Request, res: Response) {\n const { currentPrinterId, currentPrinter } = getScopedPrinter(req);\n\n const acceptedExtensions = this.getAcceptedFileExtensions(currentPrinter.printerType);\n const files = await this.multerService.multerLoadFileAsync(req, res, acceptedExtensions, true);\n\n // FormData has key-values with type string only\n const { startPrint: startPrintString } = await validateInput(req.body, uploadFileSchema);\n const startPrint = startPrintString === \"true\";\n\n if (!files?.length) {\n throw new ValidationException({\n error: `No file was available for upload. Did you upload files with one of these extensions: ${acceptedExtensions.join(\n \", \",\n )}?`,\n });\n }\n if (files.length > 1) {\n throw new ValidationException({\n error: \"Only 1 file can be uploaded at a time\",\n });\n }\n\n const uploadedFile = files[0];\n const token = this.multerService.startTrackingSession(uploadedFile, currentPrinterId);\n\n await this.printerApi\n .uploadFile({\n stream: createReadStream(uploadedFile.path),\n fileName: uploadedFile.originalname,\n contentLength: uploadedFile.size,\n startPrint,\n uploadToken: token,\n })\n .catch((e) => {\n try {\n this.multerService.clearUploadedFile(uploadedFile);\n } catch (e) {\n this.logger.error(`Could not remove uploaded file from temporary storage ${errorSummary(e)}`);\n }\n throw e;\n });\n\n // Process file: analyze, store, create job\n const ext = extname(uploadedFile.originalname);\n const tempPathWithExt = uploadedFile.path + ext;\n\n try {\n this.logger.log(`Processing uploaded file: ${uploadedFile.originalname} (ext: ${ext})`);\n\n // Copy file with proper extension for hash calculation and analysis\n if (!existsSync(uploadedFile.path)) {\n throw new NotFoundException(`Upload file does not exist: ${uploadedFile.path}`);\n }\n\n copyFileSync(uploadedFile.path, tempPathWithExt);\n\n // Calculate hash for deduplication\n const fileHash = await this.fileStorageService.calculateFileHash(tempPathWithExt);\n this.logger.log(`File hash: ${fileHash.substring(0, 12)}...`);\n\n // Check if file already analyzed (by hash)\n const existingJob = await this.fileStorageService.findDuplicateByHash(fileHash);\n\n let metadata;\n let fileStorageId;\n\n if (existingJob && existingJob.fileStorageId) {\n // Found duplicate by hash - REUSE existing storage file\n const cachedMetadata = await this.fileStorageService.loadMetadata(existingJob.fileStorageId);\n\n if (cachedMetadata) {\n // Use cached metadata from JSON file (fastest - no re-analysis, no re-storage)\n this.logger.log(\n `Duplicate file detected (job ${existingJob.id}, hash match) - reusing storage ${existingJob.fileStorageId}`,\n );\n metadata = {\n ...cachedMetadata,\n fileName: uploadedFile.originalname, // Update filename to current upload\n };\n fileStorageId = existingJob.fileStorageId; // REUSE existing storage!\n } else if (existingJob.analysisState === \"ANALYZED\" && existingJob.metadata) {\n // No JSON cache, but have metadata in DB - use it and create JSON\n this.logger.log(\n `Duplicate file with DB metadata (job ${existingJob.id}) - reusing storage ${existingJob.fileStorageId}`,\n );\n metadata = {\n ...existingJob.metadata,\n fileName: uploadedFile.originalname,\n };\n fileStorageId = existingJob.fileStorageId; // REUSE existing storage!\n\n // Save metadata JSON for future deduplication (preserve original filename)\n await this.fileStorageService.saveMetadata(fileStorageId, metadata, fileHash, uploadedFile.originalname);\n } else {\n // Duplicate hash but not analyzed - reuse storage, analyze file\n this.logger.log(`Duplicate file not analyzed - reusing storage ${existingJob.fileStorageId}, analyzing now`);\n\n // Get existing file for analysis\n const existingFilePath = this.fileStorageService.getFilePath(existingJob.fileStorageId);\n const analysisResult = await this.fileAnalysisService.analyzeFile(existingFilePath);\n metadata = analysisResult.metadata;\n\n fileStorageId = existingJob.fileStorageId; // REUSE existing storage!\n await this.fileStorageService.saveMetadata(fileStorageId, metadata, fileHash, uploadedFile.originalname);\n this.logger.log(`Analysis complete and cached: ${fileStorageId}`);\n }\n } else {\n // New file - analyze BEFORE saving (saveFile moves the original!)\n this.logger.log(`Analyzing new file: ${uploadedFile.originalname}`);\n const analysisResult = await this.fileAnalysisService.analyzeFile(tempPathWithExt);\n metadata = analysisResult.metadata;\n const thumbnails = analysisResult.thumbnails;\n this.logger.log(\n `Analysis complete: format=${metadata.fileFormat}, layers=${metadata.totalLayers}, time=${metadata.gcodePrintTimeSeconds}s, filament=${metadata.filamentUsedGrams}g, thumbnails=${thumbnails.length}`,\n );\n\n // Now save file to storage with deterministic ID (moves file from temp to permanent storage)\n fileStorageId = await this.fileStorageService.saveFile(uploadedFile, fileHash);\n this.logger.log(`Saved file to storage: ${fileStorageId} (deterministic from hash+name)`);\n\n // Save thumbnails\n let thumbnailMetadata: any[] = [];\n if (thumbnails.length > 0) {\n thumbnailMetadata = await this.fileStorageService.saveThumbnails(fileStorageId, thumbnails);\n this.logger.log(`Saved ${thumbnailMetadata.length} thumbnail(s) for ${fileStorageId}`);\n }\n\n // Save metadata JSON with thumbnail index\n await this.fileStorageService.saveMetadata(\n fileStorageId,\n metadata,\n fileHash,\n uploadedFile.originalname,\n thumbnailMetadata,\n );\n this.logger.log(`Saved metadata JSON for ${fileStorageId}`);\n }\n\n // Create job with analyzed metadata, hash, and storageId\n const job = await this.printJobService.createPendingJob(\n currentPrinterId,\n uploadedFile.originalname,\n metadata,\n currentPrinter.name,\n );\n\n job.fileStorageId = fileStorageId;\n job.fileHash = fileHash;\n await this.printJobService.updateJob(job);\n\n this.logger.log(\n `Created job ${job.id}: format=${job.fileFormat}, ` +\n `state=${job.analysisState}, ` +\n `storageId=${fileStorageId}, ` +\n `hash=${fileHash.substring(0, 8)}...`,\n );\n\n // Clean up temp analysis file\n if (existsSync(tempPathWithExt)) {\n unlinkSync(tempPathWithExt);\n }\n\n // Thumbnail will be automatically loaded from the analyzed print job\n // by the PrinterThumbnailCache when the job is analyzed\n } catch (error) {\n this.logger.error(`File processing failed: ${errorSummary(error)}`);\n captureException(error);\n // Don't throw - allow upload to succeed even if analysis/storage fails\n } finally {\n // Always clean up temp file\n try {\n this.multerService.clearUploadedFile(uploadedFile);\n } catch (e) {\n this.logger.error(`Could not remove uploaded file from temporary storage ${errorSummary(e)}`);\n }\n }\n\n res.send();\n }\n\n private getAcceptedFileExtensions(printerType: number): string[] {\n if (printerType === BambuType) {\n return AppConstants.defaultAcceptedBambuExtensions;\n }\n return AppConstants.defaultAcceptedGcodeExtensions;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __exportAll } from "../_virtual/_rolldown/runtime.js";
|
|
2
|
-
import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.
|
|
3
|
-
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
+
import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorateMetadata.js";
|
|
3
|
+
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorate.js";
|
|
4
4
|
import { validateInput } from "../handlers/validators.js";
|
|
5
5
|
import { PrinterMaintenanceLogService } from "../services/orm/printer-maintenance-log.service.js";
|
|
6
6
|
import { AppConstants } from "../server.constants.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"printer-maintenance-log.controller.js","names":[],"sources":["../../src/controllers/printer-maintenance-log.controller.ts"],"sourcesContent":["import { before, DELETE, GET, POST, route } from \"awilix-express\";\nimport { AppConstants } from \"@/server.constants\";\nimport type { Request, Response } from \"express\";\nimport { authenticate, permission } from \"@/middleware/authenticate\";\nimport { PERMS } from \"@/constants/authorization.constants\";\nimport { PrinterMaintenanceLogService } from \"@/services/orm/printer-maintenance-log.service\";\nimport type { ILoggerFactory } from \"@/handlers/logger-factory\";\nimport { LoggerService } from \"@/handlers/logger\";\nimport { validateInput } from \"@/handlers/validators\";\nimport {\n completeMaintenanceLogSchema,\n createMaintenanceLogSchema,\n getMaintenanceLogsQuerySchema,\n} from \"@/services/validators/printer-maintenance-log.validation\";\nimport { ParamId } from \"@/middleware/param-converter.middleware\";\n\n@route(AppConstants.apiRoute + \"/printer-maintenance-log\")\n@before([authenticate()])\nexport class PrinterMaintenanceLogController {\n private readonly logger: LoggerService;\n\n constructor(\n loggerFactory: ILoggerFactory,\n private readonly printerMaintenanceLogService: PrinterMaintenanceLogService,\n ) {\n this.logger = loggerFactory(PrinterMaintenanceLogController.name);\n }\n\n @GET()\n @route(\"/\")\n @before([permission(PERMS.PrinterMaintenanceLog.List)])\n async list(req: Request, res: Response) {\n const query = await validateInput(req.query, getMaintenanceLogsQuerySchema);\n const result = await this.printerMaintenanceLogService.list(query);\n\n res.send({\n logs: result.logs.map((log) => this.printerMaintenanceLogService.toDto(log)),\n total: result.total,\n page: query.page,\n pageSize: query.pageSize,\n });\n }\n\n @GET()\n @route(\"/:id\")\n @before([permission(PERMS.PrinterMaintenanceLog.Get), ParamId(\"id\")])\n async get(req: Request, res: Response) {\n const logId = req.params.id as unknown as number;\n const log = await this.printerMaintenanceLogService.get(logId);\n res.send(this.printerMaintenanceLogService.toDto(log));\n }\n\n @GET()\n @route(\"/printer/:printerId/active\")\n @before([permission(PERMS.PrinterMaintenanceLog.Get), ParamId(\"printerId\")])\n async getActiveByPrinterId(req: Request, res: Response) {\n const printerId = req.params.printerId as unknown as number;\n const log = await this.printerMaintenanceLogService.getActiveByPrinterId(printerId);\n res.send(log ? this.printerMaintenanceLogService.toDto(log) : null);\n }\n\n @POST()\n @route(\"/\")\n @before([permission(PERMS.PrinterMaintenanceLog.Create)])\n async create(req: Request, res: Response) {\n const data = await validateInput(req.body, createMaintenanceLogSchema);\n const userId = req.user?.id ?? null;\n const username = req.user?.username || \"system\";\n\n const log = await this.printerMaintenanceLogService.create(data, userId, username);\n res.send(this.printerMaintenanceLogService.toDto(log));\n }\n\n @POST()\n @route(\"/:id/complete\")\n @before([permission(PERMS.PrinterMaintenanceLog.Complete), ParamId(\"id\")])\n async complete(req: Request, res: Response) {\n const logId = req.params.id as unknown as number;\n const data = await validateInput(req.body, completeMaintenanceLogSchema);\n const userId = req.user?.id ?? null;\n const username = req.user?.username || \"system\";\n\n const log = await this.printerMaintenanceLogService.complete(logId, data, userId, username);\n res.send(this.printerMaintenanceLogService.toDto(log));\n }\n\n @DELETE()\n @route(\"/:id\")\n @before([permission(PERMS.PrinterMaintenanceLog.Delete), ParamId(\"id\")])\n async delete(req: Request, res: Response) {\n const logId = req.params.id as unknown as number;\n await this.printerMaintenanceLogService.delete(logId);\n res.send({ success: true });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAkBO,IAAA,kCAAA,mCAAA,MAAM,gCAAgC;CAC3C;CAEA,YACE,eACA,8BACA;
|
|
1
|
+
{"version":3,"file":"printer-maintenance-log.controller.js","names":[],"sources":["../../src/controllers/printer-maintenance-log.controller.ts"],"sourcesContent":["import { before, DELETE, GET, POST, route } from \"awilix-express\";\nimport { AppConstants } from \"@/server.constants\";\nimport type { Request, Response } from \"express\";\nimport { authenticate, permission } from \"@/middleware/authenticate\";\nimport { PERMS } from \"@/constants/authorization.constants\";\nimport { PrinterMaintenanceLogService } from \"@/services/orm/printer-maintenance-log.service\";\nimport type { ILoggerFactory } from \"@/handlers/logger-factory\";\nimport { LoggerService } from \"@/handlers/logger\";\nimport { validateInput } from \"@/handlers/validators\";\nimport {\n completeMaintenanceLogSchema,\n createMaintenanceLogSchema,\n getMaintenanceLogsQuerySchema,\n} from \"@/services/validators/printer-maintenance-log.validation\";\nimport { ParamId } from \"@/middleware/param-converter.middleware\";\n\n@route(AppConstants.apiRoute + \"/printer-maintenance-log\")\n@before([authenticate()])\nexport class PrinterMaintenanceLogController {\n private readonly logger: LoggerService;\n\n constructor(\n loggerFactory: ILoggerFactory,\n private readonly printerMaintenanceLogService: PrinterMaintenanceLogService,\n ) {\n this.logger = loggerFactory(PrinterMaintenanceLogController.name);\n }\n\n @GET()\n @route(\"/\")\n @before([permission(PERMS.PrinterMaintenanceLog.List)])\n async list(req: Request, res: Response) {\n const query = await validateInput(req.query, getMaintenanceLogsQuerySchema);\n const result = await this.printerMaintenanceLogService.list(query);\n\n res.send({\n logs: result.logs.map((log) => this.printerMaintenanceLogService.toDto(log)),\n total: result.total,\n page: query.page,\n pageSize: query.pageSize,\n });\n }\n\n @GET()\n @route(\"/:id\")\n @before([permission(PERMS.PrinterMaintenanceLog.Get), ParamId(\"id\")])\n async get(req: Request, res: Response) {\n const logId = req.params.id as unknown as number;\n const log = await this.printerMaintenanceLogService.get(logId);\n res.send(this.printerMaintenanceLogService.toDto(log));\n }\n\n @GET()\n @route(\"/printer/:printerId/active\")\n @before([permission(PERMS.PrinterMaintenanceLog.Get), ParamId(\"printerId\")])\n async getActiveByPrinterId(req: Request, res: Response) {\n const printerId = req.params.printerId as unknown as number;\n const log = await this.printerMaintenanceLogService.getActiveByPrinterId(printerId);\n res.send(log ? this.printerMaintenanceLogService.toDto(log) : null);\n }\n\n @POST()\n @route(\"/\")\n @before([permission(PERMS.PrinterMaintenanceLog.Create)])\n async create(req: Request, res: Response) {\n const data = await validateInput(req.body, createMaintenanceLogSchema);\n const userId = req.user?.id ?? null;\n const username = req.user?.username || \"system\";\n\n const log = await this.printerMaintenanceLogService.create(data, userId, username);\n res.send(this.printerMaintenanceLogService.toDto(log));\n }\n\n @POST()\n @route(\"/:id/complete\")\n @before([permission(PERMS.PrinterMaintenanceLog.Complete), ParamId(\"id\")])\n async complete(req: Request, res: Response) {\n const logId = req.params.id as unknown as number;\n const data = await validateInput(req.body, completeMaintenanceLogSchema);\n const userId = req.user?.id ?? null;\n const username = req.user?.username || \"system\";\n\n const log = await this.printerMaintenanceLogService.complete(logId, data, userId, username);\n res.send(this.printerMaintenanceLogService.toDto(log));\n }\n\n @DELETE()\n @route(\"/:id\")\n @before([permission(PERMS.PrinterMaintenanceLog.Delete), ParamId(\"id\")])\n async delete(req: Request, res: Response) {\n const logId = req.params.id as unknown as number;\n await this.printerMaintenanceLogService.delete(logId);\n res.send({ success: true });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAkBO,IAAA,kCAAA,mCAAA,MAAM,gCAAgC;CAC3C;CAEA,YACE,eACA,8BACA;EADiB,KAAA,+BAAA;EAEjB,KAAK,SAAS,cAAA,iCAA8C,KAAK;;CAGnE,MAGM,KAAK,KAAc,KAAe;EACtC,MAAM,QAAQ,MAAM,cAAc,IAAI,OAAO,8BAA8B;EAC3E,MAAM,SAAS,MAAM,KAAK,6BAA6B,KAAK,MAAM;EAElE,IAAI,KAAK;GACP,MAAM,OAAO,KAAK,KAAK,QAAQ,KAAK,6BAA6B,MAAM,IAAI,CAAC;GAC5E,OAAO,OAAO;GACd,MAAM,MAAM;GACZ,UAAU,MAAM;GACjB,CAAC;;CAGJ,MAGM,IAAI,KAAc,KAAe;EACrC,MAAM,QAAQ,IAAI,OAAO;EACzB,MAAM,MAAM,MAAM,KAAK,6BAA6B,IAAI,MAAM;EAC9D,IAAI,KAAK,KAAK,6BAA6B,MAAM,IAAI,CAAC;;CAGxD,MAGM,qBAAqB,KAAc,KAAe;EACtD,MAAM,YAAY,IAAI,OAAO;EAC7B,MAAM,MAAM,MAAM,KAAK,6BAA6B,qBAAqB,UAAU;EACnF,IAAI,KAAK,MAAM,KAAK,6BAA6B,MAAM,IAAI,GAAG,KAAK;;CAGrE,MAGM,OAAO,KAAc,KAAe;EACxC,MAAM,OAAO,MAAM,cAAc,IAAI,MAAM,2BAA2B;EACtE,MAAM,SAAS,IAAI,MAAM,MAAM;EAC/B,MAAM,WAAW,IAAI,MAAM,YAAY;EAEvC,MAAM,MAAM,MAAM,KAAK,6BAA6B,OAAO,MAAM,QAAQ,SAAS;EAClF,IAAI,KAAK,KAAK,6BAA6B,MAAM,IAAI,CAAC;;CAGxD,MAGM,SAAS,KAAc,KAAe;EAC1C,MAAM,QAAQ,IAAI,OAAO;EACzB,MAAM,OAAO,MAAM,cAAc,IAAI,MAAM,6BAA6B;EACxE,MAAM,SAAS,IAAI,MAAM,MAAM;EAC/B,MAAM,WAAW,IAAI,MAAM,YAAY;EAEvC,MAAM,MAAM,MAAM,KAAK,6BAA6B,SAAS,OAAO,MAAM,QAAQ,SAAS;EAC3F,IAAI,KAAK,KAAK,6BAA6B,MAAM,IAAI,CAAC;;CAGxD,MAGM,OAAO,KAAc,KAAe;EACxC,MAAM,QAAQ,IAAI,OAAO;EACzB,MAAM,KAAK,6BAA6B,OAAO,MAAM;EACrD,IAAI,KAAK,EAAE,SAAS,MAAM,CAAC;;;;CAhE5B,KAAK;CACL,MAAM,IAAI;CACV,OAAO,CAAC,WAAW,MAAM,sBAAsB,KAAK,CAAC,CAAC;;;;;;CAatD,KAAK;CACL,MAAM,OAAO;CACb,OAAO,CAAC,WAAW,MAAM,sBAAsB,IAAI,EAAE,QAAQ,KAAK,CAAC,CAAC;;;;;;CAOpE,KAAK;CACL,MAAM,6BAA6B;CACnC,OAAO,CAAC,WAAW,MAAM,sBAAsB,IAAI,EAAE,QAAQ,YAAY,CAAC,CAAC;;;;;;CAO3E,MAAM;CACN,MAAM,IAAI;CACV,OAAO,CAAC,WAAW,MAAM,sBAAsB,OAAO,CAAC,CAAC;;;;;;CAUxD,MAAM;CACN,MAAM,gBAAgB;CACtB,OAAO,CAAC,WAAW,MAAM,sBAAsB,SAAS,EAAE,QAAQ,KAAK,CAAC,CAAC;;;;;;CAWzE,QAAQ;CACR,MAAM,OAAO;CACb,OAAO,CAAC,WAAW,MAAM,sBAAsB,OAAO,EAAE,QAAQ,KAAK,CAAC,CAAC;;;;;;CAxEzE,MAAM,aAAa,WAAW,2BAA2B;CACzD,OAAO,CAAC,cAAc,CAAC,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __exportAll } from "../_virtual/_rolldown/runtime.js";
|
|
2
|
-
import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.
|
|
3
|
-
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
+
import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorateMetadata.js";
|
|
3
|
+
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorate.js";
|
|
4
4
|
import { AppConstants } from "../server.constants.js";
|
|
5
5
|
import { PERMS } from "../constants/authorization.constants.js";
|
|
6
6
|
import { authenticate, permission } from "../middleware/authenticate.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"printer-settings.controller.js","names":[],"sources":["../../src/controllers/printer-settings.controller.ts"],"sourcesContent":["import { before, GET, POST, route } from \"awilix-express\";\nimport { authenticate, permission } from \"@/middleware/authenticate\";\nimport { AppConstants } from \"@/server.constants\";\nimport { PERMS } from \"@/constants/authorization.constants\";\nimport { OctoprintClient } from \"@/services/octoprint/octoprint.client\";\nimport { PrinterCache } from \"@/state/printer.cache\";\nimport type { Request, Response } from \"express\";\nimport { ParamId } from \"@/middleware/param-converter.middleware\";\n\n@route(AppConstants.apiRoute + \"/printer-settings\")\n@before([authenticate()])\nexport class PrinterSettingsController {\n constructor(\n private readonly printerCache: PrinterCache,\n private readonly octoprintClient: OctoprintClient,\n ) {}\n\n @GET()\n @route(\"/:id\")\n @before([permission(PERMS.PrinterSettings.Get), ParamId(\"id\")])\n async get(req: Request, res: Response) {\n const loginDto = await this.printerCache.getLoginDtoAsync(req.local.id);\n const settings = await this.octoprintClient.getSettings(loginDto);\n res.send(settings.data);\n }\n\n @POST()\n @route(\"/:id/sync-printername\")\n @before([ParamId(\"id\")])\n async syncPrinterName(req: Request, res: Response) {\n const printerLogin = await this.printerCache.getLoginDtoAsync(req.local.id);\n const name = await this.printerCache.getNameAsync(req.local.id);\n const settings = await this.octoprintClient.updatePrinterNameSetting(printerLogin, name);\n res.send(settings.data);\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAWO,IAAA,4BAAA,MAAM,0BAA0B;CACrC,YACE,cACA,iBACA;
|
|
1
|
+
{"version":3,"file":"printer-settings.controller.js","names":[],"sources":["../../src/controllers/printer-settings.controller.ts"],"sourcesContent":["import { before, GET, POST, route } from \"awilix-express\";\nimport { authenticate, permission } from \"@/middleware/authenticate\";\nimport { AppConstants } from \"@/server.constants\";\nimport { PERMS } from \"@/constants/authorization.constants\";\nimport { OctoprintClient } from \"@/services/octoprint/octoprint.client\";\nimport { PrinterCache } from \"@/state/printer.cache\";\nimport type { Request, Response } from \"express\";\nimport { ParamId } from \"@/middleware/param-converter.middleware\";\n\n@route(AppConstants.apiRoute + \"/printer-settings\")\n@before([authenticate()])\nexport class PrinterSettingsController {\n constructor(\n private readonly printerCache: PrinterCache,\n private readonly octoprintClient: OctoprintClient,\n ) {}\n\n @GET()\n @route(\"/:id\")\n @before([permission(PERMS.PrinterSettings.Get), ParamId(\"id\")])\n async get(req: Request, res: Response) {\n const loginDto = await this.printerCache.getLoginDtoAsync(req.local.id);\n const settings = await this.octoprintClient.getSettings(loginDto);\n res.send(settings.data);\n }\n\n @POST()\n @route(\"/:id/sync-printername\")\n @before([ParamId(\"id\")])\n async syncPrinterName(req: Request, res: Response) {\n const printerLogin = await this.printerCache.getLoginDtoAsync(req.local.id);\n const name = await this.printerCache.getNameAsync(req.local.id);\n const settings = await this.octoprintClient.updatePrinterNameSetting(printerLogin, name);\n res.send(settings.data);\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAWO,IAAA,4BAAA,MAAM,0BAA0B;CACrC,YACE,cACA,iBACA;EAFiB,KAAA,eAAA;EACA,KAAA,kBAAA;;CAGnB,MAGM,IAAI,KAAc,KAAe;EACrC,MAAM,WAAW,MAAM,KAAK,aAAa,iBAAiB,IAAI,MAAM,GAAG;EACvE,MAAM,WAAW,MAAM,KAAK,gBAAgB,YAAY,SAAS;EACjE,IAAI,KAAK,SAAS,KAAK;;CAGzB,MAGM,gBAAgB,KAAc,KAAe;EACjD,MAAM,eAAe,MAAM,KAAK,aAAa,iBAAiB,IAAI,MAAM,GAAG;EAC3E,MAAM,OAAO,MAAM,KAAK,aAAa,aAAa,IAAI,MAAM,GAAG;EAC/D,MAAM,WAAW,MAAM,KAAK,gBAAgB,yBAAyB,cAAc,KAAK;EACxF,IAAI,KAAK,SAAS,KAAK;;;;CAhBxB,KAAK;CACL,MAAM,OAAO;CACb,OAAO,CAAC,WAAW,MAAM,gBAAgB,IAAI,EAAE,QAAQ,KAAK,CAAC,CAAC;;;;;;CAO9D,MAAM;CACN,MAAM,wBAAwB;CAC9B,OAAO,CAAC,QAAQ,KAAK,CAAC,CAAC;;;;;;CAnBzB,MAAM,aAAa,WAAW,oBAAoB;CAClD,OAAO,CAAC,cAAc,CAAC,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __exportAll } from "../_virtual/_rolldown/runtime.js";
|
|
2
|
-
import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.
|
|
3
|
-
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
+
import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorateMetadata.js";
|
|
3
|
+
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorate.js";
|
|
4
4
|
import { AppConstants } from "../server.constants.js";
|
|
5
5
|
import { ROLES } from "../constants/authorization.constants.js";
|
|
6
6
|
import { authenticate, authorizeRoles } from "../middleware/authenticate.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"printer-tag.controller.js","names":[],"sources":["../../src/controllers/printer-tag.controller.ts"],"sourcesContent":["import type { IPrinterTagService } from \"@/services/interfaces/printer-tag.service.interface\";\nimport { AppConstants } from \"@/server.constants\";\nimport { authenticate, authorizeRoles } from \"@/middleware/authenticate\";\nimport { ROLES } from \"@/constants/authorization.constants\";\nimport type { Request, Response } from \"express\";\nimport { route, before, GET, POST, DELETE, PATCH } from \"awilix-express\";\nimport { ParamId } from \"@/middleware/param-converter.middleware\";\n\n@route(AppConstants.apiRoute + \"/printer-tag\")\n@before([authenticate(), authorizeRoles([ROLES.OPERATOR, ROLES.ADMIN])])\nexport class PrinterTagController {\n constructor(private readonly printerTagService: IPrinterTagService) {}\n\n @GET()\n @route(\"/\")\n async listTags(req: Request, res: Response) {\n res.send(await this.printerTagService.listTags());\n }\n\n @GET()\n @route(\"/:id\")\n @before([ParamId(\"id\")])\n async getTagWithPrinters(req: Request, res: Response) {\n res.send(await this.printerTagService.getPrintersByTag(req.local.id));\n }\n\n @POST()\n @route(\"/\")\n async createTag(req: Request, res: Response) {\n // Safety mechanism against updating\n if (req.body.id) {\n delete req.body.id;\n }\n const entity = await this.printerTagService.createTag(req.body);\n res.send(entity);\n }\n\n @PATCH()\n @route(\"/:id/name\")\n @before([ParamId(\"id\")])\n async updateTagName(req: Request, res: Response) {\n const entity = await this.printerTagService.updateTagName(req.local.id, req.body.name);\n res.send(entity);\n }\n\n @PATCH()\n @route(\"/:id/color\")\n @before([ParamId(\"id\")])\n async updateTagColor(req: Request, res: Response) {\n const entity = await this.printerTagService.updateTagColor(req.local.id, req.body.color);\n res.send(entity);\n }\n\n @DELETE()\n @route(\"/:id\")\n @before([ParamId(\"id\")])\n async deleteTag(req: Request, res: Response) {\n res.send(await this.printerTagService.deleteTag(req.local.id));\n }\n\n @POST()\n @route(\"/:id/printer\")\n @before([ParamId(\"id\")])\n async addPrinterToTag(req: Request, res: Response) {\n const entity = await this.printerTagService.addPrinterToTag(req.local.id, req.body.printerId);\n res.send(this.printerTagService.toDto(entity));\n }\n\n @DELETE()\n @route(\"/:id/printer\")\n @before([ParamId(\"id\")])\n async removePrinterFromTag(req: Request, res: Response) {\n res.send(await this.printerTagService.removePrinterFromTag(req.local.id, req.body.printerId));\n }\n}\n"],"mappings":";;;;;;;;;;AAUO,IAAA,uBAAA,MAAM,qBAAqB;CAChC,YAAY,mBAAwD;
|
|
1
|
+
{"version":3,"file":"printer-tag.controller.js","names":[],"sources":["../../src/controllers/printer-tag.controller.ts"],"sourcesContent":["import type { IPrinterTagService } from \"@/services/interfaces/printer-tag.service.interface\";\nimport { AppConstants } from \"@/server.constants\";\nimport { authenticate, authorizeRoles } from \"@/middleware/authenticate\";\nimport { ROLES } from \"@/constants/authorization.constants\";\nimport type { Request, Response } from \"express\";\nimport { route, before, GET, POST, DELETE, PATCH } from \"awilix-express\";\nimport { ParamId } from \"@/middleware/param-converter.middleware\";\n\n@route(AppConstants.apiRoute + \"/printer-tag\")\n@before([authenticate(), authorizeRoles([ROLES.OPERATOR, ROLES.ADMIN])])\nexport class PrinterTagController {\n constructor(private readonly printerTagService: IPrinterTagService) {}\n\n @GET()\n @route(\"/\")\n async listTags(req: Request, res: Response) {\n res.send(await this.printerTagService.listTags());\n }\n\n @GET()\n @route(\"/:id\")\n @before([ParamId(\"id\")])\n async getTagWithPrinters(req: Request, res: Response) {\n res.send(await this.printerTagService.getPrintersByTag(req.local.id));\n }\n\n @POST()\n @route(\"/\")\n async createTag(req: Request, res: Response) {\n // Safety mechanism against updating\n if (req.body.id) {\n delete req.body.id;\n }\n const entity = await this.printerTagService.createTag(req.body);\n res.send(entity);\n }\n\n @PATCH()\n @route(\"/:id/name\")\n @before([ParamId(\"id\")])\n async updateTagName(req: Request, res: Response) {\n const entity = await this.printerTagService.updateTagName(req.local.id, req.body.name);\n res.send(entity);\n }\n\n @PATCH()\n @route(\"/:id/color\")\n @before([ParamId(\"id\")])\n async updateTagColor(req: Request, res: Response) {\n const entity = await this.printerTagService.updateTagColor(req.local.id, req.body.color);\n res.send(entity);\n }\n\n @DELETE()\n @route(\"/:id\")\n @before([ParamId(\"id\")])\n async deleteTag(req: Request, res: Response) {\n res.send(await this.printerTagService.deleteTag(req.local.id));\n }\n\n @POST()\n @route(\"/:id/printer\")\n @before([ParamId(\"id\")])\n async addPrinterToTag(req: Request, res: Response) {\n const entity = await this.printerTagService.addPrinterToTag(req.local.id, req.body.printerId);\n res.send(this.printerTagService.toDto(entity));\n }\n\n @DELETE()\n @route(\"/:id/printer\")\n @before([ParamId(\"id\")])\n async removePrinterFromTag(req: Request, res: Response) {\n res.send(await this.printerTagService.removePrinterFromTag(req.local.id, req.body.printerId));\n }\n}\n"],"mappings":";;;;;;;;;;AAUO,IAAA,uBAAA,MAAM,qBAAqB;CAChC,YAAY,mBAAwD;EAAvC,KAAA,oBAAA;;CAE7B,MAEM,SAAS,KAAc,KAAe;EAC1C,IAAI,KAAK,MAAM,KAAK,kBAAkB,UAAU,CAAC;;CAGnD,MAGM,mBAAmB,KAAc,KAAe;EACpD,IAAI,KAAK,MAAM,KAAK,kBAAkB,iBAAiB,IAAI,MAAM,GAAG,CAAC;;CAGvE,MAEM,UAAU,KAAc,KAAe;EAE3C,IAAI,IAAI,KAAK,IACX,OAAO,IAAI,KAAK;EAElB,MAAM,SAAS,MAAM,KAAK,kBAAkB,UAAU,IAAI,KAAK;EAC/D,IAAI,KAAK,OAAO;;CAGlB,MAGM,cAAc,KAAc,KAAe;EAC/C,MAAM,SAAS,MAAM,KAAK,kBAAkB,cAAc,IAAI,MAAM,IAAI,IAAI,KAAK,KAAK;EACtF,IAAI,KAAK,OAAO;;CAGlB,MAGM,eAAe,KAAc,KAAe;EAChD,MAAM,SAAS,MAAM,KAAK,kBAAkB,eAAe,IAAI,MAAM,IAAI,IAAI,KAAK,MAAM;EACxF,IAAI,KAAK,OAAO;;CAGlB,MAGM,UAAU,KAAc,KAAe;EAC3C,IAAI,KAAK,MAAM,KAAK,kBAAkB,UAAU,IAAI,MAAM,GAAG,CAAC;;CAGhE,MAGM,gBAAgB,KAAc,KAAe;EACjD,MAAM,SAAS,MAAM,KAAK,kBAAkB,gBAAgB,IAAI,MAAM,IAAI,IAAI,KAAK,UAAU;EAC7F,IAAI,KAAK,KAAK,kBAAkB,MAAM,OAAO,CAAC;;CAGhD,MAGM,qBAAqB,KAAc,KAAe;EACtD,IAAI,KAAK,MAAM,KAAK,kBAAkB,qBAAqB,IAAI,MAAM,IAAI,IAAI,KAAK,UAAU,CAAC;;;;CA3D9F,KAAK;CACL,MAAM,IAAI;;;;;;CAKV,KAAK;CACL,MAAM,OAAO;CACb,OAAO,CAAC,QAAQ,KAAK,CAAC,CAAC;;;;;;CAKvB,MAAM;CACN,MAAM,IAAI;;;;;;CAUV,OAAO;CACP,MAAM,YAAY;CAClB,OAAO,CAAC,QAAQ,KAAK,CAAC,CAAC;;;;;;CAMvB,OAAO;CACP,MAAM,aAAa;CACnB,OAAO,CAAC,QAAQ,KAAK,CAAC,CAAC;;;;;;CAMvB,QAAQ;CACR,MAAM,OAAO;CACb,OAAO,CAAC,QAAQ,KAAK,CAAC,CAAC;;;;;;CAKvB,MAAM;CACN,MAAM,eAAe;CACrB,OAAO,CAAC,QAAQ,KAAK,CAAC,CAAC;;;;;;CAMvB,QAAQ;CACR,MAAM,eAAe;CACrB,OAAO,CAAC,QAAQ,KAAK,CAAC,CAAC;;;;;;CA9DzB,MAAM,aAAa,WAAW,eAAe;CAC7C,OAAO,CAAC,cAAc,EAAE,eAAe,CAAC,MAAM,UAAU,MAAM,MAAM,CAAC,CAAC,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __exportAll } from "../_virtual/_rolldown/runtime.js";
|
|
2
|
-
import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.
|
|
3
|
-
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
+
import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorateMetadata.js";
|
|
3
|
+
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorate.js";
|
|
4
4
|
import { InternalServerException } from "../exceptions/runtime.exceptions.js";
|
|
5
5
|
import { validateInput, validateMiddleware } from "../handlers/validators.js";
|
|
6
6
|
import { defaultHttpProtocol } from "../utils/url.utils.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"printer.controller.js","names":[],"sources":["../../src/controllers/printer.controller.ts"],"sourcesContent":["import { before, DELETE, GET, PATCH, POST, route } from \"awilix-express\";\nimport { authenticate, authorizeRoles } from \"@/middleware/authenticate\";\nimport { validateInput, validateMiddleware } from \"@/handlers/validators\";\nimport {\n feedRateSchema,\n flowRateSchema,\n testPrinterApiSchema,\n updatePrinterDisabledReasonSchema,\n updatePrinterEnabledSchema,\n} from \"./validation/printer-controller.validation\";\nimport { AppConstants } from \"@/server.constants\";\nimport { printerResolveMiddleware } from \"@/middleware/printer\";\nimport { generateCorrelationToken } from \"@/utils/correlation-token.util\";\nimport { ROLES } from \"@/constants/authorization.constants\";\nimport { PrinterSocketStore } from \"@/state/printer-socket.store\";\nimport { TestPrinterSocketStore } from \"@/state/test-printer-socket.store\";\nimport { PrinterCache } from \"@/state/printer.cache\";\nimport { LoggerService } from \"@/handlers/logger\";\nimport { PrinterEventsCache } from \"@/state/printer-events.cache\";\nimport { FloorStore } from \"@/state/floor.store\";\nimport type { ILoggerFactory } from \"@/handlers/logger-factory\";\nimport type { Request, Response } from \"express\";\nimport type { IPrinterService } from \"@/services/interfaces/printer.service.interface\";\nimport type { LoginDto } from \"@/services/interfaces/login.dto\";\nimport { AxiosError } from \"axios\";\nimport { FailedDependencyException } from \"@/exceptions/failed-dependency.exception\";\nimport { InternalServerException } from \"@/exceptions/runtime.exceptions\";\nimport type { IPrinterApi } from \"@/services/printer-api.interface\";\nimport { PrinterApiFactory } from \"@/services/printer-api.factory\";\nimport { normalizeUrl } from \"@/utils/normalize-url\";\nimport { defaultHttpProtocol } from \"@/utils/url.utils\";\nimport { getScopedPrinter } from \"@/middleware/printer-resolver\";\n\n@route(AppConstants.apiRoute + \"/printer\")\n@before([authenticate(), authorizeRoles([ROLES.OPERATOR, ROLES.ADMIN]), printerResolveMiddleware()])\nexport class PrinterController {\n logger: LoggerService;\n\n constructor(\n loggerFactory: ILoggerFactory,\n private readonly printerApiFactory: PrinterApiFactory,\n private readonly printerSocketStore: PrinterSocketStore,\n private readonly testPrinterSocketStore: TestPrinterSocketStore,\n private readonly printerService: IPrinterService,\n private readonly printerCache: PrinterCache,\n private readonly printerEventsCache: PrinterEventsCache,\n private readonly printerApi: IPrinterApi,\n private readonly floorStore: FloorStore,\n ) {\n this.logger = loggerFactory(PrinterController.name);\n }\n\n @GET()\n @route(\"/\")\n async list(req: Request, res: Response) {\n res.send(await this.printerCache.listCachedPrinters(true));\n }\n\n @POST()\n @route(\"/\")\n async create(req: Request, res: Response) {\n const newPrinter = req.body;\n if (req.query.forceSave !== \"true\") {\n await this.testPrintApiConnection(newPrinter);\n }\n\n // Has internal validation, but might add some here above as well\n const createdPrinter = await this.printerService.create(newPrinter);\n res.send(await this.printerCache.getCachedPrinterOrThrowAsync(createdPrinter.id));\n }\n\n @POST()\n @route(\"/batch\")\n async createBatch(req: Request, res: Response) {\n const importResult = await this.printerService.batchImport(req.body);\n res.send(importResult);\n }\n\n @GET()\n @route(\"/:id\")\n async getPrinter(req: Request, res: Response) {\n const { currentPrinterId } = getScopedPrinter(req);\n res.send(await this.printerCache.getCachedPrinterOrThrowAsync(currentPrinterId));\n }\n\n @GET()\n @route(\"/:id/socket\")\n async getPrinterSocketInfo(req: Request, res: Response) {\n const { currentPrinterId } = getScopedPrinter(req);\n res.send(await this.printerEventsCache.getPrinterSocketEvents(currentPrinterId));\n }\n\n @PATCH()\n @route(\"/:id\")\n async update(req: Request, res: Response) {\n const forceSave = req.query.forceSave === \"true\";\n\n // Update the printer entity: printerURL, name, apiKey, enabled\n const { currentPrinterId } = getScopedPrinter(req);\n const updatedPrinter = req.body;\n if (!forceSave) {\n await this.testPrintApiConnection(updatedPrinter);\n }\n\n await this.printerService.update(currentPrinterId, updatedPrinter);\n res.send(await this.printerCache.getCachedPrinterOrThrowAsync(currentPrinterId));\n }\n\n @DELETE()\n @route(\"/:id\")\n async delete(req: Request, res: Response) {\n const { currentPrinterId } = getScopedPrinter(req);\n await this.printerService.delete(currentPrinterId);\n await this.floorStore.removePrinterFromAnyFloor(currentPrinterId);\n res.send();\n }\n\n @PATCH()\n @route(\"/:id/enabled\")\n async updateEnabled(req: Request, res: Response) {\n const { currentPrinterId } = getScopedPrinter(req);\n const data = await validateMiddleware(req, updatePrinterEnabledSchema);\n await this.printerService.updateEnabled(currentPrinterId, data.enabled);\n res.send({});\n }\n\n @PATCH()\n @route(\"/:id/disabled-reason\")\n async updatePrinterDisabledReason(req: Request, res: Response) {\n const { currentPrinterId } = getScopedPrinter(req);\n const data = await validateMiddleware(req, updatePrinterDisabledReasonSchema);\n await this.printerService.updateDisabledReason(currentPrinterId, data.disabledReason);\n res.send({});\n }\n\n @POST()\n @route(\"/:id/refresh-socket\")\n async refreshPrinterSocket(req: Request, res: Response) {\n const { currentPrinterId } = getScopedPrinter(req);\n this.printerSocketStore.reconnectPrinterAdapter(currentPrinterId);\n await this.printerEventsCache.deletePrinterSocketEvents(currentPrinterId);\n res.send({});\n }\n\n @POST()\n @route(\"/test-connection\")\n async testConnection(req: Request, res: Response) {\n if (req.body.printerURL?.length) {\n req.body.printerURL = normalizeUrl(req.body.printerURL, { defaultProtocol: defaultHttpProtocol });\n }\n const newPrinter = await validateMiddleware(req, testPrinterApiSchema);\n const printerCorrelationToken = generateCorrelationToken();\n this.logger.log(`Testing printer with correlation token ${printerCorrelationToken}`);\n\n try {\n await this.testPrinterSocketStore.setupTestPrinter(printerCorrelationToken, newPrinter);\n } catch (e) {\n res.send({ correlationToken: printerCorrelationToken, failure: true, error: (e as Error).toString() });\n return;\n }\n res.send({ correlationToken: printerCorrelationToken });\n }\n\n @GET()\n @route(\"/:id/login-details\")\n async getPrinterLoginDetails(req: Request, res: Response) {\n const { printerLogin } = getScopedPrinter(req);\n res.send(printerLogin);\n }\n\n /**\n * Sends gcode according to https://docs.octoprint.org/en/master/api/printer.html#send-an-arbitrary-command-to-the-printer\n */\n @POST()\n @route(\"/:id/send-emergency-m112\")\n async sendEmergencyM112(req: Request, res: Response) {\n const { printerApi } = getScopedPrinter(req);\n await printerApi.quickStop();\n res.send();\n }\n\n @POST()\n @route(\"/:id/serial-connect\")\n async sendSerialConnectCommand(req: Request, res: Response) {\n await this.printerApi.connect();\n res.send({});\n }\n\n @POST()\n @route(\"/:id/serial-disconnect\")\n async sendSerialDisconnectCommand(req: Request, res: Response) {\n await this.printerApi.disconnect();\n res.send({});\n }\n\n @POST()\n @route(\"/:id/jog\")\n @route(\"/:id/move\")\n async movePrintHead(req: Request, res: Response) {\n await this.printerApi.movePrintHead(req.body);\n res.send({});\n }\n\n @POST()\n @route(\"/:id/home\")\n async homeAxes(req: Request, res: Response) {\n await this.printerApi.homeAxes(req.body);\n res.send({});\n }\n\n @POST()\n @route(\"/:id/job/pause\")\n async pausePrint(req: Request, res: Response) {\n await this.printerApi.pausePrint();\n res.send({});\n }\n\n @POST()\n @route(\"/:id/job/resume\")\n async resumePrint(req: Request, res: Response) {\n await this.printerApi.resumePrint();\n res.send({});\n }\n\n @POST()\n @route(\"/:id/job/stop\")\n @route(\"/:id/job/cancel\")\n async cancelPrint(req: Request, res: Response) {\n await this.printerApi.cancelPrint();\n res.send({});\n }\n\n @POST()\n @route(\"/:id/octoprint/server/restart\")\n @route(\"/:id/server/restart\")\n async restartServer(req: Request, res: Response) {\n await this.printerApi.restartServer();\n res.send();\n }\n\n @PATCH()\n @route(\"/:id/feed-rate\")\n async setFeedRate(req: Request, res: Response) {\n const { currentPrinterId } = getScopedPrinter(req);\n const data = await validateMiddleware(req, feedRateSchema);\n\n await this.printerService.updateFeedRate(currentPrinterId, data.feedRate);\n res.send({});\n }\n\n @PATCH()\n @route(\"/:id/flow-rate\")\n async setFlowRate(req: Request, res: Response) {\n const { currentPrinterId } = getScopedPrinter(req);\n const data = await validateMiddleware(req, flowRateSchema);\n await this.printerService.updateFlowRate(currentPrinterId, data.flowRate);\n res.send({});\n }\n\n private async testPrintApiConnection(inputLoginDto: LoginDto) {\n await validateInput(inputLoginDto, testPrinterApiSchema);\n\n this.logger.debug(`Testing API connection for printer type: ${inputLoginDto.printerType}`, inputLoginDto);\n\n try {\n const printerApi = this.printerApiFactory.getScopedPrinter(inputLoginDto);\n await printerApi.validateConnection();\n this.logger.debug(\"Connection validation completed successfully\");\n } catch (e) {\n this.logger.log(\"Printer connection validation failed\");\n this.logger.debug(`Error details: ${e}`);\n\n if (e instanceof AxiosError) {\n this.logger.debug(e.message + \" \" + e.status + \" \" + e.response?.status);\n switch (e.response?.status) {\n case 404:\n // Bad code design or wrong service type\n break;\n case 401:\n case 403: {\n throw new FailedDependencyException(\"Authentication failed\", e.response?.status);\n }\n case 0:\n case 502:\n case 503: {\n throw new FailedDependencyException(\"Printer service unreachable\", e.response?.status);\n }\n default: {\n if (e.response?.status) {\n throw new FailedDependencyException(\n `Reaching Printer service failed with status (code ${e.code})`,\n e.response?.status,\n );\n } else {\n // F.e. http://localhost:1324\n // ENOTFOUND: DNS problem\n // ECONNREFUSED: Port has no socket bound\n // ERR_BAD_REQUEST\n throw new FailedDependencyException(`Reaching Printer service failed without status (code ${e.code})`);\n }\n }\n }\n }\n\n throw new InternalServerException(`Could not call Printer service, internal problem`, (e as Error).stack);\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"printer.controller.js","names":[],"sources":["../../src/controllers/printer.controller.ts"],"sourcesContent":["import { before, DELETE, GET, PATCH, POST, route } from \"awilix-express\";\nimport { authenticate, authorizeRoles } from \"@/middleware/authenticate\";\nimport { validateInput, validateMiddleware } from \"@/handlers/validators\";\nimport {\n feedRateSchema,\n flowRateSchema,\n testPrinterApiSchema,\n updatePrinterDisabledReasonSchema,\n updatePrinterEnabledSchema,\n} from \"./validation/printer-controller.validation\";\nimport { AppConstants } from \"@/server.constants\";\nimport { printerResolveMiddleware } from \"@/middleware/printer\";\nimport { generateCorrelationToken } from \"@/utils/correlation-token.util\";\nimport { ROLES } from \"@/constants/authorization.constants\";\nimport { PrinterSocketStore } from \"@/state/printer-socket.store\";\nimport { TestPrinterSocketStore } from \"@/state/test-printer-socket.store\";\nimport { PrinterCache } from \"@/state/printer.cache\";\nimport { LoggerService } from \"@/handlers/logger\";\nimport { PrinterEventsCache } from \"@/state/printer-events.cache\";\nimport { FloorStore } from \"@/state/floor.store\";\nimport type { ILoggerFactory } from \"@/handlers/logger-factory\";\nimport type { Request, Response } from \"express\";\nimport type { IPrinterService } from \"@/services/interfaces/printer.service.interface\";\nimport type { LoginDto } from \"@/services/interfaces/login.dto\";\nimport { AxiosError } from \"axios\";\nimport { FailedDependencyException } from \"@/exceptions/failed-dependency.exception\";\nimport { InternalServerException } from \"@/exceptions/runtime.exceptions\";\nimport type { IPrinterApi } from \"@/services/printer-api.interface\";\nimport { PrinterApiFactory } from \"@/services/printer-api.factory\";\nimport { normalizeUrl } from \"@/utils/normalize-url\";\nimport { defaultHttpProtocol } from \"@/utils/url.utils\";\nimport { getScopedPrinter } from \"@/middleware/printer-resolver\";\n\n@route(AppConstants.apiRoute + \"/printer\")\n@before([authenticate(), authorizeRoles([ROLES.OPERATOR, ROLES.ADMIN]), printerResolveMiddleware()])\nexport class PrinterController {\n logger: LoggerService;\n\n constructor(\n loggerFactory: ILoggerFactory,\n private readonly printerApiFactory: PrinterApiFactory,\n private readonly printerSocketStore: PrinterSocketStore,\n private readonly testPrinterSocketStore: TestPrinterSocketStore,\n private readonly printerService: IPrinterService,\n private readonly printerCache: PrinterCache,\n private readonly printerEventsCache: PrinterEventsCache,\n private readonly printerApi: IPrinterApi,\n private readonly floorStore: FloorStore,\n ) {\n this.logger = loggerFactory(PrinterController.name);\n }\n\n @GET()\n @route(\"/\")\n async list(req: Request, res: Response) {\n res.send(await this.printerCache.listCachedPrinters(true));\n }\n\n @POST()\n @route(\"/\")\n async create(req: Request, res: Response) {\n const newPrinter = req.body;\n if (req.query.forceSave !== \"true\") {\n await this.testPrintApiConnection(newPrinter);\n }\n\n // Has internal validation, but might add some here above as well\n const createdPrinter = await this.printerService.create(newPrinter);\n res.send(await this.printerCache.getCachedPrinterOrThrowAsync(createdPrinter.id));\n }\n\n @POST()\n @route(\"/batch\")\n async createBatch(req: Request, res: Response) {\n const importResult = await this.printerService.batchImport(req.body);\n res.send(importResult);\n }\n\n @GET()\n @route(\"/:id\")\n async getPrinter(req: Request, res: Response) {\n const { currentPrinterId } = getScopedPrinter(req);\n res.send(await this.printerCache.getCachedPrinterOrThrowAsync(currentPrinterId));\n }\n\n @GET()\n @route(\"/:id/socket\")\n async getPrinterSocketInfo(req: Request, res: Response) {\n const { currentPrinterId } = getScopedPrinter(req);\n res.send(await this.printerEventsCache.getPrinterSocketEvents(currentPrinterId));\n }\n\n @PATCH()\n @route(\"/:id\")\n async update(req: Request, res: Response) {\n const forceSave = req.query.forceSave === \"true\";\n\n // Update the printer entity: printerURL, name, apiKey, enabled\n const { currentPrinterId } = getScopedPrinter(req);\n const updatedPrinter = req.body;\n if (!forceSave) {\n await this.testPrintApiConnection(updatedPrinter);\n }\n\n await this.printerService.update(currentPrinterId, updatedPrinter);\n res.send(await this.printerCache.getCachedPrinterOrThrowAsync(currentPrinterId));\n }\n\n @DELETE()\n @route(\"/:id\")\n async delete(req: Request, res: Response) {\n const { currentPrinterId } = getScopedPrinter(req);\n await this.printerService.delete(currentPrinterId);\n await this.floorStore.removePrinterFromAnyFloor(currentPrinterId);\n res.send();\n }\n\n @PATCH()\n @route(\"/:id/enabled\")\n async updateEnabled(req: Request, res: Response) {\n const { currentPrinterId } = getScopedPrinter(req);\n const data = await validateMiddleware(req, updatePrinterEnabledSchema);\n await this.printerService.updateEnabled(currentPrinterId, data.enabled);\n res.send({});\n }\n\n @PATCH()\n @route(\"/:id/disabled-reason\")\n async updatePrinterDisabledReason(req: Request, res: Response) {\n const { currentPrinterId } = getScopedPrinter(req);\n const data = await validateMiddleware(req, updatePrinterDisabledReasonSchema);\n await this.printerService.updateDisabledReason(currentPrinterId, data.disabledReason);\n res.send({});\n }\n\n @POST()\n @route(\"/:id/refresh-socket\")\n async refreshPrinterSocket(req: Request, res: Response) {\n const { currentPrinterId } = getScopedPrinter(req);\n this.printerSocketStore.reconnectPrinterAdapter(currentPrinterId);\n await this.printerEventsCache.deletePrinterSocketEvents(currentPrinterId);\n res.send({});\n }\n\n @POST()\n @route(\"/test-connection\")\n async testConnection(req: Request, res: Response) {\n if (req.body.printerURL?.length) {\n req.body.printerURL = normalizeUrl(req.body.printerURL, { defaultProtocol: defaultHttpProtocol });\n }\n const newPrinter = await validateMiddleware(req, testPrinterApiSchema);\n const printerCorrelationToken = generateCorrelationToken();\n this.logger.log(`Testing printer with correlation token ${printerCorrelationToken}`);\n\n try {\n await this.testPrinterSocketStore.setupTestPrinter(printerCorrelationToken, newPrinter);\n } catch (e) {\n res.send({ correlationToken: printerCorrelationToken, failure: true, error: (e as Error).toString() });\n return;\n }\n res.send({ correlationToken: printerCorrelationToken });\n }\n\n @GET()\n @route(\"/:id/login-details\")\n async getPrinterLoginDetails(req: Request, res: Response) {\n const { printerLogin } = getScopedPrinter(req);\n res.send(printerLogin);\n }\n\n /**\n * Sends gcode according to https://docs.octoprint.org/en/master/api/printer.html#send-an-arbitrary-command-to-the-printer\n */\n @POST()\n @route(\"/:id/send-emergency-m112\")\n async sendEmergencyM112(req: Request, res: Response) {\n const { printerApi } = getScopedPrinter(req);\n await printerApi.quickStop();\n res.send();\n }\n\n @POST()\n @route(\"/:id/serial-connect\")\n async sendSerialConnectCommand(req: Request, res: Response) {\n await this.printerApi.connect();\n res.send({});\n }\n\n @POST()\n @route(\"/:id/serial-disconnect\")\n async sendSerialDisconnectCommand(req: Request, res: Response) {\n await this.printerApi.disconnect();\n res.send({});\n }\n\n @POST()\n @route(\"/:id/jog\")\n @route(\"/:id/move\")\n async movePrintHead(req: Request, res: Response) {\n await this.printerApi.movePrintHead(req.body);\n res.send({});\n }\n\n @POST()\n @route(\"/:id/home\")\n async homeAxes(req: Request, res: Response) {\n await this.printerApi.homeAxes(req.body);\n res.send({});\n }\n\n @POST()\n @route(\"/:id/job/pause\")\n async pausePrint(req: Request, res: Response) {\n await this.printerApi.pausePrint();\n res.send({});\n }\n\n @POST()\n @route(\"/:id/job/resume\")\n async resumePrint(req: Request, res: Response) {\n await this.printerApi.resumePrint();\n res.send({});\n }\n\n @POST()\n @route(\"/:id/job/stop\")\n @route(\"/:id/job/cancel\")\n async cancelPrint(req: Request, res: Response) {\n await this.printerApi.cancelPrint();\n res.send({});\n }\n\n @POST()\n @route(\"/:id/octoprint/server/restart\")\n @route(\"/:id/server/restart\")\n async restartServer(req: Request, res: Response) {\n await this.printerApi.restartServer();\n res.send();\n }\n\n @PATCH()\n @route(\"/:id/feed-rate\")\n async setFeedRate(req: Request, res: Response) {\n const { currentPrinterId } = getScopedPrinter(req);\n const data = await validateMiddleware(req, feedRateSchema);\n\n await this.printerService.updateFeedRate(currentPrinterId, data.feedRate);\n res.send({});\n }\n\n @PATCH()\n @route(\"/:id/flow-rate\")\n async setFlowRate(req: Request, res: Response) {\n const { currentPrinterId } = getScopedPrinter(req);\n const data = await validateMiddleware(req, flowRateSchema);\n await this.printerService.updateFlowRate(currentPrinterId, data.flowRate);\n res.send({});\n }\n\n private async testPrintApiConnection(inputLoginDto: LoginDto) {\n await validateInput(inputLoginDto, testPrinterApiSchema);\n\n this.logger.debug(`Testing API connection for printer type: ${inputLoginDto.printerType}`, inputLoginDto);\n\n try {\n const printerApi = this.printerApiFactory.getScopedPrinter(inputLoginDto);\n await printerApi.validateConnection();\n this.logger.debug(\"Connection validation completed successfully\");\n } catch (e) {\n this.logger.log(\"Printer connection validation failed\");\n this.logger.debug(`Error details: ${e}`);\n\n if (e instanceof AxiosError) {\n this.logger.debug(e.message + \" \" + e.status + \" \" + e.response?.status);\n switch (e.response?.status) {\n case 404:\n // Bad code design or wrong service type\n break;\n case 401:\n case 403: {\n throw new FailedDependencyException(\"Authentication failed\", e.response?.status);\n }\n case 0:\n case 502:\n case 503: {\n throw new FailedDependencyException(\"Printer service unreachable\", e.response?.status);\n }\n default: {\n if (e.response?.status) {\n throw new FailedDependencyException(\n `Reaching Printer service failed with status (code ${e.code})`,\n e.response?.status,\n );\n } else {\n // F.e. http://localhost:1324\n // ENOTFOUND: DNS problem\n // ECONNREFUSED: Port has no socket bound\n // ERR_BAD_REQUEST\n throw new FailedDependencyException(`Reaching Printer service failed without status (code ${e.code})`);\n }\n }\n }\n }\n\n throw new InternalServerException(`Could not call Printer service, internal problem`, (e as Error).stack);\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __exportAll } from "../_virtual/_rolldown/runtime.js";
|
|
2
|
-
import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.
|
|
3
|
-
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
+
import { __decorateMetadata } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorateMetadata.js";
|
|
3
|
+
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.129.0/helpers/decorate.js";
|
|
4
4
|
import { validateMiddleware } from "../handlers/validators.js";
|
|
5
5
|
import { AppConstants } from "../server.constants.js";
|
|
6
6
|
import { ServerReleaseService } from "../services/core/server-release.service.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-private.controller.js","names":[],"sources":["../../src/controllers/server-private.controller.ts"],"sourcesContent":["import { PassThrough } from \"node:stream\";\nimport { before, DELETE, GET, POST, route } from \"awilix-express\";\nimport { authenticate, authorizeRoles } from \"@/middleware/authenticate\";\nimport { AppConstants } from \"@/server.constants\";\nimport { ROLES } from \"@/constants/authorization.constants\";\nimport { validateMiddleware } from \"@/handlers/validators\";\nimport { ServerReleaseService } from \"@/services/core/server-release.service\";\nimport { ClientBundleService } from \"@/services/core/client-bundle.service\";\nimport { PrinterCache } from \"@/state/printer.cache\";\nimport { YamlService } from \"@/services/core/yaml.service\";\nimport { MulterService } from \"@/services/core/multer.service\";\nimport { LogDumpService } from \"@/services/core/logs-manager.service\";\nimport type { Request, Response } from \"express\";\nimport { demoUserNotAllowed } from \"@/middleware/demo.middleware\";\nimport { GithubService } from \"@/services/core/github.service\";\nimport type { IPrinterService } from \"@/services/interfaces/printer.service.interface\";\nimport { updateClientBundleSchema } from \"@/controllers/validation/server-private.validation\";\nimport type { ILoggerFactory } from \"@/handlers/logger-factory\";\n\n@route(AppConstants.apiRoute + \"/server\")\n@before([authenticate(), authorizeRoles([ROLES.ADMIN]), demoUserNotAllowed])\nexport class ServerPrivateController {\n private readonly logger;\n\n constructor(\n loggerFactory: ILoggerFactory,\n private readonly serverReleaseService: ServerReleaseService,\n private readonly printerCache: PrinterCache,\n private readonly printerService: IPrinterService,\n private readonly clientBundleService: ClientBundleService,\n private readonly githubService: GithubService,\n private readonly logDumpService: LogDumpService,\n private readonly yamlService: YamlService,\n private readonly multerService: MulterService,\n ) {\n this.logger = loggerFactory(ServerPrivateController.name);\n }\n\n @GET()\n @route(\"/\")\n async getReleaseStateInfo(req: Request, res: Response) {\n await this.serverReleaseService.syncLatestRelease();\n const updateState = this.serverReleaseService.getState();\n res.send(updateState);\n }\n\n @GET()\n @route(\"/client-releases\")\n async getClientReleases(_req: Request, res: Response) {\n const releaseSpec = await this.clientBundleService.getReleases();\n res.send(releaseSpec);\n }\n\n /**\n * It is not advised to downgrade beyond the default minimum version, any server restart will\n * update the bundle back to minimum version (if ENABLE_CLIENT_DIST_AUTO_UPDATE === 'true').\n */\n @POST()\n @route(\"/update-client-bundle-github\")\n async updateClientBundleGithub(req: Request, res: Response) {\n const updateDto = await validateMiddleware(req, updateClientBundleSchema);\n\n const willExecute = await this.clientBundleService.shouldUpdateWithReason(\n true,\n AppConstants.defaultClientMinimum,\n updateDto.downloadRelease,\n updateDto.allowDowngrade,\n );\n\n this.logger.log(`Will execute: ${willExecute?.shouldUpdate}, reason: ${willExecute?.reason}`);\n if (!willExecute?.shouldUpdate) {\n return res.send({\n executed: false,\n requestedVersion: willExecute.requestedVersion,\n currentVersion: willExecute.currentVersion,\n minimumVersion: willExecute.minimumVersion,\n shouldUpdate: willExecute.shouldUpdate,\n targetVersion: willExecute.targetVersion,\n reason: willExecute?.reason,\n });\n }\n\n if (willExecute.targetVersion) {\n await this.clientBundleService.downloadClientUpdate(willExecute.targetVersion);\n }\n\n return res.send({\n executed: true,\n requestedVersion: willExecute.requestedVersion,\n currentVersion: willExecute.currentVersion,\n minimumVersion: willExecute.minimumVersion,\n shouldUpdate: willExecute.shouldUpdate,\n targetVersion: willExecute.targetVersion,\n reason: willExecute?.reason,\n });\n }\n\n @GET()\n @route(\"/github-rate-limit\")\n async getGithubRateLimit(req: Request, res: Response) {\n const rateLimitResponse = await this.githubService.getRateLimit();\n res.send(rateLimitResponse.data);\n }\n\n @POST()\n @route(\"/yaml-import\")\n async importYaml(req: Request, res: Response) {\n const files = await this.multerService.multerLoadFileAsync(req, res, [\".yaml\", \".yml\"], false);\n const firstFile = files[0];\n const spec = await this.yamlService.importYaml(firstFile.buffer.toString());\n\n res.send({\n success: true,\n spec,\n });\n }\n\n @POST()\n @route(\"/yaml-export\")\n async exportYaml(req: Request, res: Response) {\n const yaml = await this.yamlService.exportYaml(req.body);\n const fileContents = Buffer.from(yaml);\n const readStream = new PassThrough();\n readStream.end(fileContents);\n\n const fileName = `export-${AppConstants.serverRepoName}-` + Date.now() + \".yaml\";\n res.set(\"Content-disposition\", \"attachment; filename=\" + fileName);\n res.set(\"Content-Type\", \"text/plain\");\n readStream.pipe(res);\n }\n\n @DELETE()\n @route(\"/delete-all-printers\")\n async deleteAllPrinters(req: Request, res: Response) {\n const printers = await this.printerCache.listCachedPrinters(true);\n const printerIds = printers.map((p) => p.id);\n await this.printerService.deleteMany(printerIds);\n res.send();\n }\n\n @DELETE()\n @route(\"/clear-outdated-fdm-monster-logs\")\n async clearLogs(req: Request, res: Response) {\n const counts = await this.logDumpService.deleteOlderThanWeekAndMismatchingLogFiles();\n res.send(counts);\n }\n\n @GET()\n @POST()\n @route(\"/dump-fdm-monster-logs\")\n async dumpLogZips(req: Request, res: Response) {\n const filePath = await this.logDumpService.dumpZip();\n res.sendFile(filePath);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAqBO,IAAA,0BAAA,2BAAA,MAAM,wBAAwB;CACnC;CAEA,YACE,eACA,sBACA,cACA,gBACA,qBACA,eACA,gBACA,aACA,eACA;
|
|
1
|
+
{"version":3,"file":"server-private.controller.js","names":[],"sources":["../../src/controllers/server-private.controller.ts"],"sourcesContent":["import { PassThrough } from \"node:stream\";\nimport { before, DELETE, GET, POST, route } from \"awilix-express\";\nimport { authenticate, authorizeRoles } from \"@/middleware/authenticate\";\nimport { AppConstants } from \"@/server.constants\";\nimport { ROLES } from \"@/constants/authorization.constants\";\nimport { validateMiddleware } from \"@/handlers/validators\";\nimport { ServerReleaseService } from \"@/services/core/server-release.service\";\nimport { ClientBundleService } from \"@/services/core/client-bundle.service\";\nimport { PrinterCache } from \"@/state/printer.cache\";\nimport { YamlService } from \"@/services/core/yaml.service\";\nimport { MulterService } from \"@/services/core/multer.service\";\nimport { LogDumpService } from \"@/services/core/logs-manager.service\";\nimport type { Request, Response } from \"express\";\nimport { demoUserNotAllowed } from \"@/middleware/demo.middleware\";\nimport { GithubService } from \"@/services/core/github.service\";\nimport type { IPrinterService } from \"@/services/interfaces/printer.service.interface\";\nimport { updateClientBundleSchema } from \"@/controllers/validation/server-private.validation\";\nimport type { ILoggerFactory } from \"@/handlers/logger-factory\";\n\n@route(AppConstants.apiRoute + \"/server\")\n@before([authenticate(), authorizeRoles([ROLES.ADMIN]), demoUserNotAllowed])\nexport class ServerPrivateController {\n private readonly logger;\n\n constructor(\n loggerFactory: ILoggerFactory,\n private readonly serverReleaseService: ServerReleaseService,\n private readonly printerCache: PrinterCache,\n private readonly printerService: IPrinterService,\n private readonly clientBundleService: ClientBundleService,\n private readonly githubService: GithubService,\n private readonly logDumpService: LogDumpService,\n private readonly yamlService: YamlService,\n private readonly multerService: MulterService,\n ) {\n this.logger = loggerFactory(ServerPrivateController.name);\n }\n\n @GET()\n @route(\"/\")\n async getReleaseStateInfo(req: Request, res: Response) {\n await this.serverReleaseService.syncLatestRelease();\n const updateState = this.serverReleaseService.getState();\n res.send(updateState);\n }\n\n @GET()\n @route(\"/client-releases\")\n async getClientReleases(_req: Request, res: Response) {\n const releaseSpec = await this.clientBundleService.getReleases();\n res.send(releaseSpec);\n }\n\n /**\n * It is not advised to downgrade beyond the default minimum version, any server restart will\n * update the bundle back to minimum version (if ENABLE_CLIENT_DIST_AUTO_UPDATE === 'true').\n */\n @POST()\n @route(\"/update-client-bundle-github\")\n async updateClientBundleGithub(req: Request, res: Response) {\n const updateDto = await validateMiddleware(req, updateClientBundleSchema);\n\n const willExecute = await this.clientBundleService.shouldUpdateWithReason(\n true,\n AppConstants.defaultClientMinimum,\n updateDto.downloadRelease,\n updateDto.allowDowngrade,\n );\n\n this.logger.log(`Will execute: ${willExecute?.shouldUpdate}, reason: ${willExecute?.reason}`);\n if (!willExecute?.shouldUpdate) {\n return res.send({\n executed: false,\n requestedVersion: willExecute.requestedVersion,\n currentVersion: willExecute.currentVersion,\n minimumVersion: willExecute.minimumVersion,\n shouldUpdate: willExecute.shouldUpdate,\n targetVersion: willExecute.targetVersion,\n reason: willExecute?.reason,\n });\n }\n\n if (willExecute.targetVersion) {\n await this.clientBundleService.downloadClientUpdate(willExecute.targetVersion);\n }\n\n return res.send({\n executed: true,\n requestedVersion: willExecute.requestedVersion,\n currentVersion: willExecute.currentVersion,\n minimumVersion: willExecute.minimumVersion,\n shouldUpdate: willExecute.shouldUpdate,\n targetVersion: willExecute.targetVersion,\n reason: willExecute?.reason,\n });\n }\n\n @GET()\n @route(\"/github-rate-limit\")\n async getGithubRateLimit(req: Request, res: Response) {\n const rateLimitResponse = await this.githubService.getRateLimit();\n res.send(rateLimitResponse.data);\n }\n\n @POST()\n @route(\"/yaml-import\")\n async importYaml(req: Request, res: Response) {\n const files = await this.multerService.multerLoadFileAsync(req, res, [\".yaml\", \".yml\"], false);\n const firstFile = files[0];\n const spec = await this.yamlService.importYaml(firstFile.buffer.toString());\n\n res.send({\n success: true,\n spec,\n });\n }\n\n @POST()\n @route(\"/yaml-export\")\n async exportYaml(req: Request, res: Response) {\n const yaml = await this.yamlService.exportYaml(req.body);\n const fileContents = Buffer.from(yaml);\n const readStream = new PassThrough();\n readStream.end(fileContents);\n\n const fileName = `export-${AppConstants.serverRepoName}-` + Date.now() + \".yaml\";\n res.set(\"Content-disposition\", \"attachment; filename=\" + fileName);\n res.set(\"Content-Type\", \"text/plain\");\n readStream.pipe(res);\n }\n\n @DELETE()\n @route(\"/delete-all-printers\")\n async deleteAllPrinters(req: Request, res: Response) {\n const printers = await this.printerCache.listCachedPrinters(true);\n const printerIds = printers.map((p) => p.id);\n await this.printerService.deleteMany(printerIds);\n res.send();\n }\n\n @DELETE()\n @route(\"/clear-outdated-fdm-monster-logs\")\n async clearLogs(req: Request, res: Response) {\n const counts = await this.logDumpService.deleteOlderThanWeekAndMismatchingLogFiles();\n res.send(counts);\n }\n\n @GET()\n @POST()\n @route(\"/dump-fdm-monster-logs\")\n async dumpLogZips(req: Request, res: Response) {\n const filePath = await this.logDumpService.dumpZip();\n res.sendFile(filePath);\n }\n}\n"],"mappings":"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"}
|