@idevconn/create-icore 0.6.3 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/dist/cli.js +384 -276
  2. package/dist/index.cjs +385 -277
  3. package/dist/index.d.cts +3 -3
  4. package/dist/index.d.ts +3 -3
  5. package/dist/index.js +382 -274
  6. package/package.json +1 -1
  7. package/templates/.yarn/releases/yarn-4.16.0.cjs +944 -0
  8. package/templates/.yarnrc.yml +1 -1
  9. package/templates/apps/api/src/app/storage/storage.controller.ts +28 -0
  10. package/templates/apps/microservices/auth/src/app/app.module.ts +20 -2
  11. package/templates/apps/microservices/notes/src/app/app.module.ts +17 -2
  12. package/templates/apps/microservices/upload/src/app/app.module.ts +17 -2
  13. package/templates/apps/microservices/upload/src/app/storage.controller.ts +7 -0
  14. package/templates/apps/templates/client-antd/src/components/auth/AuthBrandPanel.tsx +59 -0
  15. package/templates/apps/templates/client-antd/src/components/auth/CheckEmailScreen.tsx +28 -0
  16. package/templates/apps/templates/client-antd/src/components/auth/LoginForm.tsx +116 -0
  17. package/templates/apps/templates/client-antd/src/components/auth/MagicLinkForm.tsx +95 -0
  18. package/templates/apps/templates/client-antd/src/components/auth/RegisterForm.tsx +98 -0
  19. package/templates/apps/templates/client-antd/src/globals.less +6 -0
  20. package/templates/apps/templates/client-antd/src/main.tsx +1 -1
  21. package/templates/apps/templates/client-antd/src/routes/login.tsx +45 -181
  22. package/templates/apps/templates/client-mui/src/components/auth/AuthBrandPanel.tsx +59 -0
  23. package/templates/apps/templates/client-mui/src/components/auth/CheckEmailScreen.tsx +28 -0
  24. package/templates/apps/templates/client-mui/src/components/auth/LoginForm.tsx +141 -0
  25. package/templates/apps/templates/client-mui/src/components/auth/MagicLinkForm.tsx +106 -0
  26. package/templates/apps/templates/client-mui/src/components/auth/RegisterForm.tsx +113 -0
  27. package/templates/apps/templates/client-mui/src/main.tsx +1 -1
  28. package/templates/apps/templates/client-mui/src/routes/login.tsx +50 -186
  29. package/templates/apps/templates/client-shadcn/src/components/auth/AuthBrandPanel.tsx +52 -0
  30. package/templates/apps/templates/client-shadcn/src/components/auth/CheckEmailScreen.tsx +29 -0
  31. package/templates/apps/templates/client-shadcn/src/components/auth/LoginForm.tsx +161 -0
  32. package/templates/apps/templates/client-shadcn/src/components/auth/MagicLinkForm.tsx +110 -0
  33. package/templates/apps/templates/client-shadcn/src/components/auth/RegisterForm.tsx +107 -0
  34. package/templates/apps/templates/client-shadcn/src/components/layout/LayoutHeader.tsx +31 -10
  35. package/templates/apps/templates/client-shadcn/src/components/layout/LayoutSider.tsx +22 -27
  36. package/templates/apps/templates/client-shadcn/src/components/ui/card.tsx +1 -1
  37. package/templates/apps/templates/client-shadcn/src/globals.css +39 -13
  38. package/templates/apps/templates/client-shadcn/src/routes/auth.callback.tsx +1 -1
  39. package/templates/apps/templates/client-shadcn/src/routes/login.tsx +55 -165
  40. package/templates/libs/auth-strategies/mongodb/CHANGELOG.md +8 -0
  41. package/templates/libs/auth-strategies/mongodb/README.md +11 -0
  42. package/templates/libs/auth-strategies/mongodb/eslint.config.mjs +19 -0
  43. package/templates/libs/auth-strategies/mongodb/jest.config.cts +10 -0
  44. package/templates/libs/auth-strategies/mongodb/package.json +16 -0
  45. package/templates/libs/auth-strategies/mongodb/project.json +19 -0
  46. package/templates/libs/auth-strategies/mongodb/src/index.ts +1 -0
  47. package/templates/libs/auth-strategies/mongodb/src/lib/__tests__/mongodb-auth.strategy.unit.test.ts +42 -0
  48. package/templates/libs/auth-strategies/mongodb/src/lib/auth-mongodb.spec.ts +7 -0
  49. package/templates/libs/auth-strategies/mongodb/src/lib/auth-mongodb.ts +3 -0
  50. package/templates/libs/auth-strategies/mongodb/src/lib/mongodb-auth.strategy.ts +188 -0
  51. package/templates/libs/auth-strategies/mongodb/tsconfig.json +23 -0
  52. package/templates/libs/auth-strategies/mongodb/tsconfig.lib.json +10 -0
  53. package/templates/libs/auth-strategies/mongodb/tsconfig.spec.json +16 -0
  54. package/templates/libs/db-strategies/mongodb/CHANGELOG.md +7 -0
  55. package/templates/libs/db-strategies/mongodb/README.md +11 -0
  56. package/templates/libs/db-strategies/mongodb/eslint.config.mjs +19 -0
  57. package/templates/libs/db-strategies/mongodb/jest.config.cts +10 -0
  58. package/templates/libs/db-strategies/mongodb/package.json +14 -0
  59. package/templates/libs/db-strategies/mongodb/project.json +19 -0
  60. package/templates/libs/db-strategies/mongodb/src/index.ts +1 -0
  61. package/templates/libs/db-strategies/mongodb/src/lib/__tests__/mongodb-db.strategy.unit.test.ts +38 -0
  62. package/templates/libs/db-strategies/mongodb/src/lib/mongodb-db.strategy.ts +108 -0
  63. package/templates/libs/db-strategies/mongodb/src/lib/mongodb.spec.ts +7 -0
  64. package/templates/libs/db-strategies/mongodb/src/lib/mongodb.ts +3 -0
  65. package/templates/libs/db-strategies/mongodb/tsconfig.json +23 -0
  66. package/templates/libs/db-strategies/mongodb/tsconfig.lib.json +10 -0
  67. package/templates/libs/db-strategies/mongodb/tsconfig.spec.json +16 -0
  68. package/templates/libs/shared/src/strategies/storage.ts +3 -0
  69. package/templates/libs/storage-strategies/mongodb/CHANGELOG.md +8 -0
  70. package/templates/libs/storage-strategies/mongodb/README.md +11 -0
  71. package/templates/libs/storage-strategies/mongodb/eslint.config.mjs +19 -0
  72. package/templates/libs/storage-strategies/mongodb/jest.config.cts +10 -0
  73. package/templates/libs/storage-strategies/mongodb/package.json +14 -0
  74. package/templates/libs/storage-strategies/mongodb/project.json +19 -0
  75. package/templates/libs/storage-strategies/mongodb/src/index.ts +1 -0
  76. package/templates/libs/storage-strategies/mongodb/src/lib/__tests__/mongodb-storage.strategy.unit.test.ts +38 -0
  77. package/templates/libs/storage-strategies/mongodb/src/lib/mongodb-storage.strategy.ts +93 -0
  78. package/templates/libs/storage-strategies/mongodb/src/lib/storage-mongodb.spec.ts +7 -0
  79. package/templates/libs/storage-strategies/mongodb/src/lib/storage-mongodb.ts +3 -0
  80. package/templates/libs/storage-strategies/mongodb/tsconfig.json +23 -0
  81. package/templates/libs/storage-strategies/mongodb/tsconfig.lib.json +10 -0
  82. package/templates/libs/storage-strategies/mongodb/tsconfig.spec.json +16 -0
  83. package/templates/libs/template-shared/src/lib/i18n/keys.ts +216 -56
  84. package/templates/libs/template-shared/src/lib/stores/theme.store.ts +1 -6
  85. package/templates/libs/upload-client/src/lib/upload-client.service.ts +7 -0
  86. package/templates/tsconfig.base.json +4 -1
  87. package/templates/.yarn/releases/yarn-4.15.0.cjs +0 -940
@@ -0,0 +1,19 @@
1
+ import baseConfig from '../../../eslint.config.mjs';
2
+
3
+ export default [
4
+ ...baseConfig,
5
+ {
6
+ files: ['**/*.json'],
7
+ rules: {
8
+ '@nx/dependency-checks': [
9
+ 'error',
10
+ {
11
+ ignoredFiles: ['{projectRoot}/eslint.config.{js,cjs,mjs,ts,cts,mts}'],
12
+ },
13
+ ],
14
+ },
15
+ languageOptions: {
16
+ parser: await import('jsonc-eslint-parser'),
17
+ },
18
+ },
19
+ ];
@@ -0,0 +1,10 @@
1
+ module.exports = {
2
+ displayName: 'mongodb',
3
+ preset: '../../../jest.preset.js',
4
+ testEnvironment: 'node',
5
+ transform: {
6
+ '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
7
+ },
8
+ moduleFileExtensions: ['ts', 'js', 'html'],
9
+ coverageDirectory: '../../../coverage/libs/db-strategies/mongodb',
10
+ };
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "@icore/db-mongodb",
3
+ "version": "0.0.2",
4
+ "private": true,
5
+ "type": "commonjs",
6
+ "main": "./src/index.js",
7
+ "types": "./src/index.d.ts",
8
+ "dependencies": {
9
+ "@icore/shared": "workspace:*",
10
+ "mongodb-memory-server": "^11.2.0",
11
+ "mongoose": "^9.6.3",
12
+ "tslib": "^2.3.0"
13
+ }
14
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "db-mongodb",
3
+ "$schema": "../../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "libs/db-strategies/mongodb/src",
5
+ "projectType": "library",
6
+ "tags": [],
7
+ "targets": {
8
+ "build": {
9
+ "executor": "@nx/js:tsc",
10
+ "outputs": ["{options.outputPath}"],
11
+ "options": {
12
+ "outputPath": "dist/libs/db-strategies/mongodb",
13
+ "main": "libs/db-strategies/mongodb/src/index.ts",
14
+ "tsConfig": "libs/db-strategies/mongodb/tsconfig.lib.json",
15
+ "assets": ["libs/db-strategies/mongodb/*.md"]
16
+ }
17
+ }
18
+ }
19
+ }
@@ -0,0 +1 @@
1
+ export * from './lib/mongodb-db.strategy';
@@ -0,0 +1,38 @@
1
+ import { MongoMemoryServer } from 'mongodb-memory-server';
2
+ import { connect, Connection } from 'mongoose';
3
+ import { MongoDbDBStrategy } from '../mongodb-db.strategy';
4
+ import { runDBContract } from '@icore/shared/testing';
5
+
6
+ describe('MongoDbDBStrategy', () => {
7
+ let mongod: MongoMemoryServer;
8
+ let connection: Connection;
9
+ let strategy: MongoDbDBStrategy;
10
+
11
+ beforeAll(async () => {
12
+ mongod = await MongoMemoryServer.create();
13
+ const uri = mongod.getUri();
14
+ const conn = await connect(uri);
15
+ connection = conn.connection;
16
+ }, 30000);
17
+
18
+ afterAll(async () => {
19
+ if (connection) await connection.close();
20
+ if (mongod) await mongod.stop();
21
+ });
22
+
23
+ beforeEach(async () => {
24
+ if (connection.db) {
25
+ const collections = await connection.db.collections();
26
+ for (const collection of collections) {
27
+ await collection.deleteMany({});
28
+ }
29
+ }
30
+ });
31
+
32
+ runDBContract('mongodb', () => {
33
+ if (!strategy) {
34
+ strategy = new MongoDbDBStrategy({ connection });
35
+ }
36
+ return strategy;
37
+ });
38
+ });
@@ -0,0 +1,108 @@
1
+ import { Connection, Model, Schema } from 'mongoose';
2
+ import { DBStrategy, DBDocument, QueryOptions } from '@icore/shared';
3
+
4
+ export interface MongoDbDBStrategyOptions {
5
+ connection: Connection;
6
+ }
7
+
8
+ export class MongoDbDBStrategy implements DBStrategy {
9
+ private models: Map<string, Model<unknown>> = new Map();
10
+
11
+ constructor(private readonly opts: MongoDbDBStrategyOptions) {}
12
+
13
+ private getModel(collection: string): Model<{ id: string; data: unknown }> {
14
+ if (!this.models.has(collection)) {
15
+ const schema = new Schema(
16
+ {
17
+ id: { type: String, required: true, index: true },
18
+ data: { type: Schema.Types.Mixed, required: true },
19
+ },
20
+ { strict: false, timestamps: true },
21
+ );
22
+ const model = this.opts.connection.model(collection, schema);
23
+ this.models.set(collection, model);
24
+ return model;
25
+ }
26
+ return this.models.get(collection) as Model<{ id: string; data: unknown }>;
27
+ }
28
+
29
+ async get<T>(collection: string, id: string): Promise<DBDocument<T> | null> {
30
+ const model = this.getModel(collection);
31
+ const doc = await model.findOne({ id }).lean().exec();
32
+ if (!doc) return null;
33
+ return { id: doc.id, data: doc.data as T };
34
+ }
35
+
36
+ async set<T>(collection: string, id: string, data: T): Promise<void> {
37
+ const model = this.getModel(collection);
38
+ await model.findOneAndUpdate({ id }, { id, data }, { upsert: true }).exec();
39
+ }
40
+
41
+ async update<T>(collection: string, id: string, patch: Partial<T>): Promise<void> {
42
+ const model = this.getModel(collection);
43
+ // Use dot notation for partial updates of the 'data' field
44
+ const update: Record<string, unknown> = {};
45
+ for (const [key, value] of Object.entries(patch)) {
46
+ update[`data.${key}`] = value;
47
+ }
48
+ const result = await model.findOneAndUpdate({ id }, { $set: update }).exec();
49
+ if (!result) throw new Error(`not_found: ${collection}/${id}`);
50
+ }
51
+
52
+ async delete(collection: string, id: string): Promise<void> {
53
+ const model = this.getModel(collection);
54
+ const result = await model.findOneAndDelete({ id }).exec();
55
+ if (!result) throw new Error(`not_found: ${collection}/${id}`);
56
+ }
57
+
58
+ async list<T>(collection: string, opts?: QueryOptions): Promise<DBDocument<T>[]> {
59
+ const model = this.getModel(collection);
60
+ let q = model.find();
61
+
62
+ if (opts?.where) {
63
+ const filter: Record<string, unknown> = {};
64
+ for (const c of opts.where) {
65
+ const path = c.field === 'id' ? 'id' : `data.${c.field}`;
66
+ switch (c.op) {
67
+ case '==':
68
+ filter[path] = c.value;
69
+ break;
70
+ case '!=':
71
+ filter[path] = { $ne: c.value };
72
+ break;
73
+ case '<':
74
+ filter[path] = { $lt: c.value };
75
+ break;
76
+ case '<=':
77
+ filter[path] = { $lte: c.value };
78
+ break;
79
+ case '>':
80
+ filter[path] = { $gt: c.value };
81
+ break;
82
+ case '>=':
83
+ filter[path] = { $gte: c.value };
84
+ break;
85
+ case 'in':
86
+ filter[path] = { $in: c.value };
87
+ break;
88
+ }
89
+ }
90
+ q = q.where(filter);
91
+ }
92
+
93
+ if (opts?.orderBy) {
94
+ const field = opts.orderBy.field === 'id' ? 'id' : `data.${opts.orderBy.field}`;
95
+ q = q.sort({ [field]: opts.orderBy.direction === 'desc' ? -1 : 1 });
96
+ }
97
+
98
+ if (opts?.limit != null) {
99
+ q = q.limit(opts.limit);
100
+ }
101
+
102
+ const docs = await q.lean().exec();
103
+ return docs.map((doc) => ({
104
+ id: (doc as { id: string }).id,
105
+ data: (doc as { data: T }).data,
106
+ }));
107
+ }
108
+ }
@@ -0,0 +1,7 @@
1
+ import { mongodb } from './mongodb';
2
+
3
+ describe('mongodb', () => {
4
+ it('should work', () => {
5
+ expect(mongodb()).toEqual('mongodb');
6
+ });
7
+ });
@@ -0,0 +1,3 @@
1
+ export function mongodb(): string {
2
+ return 'mongodb';
3
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "extends": "../../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "module": "commonjs",
5
+ "forceConsistentCasingInFileNames": true,
6
+ "strict": true,
7
+ "importHelpers": true,
8
+ "noImplicitOverride": true,
9
+ "noImplicitReturns": true,
10
+ "noFallthroughCasesInSwitch": true,
11
+ "noPropertyAccessFromIndexSignature": true
12
+ },
13
+ "files": [],
14
+ "include": [],
15
+ "references": [
16
+ {
17
+ "path": "./tsconfig.lib.json"
18
+ },
19
+ {
20
+ "path": "./tsconfig.spec.json"
21
+ }
22
+ ]
23
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../../dist/out-tsc",
5
+ "declaration": true,
6
+ "types": ["node"]
7
+ },
8
+ "include": ["src/**/*.ts"],
9
+ "exclude": ["jest.config.ts", "jest.config.cts", "src/**/*.spec.ts", "src/**/*.test.ts"]
10
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../../dist/out-tsc",
5
+ "module": "commonjs",
6
+ "moduleResolution": "node10",
7
+ "types": ["jest", "node"]
8
+ },
9
+ "include": [
10
+ "jest.config.ts",
11
+ "jest.config.cts",
12
+ "src/**/*.test.ts",
13
+ "src/**/*.spec.ts",
14
+ "src/**/*.d.ts"
15
+ ]
16
+ }
@@ -14,4 +14,7 @@ export interface StorageStrategy {
14
14
  remove(userId: string, ref: StorageRef): Promise<void>;
15
15
  getSignedUrl(userId: string, ref: StorageRef, ttlSec?: number): Promise<string>;
16
16
  list(userId: string, prefix?: string): Promise<StorageRef[]>;
17
+ /** Providers that can't issue real signed URLs (e.g. GridFS) implement this
18
+ * so the gateway can proxy the file. Returns undefined for other providers. */
19
+ downloadBuffer?(userId: string, ref: StorageRef): Promise<Buffer>;
17
20
  }
@@ -0,0 +1,8 @@
1
+ # @icore/storage-mongodb
2
+
3
+ ## 0.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 2c29eac: Fix ESLint issues, update dependencies, and add MongoDB configuration examples to .env templates.
8
+ - af27cae: Fix MongoDB review bugs and wire GridFS download: guard model re-registration, fix expiresIn calculation, escape regex in list(), replace `as never` cast, drop non-existent uuid v14 dep. Add downloadBuffer to StorageStrategy interface + MongoDbStorageStrategy impl + upload MS handler + UploadClientService method + GET /api/storage/file gateway endpoint so MongoDB storage downloads actually work.
@@ -0,0 +1,11 @@
1
+ # storage-mongodb
2
+
3
+ This library was generated with [Nx](https://nx.dev).
4
+
5
+ ## Building
6
+
7
+ Run `nx build storage-mongodb` to build the library.
8
+
9
+ ## Running unit tests
10
+
11
+ Run `nx test storage-mongodb` to execute the unit tests via [Jest](https://jestjs.io).
@@ -0,0 +1,19 @@
1
+ import baseConfig from '../../../eslint.config.mjs';
2
+
3
+ export default [
4
+ ...baseConfig,
5
+ {
6
+ files: ['**/*.json'],
7
+ rules: {
8
+ '@nx/dependency-checks': [
9
+ 'error',
10
+ {
11
+ ignoredFiles: ['{projectRoot}/eslint.config.{js,cjs,mjs,ts,cts,mts}'],
12
+ },
13
+ ],
14
+ },
15
+ languageOptions: {
16
+ parser: await import('jsonc-eslint-parser'),
17
+ },
18
+ },
19
+ ];
@@ -0,0 +1,10 @@
1
+ module.exports = {
2
+ displayName: 'storage-mongodb',
3
+ preset: '../../../jest.preset.js',
4
+ testEnvironment: 'node',
5
+ transform: {
6
+ '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
7
+ },
8
+ moduleFileExtensions: ['ts', 'js', 'html'],
9
+ coverageDirectory: '../../../coverage/libs/storage-strategies/mongodb',
10
+ };
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "@icore/storage-mongodb",
3
+ "version": "0.0.2",
4
+ "private": true,
5
+ "type": "commonjs",
6
+ "main": "./src/index.js",
7
+ "types": "./src/index.d.ts",
8
+ "dependencies": {
9
+ "@icore/shared": "workspace:*",
10
+ "mongodb-memory-server": "^11.2.0",
11
+ "mongoose": "^9.6.3",
12
+ "tslib": "^2.3.0"
13
+ }
14
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "storage-mongodb",
3
+ "$schema": "../../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "libs/storage-strategies/mongodb/src",
5
+ "projectType": "library",
6
+ "tags": [],
7
+ "targets": {
8
+ "build": {
9
+ "executor": "@nx/js:tsc",
10
+ "outputs": ["{options.outputPath}"],
11
+ "options": {
12
+ "outputPath": "dist/libs/storage-strategies/mongodb",
13
+ "main": "libs/storage-strategies/mongodb/src/index.ts",
14
+ "tsConfig": "libs/storage-strategies/mongodb/tsconfig.lib.json",
15
+ "assets": ["libs/storage-strategies/mongodb/*.md"]
16
+ }
17
+ }
18
+ }
19
+ }
@@ -0,0 +1 @@
1
+ export * from './lib/mongodb-storage.strategy';
@@ -0,0 +1,38 @@
1
+ import { MongoMemoryServer } from 'mongodb-memory-server';
2
+ import { connect, Connection } from 'mongoose';
3
+ import { MongoDbStorageStrategy } from '../mongodb-storage.strategy';
4
+ import { runStorageContract } from '@icore/shared/testing';
5
+
6
+ describe('MongoDbStorageStrategy', () => {
7
+ let mongod: MongoMemoryServer;
8
+ let connection: Connection;
9
+ let strategy: MongoDbStorageStrategy;
10
+
11
+ beforeAll(async () => {
12
+ mongod = await MongoMemoryServer.create();
13
+ const uri = mongod.getUri();
14
+ const conn = await connect(uri);
15
+ connection = conn.connection;
16
+ }, 30000);
17
+
18
+ afterAll(async () => {
19
+ if (connection) await connection.close();
20
+ if (mongod) await mongod.stop();
21
+ });
22
+
23
+ beforeEach(async () => {
24
+ if (connection.db) {
25
+ const collections = await connection.db.collections();
26
+ for (const collection of collections) {
27
+ await collection.deleteMany({});
28
+ }
29
+ }
30
+ });
31
+
32
+ runStorageContract('mongodb', () => {
33
+ if (!strategy) {
34
+ strategy = new MongoDbStorageStrategy({ connection });
35
+ }
36
+ return strategy;
37
+ });
38
+ });
@@ -0,0 +1,93 @@
1
+ import { Connection, mongo } from 'mongoose';
2
+ import { Readable } from 'node:stream';
3
+ import { StorageStrategy, StorageRef, FileInput } from '@icore/shared';
4
+
5
+ export interface MongoDbStorageStrategyOptions {
6
+ connection: Connection;
7
+ bucketName?: string;
8
+ }
9
+
10
+ export class MongoDbStorageStrategy implements StorageStrategy {
11
+ private bucket: mongo.GridFSBucket;
12
+
13
+ constructor(private readonly opts: MongoDbStorageStrategyOptions) {
14
+ this.bucket = new mongo.GridFSBucket(this.opts.connection.db as unknown as mongo.Db, {
15
+ bucketName: this.opts.bucketName || 'uploads',
16
+ });
17
+ }
18
+
19
+ async upload(userId: string, file: FileInput): Promise<StorageRef> {
20
+ const path = `${userId}/${file.filename}`;
21
+ const bucket = 'uploads';
22
+ const filename = `${bucket}/${path}`;
23
+ const uploadStream = this.bucket.openUploadStream(filename, {
24
+ metadata: { bucket, path, userId, contentType: file.mimeType },
25
+ });
26
+
27
+ return new Promise((resolve, reject) => {
28
+ const readable = new Readable();
29
+ readable.push(file.buffer);
30
+ readable.push(null);
31
+ readable
32
+ .pipe(uploadStream)
33
+ .on('error', reject)
34
+ .on('finish', () => resolve({ bucket, path }));
35
+ });
36
+ }
37
+
38
+ async remove(userId: string, ref: StorageRef): Promise<void> {
39
+ if (!ref.path.startsWith(`${userId}/`)) {
40
+ throw new Error('forbidden: ownership_mismatch');
41
+ }
42
+ const filename = `${ref.bucket}/${ref.path}`;
43
+ const files = await this.bucket.find({ filename }).toArray();
44
+ if (files.length === 0) throw new Error(`not_found: ${filename}`);
45
+
46
+ for (const file of files) {
47
+ await this.bucket.delete(file._id);
48
+ }
49
+ }
50
+
51
+ async getSignedUrl(userId: string, ref: StorageRef, _ttlSeconds?: number): Promise<string> {
52
+ if (!ref.path.startsWith(`${userId}/`)) {
53
+ throw new Error('forbidden: ownership_mismatch');
54
+ }
55
+ // In GridFS, we don't have built-in signed URLs like S3.
56
+ // For iCore, we proxy GridFS through the Gateway.
57
+ return `/api/storage/file?bucket=${ref.bucket}&path=${ref.path}`;
58
+ }
59
+
60
+ async list(userId: string, prefix?: string): Promise<StorageRef[]> {
61
+ const userPrefix = prefix ? `${userId}/${prefix}` : `${userId}/`;
62
+ const query: Record<string, unknown> = {
63
+ 'metadata.userId': userId,
64
+ 'metadata.path': new RegExp('^' + userPrefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')),
65
+ };
66
+
67
+ const files = await this.bucket.find(query).toArray();
68
+ return files.map((file) => ({
69
+ bucket: file.metadata?.['bucket'],
70
+ path: file.metadata?.['path'],
71
+ }));
72
+ }
73
+
74
+ async downloadBuffer(userId: string, ref: StorageRef): Promise<Buffer> {
75
+ if (!ref.path.startsWith(`${userId}/`)) {
76
+ throw new Error('forbidden: ownership_mismatch');
77
+ }
78
+ const filename = `${ref.bucket}/${ref.path}`;
79
+ const files = await this.bucket.find({ filename }).toArray();
80
+ if (files.length === 0) throw new Error(`not_found: ${filename}`);
81
+
82
+ const fileId = files[0]?._id;
83
+ if (!fileId) throw new Error('not_found');
84
+
85
+ const stream = this.bucket.openDownloadStream(fileId as never);
86
+ return new Promise<Buffer>((resolve, reject) => {
87
+ const chunks: Buffer[] = [];
88
+ stream.on('data', (c: Buffer) => chunks.push(c));
89
+ stream.on('end', () => resolve(Buffer.concat(chunks)));
90
+ stream.on('error', reject);
91
+ });
92
+ }
93
+ }
@@ -0,0 +1,7 @@
1
+ import { storageMongodb } from './storage-mongodb';
2
+
3
+ describe('storageMongodb', () => {
4
+ it('should work', () => {
5
+ expect(storageMongodb()).toEqual('storage-mongodb');
6
+ });
7
+ });
@@ -0,0 +1,3 @@
1
+ export function storageMongodb(): string {
2
+ return 'storage-mongodb';
3
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "extends": "../../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "module": "commonjs",
5
+ "forceConsistentCasingInFileNames": true,
6
+ "strict": true,
7
+ "importHelpers": true,
8
+ "noImplicitOverride": true,
9
+ "noImplicitReturns": true,
10
+ "noFallthroughCasesInSwitch": true,
11
+ "noPropertyAccessFromIndexSignature": true
12
+ },
13
+ "files": [],
14
+ "include": [],
15
+ "references": [
16
+ {
17
+ "path": "./tsconfig.lib.json"
18
+ },
19
+ {
20
+ "path": "./tsconfig.spec.json"
21
+ }
22
+ ]
23
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../../dist/out-tsc",
5
+ "declaration": true,
6
+ "types": ["node"]
7
+ },
8
+ "include": ["src/**/*.ts"],
9
+ "exclude": ["jest.config.ts", "jest.config.cts", "src/**/*.spec.ts", "src/**/*.test.ts"]
10
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../../dist/out-tsc",
5
+ "module": "commonjs",
6
+ "moduleResolution": "node10",
7
+ "types": ["jest", "node"]
8
+ },
9
+ "include": [
10
+ "jest.config.ts",
11
+ "jest.config.cts",
12
+ "src/**/*.test.ts",
13
+ "src/**/*.spec.ts",
14
+ "src/**/*.d.ts"
15
+ ]
16
+ }