arkos 2.0.0-next.18 → 2.0.0-next.19
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/cli.js +8 -1
- package/dist/esm/app.js +1 -1
- package/dist/esm/app.js.map +1 -1
- package/dist/esm/components/arkos-gateway/arkos-gateway.js +5 -4
- package/dist/esm/components/arkos-gateway/arkos-gateway.js.map +1 -1
- package/dist/esm/components/arkos-gateway/socket-extensions.js +7 -0
- package/dist/esm/components/arkos-gateway/socket-extensions.js.map +1 -1
- package/dist/esm/components/arkos-gateway/utils/memory-gateway-store.js +2 -4
- package/dist/esm/components/arkos-gateway/utils/memory-gateway-store.js.map +1 -1
- package/dist/esm/components/arkos-gateway/utils/multi-tier-arkos-gateway-store.js +1 -0
- package/dist/esm/components/arkos-gateway/utils/multi-tier-arkos-gateway-store.js.map +1 -1
- package/dist/esm/components/arkos-loadable-registry/index.js +1 -3
- package/dist/esm/components/arkos-loadable-registry/index.js.map +1 -1
- package/dist/esm/components/arkos-route-hook/reader.js.map +1 -1
- package/dist/esm/components/arkos-route-hook/types.js.map +1 -1
- package/dist/esm/exports/index.js +1 -1
- package/dist/esm/exports/index.js.map +1 -1
- package/dist/esm/exports/middlewares/index.js +1 -1
- package/dist/esm/exports/middlewares/index.js.map +1 -1
- package/dist/esm/modules/auth/auth.controller.js +213 -221
- package/dist/esm/modules/auth/auth.controller.js.map +1 -1
- package/dist/esm/modules/auth/auth.service.js +11 -58
- package/dist/esm/modules/auth/auth.service.js.map +1 -1
- package/dist/esm/modules/auth/utils/auth-prisma-json-schema-generator.js +1 -0
- package/dist/esm/modules/auth/utils/auth-prisma-json-schema-generator.js.map +1 -1
- package/dist/esm/modules/auth/utils/services/auth-action.service.js +10 -12
- package/dist/esm/modules/auth/utils/services/auth-action.service.js.map +1 -1
- package/dist/esm/modules/base/base.controller.js +179 -177
- package/dist/esm/modules/base/base.controller.js.map +1 -1
- package/dist/esm/modules/base/base.middlewares.js +6 -21
- package/dist/esm/modules/base/base.middlewares.js.map +1 -1
- package/dist/esm/modules/base/base.service.js +28 -25
- package/dist/esm/modules/base/base.service.js.map +1 -1
- package/dist/esm/modules/base/utils/error-prettifier.js +26 -30
- package/dist/esm/modules/base/utils/error-prettifier.js.map +1 -1
- package/dist/esm/modules/email/email.service.js +2 -2
- package/dist/esm/modules/email/email.service.js.map +1 -1
- package/dist/esm/modules/error-handler/utils/app-error.js +5 -1
- package/dist/esm/modules/error-handler/utils/app-error.js.map +1 -1
- package/dist/esm/modules/file-upload/file-upload.controller.js +203 -205
- package/dist/esm/modules/file-upload/file-upload.controller.js.map +1 -1
- package/dist/esm/modules/file-upload/file-upload.router.js +3 -3
- package/dist/esm/modules/file-upload/file-upload.router.js.map +1 -1
- package/dist/esm/modules/file-upload/file-upload.service.js +12 -7
- package/dist/esm/modules/file-upload/file-upload.service.js.map +1 -1
- package/dist/esm/modules/file-upload/utils/file-upload-json-schema-generator.js +1 -1
- package/dist/esm/modules/file-upload/utils/file-upload-json-schema-generator.js.map +1 -1
- package/dist/esm/modules/swagger/swagger.router.js.map +1 -1
- package/dist/esm/modules/swagger/utils/helpers/openapi-schema-converter.js +1 -0
- package/dist/esm/modules/swagger/utils/helpers/openapi-schema-converter.js.map +1 -1
- package/dist/esm/types/index.js.map +1 -1
- package/dist/esm/types/new-arkos-config.js.map +1 -1
- package/dist/esm/types/router-config.js.map +1 -1
- package/dist/esm/types/validation/validation-manager.js +21 -22
- package/dist/esm/types/validation/validation-manager.js.map +1 -1
- package/dist/esm/utils/arkos-router/index.js +1 -6
- package/dist/esm/utils/arkos-router/index.js.map +1 -1
- package/dist/esm/utils/arkos-router/route-config-registry.js +2 -3
- package/dist/esm/utils/arkos-router/route-config-registry.js.map +1 -1
- package/dist/esm/utils/arkos-router/types/index.js.map +1 -1
- package/dist/esm/utils/arkos-router/utils/helpers/index.js +86 -43
- package/dist/esm/utils/arkos-router/utils/helpers/index.js.map +1 -1
- package/dist/esm/utils/arkos-router/utils/helpers/upload-manager.js +12 -14
- package/dist/esm/utils/arkos-router/utils/helpers/upload-manager.js.map +1 -1
- package/dist/esm/utils/bundler.js +6 -6
- package/dist/esm/utils/bundler.js.map +1 -1
- package/dist/esm/utils/cli/dev.js +4 -17
- package/dist/esm/utils/cli/dev.js.map +1 -1
- package/dist/esm/utils/cli/prisma-generate.js +8 -37
- package/dist/esm/utils/cli/prisma-generate.js.map +1 -1
- package/dist/esm/utils/cli/utils/cli.helpers.js +1 -1
- package/dist/esm/utils/cli/utils/smart-fs-watcher.js +3 -0
- package/dist/esm/utils/cli/utils/smart-fs-watcher.js.map +1 -1
- package/dist/esm/utils/cli/utils/template-generator/templates/router-template.js +1 -1
- package/dist/esm/utils/cli/utils/template-generator/templates/router-template.js.map +1 -1
- package/dist/esm/utils/dynamic-loader.js +2 -1
- package/dist/esm/utils/dynamic-loader.js.map +1 -1
- package/dist/esm/utils/features/api.features.js +24 -20
- package/dist/esm/utils/features/api.features.js.map +1 -1
- package/dist/esm/utils/features/port-and-host-allocator.js +4 -3
- package/dist/esm/utils/features/port-and-host-allocator.js.map +1 -1
- package/dist/esm/utils/helpers/arkos-config.helpers.js.map +1 -1
- package/dist/esm/utils/helpers/query-parser.helpers.js +7 -22
- package/dist/esm/utils/helpers/query-parser.helpers.js.map +1 -1
- package/dist/esm/utils/initialize-app.js +1 -1
- package/dist/esm/utils/initialize-app.js.map +1 -1
- package/dist/esm/utils/prisma/prisma-json-schema-generator.js +1 -3
- package/dist/esm/utils/prisma/prisma-json-schema-generator.js.map +1 -1
- package/dist/esm/utils/prisma/prisma-schema-parser.js +14 -8
- package/dist/esm/utils/prisma/prisma-schema-parser.js.map +1 -1
- package/dist/esm/utils/validate-schema.js +23 -21
- package/dist/esm/utils/validate-schema.js.map +1 -1
- package/dist/types/app.d.ts +1 -1
- package/dist/types/components/arkos-route-hook/reader.d.ts +2 -5
- package/dist/types/components/arkos-route-hook/types.d.ts +4 -4
- package/dist/types/exports/index.d.ts +3 -4
- package/dist/types/exports/middlewares/index.d.ts +1 -1
- package/dist/types/modules/auth/auth.service.d.ts +1 -40
- package/dist/types/modules/base/base.middlewares.d.ts +1 -10
- package/dist/types/modules/swagger/swagger.router.d.ts +1 -2
- package/dist/types/types/index.d.ts +3 -3
- package/dist/types/types/new-arkos-config.d.ts +0 -14
- package/dist/types/types/router-config.d.ts +0 -111
- package/dist/types/utils/arkos-router/types/index.d.ts +3 -3
- package/dist/types/utils/arkos-router/utils/helpers/index.d.ts +2 -6
- package/dist/types/utils/dynamic-loader.d.ts +2 -2
- package/dist/types/utils/helpers/query-parser.helpers.d.ts +1 -1
- package/dist/types/utils/prisma/prisma-schema-parser.d.ts +1 -0
- package/package.json +41 -41
- package/dist/esm/components/arkos-loadable-translator/index.js +0 -138
- package/dist/esm/components/arkos-loadable-translator/index.js.map +0 -1
- package/dist/types/components/arkos-loadable-translator/index.d.ts +0 -53
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email.service.js","sourceRoot":"","sources":["../../../../src/modules/email/email.service.ts"],"names":[],"mappings":"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","sourcesContent":["import nodemailer, { SendMailOptions, Transporter } from \"nodemailer\";\nimport { convert } from \"html-to-text\";\nimport { getArkosConfig } from \"../../server\";\nimport AppError from \"../error-handler/utils/app-error\";\n\n/**\n * Defines the options for sending an email.\n */\nexport type EmailOptions = {\n subject: string;\n} & SendMailOptions;\n\n/**\n * Defines the authentication options for SMTP.\n */\nexport type SMTPAuthOptions = {\n user: string;\n pass: string;\n};\n\n/**\n * Defines the connection options for SMTP server.\n */\nexport type SMTPConnectionOptions = {\n host?: string;\n port?: number;\n secure?: boolean;\n auth?: SMTPAuthOptions;\n name?: string;\n};\n\n/**\n * A service class to handle email-related tasks, including sending emails.\n *\n * See the api reference [www.arkosjs.com/docs/reference/the-email-service-class](https://www.arkosjs.com/docs/reference/the-email-service-class)\n */\nexport class EmailService {\n transporter: Transporter | null = null;\n private customConfig: SMTPConnectionOptions | null = null;\n\n /**\n * Creates an instance of the EmailService class.\n *\n * @param {SMTPConnectionOptions} [config] - Optional custom SMTP configuration.\n * If provided, these settings will be used instead of the Arkos config.\n */\n constructor(config?: SMTPConnectionOptions) {\n if (config) {\n this.customConfig = config;\n }\n }\n\n /**\n * Gets the email configuration from multiple sources with priority:\n * 1. Constructor customConfig\n * 2. ArkosConfig\n * 3. Environment variables\n * @returns Configuration object with host, port, and auth details\n * @throws AppError if required email configuration is not set\n */\n private getEmailConfig(): SMTPConnectionOptions {\n if (this.customConfig) {\n return this.customConfig;\n }\n\n const { email: emailConfigs } = getArkosConfig();\n const host = emailConfigs?.host || process.env.EMAIL_HOST;\n const port =\n emailConfigs?.port ||\n (process.env.EMAIL_PORT ? parseInt(process.env.EMAIL_PORT) : undefined);\n const secure =\n emailConfigs?.secure !== undefined\n ? emailConfigs.secure\n : process.env.EMAIL_SECURE\n ? process.env.EMAIL_SECURE === \"true\"\n : undefined;\n const user = emailConfigs?.auth?.user || process.env.EMAIL_USER;\n const pass = emailConfigs?.auth?.pass || process.env.EMAIL_PASSWORD;\n const name = emailConfigs?.name || process.env.EMAIL_NAME;\n\n if (!host) {\n throw new AppError(\n \"You are trying to use emailService without setting email configurations. \" +\n \"Please configure either arkosConfig.email or environment variables (EMAIL_HOST)\",\n 500,\n {\n docs: \"Read more about emailService at https://www.arkosjs.com/docs/guides/email-service\",\n }\n );\n }\n\n return {\n ...emailConfigs,\n host,\n port: port || 465,\n secure: secure !== undefined ? secure : true,\n auth:\n user && pass\n ? {\n user,\n pass,\n }\n : undefined,\n name,\n };\n }\n\n /**\n * Gets or creates a transporter using the email configuration\n * @param customConfig Optional override connection settings (takes full priority if provided)\n * @returns A configured nodemailer transporter\n */\n private getTransporter(customConfig?: SMTPConnectionOptions): Transporter {\n if (customConfig) {\n const { name, ...config } = customConfig;\n return nodemailer.createTransport(config);\n }\n\n if (!this.transporter) {\n const { name, ...config } = this.getEmailConfig() || {};\n this.transporter = nodemailer.createTransport(config);\n }\n return this.transporter;\n }\n\n /**\n * Sends an email with the provided options.\n * Can use either the default configuration or custom connection options.\n *\n * @param {EmailOptions} options - The options for the email to be sent.\n * @param {SMTPConnectionOptions} [connectionOptions] - Optional custom connection settings.\n * @param {boolean} [skipVerification=false] - Whether to skip connection verification.\n * @returns {Promise<{ success: boolean; messageId?: string } & Record<string, any>>} Result with message ID on success.\n */\n public async send(\n options: EmailOptions & SendMailOptions,\n connectionOptions?: SMTPConnectionOptions,\n skipVerification: boolean = true\n ): Promise<{ success: boolean; messageId?: string } & Record<string, any>> {\n const config = this.getEmailConfig();\n const transporter = connectionOptions\n ? this.getTransporter(connectionOptions)\n : this.getTransporter();\n\n const fromAddress =\n options.from || connectionOptions?.auth?.user || config.auth?.user;\n\n if (connectionOptions || !skipVerification) {\n const isConnected = await this.verifyConnection(transporter);\n if (!isConnected) throw new Error(\"Failed to connect to email server\");\n }\n\n const info = await transporter.sendMail({\n ...options,\n from: fromAddress\n ? config.name\n ? `${config.name}<${fromAddress}>`\n : fromAddress\n : undefined,\n text:\n options?.text ||\n (typeof options.html === \"string\" && options.html\n ? convert(options.html as string)\n : undefined),\n });\n\n return { success: true, ...info };\n }\n\n /**\n * Verifies the connection to the email server.\n * @param {Transporter} [transporterToVerify] - Optional transporter to verify.\n * @returns {Promise<boolean>} A promise that resolves to true if connection is valid.\n */\n public async verifyConnection(\n transporterToVerify?: Transporter\n ): Promise<boolean> {\n try {\n const transporter = transporterToVerify || this.getTransporter();\n await transporter.verify();\n return true;\n } catch (error) {\n console.error(\"Email Server Connection Failed\", error);\n return false;\n }\n }\n\n /**\n * Updates the custom configuration for this email service instance.\n * @param {SMTPConnectionOptions} config - The new connection options.\n */\n public updateConfig(config: SMTPConnectionOptions): void {\n this.customConfig = config;\n this.transporter = null; // Reset transporter so it will be recreated with new config\n }\n\n /**\n * Creates a new instance of EmailService with custom configuration.\n * @param {SMTPConnectionOptions} config - The connection options for the new instance.\n * @returns {EmailService} A new EmailService instance.\n */\n public static create(config: SMTPConnectionOptions): EmailService {\n return new EmailService(config);\n }\n}\n\nconst emailService = new EmailService();\n\nexport default emailService;\n"]}
|
|
1
|
+
{"version":3,"file":"email.service.js","sourceRoot":"","sources":["../../../../src/modules/email/email.service.ts"],"names":[],"mappings":"AAAA,OAAO,UAA4C,MAAM,YAAY,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,QAAQ,MAAM,kCAAkC,CAAC;AAiCxD,MAAM,OAAO,YAAY;IACvB,WAAW,GAAuB,IAAI,CAAC;IAC/B,YAAY,GAAiC,IAAI,CAAC;IAQ1D,YAAY,MAA8B;QACxC,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC;QAC7B,CAAC;IACH,CAAC;IAUO,cAAc;QACpB,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,OAAO,IAAI,CAAC,YAAY,CAAC;QAC3B,CAAC;QAED,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,cAAc,EAAE,CAAC;QACjD,MAAM,IAAI,GAAG,YAAY,EAAE,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;QAC1D,MAAM,IAAI,GACR,YAAY,EAAE,IAAI;YAClB,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAC1E,MAAM,MAAM,GACV,YAAY,EAAE,MAAM,KAAK,SAAS;YAChC,CAAC,CAAC,YAAY,CAAC,MAAM;YACrB,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY;gBACxB,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,KAAK,MAAM;gBACrC,CAAC,CAAC,SAAS,CAAC;QAClB,MAAM,IAAI,GAAG,YAAY,EAAE,IAAI,EAAE,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;QAChE,MAAM,IAAI,GAAG,YAAY,EAAE,IAAI,EAAE,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;QACpE,MAAM,IAAI,GAAG,YAAY,EAAE,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;QAE1D,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,QAAQ,CAChB,2EAA2E;gBACzE,iFAAiF,EACnF,GAAG,EACH;gBACE,IAAI,EAAE,mFAAmF;aAC1F,CACF,CAAC;QACJ,CAAC;QAED,OAAO;YACL,GAAG,YAAY;YACf,IAAI;YACJ,IAAI,EAAE,IAAI,IAAI,GAAG;YACjB,MAAM,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI;YAC5C,IAAI,EACF,IAAI,IAAI,IAAI;gBACV,CAAC,CAAC;oBACE,IAAI;oBACJ,IAAI;iBACL;gBACH,CAAC,CAAC,SAAS;YACf,IAAI;SACL,CAAC;IACJ,CAAC;IAOO,cAAc,CAAC,YAAoC;QACzD,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,GAAG,YAAY,CAAC;YACzC,OAAO,UAAU,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QAC5C,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC;YACxD,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QACxD,CAAC;QACD,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAWM,KAAK,CAAC,IAAI,CACf,OAAuC,EACvC,iBAAyC,EACzC,mBAA4B,IAAI;QAEhC,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;QACrC,MAAM,WAAW,GAAG,iBAAiB;YACnC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,iBAAiB,CAAC;YACxC,CAAC,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;QAE1B,MAAM,WAAW,GACf,OAAO,CAAC,IAAI,IAAI,iBAAiB,EAAE,IAAI,EAAE,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC;QAErE,IAAI,iBAAiB,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC3C,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC;YAC7D,IAAI,CAAC,WAAW;gBAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;QACzE,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC;YACtC,GAAG,OAAO;YACV,IAAI,EAAE,WAAW;gBACf,CAAC,CAAC,MAAM,CAAC,IAAI;oBACX,CAAC,CAAC,GAAG,MAAM,CAAC,IAAI,IAAI,WAAW,GAAG;oBAClC,CAAC,CAAC,WAAW;gBACf,CAAC,CAAC,SAAS;YACb,IAAI,EACF,OAAO,EAAE,IAAI;gBACb,CAAC,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI;oBAC/C,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAc,CAAC;oBACjC,CAAC,CAAC,SAAS,CAAC;SACjB,CAAC,CAAC;QAEH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC;IACpC,CAAC;IAOM,KAAK,CAAC,gBAAgB,CAC3B,mBAAiC;QAEjC,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,mBAAmB,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACjE,MAAM,WAAW,CAAC,MAAM,EAAE,CAAC;YAC3B,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,KAAK,CAAC,CAAC;YACvD,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAMM,YAAY,CAAC,MAA6B;QAC/C,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC;QAC3B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;IAC1B,CAAC;IAOM,MAAM,CAAC,MAAM,CAAC,MAA6B;QAChD,OAAO,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;CACF;AAED,MAAM,YAAY,GAAG,IAAI,YAAY,EAAE,CAAC;AAExC,eAAe,YAAY,CAAC","sourcesContent":["import nodemailer, { SendMailOptions, Transporter } from \"nodemailer\";\nimport { convert } from \"html-to-text\";\nimport { getArkosConfig } from \"../../server\";\nimport AppError from \"../error-handler/utils/app-error\";\n\n/**\n * Defines the options for sending an email.\n */\nexport type EmailOptions = {\n subject: string;\n} & SendMailOptions;\n\n/**\n * Defines the authentication options for SMTP.\n */\nexport type SMTPAuthOptions = {\n user: string;\n pass: string;\n};\n\n/**\n * Defines the connection options for SMTP server.\n */\nexport type SMTPConnectionOptions = {\n host?: string;\n port?: number;\n secure?: boolean;\n auth?: SMTPAuthOptions;\n name?: string;\n};\n\n/**\n * A service class to handle email-related tasks, including sending emails.\n *\n * See the api reference [www.arkosjs.com/docs/reference/the-email-service-class](https://www.arkosjs.com/docs/reference/the-email-service-class)\n */\nexport class EmailService {\n transporter: Transporter | null = null;\n private customConfig: SMTPConnectionOptions | null = null;\n\n /**\n * Creates an instance of the EmailService class.\n *\n * @param {SMTPConnectionOptions} [config] - Optional custom SMTP configuration.\n * If provided, these settings will be used instead of the Arkos config.\n */\n constructor(config?: SMTPConnectionOptions) {\n if (config) {\n this.customConfig = config;\n }\n }\n\n /**\n * Gets the email configuration from multiple sources with priority:\n * 1. Constructor customConfig\n * 2. ArkosConfig\n * 3. Environment variables\n * @returns Configuration object with host, port, and auth details\n * @throws AppError if required email configuration is not set\n */\n private getEmailConfig(): SMTPConnectionOptions {\n if (this.customConfig) {\n return this.customConfig;\n }\n\n const { email: emailConfigs } = getArkosConfig();\n const host = emailConfigs?.host || process.env.EMAIL_HOST;\n const port =\n emailConfigs?.port ||\n (process.env.EMAIL_PORT ? parseInt(process.env.EMAIL_PORT) : undefined);\n const secure =\n emailConfigs?.secure !== undefined\n ? emailConfigs.secure\n : process.env.EMAIL_SECURE\n ? process.env.EMAIL_SECURE === \"true\"\n : undefined;\n const user = emailConfigs?.auth?.user || process.env.EMAIL_USER;\n const pass = emailConfigs?.auth?.pass || process.env.EMAIL_PASSWORD;\n const name = emailConfigs?.name || process.env.EMAIL_NAME;\n\n if (!host) {\n throw new AppError(\n \"You are trying to use emailService without setting email configurations. \" +\n \"Please configure either arkosConfig.email or environment variables (EMAIL_HOST)\",\n 500,\n {\n docs: \"Read more about emailService at https://www.arkosjs.com/docs/guides/email-service\",\n }\n );\n }\n\n return {\n ...emailConfigs,\n host,\n port: port || 465,\n secure: secure !== undefined ? secure : true,\n auth:\n user && pass\n ? {\n user,\n pass,\n }\n : undefined,\n name,\n };\n }\n\n /**\n * Gets or creates a transporter using the email configuration\n * @param customConfig Optional override connection settings (takes full priority if provided)\n * @returns A configured nodemailer transporter\n */\n private getTransporter(customConfig?: SMTPConnectionOptions): Transporter {\n if (customConfig) {\n const { name, ...config } = customConfig;\n return nodemailer.createTransport(config);\n }\n\n if (!this.transporter) {\n const { name, ...config } = this.getEmailConfig() || {};\n this.transporter = nodemailer.createTransport(config);\n }\n return this.transporter;\n }\n\n /**\n * Sends an email with the provided options.\n * Can use either the default configuration or custom connection options.\n *\n * @param {EmailOptions} options - The options for the email to be sent.\n * @param {SMTPConnectionOptions} [connectionOptions] - Optional custom connection settings.\n * @param {boolean} [skipVerification=false] - Whether to skip connection verification.\n * @returns {Promise<{ success: boolean; messageId?: string } & Record<string, any>>} Result with message ID on success.\n */\n public async send(\n options: EmailOptions & SendMailOptions,\n connectionOptions?: SMTPConnectionOptions,\n skipVerification: boolean = true\n ): Promise<{ success: boolean; messageId?: string } & Record<string, any>> {\n const config = this.getEmailConfig();\n const transporter = connectionOptions\n ? this.getTransporter(connectionOptions)\n : this.getTransporter();\n\n const fromAddress =\n options.from || connectionOptions?.auth?.user || config.auth?.user;\n\n if (connectionOptions || !skipVerification) {\n const isConnected = await this.verifyConnection(transporter);\n if (!isConnected) throw new Error(\"Failed to connect to email server\");\n }\n\n const info = await transporter.sendMail({\n ...options,\n from: fromAddress\n ? config.name\n ? `${config.name}<${fromAddress}>`\n : fromAddress\n : undefined,\n text:\n options?.text ||\n (typeof options.html === \"string\" && options.html\n ? convert(options.html as string)\n : undefined),\n });\n\n return { success: true, ...info };\n }\n\n /**\n * Verifies the connection to the email server.\n * @param {Transporter} [transporterToVerify] - Optional transporter to verify.\n * @returns {Promise<boolean>} A promise that resolves to true if connection is valid.\n */\n public async verifyConnection(\n transporterToVerify?: Transporter\n ): Promise<boolean> {\n try {\n const transporter = transporterToVerify || this.getTransporter();\n await transporter.verify();\n return true;\n } catch (error) {\n console.error(\"Email Server Connection Failed\", error);\n return false;\n }\n }\n\n /**\n * Updates the custom configuration for this email service instance.\n * @param {SMTPConnectionOptions} config - The new connection options.\n */\n public updateConfig(config: SMTPConnectionOptions): void {\n this.customConfig = config;\n this.transporter = null; // Reset transporter so it will be recreated with new config\n }\n\n /**\n * Creates a new instance of EmailService with custom configuration.\n * @param {SMTPConnectionOptions} config - The connection options for the new instance.\n * @returns {EmailService} A new EmailService instance.\n */\n public static create(config: SMTPConnectionOptions): EmailService {\n return new EmailService(config);\n }\n}\n\nconst emailService = new EmailService();\n\nexport default emailService;\n"]}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
class AppError extends Error {
|
|
2
|
+
statusCode;
|
|
3
|
+
status;
|
|
4
|
+
isOperational;
|
|
5
|
+
code = "Unknown";
|
|
6
|
+
meta;
|
|
2
7
|
constructor(message, statusCode, code, meta) {
|
|
3
8
|
super(message);
|
|
4
|
-
this.code = "Unknown";
|
|
5
9
|
this.message = message || "An error occurred, try again!";
|
|
6
10
|
this.statusCode = statusCode;
|
|
7
11
|
this.status = `${statusCode}`.startsWith("4") ? "fail" : "error";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-error.js","sourceRoot":"","sources":["../../../../../src/modules/error-handler/utils/app-error.ts"],"names":[],"mappings":"AA+BA,MAAM,QAAS,SAAQ,KAAK;
|
|
1
|
+
{"version":3,"file":"app-error.js","sourceRoot":"","sources":["../../../../../src/modules/error-handler/utils/app-error.ts"],"names":[],"mappings":"AA+BA,MAAM,QAAS,SAAQ,KAAK;IAC1B,UAAU,CAAS;IACnB,MAAM,CAAS;IACR,aAAa,CAAU;IAC9B,IAAI,GAAY,SAAS,CAAC;IAC1B,IAAI,CAAuB;IAU3B,YACE,OAAe,EACf,UAAkB,EAClB,IAAmC,EACnC,IAAmC;QAEnC,KAAK,CAAC,OAAO,CAAC,CAAC;QAEf,IAAI,CAAC,OAAO,GAAG,OAAO,IAAI,+BAA+B,CAAC;QAC1D,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;QACjE,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAE1B,IAAI,IAAI,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,OAAO,IAAI;YAC7C,MAAM,IAAI,QAAQ,CAChB,kCAAkC,OAAO,IAAI,+DAA+D,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,EAC5I,GAAG,EACH,gBAAgB,EAChB,EAAE,IAAI,EAAE,IAAI,EAAE,CACf,CAAC;QAEJ,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,IAAI,CAAC,IAAI,GAAG,IAAI,IAAI,SAAS,CAAC;QAC5D,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QAC/C,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,IAAI,CAAC,IAAI,GAAG,IAAI,IAAI,SAAS,CAAC;QAC5D,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QAC/C,IAAI,CAAC,IAAI;YAAE,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC;QAEjC,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IAClD,CAAC;CACF;AAED,eAAe,QAAQ,CAAC","sourcesContent":["/**\n * Custom error class for handling application errors.\n *\n * @extends {Error}\n *\n * @property {number} statusCode - HTTP status code of the error.\n * @property {string} status - Status message derived from the status code (`fail` for 4xx, `error` for 5xx).\n * @property {boolean} isOperational - Indicates if the error is operational (intended for client visibility).\n * @property {string} [code] - Optional error code for categorization.\n * @property {Record<string, any>} [meta] - Additional metadata related to the error.\n *\n * @example\n * ```typescript\n *\n * function getUser(id: string) {\n * if (!id) {\n * throw new AppError('User ID is required', 400, 'UserIDMissing', { field: 'id' });\n * }\n * // Simulate a user not found scenario\n * throw new AppError('User not found', 404, 'UserNotFound', { userId: id });\n * }\n *\n * try {\n * getUser('');\n * } catch (error) {\n * if (error instanceof AppError) {\n * console.error(`Error: ${error.message}, Code: ${error.code}, Status: ${error.status}`);\n * }\n * }\n * ```\n */\nclass AppError extends Error {\n statusCode: number;\n status: string;\n public isOperational: boolean;\n code?: string = \"Unknown\";\n meta?: Record<string, any>;\n\n /**\n * Creates an instance of AppError.\n *\n * @param {string} message - The error message.\n * @param {number} statusCode - The HTTP status code.\n * @param {Record<string, any>} [meta] - Additional metadata for debugging or client feedback.\n * @param {string} [code] - A custom error code for categorization.\n */\n constructor(\n message: string,\n statusCode: number,\n code?: string | Record<string, any>,\n meta?: Record<string, any> | string\n ) {\n super(message);\n\n this.message = message || \"An error occurred, try again!\";\n this.statusCode = statusCode;\n this.status = `${statusCode}`.startsWith(\"4\") ? \"fail\" : \"error\";\n this.isOperational = true;\n\n if (code && meta && typeof code === typeof meta)\n throw new AppError(\n `meta and code must not both be ${typeof code}, one must be of type object and other string. but received ${JSON.stringify({ meta, code })}`,\n 500,\n \"AppErrorMisuse\",\n { meta, code }\n );\n\n if (typeof code === \"string\") this.code = code || \"Unknown\";\n if (typeof code === \"object\") this.meta = code;\n if (typeof meta === \"string\") this.code = meta || \"Unknown\";\n if (typeof meta === \"object\") this.meta = meta;\n if (!code) this.code = \"Unknown\";\n\n Error.captureStackTrace(this, this.constructor);\n }\n}\n\nexport default AppError;\n"]}
|
|
@@ -13,232 +13,230 @@ function matchesUploadUrl(originalUrl, baseUploadRoute, fileType, fileName) {
|
|
|
13
13
|
new RegExp(`${baseUploadRoute}/${fileType}/${fileName}`).test(originalUrl));
|
|
14
14
|
}
|
|
15
15
|
export class FileUploadController {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
data = await processImage(req, next, req.file.path, options);
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
data = processFile(req, req.file.path);
|
|
67
|
-
}
|
|
16
|
+
getRouteHook() {
|
|
17
|
+
return loadableRegistry.getItem("ArkosRouteHook", "file-upload");
|
|
18
|
+
}
|
|
19
|
+
getAfterHook(operation) {
|
|
20
|
+
const hook = this.getRouteHook();
|
|
21
|
+
if (!hook)
|
|
22
|
+
return null;
|
|
23
|
+
return (routeHookReader.getHooks("file-upload", operation)?.after ?? null);
|
|
24
|
+
}
|
|
25
|
+
uploadFile = catchAsync(async (req, res, next) => {
|
|
26
|
+
const { fileType } = req.params;
|
|
27
|
+
const { format, width, height, resizeTo } = req.query;
|
|
28
|
+
const options = { format, width, height, resizeTo };
|
|
29
|
+
const { documentUploadService, fileUploadService, imageUploadService, videoUploadService, } = getFileUploadServices();
|
|
30
|
+
const { fileUpload } = getArkosConfig();
|
|
31
|
+
const baseUploadDir = fileUpload?.baseUploadDir || "/uploads";
|
|
32
|
+
const uploadPath = path.resolve(path.join(process.cwd(), baseUploadDir, fileType));
|
|
33
|
+
try {
|
|
34
|
+
await fs.promises.access(uploadPath);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
await fs.promises.mkdir(uploadPath, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
let uploader;
|
|
40
|
+
switch (fileType) {
|
|
41
|
+
case "images":
|
|
42
|
+
uploader = imageUploadService;
|
|
43
|
+
break;
|
|
44
|
+
case "videos":
|
|
45
|
+
uploader = videoUploadService;
|
|
46
|
+
break;
|
|
47
|
+
case "documents":
|
|
48
|
+
uploader = documentUploadService;
|
|
49
|
+
break;
|
|
50
|
+
case "files":
|
|
51
|
+
uploader = fileUploadService;
|
|
52
|
+
break;
|
|
53
|
+
default:
|
|
54
|
+
return next(new AppError("Invalid file type", 400, "InvalidFileType"));
|
|
55
|
+
}
|
|
56
|
+
uploader.handleMultipleUpload()(req, res, async (err) => {
|
|
57
|
+
if (err)
|
|
58
|
+
throw err;
|
|
59
|
+
let data;
|
|
60
|
+
if (req.files && Array.isArray(req.files) && req.files.length > 0) {
|
|
61
|
+
if (fileType === "images") {
|
|
62
|
+
data = await Promise.all(req.files.map((file) => processImage(req, next, file.path, options)));
|
|
68
63
|
}
|
|
69
64
|
else {
|
|
70
|
-
|
|
65
|
+
data = req.files.map((file) => processFile(req, file.path));
|
|
71
66
|
}
|
|
72
|
-
|
|
73
|
-
success: true,
|
|
74
|
-
data,
|
|
75
|
-
message: Array.isArray(data)
|
|
76
|
-
? `${data.length} files uploaded successfully`
|
|
77
|
-
: "File uploaded successfully",
|
|
78
|
-
};
|
|
79
|
-
const afterHook = this.getAfterHook("uploadFile");
|
|
80
|
-
if (afterHook) {
|
|
81
|
-
res.originalData = jsonContent;
|
|
82
|
-
req.responseData = jsonContent;
|
|
83
|
-
res.locals.data = jsonContent;
|
|
84
|
-
res.originalStatus = 200;
|
|
85
|
-
req.responseStatus = 200;
|
|
86
|
-
res.locals.status = 200;
|
|
87
|
-
return next();
|
|
88
|
-
}
|
|
89
|
-
res.status(200).json(jsonContent);
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
this.deleteFile = catchAsync(async (req, res, next) => {
|
|
93
|
-
const { fileType, fileName } = req.params;
|
|
94
|
-
const { documentUploadService, fileUploadService, imageUploadService, videoUploadService, } = getFileUploadServices();
|
|
95
|
-
let uploader;
|
|
96
|
-
switch (fileType) {
|
|
97
|
-
case "images":
|
|
98
|
-
uploader = imageUploadService;
|
|
99
|
-
break;
|
|
100
|
-
case "videos":
|
|
101
|
-
uploader = videoUploadService;
|
|
102
|
-
break;
|
|
103
|
-
case "documents":
|
|
104
|
-
uploader = documentUploadService;
|
|
105
|
-
break;
|
|
106
|
-
case "files":
|
|
107
|
-
uploader = fileUploadService;
|
|
108
|
-
break;
|
|
109
|
-
default:
|
|
110
|
-
return next(new AppError("Invalid file type", 400, "InvalidFileType"));
|
|
67
|
+
data = data.filter((url) => url !== null);
|
|
111
68
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const baseUploadRoute = fileUpload?.baseRoute || "/api/uploads";
|
|
116
|
-
const urlPattern = new RegExp(`${escapeRegExp(baseUploadRoute)}/${escapeRegExp(fileType)}/${escapeRegExp(fileName)}`);
|
|
117
|
-
const isExpectedUrlPattern = urlPattern.test(req.originalUrl);
|
|
118
|
-
if (isExpectedUrlPattern) {
|
|
119
|
-
const fullUrl = `${req.protocol}://${req.get("host")}${req.originalUrl}`;
|
|
120
|
-
await uploader.deleteFileByUrl(fullUrl);
|
|
69
|
+
else if (req.file) {
|
|
70
|
+
if (fileType === "images") {
|
|
71
|
+
data = await processImage(req, next, req.file.path, options);
|
|
121
72
|
}
|
|
122
73
|
else {
|
|
123
|
-
|
|
74
|
+
data = processFile(req, req.file.path);
|
|
124
75
|
}
|
|
125
|
-
const afterHook = this.getAfterHook("deleteFile");
|
|
126
|
-
if (afterHook) {
|
|
127
|
-
req.responseStatus = 204;
|
|
128
|
-
return next();
|
|
129
|
-
}
|
|
130
|
-
res.status(204).json();
|
|
131
76
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
77
|
+
else {
|
|
78
|
+
return next(new AppError(`No file or files were attached on field ${fileType} on the request body as form data.`, 400, "NoFileOrFilesAttached"));
|
|
79
|
+
}
|
|
80
|
+
const jsonContent = {
|
|
81
|
+
success: true,
|
|
82
|
+
data,
|
|
83
|
+
message: Array.isArray(data)
|
|
84
|
+
? `${data.length} files uploaded successfully`
|
|
85
|
+
: "File uploaded successfully",
|
|
86
|
+
};
|
|
87
|
+
const afterHook = this.getAfterHook("uploadFile");
|
|
88
|
+
if (afterHook) {
|
|
89
|
+
res.originalData = jsonContent;
|
|
90
|
+
req.responseData = jsonContent;
|
|
91
|
+
res.locals.data = jsonContent;
|
|
92
|
+
res.originalStatus = 200;
|
|
93
|
+
req.responseStatus = 200;
|
|
94
|
+
res.locals.status = 200;
|
|
95
|
+
return next();
|
|
136
96
|
}
|
|
97
|
+
res.status(200).json(jsonContent);
|
|
137
98
|
});
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
99
|
+
});
|
|
100
|
+
deleteFile = catchAsync(async (req, res, next) => {
|
|
101
|
+
const { fileType, fileName } = req.params;
|
|
102
|
+
const { documentUploadService, fileUploadService, imageUploadService, videoUploadService, } = getFileUploadServices();
|
|
103
|
+
let uploader;
|
|
104
|
+
switch (fileType) {
|
|
105
|
+
case "images":
|
|
106
|
+
uploader = imageUploadService;
|
|
107
|
+
break;
|
|
108
|
+
case "videos":
|
|
109
|
+
uploader = videoUploadService;
|
|
110
|
+
break;
|
|
111
|
+
case "documents":
|
|
112
|
+
uploader = documentUploadService;
|
|
113
|
+
break;
|
|
114
|
+
case "files":
|
|
115
|
+
uploader = fileUploadService;
|
|
116
|
+
break;
|
|
117
|
+
default:
|
|
118
|
+
return next(new AppError("Invalid file type", 400, "InvalidFileType"));
|
|
119
|
+
}
|
|
120
|
+
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
121
|
+
try {
|
|
143
122
|
const { fileUpload } = getArkosConfig();
|
|
144
|
-
const
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
123
|
+
const baseUploadRoute = fileUpload?.baseRoute || "/api/uploads";
|
|
124
|
+
const urlPattern = new RegExp(`${escapeRegExp(baseUploadRoute)}/${escapeRegExp(fileType)}/${escapeRegExp(fileName)}`);
|
|
125
|
+
const isExpectedUrlPattern = urlPattern.test(req.originalUrl);
|
|
126
|
+
if (isExpectedUrlPattern) {
|
|
127
|
+
const fullUrl = `${req.protocol}://${req.get("host")}${req.originalUrl}`;
|
|
128
|
+
await uploader.deleteFileByUrl(fullUrl);
|
|
148
129
|
}
|
|
149
|
-
|
|
150
|
-
await
|
|
130
|
+
else {
|
|
131
|
+
await uploader.deleteFileByName(fileName, fileType);
|
|
151
132
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
break;
|
|
157
|
-
case "videos":
|
|
158
|
-
uploader = videoUploadService;
|
|
159
|
-
break;
|
|
160
|
-
case "documents":
|
|
161
|
-
uploader = documentUploadService;
|
|
162
|
-
break;
|
|
163
|
-
case "files":
|
|
164
|
-
uploader = fileUploadService;
|
|
165
|
-
break;
|
|
166
|
-
default:
|
|
167
|
-
return next(new AppError("Invalid file type", 400, "InvalidFileType"));
|
|
133
|
+
const afterHook = this.getAfterHook("deleteFile");
|
|
134
|
+
if (afterHook) {
|
|
135
|
+
req.responseStatus = 204;
|
|
136
|
+
return next();
|
|
168
137
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
138
|
+
res.status(204).json();
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
if (error instanceof AppError)
|
|
142
|
+
return next(error);
|
|
143
|
+
return next(new AppError("File not found", 404, "FileNotFound"));
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
updateFile = catchAsync(async (req, res, next) => {
|
|
147
|
+
const { fileType, fileName } = req.params;
|
|
148
|
+
const { format, width, height, resizeTo } = req.query;
|
|
149
|
+
const options = { format, width, height, resizeTo };
|
|
150
|
+
const { documentUploadService, fileUploadService, imageUploadService, videoUploadService, } = getFileUploadServices();
|
|
151
|
+
const { fileUpload } = getArkosConfig();
|
|
152
|
+
const baseUploadDir = fileUpload?.baseUploadDir || "/uploads";
|
|
153
|
+
const uploadPath = path.resolve(path.join(process.cwd(), baseUploadDir, fileType));
|
|
154
|
+
try {
|
|
155
|
+
await fs.promises.access(uploadPath);
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
await fs.promises.mkdir(uploadPath, { recursive: true });
|
|
159
|
+
}
|
|
160
|
+
let uploader;
|
|
161
|
+
switch (fileType) {
|
|
162
|
+
case "images":
|
|
163
|
+
uploader = imageUploadService;
|
|
164
|
+
break;
|
|
165
|
+
case "videos":
|
|
166
|
+
uploader = videoUploadService;
|
|
167
|
+
break;
|
|
168
|
+
case "documents":
|
|
169
|
+
uploader = documentUploadService;
|
|
170
|
+
break;
|
|
171
|
+
case "files":
|
|
172
|
+
uploader = fileUploadService;
|
|
173
|
+
break;
|
|
174
|
+
default:
|
|
175
|
+
return next(new AppError("Invalid file type", 400, "InvalidFileType"));
|
|
176
|
+
}
|
|
177
|
+
uploader.handleMultipleUpload()(req, res, async (err) => {
|
|
178
|
+
if (err)
|
|
179
|
+
throw err;
|
|
180
|
+
if (!req.file &&
|
|
181
|
+
(!req.files || !Array.isArray(req.files) || req.files.length === 0))
|
|
182
|
+
return next(new AppError("No new file uploaded", 400, "MissingNewFile"));
|
|
183
|
+
if (fileName && fileName.trim() !== "") {
|
|
184
|
+
try {
|
|
185
|
+
const isExpectedUrlPattern = matchesUploadUrl(req.originalUrl, fileUpload?.baseRoute || "/api/uploads", fileType, fileName);
|
|
186
|
+
if (isExpectedUrlPattern) {
|
|
187
|
+
const oldFileUrl = `${req.protocol}://${req.get("host")}${req.originalUrl}`;
|
|
188
|
+
await uploader.deleteFileByUrl(oldFileUrl);
|
|
194
189
|
}
|
|
195
190
|
else {
|
|
196
|
-
|
|
191
|
+
await uploader.deleteFileByName(fileName, fileType);
|
|
197
192
|
}
|
|
198
|
-
data = data.filter((url) => url !== null);
|
|
199
193
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
data = await processImage(req, next, req.file.path, options);
|
|
203
|
-
}
|
|
204
|
-
else {
|
|
205
|
-
data = processFile(req, req.file.path);
|
|
206
|
-
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
console.warn(`Could not delete old file: ${fileName}`, error);
|
|
207
196
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
? `File updated successfully. ${data.length} new files uploaded`
|
|
214
|
-
: `${data.length} files uploaded successfully`
|
|
215
|
-
: fileName && fileName.trim() !== ""
|
|
216
|
-
? "File updated successfully"
|
|
217
|
-
: "File uploaded successfully",
|
|
218
|
-
};
|
|
219
|
-
const afterHook = this.getAfterHook("updateFile");
|
|
220
|
-
if (afterHook) {
|
|
221
|
-
res.originalData = jsonContent;
|
|
222
|
-
req.responseData = jsonContent;
|
|
223
|
-
res.locals.data = jsonContent;
|
|
224
|
-
res.originalStatus = 200;
|
|
225
|
-
req.responseStatus = 200;
|
|
226
|
-
res.locals.status = 200;
|
|
227
|
-
return next();
|
|
197
|
+
}
|
|
198
|
+
let data;
|
|
199
|
+
if (req.files && Array.isArray(req.files) && req.files.length > 0) {
|
|
200
|
+
if (fileType === "images") {
|
|
201
|
+
data = await Promise.all(req.files.map((file) => processImage(req, next, file.path, options)));
|
|
228
202
|
}
|
|
229
|
-
|
|
230
|
-
|
|
203
|
+
else {
|
|
204
|
+
data = req.files.map((file) => processFile(req, file.path));
|
|
205
|
+
}
|
|
206
|
+
data = data.filter((url) => url !== null);
|
|
207
|
+
}
|
|
208
|
+
else if (req.file) {
|
|
209
|
+
if (fileType === "images") {
|
|
210
|
+
data = await processImage(req, next, req.file.path, options);
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
data = processFile(req, req.file.path);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const jsonContent = {
|
|
217
|
+
success: true,
|
|
218
|
+
data,
|
|
219
|
+
message: Array.isArray(data)
|
|
220
|
+
? fileName && fileName.trim() !== ""
|
|
221
|
+
? `File updated successfully. ${data.length} new files uploaded`
|
|
222
|
+
: `${data.length} files uploaded successfully`
|
|
223
|
+
: fileName && fileName.trim() !== ""
|
|
224
|
+
? "File updated successfully"
|
|
225
|
+
: "File uploaded successfully",
|
|
226
|
+
};
|
|
227
|
+
const afterHook = this.getAfterHook("updateFile");
|
|
228
|
+
if (afterHook) {
|
|
229
|
+
res.originalData = jsonContent;
|
|
230
|
+
req.responseData = jsonContent;
|
|
231
|
+
res.locals.data = jsonContent;
|
|
232
|
+
res.originalStatus = 200;
|
|
233
|
+
req.responseStatus = 200;
|
|
234
|
+
res.locals.status = 200;
|
|
235
|
+
return next();
|
|
236
|
+
}
|
|
237
|
+
res.status(200).json(jsonContent);
|
|
231
238
|
});
|
|
232
|
-
}
|
|
233
|
-
getRouteHook() {
|
|
234
|
-
return loadableRegistry.getItem("ArkosRouteHook", "file-upload");
|
|
235
|
-
}
|
|
236
|
-
getAfterHook(operation) {
|
|
237
|
-
const hook = this.getRouteHook();
|
|
238
|
-
if (!hook)
|
|
239
|
-
return null;
|
|
240
|
-
return (routeHookReader.getHooks("file-upload", operation)?.after ?? null);
|
|
241
|
-
}
|
|
239
|
+
});
|
|
242
240
|
}
|
|
243
241
|
const fileUploadController = new FileUploadController();
|
|
244
242
|
export default fileUploadController;
|