@modern-admin/feature-upload 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/graphql.d.ts +36 -0
  2. package/dist/graphql.d.ts.map +1 -0
  3. package/dist/graphql.js +141 -0
  4. package/dist/graphql.js.map +1 -0
  5. package/dist/index.d.ts +7 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +12 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/nest/index.d.ts +3 -0
  10. package/dist/nest/index.d.ts.map +1 -0
  11. package/dist/nest/index.js +4 -0
  12. package/dist/nest/index.js.map +1 -0
  13. package/dist/nest/upload-sweeper.service.d.ts +32 -0
  14. package/dist/nest/upload-sweeper.service.d.ts.map +1 -0
  15. package/dist/nest/upload-sweeper.service.js +68 -0
  16. package/dist/nest/upload-sweeper.service.js.map +1 -0
  17. package/dist/nest/upload.controller.d.ts +51 -0
  18. package/dist/nest/upload.controller.d.ts.map +1 -0
  19. package/dist/nest/upload.controller.js +220 -0
  20. package/dist/nest/upload.controller.js.map +1 -0
  21. package/dist/nest/upload.module.d.ts +28 -0
  22. package/dist/nest/upload.module.d.ts.map +1 -0
  23. package/dist/nest/upload.module.js +51 -0
  24. package/dist/nest/upload.module.js.map +1 -0
  25. package/dist/nest/upload.tokens.d.ts +23 -0
  26. package/dist/nest/upload.tokens.d.ts.map +1 -0
  27. package/dist/nest/upload.tokens.js +6 -0
  28. package/dist/nest/upload.tokens.js.map +1 -0
  29. package/dist/pending-registry.d.ts +60 -0
  30. package/dist/pending-registry.d.ts.map +1 -0
  31. package/dist/pending-registry.js +106 -0
  32. package/dist/pending-registry.js.map +1 -0
  33. package/dist/providers/local.d.ts +36 -0
  34. package/dist/providers/local.d.ts.map +1 -0
  35. package/dist/providers/local.js +44 -0
  36. package/dist/providers/local.js.map +1 -0
  37. package/dist/providers/s3.d.ts +96 -0
  38. package/dist/providers/s3.d.ts.map +1 -0
  39. package/dist/providers/s3.js +164 -0
  40. package/dist/providers/s3.js.map +1 -0
  41. package/dist/registry.d.ts +27 -0
  42. package/dist/registry.d.ts.map +1 -0
  43. package/dist/registry.js +25 -0
  44. package/dist/registry.js.map +1 -0
  45. package/dist/types.d.ts +130 -0
  46. package/dist/types.d.ts.map +1 -0
  47. package/dist/types.js +9 -0
  48. package/dist/types.js.map +1 -0
  49. package/dist/upload-feature.d.ts +31 -0
  50. package/dist/upload-feature.d.ts.map +1 -0
  51. package/dist/upload-feature.js +170 -0
  52. package/dist/upload-feature.js.map +1 -0
  53. package/package.json +94 -0
  54. package/src/graphql.ts +185 -0
  55. package/src/index.ts +22 -0
  56. package/src/nest/index.ts +4 -0
  57. package/src/nest/upload-sweeper.service.ts +52 -0
  58. package/src/nest/upload.controller.ts +228 -0
  59. package/src/nest/upload.module.ts +44 -0
  60. package/src/nest/upload.tokens.ts +24 -0
  61. package/src/pending-registry.ts +115 -0
  62. package/src/providers/local.ts +63 -0
  63. package/src/providers/s3.ts +226 -0
  64. package/src/registry.ts +39 -0
  65. package/src/types.ts +137 -0
  66. package/src/upload-feature.ts +208 -0
@@ -0,0 +1,36 @@
1
+ /**
2
+ * GraphQL extension for the upload feature — mirrors the REST upload
3
+ * controller (`POST /admin/api/resources/:id/actions/upload` and `DELETE`)
4
+ * with two mutations:
5
+ *
6
+ * - `adminUpload(resourceId, field, file)` — upload a single file via the
7
+ * `Upload` scalar (multipart/form-data per the GraphQL multipart spec).
8
+ * Returns `UploadedFileInfo`.
9
+ * - `adminCancelUpload(resourceId, field, key)` — cancel a still-pending
10
+ * upload (file uploaded but record not yet saved). Returns `Boolean`.
11
+ *
12
+ * The extension is wired in the host application:
13
+ * ```ts
14
+ * ModernAdminGraphqlModule.forRoot({
15
+ * extensions: [uploadGraphqlExtension()],
16
+ * })
17
+ * ```
18
+ *
19
+ * Authorisation re-uses the same `ModernAdmin.invoke()` access checks via
20
+ * `findResource(resourceId, currentAdmin)`.
21
+ *
22
+ * `@modern-admin/graphql` is an *optional peer dependency* of feature-upload —
23
+ * if you do not use the GraphQL transport you do not need to import this
24
+ * module at all.
25
+ */
26
+ import { type ExtensionContext, type GraphqlSchemaExtension } from '@modern-admin/graphql';
27
+ export interface UploadGraphqlExtensionOptions {
28
+ /**
29
+ * TTL applied when registering an uploaded key as pending. Mirrors the
30
+ * `pendingTtlMs` option on `ModernAdminUploadModule.forRoot()` — set both
31
+ * to the same value to keep REST and GraphQL behaviour identical.
32
+ */
33
+ pendingTtlMs?: number;
34
+ }
35
+ export declare function uploadGraphqlExtension(options?: UploadGraphqlExtensionOptions): (ctx: ExtensionContext) => GraphqlSchemaExtension;
36
+ //# sourceMappingURL=graphql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphql.d.ts","sourceRoot":"","sources":["../src/graphql.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AASH,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAE5B,MAAM,uBAAuB,CAAA;AAS9B,MAAM,WAAW,6BAA6B;IAC5C;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,wBAAgB,sBAAsB,CACpC,OAAO,GAAE,6BAAkC,GAC1C,CAAC,GAAG,EAAE,gBAAgB,KAAK,sBAAsB,CAwFnD"}
@@ -0,0 +1,141 @@
1
+ /**
2
+ * GraphQL extension for the upload feature — mirrors the REST upload
3
+ * controller (`POST /admin/api/resources/:id/actions/upload` and `DELETE`)
4
+ * with two mutations:
5
+ *
6
+ * - `adminUpload(resourceId, field, file)` — upload a single file via the
7
+ * `Upload` scalar (multipart/form-data per the GraphQL multipart spec).
8
+ * Returns `UploadedFileInfo`.
9
+ * - `adminCancelUpload(resourceId, field, key)` — cancel a still-pending
10
+ * upload (file uploaded but record not yet saved). Returns `Boolean`.
11
+ *
12
+ * The extension is wired in the host application:
13
+ * ```ts
14
+ * ModernAdminGraphqlModule.forRoot({
15
+ * extensions: [uploadGraphqlExtension()],
16
+ * })
17
+ * ```
18
+ *
19
+ * Authorisation re-uses the same `ModernAdmin.invoke()` access checks via
20
+ * `findResource(resourceId, currentAdmin)`.
21
+ *
22
+ * `@modern-admin/graphql` is an *optional peer dependency* of feature-upload —
23
+ * if you do not use the GraphQL transport you do not need to import this
24
+ * module at all.
25
+ */
26
+ import { GraphQLBoolean, GraphQLNonNull, GraphQLObjectType, GraphQLString, GraphQLInt, } from 'graphql';
27
+ import { ForbiddenError, ResourceNotFoundError } from '@modern-admin/core';
28
+ import { UploadProviderRegistry } from './registry.js';
29
+ import { PendingUploadsRegistry } from './pending-registry.js';
30
+ /** Default TTL for pending upload entries, mirrored from the controller. */
31
+ const DEFAULT_PENDING_TTL_MS = 60 * 60 * 1000;
32
+ export function uploadGraphqlExtension(options = {}) {
33
+ const ttlMs = options.pendingTtlMs ?? DEFAULT_PENDING_TTL_MS;
34
+ return ({ Upload }) => {
35
+ const UploadedFileInfoType = new GraphQLObjectType({
36
+ name: 'UploadedFileInfo',
37
+ description: 'Metadata returned for one successfully uploaded file.',
38
+ fields: () => ({
39
+ key: { type: new GraphQLNonNull(GraphQLString) },
40
+ url: { type: new GraphQLNonNull(GraphQLString) },
41
+ name: { type: new GraphQLNonNull(GraphQLString) },
42
+ size: { type: new GraphQLNonNull(GraphQLInt) },
43
+ mimeType: { type: new GraphQLNonNull(GraphQLString) },
44
+ }),
45
+ });
46
+ return {
47
+ name: 'feature-upload',
48
+ types: [UploadedFileInfoType],
49
+ mutations: {
50
+ adminUpload: {
51
+ type: new GraphQLNonNull(UploadedFileInfoType),
52
+ description: 'Upload a single file for an upload-enabled property. Multi-file ' +
53
+ 'properties accept one mutation invocation per file.',
54
+ args: {
55
+ resourceId: { type: new GraphQLNonNull(GraphQLString) },
56
+ field: { type: new GraphQLNonNull(GraphQLString) },
57
+ file: { type: new GraphQLNonNull(Upload) },
58
+ },
59
+ async resolve(_src, args, ctx) {
60
+ const { resourceId, field } = args;
61
+ const { providerId, registered } = resolveProperty(ctx, resourceId, field);
62
+ const upload = args.file;
63
+ const computedKey = registered.uploadPath
64
+ ? registered.uploadPath(upload.filename)
65
+ : undefined;
66
+ const key = await registered.provider.upload({
67
+ originalName: upload.filename,
68
+ mimeType: upload.mimeType,
69
+ size: upload.size,
70
+ buffer: upload.buffer,
71
+ }, computedKey);
72
+ PendingUploadsRegistry.track(key, providerId, ttlMs);
73
+ const url = await registered.provider.getUrl(key);
74
+ const info = {
75
+ key,
76
+ url,
77
+ name: upload.filename,
78
+ size: upload.size,
79
+ mimeType: upload.mimeType,
80
+ };
81
+ return info;
82
+ },
83
+ },
84
+ adminCancelUpload: {
85
+ type: new GraphQLNonNull(GraphQLBoolean),
86
+ description: 'Cancel a still-pending upload (file uploaded but record not yet ' +
87
+ 'saved). Returns false when the key is no longer pending.',
88
+ args: {
89
+ resourceId: { type: new GraphQLNonNull(GraphQLString) },
90
+ field: { type: new GraphQLNonNull(GraphQLString) },
91
+ key: { type: new GraphQLNonNull(GraphQLString) },
92
+ },
93
+ async resolve(_src, args, ctx) {
94
+ const { resourceId, field, key } = args;
95
+ // Validate the resource/field exists & is upload-enabled. Mirrors
96
+ // the REST controller's check — prevents leaking arbitrary
97
+ // resource ids to authenticated clients.
98
+ resolveProperty(ctx, resourceId, field);
99
+ return PendingUploadsRegistry.cancel(key);
100
+ },
101
+ },
102
+ },
103
+ };
104
+ };
105
+ }
106
+ /**
107
+ * Resolve the resource + property and ensure it is wired to an upload
108
+ * provider. Mirrors the private helper in the REST upload controller — kept
109
+ * private to this module to avoid coupling. Throws GraphQL-friendly Error
110
+ * objects (not Nest exceptions) so they surface as standard GraphQL errors.
111
+ */
112
+ function resolveProperty(ctx, resourceId, field) {
113
+ let resource;
114
+ try {
115
+ resource = ctx.admin.findResource(resourceId);
116
+ }
117
+ catch (err) {
118
+ if (err instanceof ResourceNotFoundError)
119
+ throw new Error(err.message);
120
+ if (err instanceof ForbiddenError)
121
+ throw new Error(err.message);
122
+ throw err;
123
+ }
124
+ const decorator = resource.decorate();
125
+ const prop = decorator.getPropertyByKey(field);
126
+ if (!prop) {
127
+ throw new Error(`Property "${field}" not found on resource "${resourceId}"`);
128
+ }
129
+ const propJson = prop.toJSON();
130
+ const providerId = propJson.custom?.uploadProviderId;
131
+ if (!providerId) {
132
+ throw new Error(`Property "${field}" on resource "${resourceId}" is not configured for upload. ` +
133
+ 'Apply uploadFeature() to the resource.');
134
+ }
135
+ const registered = UploadProviderRegistry.get(providerId);
136
+ if (!registered) {
137
+ throw new Error(`Upload provider "${providerId}" is not registered.`);
138
+ }
139
+ return { providerId, registered };
140
+ }
141
+ //# sourceMappingURL=graphql.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphql.js","sourceRoot":"","sources":["../src/graphql.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export { uploadFeature } from './upload-feature.js';
2
+ export { UploadProviderRegistry } from './registry.js';
3
+ export { PendingUploadsRegistry } from './pending-registry.js';
4
+ export { LocalUploadProvider, type LocalUploadOptions } from './providers/local.js';
5
+ export { S3UploadProvider, type S3UploadOptions } from './providers/s3.js';
6
+ export type { IUploadProvider, UploadedFile, UploadedFileInfo, UploadFeatureOptions, UploadPropertyConfig, } from './types.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAG9D,OAAO,EAAE,mBAAmB,EAAE,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACnF,OAAO,EAAE,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAG1E,YAAY,EACV,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,YAAY,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ // @modern-admin/feature-upload — file upload plugin for modern-admin resources.
2
+ //
3
+ // Usage:
4
+ // import { uploadFeature, LocalUploadProvider, S3UploadProvider } from '@modern-admin/feature-upload'
5
+ // import { ModernAdminUploadModule } from '@modern-admin/feature-upload/nest'
6
+ export { uploadFeature } from './upload-feature.js';
7
+ export { UploadProviderRegistry } from './registry.js';
8
+ export { PendingUploadsRegistry } from './pending-registry.js';
9
+ // Built-in providers
10
+ export { LocalUploadProvider } from './providers/local.js';
11
+ export { S3UploadProvider } from './providers/s3.js';
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,EAAE;AACF,SAAS;AACT,wGAAwG;AACxG,gFAAgF;AAEhF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAE9D,qBAAqB;AACrB,OAAO,EAAE,mBAAmB,EAA2B,MAAM,sBAAsB,CAAA;AACnF,OAAO,EAAE,gBAAgB,EAAwB,MAAM,mBAAmB,CAAA"}
@@ -0,0 +1,3 @@
1
+ export { ModernAdminUploadModule, type ModernAdminUploadModuleOptions } from './upload.module.js';
2
+ export { UploadController } from './upload.controller.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/nest/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,uBAAuB,EAAE,KAAK,8BAA8B,EAAE,MAAM,oBAAoB,CAAA;AACjG,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA"}
@@ -0,0 +1,4 @@
1
+ // @modern-admin/feature-upload/nest — NestJS module + controller for file uploads.
2
+ export { ModernAdminUploadModule } from './upload.module.js';
3
+ export { UploadController } from './upload.controller.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/nest/index.ts"],"names":[],"mappings":"AAAA,mFAAmF;AAEnF,OAAO,EAAE,uBAAuB,EAAuC,MAAM,oBAAoB,CAAA;AACjG,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * UploadSweeperService — periodic background task that purges expired entries
3
+ * from `PendingUploadsRegistry`.
4
+ *
5
+ * The interval starts when Nest calls `onModuleInit` and stops on
6
+ * `onModuleDestroy`. Set `sweepIntervalMs: 0` in the module options to
7
+ * disable scheduling (useful for tests that drive `sweep()` manually).
8
+ *
9
+ * TODO(roadmap): replace the in-process `setInterval` driver with a BullMQ
10
+ * (Redis-backed) job queue. Reasons:
11
+ * - the registry will move to Redis in the multi-instance deployment story,
12
+ * and the sweeper job belongs in the same place;
13
+ * - BullMQ gives us cron scheduling, retries on transient storage errors,
14
+ * visibility/metrics through Bull Board, and crash safety (a process
15
+ * restart resumes scheduled sweeps instead of forgetting them);
16
+ * - per-key cancel can become a delayed job (`removeJob` on confirm) which
17
+ * is more efficient than a periodic full-table scan once we have many
18
+ * pending entries.
19
+ * The migration should keep the same `PendingUploadsRegistry` API surface so
20
+ * call sites (controller, hooks) do not need to change — only the storage
21
+ * (Map → Redis) and the sweeper driver (setInterval → BullMQ Worker) flip.
22
+ */
23
+ import { type OnModuleDestroy, type OnModuleInit } from '@nestjs/common';
24
+ import { type ModernAdminUploadModuleOptions } from './upload.tokens.js';
25
+ export declare class UploadSweeperService implements OnModuleInit, OnModuleDestroy {
26
+ private readonly options;
27
+ private timer;
28
+ constructor(options: ModernAdminUploadModuleOptions);
29
+ onModuleInit(): void;
30
+ onModuleDestroy(): void;
31
+ }
32
+ //# sourceMappingURL=upload-sweeper.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-sweeper.service.d.ts","sourceRoot":"","sources":["../../src/nest/upload-sweeper.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAsB,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE5F,OAAO,EAAyB,KAAK,8BAA8B,EAAE,MAAM,oBAAoB,CAAA;AAI/F,qBACa,oBAAqB,YAAW,YAAY,EAAE,eAAe;IAG7B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFnE,OAAO,CAAC,KAAK,CAA8C;gBAEC,OAAO,EAAE,8BAA8B;IAEnG,YAAY,IAAI,IAAI;IAUpB,eAAe,IAAI,IAAI;CAMxB"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * UploadSweeperService — periodic background task that purges expired entries
3
+ * from `PendingUploadsRegistry`.
4
+ *
5
+ * The interval starts when Nest calls `onModuleInit` and stops on
6
+ * `onModuleDestroy`. Set `sweepIntervalMs: 0` in the module options to
7
+ * disable scheduling (useful for tests that drive `sweep()` manually).
8
+ *
9
+ * TODO(roadmap): replace the in-process `setInterval` driver with a BullMQ
10
+ * (Redis-backed) job queue. Reasons:
11
+ * - the registry will move to Redis in the multi-instance deployment story,
12
+ * and the sweeper job belongs in the same place;
13
+ * - BullMQ gives us cron scheduling, retries on transient storage errors,
14
+ * visibility/metrics through Bull Board, and crash safety (a process
15
+ * restart resumes scheduled sweeps instead of forgetting them);
16
+ * - per-key cancel can become a delayed job (`removeJob` on confirm) which
17
+ * is more efficient than a periodic full-table scan once we have many
18
+ * pending entries.
19
+ * The migration should keep the same `PendingUploadsRegistry` API surface so
20
+ * call sites (controller, hooks) do not need to change — only the storage
21
+ * (Map → Redis) and the sweeper driver (setInterval → BullMQ Worker) flip.
22
+ */
23
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
24
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
25
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
26
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
27
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
28
+ };
29
+ var __metadata = (this && this.__metadata) || function (k, v) {
30
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
31
+ };
32
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
33
+ return function (target, key) { decorator(target, key, paramIndex); }
34
+ };
35
+ import { Inject, Injectable } from '@nestjs/common';
36
+ import { PendingUploadsRegistry } from '../pending-registry.js';
37
+ import { UPLOAD_MODULE_OPTIONS } from './upload.tokens.js';
38
+ const DEFAULT_SWEEP_INTERVAL_MS = 5 * 60 * 1000;
39
+ let UploadSweeperService = class UploadSweeperService {
40
+ constructor(options) {
41
+ this.options = options;
42
+ this.timer = null;
43
+ }
44
+ onModuleInit() {
45
+ const interval = this.options.sweepIntervalMs ?? DEFAULT_SWEEP_INTERVAL_MS;
46
+ if (interval <= 0)
47
+ return;
48
+ this.timer = setInterval(() => {
49
+ void PendingUploadsRegistry.sweep();
50
+ }, interval);
51
+ // Allow the Node.js process to exit even if the timer is still scheduled.
52
+ if (typeof this.timer.unref === 'function')
53
+ this.timer.unref();
54
+ }
55
+ onModuleDestroy() {
56
+ if (this.timer) {
57
+ clearInterval(this.timer);
58
+ this.timer = null;
59
+ }
60
+ }
61
+ };
62
+ UploadSweeperService = __decorate([
63
+ Injectable(),
64
+ __param(0, Inject(UPLOAD_MODULE_OPTIONS)),
65
+ __metadata("design:paramtypes", [Object])
66
+ ], UploadSweeperService);
67
+ export { UploadSweeperService };
68
+ //# sourceMappingURL=upload-sweeper.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-sweeper.service.js","sourceRoot":"","sources":["../../src/nest/upload-sweeper.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;;;;;;;;;;;;AAEH,OAAO,EAAE,MAAM,EAAE,UAAU,EAA2C,MAAM,gBAAgB,CAAA;AAC5F,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,EAAE,qBAAqB,EAAuC,MAAM,oBAAoB,CAAA;AAE/F,MAAM,yBAAyB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAA;AAGxC,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IAG/B,YAA2C,OAAwD;QAAvC,YAAO,GAAP,OAAO,CAAgC;QAF3F,UAAK,GAA0C,IAAI,CAAA;IAE2C,CAAC;IAEvG,YAAY;QACV,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,IAAI,yBAAyB,CAAA;QAC1E,IAAI,QAAQ,IAAI,CAAC;YAAE,OAAM;QACzB,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;YAC5B,KAAK,sBAAsB,CAAC,KAAK,EAAE,CAAA;QACrC,CAAC,EAAE,QAAQ,CAAC,CAAA;QACZ,0EAA0E;QAC1E,IAAI,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,UAAU;YAAE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;IAChE,CAAC;IAED,eAAe;QACb,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YACzB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;QACnB,CAAC;IACH,CAAC;CACF,CAAA;AArBY,oBAAoB;IADhC,UAAU,EAAE;IAIE,WAAA,MAAM,CAAC,qBAAqB,CAAC,CAAA;;GAH/B,oBAAoB,CAqBhC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * UploadController — handles file uploads for `type: 'file'` properties.
3
+ *
4
+ * Endpoints
5
+ * ---------
6
+ * POST /admin/api/resources/:resourceId/actions/upload?field=<propertyPath>
7
+ * Content-Type: multipart/form-data
8
+ * Accepts one or more files (any field names). Returns `UploadedFileInfo[]` —
9
+ * callers writing to a single-value `'file'` property take the first item;
10
+ * `isArray: true` properties consume the whole array.
11
+ *
12
+ * DELETE /admin/api/resources/:resourceId/actions/upload?field=<path>&key=<key>
13
+ * Cancels a still-pending upload (uploaded but not yet saved). Calls
14
+ * `PendingUploadsRegistry.cancel(key)` which deletes the file from
15
+ * storage. No-op (404) if the key is no longer pending — protects already
16
+ * persisted files from being deleted via this endpoint.
17
+ *
18
+ * Authorisation re-uses `ModernAdminAuthGuard` from `@modern-admin/nest` —
19
+ * requires `ModernAdminModule.forRoot()` to be registered (globally or as a
20
+ * parent module).
21
+ */
22
+ import type { IncomingMessage } from 'node:http';
23
+ import { type ModernAdmin } from '@modern-admin/core';
24
+ import { type ModernAdminUploadModuleOptions } from './upload.tokens.js';
25
+ import type { UploadedFileInfo } from '../types.js';
26
+ export declare class UploadController {
27
+ private readonly admin;
28
+ private readonly moduleOptions;
29
+ constructor(admin: ModernAdmin, moduleOptions: ModernAdminUploadModuleOptions);
30
+ /**
31
+ * Upload one or more files for a specific resource property.
32
+ *
33
+ * @param resourceId Admin resource id (e.g. `'users'`)
34
+ * @param field Property path (e.g. `'avatar'` / `'gallery'`)
35
+ * @param req Raw Express / Node.js `IncomingMessage`
36
+ */
37
+ upload(resourceId: string, field: string, req: IncomingMessage): Promise<UploadedFileInfo[]>;
38
+ /**
39
+ * Cancel a still-pending upload — delete the file from storage immediately.
40
+ * Used by the editor when the user removes a file *before* saving the form.
41
+ *
42
+ * Returns 204 if the cancel succeeded, 404 if the key is not pending.
43
+ * The "not pending" branch protects already-persisted files: once a file
44
+ * has been confirmed via the action hooks it can no longer be removed via
45
+ * this endpoint — only via the regular edit/delete actions.
46
+ */
47
+ cancel(resourceId: string, field: string, key: string): Promise<void>;
48
+ /** Resolve the resource + property and ensure it is wired to an upload provider. */
49
+ private resolveProperty;
50
+ }
51
+ //# sourceMappingURL=upload.controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload.controller.d.ts","sourceRoot":"","sources":["../../src/nest/upload.controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAkBH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAEhD,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,oBAAoB,CAAA;AAI3B,OAAO,EAAyB,KAAK,8BAA8B,EAAE,MAAM,oBAAoB,CAAA;AAC/F,OAAO,KAAK,EAAgB,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAiEjE,qBAIa,gBAAgB;IAEH,OAAO,CAAC,QAAQ,CAAC,KAAK;IACb,OAAO,CAAC,QAAQ,CAAC,aAAa;gBADtB,KAAK,EAAE,WAAW,EACT,aAAa,EAAE,8BAA8B;IAG/F;;;;;;OAMG;IAEG,MAAM,CACW,UAAU,EAAE,MAAM,EACvB,KAAK,EAAE,MAAM,EACtB,GAAG,EAAE,eAAe,GAC1B,OAAO,CAAC,gBAAgB,EAAE,CAAC;IA2B9B;;;;;;;;OAQG;IAGG,MAAM,CACW,UAAU,EAAE,MAAM,EACvB,KAAK,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,GACxB,OAAO,CAAC,IAAI,CAAC;IAchB,oFAAoF;IACpF,OAAO,CAAC,eAAe;CAkCxB"}
@@ -0,0 +1,220 @@
1
+ /**
2
+ * UploadController — handles file uploads for `type: 'file'` properties.
3
+ *
4
+ * Endpoints
5
+ * ---------
6
+ * POST /admin/api/resources/:resourceId/actions/upload?field=<propertyPath>
7
+ * Content-Type: multipart/form-data
8
+ * Accepts one or more files (any field names). Returns `UploadedFileInfo[]` —
9
+ * callers writing to a single-value `'file'` property take the first item;
10
+ * `isArray: true` properties consume the whole array.
11
+ *
12
+ * DELETE /admin/api/resources/:resourceId/actions/upload?field=<path>&key=<key>
13
+ * Cancels a still-pending upload (uploaded but not yet saved). Calls
14
+ * `PendingUploadsRegistry.cancel(key)` which deletes the file from
15
+ * storage. No-op (404) if the key is no longer pending — protects already
16
+ * persisted files from being deleted via this endpoint.
17
+ *
18
+ * Authorisation re-uses `ModernAdminAuthGuard` from `@modern-admin/nest` —
19
+ * requires `ModernAdminModule.forRoot()` to be registered (globally or as a
20
+ * parent module).
21
+ */
22
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
23
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
24
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
25
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
26
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
27
+ };
28
+ var __metadata = (this && this.__metadata) || function (k, v) {
29
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
30
+ };
31
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
32
+ return function (target, key) { decorator(target, key, paramIndex); }
33
+ };
34
+ import { BadRequestException, Controller, Delete, ForbiddenException, HttpCode, Inject, InternalServerErrorException, NotFoundException, Post, Param, Query, Req, UseGuards, } from '@nestjs/common';
35
+ import { ApiCookieAuth, ApiTags } from '@nestjs/swagger';
36
+ import Busboy from 'busboy';
37
+ import { ForbiddenError, ResourceNotFoundError, } from '@modern-admin/core';
38
+ import { MODERN_ADMIN, ModernAdminAuthGuard } from '@modern-admin/nest';
39
+ import { UploadProviderRegistry } from '../registry.js';
40
+ import { PendingUploadsRegistry } from '../pending-registry.js';
41
+ import { UPLOAD_MODULE_OPTIONS } from './upload.tokens.js';
42
+ /** Reads every file from a multipart/form-data stream using busboy. */
43
+ function parseAllFiles(req) {
44
+ return new Promise((resolve, reject) => {
45
+ let bb;
46
+ try {
47
+ bb = Busboy({ headers: req.headers });
48
+ }
49
+ catch {
50
+ reject(new BadRequestException('Request is not multipart/form-data'));
51
+ return;
52
+ }
53
+ const files = [];
54
+ /** Number of streams that have started but not yet ended. */
55
+ let pending = 0;
56
+ let finished = false;
57
+ let settled = false;
58
+ let firstError;
59
+ const tryResolve = () => {
60
+ if (settled || !finished || pending > 0)
61
+ return;
62
+ settled = true;
63
+ if (firstError)
64
+ reject(firstError);
65
+ else if (files.length === 0)
66
+ reject(new BadRequestException('No file found in request body'));
67
+ else
68
+ resolve(files);
69
+ };
70
+ bb.on('file', (_fieldname, stream, info) => {
71
+ pending++;
72
+ const chunks = [];
73
+ stream.on('data', (chunk) => chunks.push(chunk));
74
+ stream.on('end', () => {
75
+ const buffer = Buffer.concat(chunks);
76
+ files.push({
77
+ originalName: info.filename || 'upload',
78
+ mimeType: info.mimeType || 'application/octet-stream',
79
+ size: buffer.length,
80
+ buffer,
81
+ });
82
+ pending--;
83
+ tryResolve();
84
+ });
85
+ stream.on('error', (err) => {
86
+ firstError = firstError ?? err;
87
+ pending--;
88
+ tryResolve();
89
+ });
90
+ });
91
+ bb.on('finish', () => {
92
+ finished = true;
93
+ tryResolve();
94
+ });
95
+ bb.on('error', (err) => {
96
+ firstError = firstError ?? err;
97
+ finished = true;
98
+ tryResolve();
99
+ });
100
+ req.pipe(bb);
101
+ });
102
+ }
103
+ let UploadController = class UploadController {
104
+ constructor(admin, moduleOptions) {
105
+ this.admin = admin;
106
+ this.moduleOptions = moduleOptions;
107
+ }
108
+ /**
109
+ * Upload one or more files for a specific resource property.
110
+ *
111
+ * @param resourceId Admin resource id (e.g. `'users'`)
112
+ * @param field Property path (e.g. `'avatar'` / `'gallery'`)
113
+ * @param req Raw Express / Node.js `IncomingMessage`
114
+ */
115
+ async upload(resourceId, field, req) {
116
+ const { providerId, registered } = this.resolveProperty(resourceId, field);
117
+ // 1 — Parse the multipart body (collect every file).
118
+ const files = await parseAllFiles(req);
119
+ // 2 — If the property is single-value, only the first file is honoured.
120
+ // The frontend sends one anyway; this is a safety bound.
121
+ const accepted = registered.isArray ? files : files.slice(0, 1);
122
+ // 3 — Upload each file, computing the storage key per-file so a custom
123
+ // `uploadPath` generator can produce unique keys for each upload.
124
+ const ttlMs = this.moduleOptions.pendingTtlMs ?? 60 * 60 * 1000;
125
+ const results = [];
126
+ for (const file of accepted) {
127
+ const computedKey = registered.uploadPath ? registered.uploadPath(file.originalName) : undefined;
128
+ const key = await registered.provider.upload(file, computedKey);
129
+ // Track as pending — it will be confirmed by `new.after`/`edit.after`
130
+ // when the form is saved, or cleaned up by the sweeper when the TTL
131
+ // expires (whichever comes first).
132
+ PendingUploadsRegistry.track(key, providerId, ttlMs);
133
+ const url = await registered.provider.getUrl(key);
134
+ results.push({ key, url, name: file.originalName, size: file.size, mimeType: file.mimeType });
135
+ }
136
+ return results;
137
+ }
138
+ /**
139
+ * Cancel a still-pending upload — delete the file from storage immediately.
140
+ * Used by the editor when the user removes a file *before* saving the form.
141
+ *
142
+ * Returns 204 if the cancel succeeded, 404 if the key is not pending.
143
+ * The "not pending" branch protects already-persisted files: once a file
144
+ * has been confirmed via the action hooks it can no longer be removed via
145
+ * this endpoint — only via the regular edit/delete actions.
146
+ */
147
+ async cancel(resourceId, field, key) {
148
+ if (!key)
149
+ throw new BadRequestException('Missing "key" query parameter');
150
+ // Validate that the resource/field is actually upload-enabled. This avoids
151
+ // leaking the existence of arbitrary resource ids to unauthenticated callers
152
+ // (the auth guard already requires authentication).
153
+ this.resolveProperty(resourceId, field);
154
+ const cancelled = await PendingUploadsRegistry.cancel(key);
155
+ if (!cancelled) {
156
+ throw new NotFoundException('Key is not pending — already saved or unknown');
157
+ }
158
+ }
159
+ // ─── Internals ─────────────────────────────────────────────────────────────
160
+ /** Resolve the resource + property and ensure it is wired to an upload provider. */
161
+ resolveProperty(resourceId, field) {
162
+ let resource;
163
+ try {
164
+ resource = this.admin.findResource(resourceId);
165
+ }
166
+ catch (err) {
167
+ if (err instanceof ResourceNotFoundError)
168
+ throw new NotFoundException(err.message);
169
+ if (err instanceof ForbiddenError)
170
+ throw new ForbiddenException(err.message);
171
+ throw err;
172
+ }
173
+ const decorator = resource.decorate();
174
+ const prop = decorator.getPropertyByKey(field);
175
+ if (!prop) {
176
+ throw new NotFoundException(`Property "${field}" not found on resource "${resourceId}"`);
177
+ }
178
+ const propJson = prop.toJSON();
179
+ const providerId = propJson.custom?.uploadProviderId;
180
+ if (!providerId) {
181
+ throw new BadRequestException(`Property "${field}" on resource "${resourceId}" is not configured for upload. ` +
182
+ 'Apply uploadFeature() to the resource.');
183
+ }
184
+ const registered = UploadProviderRegistry.get(providerId);
185
+ if (!registered) {
186
+ throw new InternalServerErrorException(`Upload provider "${providerId}" is not registered.`);
187
+ }
188
+ return { providerId, registered };
189
+ }
190
+ };
191
+ __decorate([
192
+ Post('upload'),
193
+ __param(0, Param('resourceId')),
194
+ __param(1, Query('field')),
195
+ __param(2, Req()),
196
+ __metadata("design:type", Function),
197
+ __metadata("design:paramtypes", [String, String, Function]),
198
+ __metadata("design:returntype", Promise)
199
+ ], UploadController.prototype, "upload", null);
200
+ __decorate([
201
+ Delete('upload'),
202
+ HttpCode(204),
203
+ __param(0, Param('resourceId')),
204
+ __param(1, Query('field')),
205
+ __param(2, Query('key')),
206
+ __metadata("design:type", Function),
207
+ __metadata("design:paramtypes", [String, String, String]),
208
+ __metadata("design:returntype", Promise)
209
+ ], UploadController.prototype, "cancel", null);
210
+ UploadController = __decorate([
211
+ ApiTags('Admin / Uploads'),
212
+ ApiCookieAuth('session'),
213
+ Controller('admin/api/resources/:resourceId/actions'),
214
+ UseGuards(ModernAdminAuthGuard),
215
+ __param(0, Inject(MODERN_ADMIN)),
216
+ __param(1, Inject(UPLOAD_MODULE_OPTIONS)),
217
+ __metadata("design:paramtypes", [Function, Object])
218
+ ], UploadController);
219
+ export { UploadController };
220
+ //# sourceMappingURL=upload.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload.controller.js","sourceRoot":"","sources":["../../src/nest/upload.controller.ts"],"names":[],"mappings":"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"}