@solidxai/core 0.1.12-beta.2 → 0.1.12-beta.4
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/.claude/settings.local.json +16 -0
- package/CLAUDE.md +26 -0
- package/dist/commands/seed.command.d.ts.map +1 -1
- package/dist/commands/seed.command.js +15 -2
- package/dist/commands/seed.command.js.map +1 -1
- package/dist/constants/media-file-types.d.ts +5 -0
- package/dist/constants/media-file-types.d.ts.map +1 -0
- package/dist/constants/media-file-types.js +27 -0
- package/dist/constants/media-file-types.js.map +1 -0
- package/dist/controllers/media.controller.d.ts +14 -11
- package/dist/controllers/media.controller.d.ts.map +1 -1
- package/dist/controllers/media.controller.js +72 -33
- package/dist/controllers/media.controller.js.map +1 -1
- package/dist/dtos/create-media.dto.d.ts +1 -0
- package/dist/dtos/create-media.dto.d.ts.map +1 -1
- package/dist/dtos/create-media.dto.js +7 -1
- package/dist/dtos/create-media.dto.js.map +1 -1
- package/dist/dtos/create-role-metadata.dto.d.ts +2 -0
- package/dist/dtos/create-role-metadata.dto.d.ts.map +1 -1
- package/dist/dtos/create-role-metadata.dto.js +3 -1
- package/dist/dtos/create-role-metadata.dto.js.map +1 -1
- package/dist/dtos/update-media.dto.d.ts +1 -0
- package/dist/dtos/update-media.dto.d.ts.map +1 -1
- package/dist/dtos/update-media.dto.js +7 -1
- package/dist/dtos/update-media.dto.js.map +1 -1
- package/dist/entities/media.entity.d.ts +1 -0
- package/dist/entities/media.entity.d.ts.map +1 -1
- package/dist/entities/media.entity.js +9 -1
- package/dist/entities/media.entity.js.map +1 -1
- package/dist/enums/auth-type.enum.d.ts +2 -1
- package/dist/enums/auth-type.enum.d.ts.map +1 -1
- package/dist/enums/auth-type.enum.js +1 -0
- package/dist/enums/auth-type.enum.js.map +1 -1
- package/dist/guards/authentication.guard.d.ts +3 -1
- package/dist/guards/authentication.guard.d.ts.map +1 -1
- package/dist/guards/authentication.guard.js +5 -1
- package/dist/guards/authentication.guard.js.map +1 -1
- package/dist/guards/media-signed-url.guard.d.ts +9 -0
- package/dist/guards/media-signed-url.guard.d.ts.map +1 -0
- package/dist/guards/media-signed-url.guard.js +49 -0
- package/dist/guards/media-signed-url.guard.js.map +1 -0
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.d.ts.map +1 -1
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js +10 -13
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js.map +1 -1
- package/dist/interfaces.d.ts +19 -1
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js +2 -0
- package/dist/interfaces.js.map +1 -1
- package/dist/repository/media.repository.d.ts.map +1 -1
- package/dist/repository/media.repository.js +2 -0
- package/dist/repository/media.repository.js.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts +7 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.js +87 -3
- package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
- package/dist/seeders/permission-metadata-seeder.service.d.ts +1 -0
- package/dist/seeders/permission-metadata-seeder.service.d.ts.map +1 -1
- package/dist/seeders/permission-metadata-seeder.service.js +6 -1
- package/dist/seeders/permission-metadata-seeder.service.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +114 -13
- package/dist/services/authentication.service.d.ts +1 -0
- package/dist/services/authentication.service.d.ts.map +1 -1
- package/dist/services/authentication.service.js +19 -1
- package/dist/services/authentication.service.js.map +1 -1
- package/dist/services/media-download-url.service.d.ts +21 -0
- package/dist/services/media-download-url.service.d.ts.map +1 -0
- package/dist/services/media-download-url.service.js +67 -0
- package/dist/services/media-download-url.service.js.map +1 -0
- package/dist/services/media-storage.utils.d.ts +27 -0
- package/dist/services/media-storage.utils.d.ts.map +1 -0
- package/dist/services/media-storage.utils.js +82 -0
- package/dist/services/media-storage.utils.js.map +1 -0
- package/dist/services/media.service.d.ts +20 -4
- package/dist/services/media.service.d.ts.map +1 -1
- package/dist/services/media.service.js +157 -55
- package/dist/services/media.service.js.map +1 -1
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.d.ts +4 -4
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.d.ts.map +1 -1
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.js +37 -63
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
- package/dist/services/mediaStorageProviders/file-storage-provider.d.ts +5 -5
- package/dist/services/mediaStorageProviders/file-storage-provider.d.ts.map +1 -1
- package/dist/services/mediaStorageProviders/file-storage-provider.js +37 -38
- package/dist/services/mediaStorageProviders/file-storage-provider.js.map +1 -1
- package/dist/services/role-metadata.service.d.ts +1 -0
- package/dist/services/role-metadata.service.d.ts.map +1 -1
- package/dist/services/role-metadata.service.js +8 -5
- package/dist/services/role-metadata.service.js.map +1 -1
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +11 -1
- package/dist/solid-core.module.js.map +1 -1
- package/dist/subscribers/audit.subscriber.js +1 -1
- package/dist/subscribers/audit.subscriber.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/seed.command.ts +17 -3
- package/src/constants/media-file-types.ts +44 -0
- package/src/controllers/media.controller.ts +44 -24
- package/src/dtos/create-media.dto.ts +8 -3
- package/src/dtos/create-role-metadata.dto.ts +2 -0
- package/src/dtos/update-media.dto.ts +8 -3
- package/src/entities/media.entity.ts +4 -1
- package/src/enums/auth-type.enum.ts +1 -0
- package/src/guards/authentication.guard.ts +3 -0
- package/src/guards/media-signed-url.guard.ts +41 -0
- package/src/helpers/field-crud-managers/MediaFieldCrudManager.ts +12 -24
- package/src/interfaces.ts +20 -0
- package/src/repository/media.repository.ts +2 -0
- package/src/seeders/module-metadata-seeder.service.ts +109 -3
- package/src/seeders/permission-metadata-seeder.service.ts +6 -1
- package/src/seeders/seed-data/solid-core-metadata.json +114 -13
- package/src/services/1.js +6 -0
- package/src/services/authentication.service.ts +24 -1
- package/src/services/media-download-url.service.ts +67 -0
- package/src/services/media-storage.utils.ts +79 -0
- package/src/services/media.service.ts +189 -59
- package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +52 -76
- package/src/services/mediaStorageProviders/file-storage-provider.ts +44 -48
- package/src/services/role-metadata.service.ts +17 -5
- package/src/solid-core.module.ts +21 -2
- package/src/subscribers/audit.subscriber.ts +1 -1
- package/dist-tests/api/authenticate.spec.js +0 -119
- package/dist-tests/api/authenticate.spec.js.map +0 -1
- package/dist-tests/api/crud-service.findOne.cityMaster.spec.js +0 -97
- package/dist-tests/api/crud-service.findOne.cityMaster.spec.js.map +0 -1
- package/dist-tests/api/ping.spec.js +0 -21
- package/dist-tests/api/ping.spec.js.map +0 -1
- package/dist-tests/helpers/auth.js +0 -41
- package/dist-tests/helpers/auth.js.map +0 -1
- package/dist-tests/helpers/env.js +0 -11
- package/dist-tests/helpers/env.js.map +0 -1
- package/docs/agent-builder/notebook-lm-prompt-for-agent-builder.md +0 -136
- package/docs/agent-builder/team-ready-note-agent-builder.md +0 -305
- package/docs/agent-hub-grooming.md +0 -301
- package/docs/dashboards/AGENTIC_DASHBOARD_IMPLEMENTATION_PLAN.md +0 -438
- package/docs/dashboards/dashboard-curl-smoke-tests.txt +0 -146
- package/docs/dashboards/delete-legacy-dashboard-metadata.sql +0 -172
- package/docs/datasource-introspection-ddl-analysis.md +0 -326
- package/docs/datasource-introspection-implementation-plan.md +0 -306
- package/docs/grouping-enhancements.md +0 -89
- package/docs/java-spring/README.md +0 -3
- package/docs/java-spring/solid-core-module-deep-dive-report.md +0 -1317
- package/docs/module-package-import-handoff.md +0 -691
- package/docs/seed-changes.md +0 -65
- package/docs/test-data-workflow.md +0 -200
- package/docs/type-declaration-import-issue.md +0 -24
- package/docs/workflow/business-automation-example-notes.md +0 -309
- package/docs/workflow/data-engineering-pipeline-example-notes.md +0 -330
- package/docs/workflow/foreach-example-notes.md +0 -187
- package/docs/workflow/hello-world-example-notes.md +0 -271
- package/docs/workflow/kestra-concepts-plugins-blueprints.md +0 -315
- package/docs/workflow/loop-until-example-notes.md +0 -198
- package/docs/workflow/microservices-and-apis-example-notes.md +0 -264
- package/docs/workflow/samples.md +0 -25
- package/docs/workflow/what-is-kestra.md +0 -79
- package/docs/workflow/workflow-core-module-handoff-summary.md +0 -191
- package/docs/workflow/workflow-module-brd.md +0 -185
- package/docs/workflow/workflow-module-domain-model.md +0 -252
- package/docs/workflow/workflow-module-metadata-dsl.md +0 -302
- package/docs/workflow/workflow-module-technical-spec.md +0 -293
- package/docs/workflow/workflow-node-type-contracts.md +0 -635
- package/docs/workflow/workflow-node-ui-metadata-contract.md +0 -1262
- package/docs/workflow/workflow-solidx-model-field-spec.md +0 -284
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { forwardRef, Inject, Injectable, Logger } from '@nestjs/common';
|
|
2
|
+
import { EventEmitter2 } from '@nestjs/event-emitter';
|
|
2
3
|
import * as fs from 'fs';
|
|
3
4
|
import * as os from 'os';
|
|
4
5
|
import * as path from 'path';
|
|
@@ -39,7 +40,12 @@ import { User } from '../entities/user.entity';
|
|
|
39
40
|
import { MENU_ROLE_JOIN_TABLE_NAME, MENU_ROLE_JOIN_TABLE_NAME_MENU_COL, MENU_ROLE_JOIN_TABLE_NAME_ROLE_COL } from '../dtos/create-menu-item-metadata.dto';
|
|
40
41
|
import { DEFAULT_SA_PASSWORD } from '../dtos/create-user.dto';
|
|
41
42
|
import { SignUpDto } from '../dtos/sign-up.dto';
|
|
42
|
-
import {
|
|
43
|
+
import {
|
|
44
|
+
ADMIN_ROLE_NAME,
|
|
45
|
+
ALLOWED_TO_EXPORT_ROLE_NAME,
|
|
46
|
+
ALLOWED_TO_IMPORT_ROLE_NAME,
|
|
47
|
+
CreateRoleMetadataDto,
|
|
48
|
+
} from 'src/dtos/create-role-metadata.dto';
|
|
43
49
|
import { CreateSavedFiltersDto } from 'src/dtos/create-saved-filters.dto';
|
|
44
50
|
import { CreateScheduledJobDto } from 'src/dtos/create-scheduled-job.dto';
|
|
45
51
|
import { CreateLocaleDto } from 'src/dtos/create-locale.dto';
|
|
@@ -60,6 +66,7 @@ import { FieldMetadata } from 'src/entities/field-metadata.entity';
|
|
|
60
66
|
import { ModelMetadata } from 'src/entities/model-metadata.entity';
|
|
61
67
|
import { PermissionMetadata } from 'src/entities/permission-metadata.entity';
|
|
62
68
|
import { ViewMetadata } from 'src/entities/view-metadata.entity';
|
|
69
|
+
import { EventDetails, EventType, ModuleMetadataSeederEventPayload } from 'src/interfaces';
|
|
63
70
|
|
|
64
71
|
/**
|
|
65
72
|
* Central metadata seeder for both solid-core and consuming modules.
|
|
@@ -79,6 +86,11 @@ import { ViewMetadata } from 'src/entities/view-metadata.entity';
|
|
|
79
86
|
*/
|
|
80
87
|
@Injectable()
|
|
81
88
|
export class ModuleMetadataSeederService {
|
|
89
|
+
private readonly adminPermissionExclusionPrefixes = [
|
|
90
|
+
'ImportTransactionController.',
|
|
91
|
+
'ExportTemplateController.',
|
|
92
|
+
'ExportTransactionController.',
|
|
93
|
+
];
|
|
82
94
|
private readonly logger = new Logger(ModuleMetadataSeederService.name);
|
|
83
95
|
// Stable tag used on all verbose seed timing logs so runs can be grepped quickly.
|
|
84
96
|
private readonly seedTimingTag = 'SEED_TIMING';
|
|
@@ -98,6 +110,7 @@ export class ModuleMetadataSeederService {
|
|
|
98
110
|
private readonly moduleMetadataService: ModuleMetadataService,
|
|
99
111
|
@Inject(forwardRef(() => ModelMetadataService))
|
|
100
112
|
private readonly modelMetadataService: ModelMetadataService,
|
|
113
|
+
private readonly eventEmitter: EventEmitter2,
|
|
101
114
|
private readonly fieldMetadataService: FieldMetadataService,
|
|
102
115
|
private readonly mediaStorageProviderMetadataService: MediaStorageProviderMetadataService,
|
|
103
116
|
private readonly roleService: RoleMetadataService,
|
|
@@ -131,7 +144,26 @@ export class ModuleMetadataSeederService {
|
|
|
131
144
|
let currentModule = 'global';
|
|
132
145
|
let currentStep = 'bootstrap';
|
|
133
146
|
let modulesToSeed: string[] | null = null;
|
|
147
|
+
const requestedModulesToSeed = Array.isArray(conf?.modulesToSeed) ? [...conf.modulesToSeed] : null;
|
|
134
148
|
const shouldSeedGlobalMetadata = conf?.seedGlobalMetadata !== false;
|
|
149
|
+
const seedOptions: ModuleMetadataSeederEventPayload['options'] = {
|
|
150
|
+
modulesToSeed: requestedModulesToSeed,
|
|
151
|
+
pruneMetadata: Boolean(conf?.pruneMetadata),
|
|
152
|
+
seedGlobalMetadata: shouldSeedGlobalMetadata,
|
|
153
|
+
};
|
|
154
|
+
const seedRunId = uuidv4();
|
|
155
|
+
const startedAt = new Date();
|
|
156
|
+
const seededModuleNames: string[] = [];
|
|
157
|
+
|
|
158
|
+
this.emitModuleMetadataSeederEvent(
|
|
159
|
+
EventType.MODULE_METADATA_SEEDER_STARTED,
|
|
160
|
+
{
|
|
161
|
+
seedRunId,
|
|
162
|
+
options: seedOptions,
|
|
163
|
+
startedAt: startedAt.toISOString(),
|
|
164
|
+
currentStep,
|
|
165
|
+
},
|
|
166
|
+
);
|
|
135
167
|
|
|
136
168
|
try {
|
|
137
169
|
this.enablePruning = Boolean(conf?.pruneMetadata);
|
|
@@ -190,6 +222,7 @@ export class ModuleMetadataSeederService {
|
|
|
190
222
|
|
|
191
223
|
console.log(`▶ Seeding Metadata for Module: ${moduleMetadata.name}`);
|
|
192
224
|
this.logger.log(`Seeding Metadata for Module: ${moduleMetadata.name}`);
|
|
225
|
+
seededModuleNames.push(moduleMetadata.name);
|
|
193
226
|
|
|
194
227
|
await this.timeOperation('module-total', async () => {
|
|
195
228
|
currentStep = 'seedMediaStorageProviders';
|
|
@@ -283,10 +316,37 @@ export class ModuleMetadataSeederService {
|
|
|
283
316
|
// Add a console log indicating seeding is finished. This needs to be console.log so that it looks proper when this code is run via CLI.
|
|
284
317
|
console.log(`✔ Seeding completed.`);
|
|
285
318
|
//this.logger.log(`All Seeders finished`);
|
|
319
|
+
await this.emitModuleMetadataSeederFinishedEvent(
|
|
320
|
+
EventType.MODULE_METADATA_SEEDER_FINISHED,
|
|
321
|
+
{
|
|
322
|
+
seedRunId,
|
|
323
|
+
options: seedOptions,
|
|
324
|
+
startedAt: startedAt.toISOString(),
|
|
325
|
+
finishedAt: new Date().toISOString(),
|
|
326
|
+
durationMs: Date.now() - startedAt.getTime(),
|
|
327
|
+
success: true,
|
|
328
|
+
seededModuleNames,
|
|
329
|
+
currentStep,
|
|
330
|
+
},
|
|
331
|
+
);
|
|
286
332
|
|
|
287
333
|
//FIXME: Handle displaying the created users credentials in a better way.
|
|
288
334
|
// this.logger.log(`Newly created username is: ${usersDetail?.length > 0 ? usersDetail[0]?.username : ''} and password is ${usersDetail?.length > 0 ? usersDetail[0]?.password : ''}`);
|
|
289
335
|
} catch (error: any) {
|
|
336
|
+
await this.emitModuleMetadataSeederFinishedEvent(
|
|
337
|
+
EventType.MODULE_METADATA_SEEDER_FINISHED,
|
|
338
|
+
{
|
|
339
|
+
seedRunId,
|
|
340
|
+
options: seedOptions,
|
|
341
|
+
startedAt: startedAt.toISOString(),
|
|
342
|
+
finishedAt: new Date().toISOString(),
|
|
343
|
+
durationMs: Date.now() - startedAt.getTime(),
|
|
344
|
+
success: false,
|
|
345
|
+
seededModuleNames,
|
|
346
|
+
currentStep,
|
|
347
|
+
errorMessage: error?.message,
|
|
348
|
+
},
|
|
349
|
+
);
|
|
290
350
|
this.logSeedFailureForCli(error, {
|
|
291
351
|
moduleName: currentModule,
|
|
292
352
|
step: currentStep,
|
|
@@ -297,6 +357,31 @@ export class ModuleMetadataSeederService {
|
|
|
297
357
|
}
|
|
298
358
|
}
|
|
299
359
|
|
|
360
|
+
private emitModuleMetadataSeederEvent(
|
|
361
|
+
type: EventType.MODULE_METADATA_SEEDER_STARTED | EventType.MODULE_METADATA_SEEDER_FINISHED,
|
|
362
|
+
payload: ModuleMetadataSeederEventPayload,
|
|
363
|
+
): void {
|
|
364
|
+
try {
|
|
365
|
+
this.eventEmitter.emit(type, new EventDetails<ModuleMetadataSeederEventPayload>(type, payload));
|
|
366
|
+
} catch (error: any) {
|
|
367
|
+
this.logger.warn(`Failed to emit ${type}: ${error?.message ?? error}`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
private async emitModuleMetadataSeederFinishedEvent(
|
|
372
|
+
type: EventType.MODULE_METADATA_SEEDER_FINISHED,
|
|
373
|
+
payload: ModuleMetadataSeederEventPayload,
|
|
374
|
+
): Promise<void> {
|
|
375
|
+
try {
|
|
376
|
+
console.log(`▶ Running post-seed hooks for runId=${payload.seedRunId}`);
|
|
377
|
+
await this.eventEmitter.emitAsync(type, new EventDetails<ModuleMetadataSeederEventPayload>(type, payload));
|
|
378
|
+
console.log(`✔ Post-seed hooks completed for runId=${payload.seedRunId}`);
|
|
379
|
+
} catch (error: any) {
|
|
380
|
+
console.log(`✖ Post-seed hooks failed for runId=${payload.seedRunId}: ${error?.message ?? error}`);
|
|
381
|
+
this.logger.warn(`Failed to emit ${type}: ${error?.message ?? error}`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
300
385
|
private buildTimingPrefix(itemTag: string, options?: { moduleName?: string; component?: string; serviceCall?: string }): string {
|
|
301
386
|
const parts = [`[${this.seedTimingTag}]`, `[ITEM:${itemTag}]`];
|
|
302
387
|
if (options?.moduleName) {
|
|
@@ -498,10 +583,17 @@ export class ModuleMetadataSeederService {
|
|
|
498
583
|
|
|
499
584
|
private async setupDefaultRolesWithPermissions() {
|
|
500
585
|
this.logger.debug(`About to add all permissions to the Admin role`);
|
|
501
|
-
await this.timeOperation('role-add-all-permissions', () => this.roleService.
|
|
586
|
+
await this.timeOperation('role-add-all-permissions', () => this.roleService.addAllPermissionsToRoleExceptPrefixes(ADMIN_ROLE_NAME, this.adminPermissionExclusionPrefixes), {
|
|
587
|
+
moduleName: 'global',
|
|
588
|
+
component: 'default-roles',
|
|
589
|
+
serviceCall: 'roleService.addAllPermissionsToRoleExceptPrefixes',
|
|
590
|
+
details: `role=${ADMIN_ROLE_NAME}`,
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
await this.timeOperation('attach-capability-roles-to-admin-users', () => this.attachCapabilityRolesToAdminUsers(), {
|
|
502
594
|
moduleName: 'global',
|
|
503
595
|
component: 'default-roles',
|
|
504
|
-
serviceCall: '
|
|
596
|
+
serviceCall: 'attachCapabilityRolesToAdminUsers',
|
|
505
597
|
details: `role=${ADMIN_ROLE_NAME}`,
|
|
506
598
|
});
|
|
507
599
|
|
|
@@ -514,6 +606,20 @@ export class ModuleMetadataSeederService {
|
|
|
514
606
|
// await this.roleService.addPermissionToRole(PUBLIC_ROLE_NAME, ['SettingController.wrapSettings', 'AuthenticationController.logout']);
|
|
515
607
|
}
|
|
516
608
|
|
|
609
|
+
private async attachCapabilityRolesToAdminUsers(): Promise<void> {
|
|
610
|
+
const adminUsers = await this.userService.findUsersByRole(ADMIN_ROLE_NAME, {});
|
|
611
|
+
if (!adminUsers?.length) {
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
const capabilityRoles = [ALLOWED_TO_IMPORT_ROLE_NAME, ALLOWED_TO_EXPORT_ROLE_NAME];
|
|
616
|
+
for (const adminUser of adminUsers) {
|
|
617
|
+
for (const capabilityRole of capabilityRoles) {
|
|
618
|
+
await this.userService.addRoleToUser(adminUser.username, capabilityRole);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
517
623
|
private async seedSecurityRules(overallMetadata: any): Promise<{ pruned: number; upserted: number }> {
|
|
518
624
|
const securityRules = this.getSeedArray<CreateSecurityRuleDto>(overallMetadata?.securityRules);
|
|
519
625
|
const pruned = this.enablePruning ? await this.timeOperation('prune-security-rules', () => this.pruneSecurityRules(securityRules, overallMetadata?.moduleMetadata?.name), {
|
|
@@ -6,6 +6,11 @@ import { RoleMetadataService } from '../services/role-metadata.service';
|
|
|
6
6
|
@Injectable()
|
|
7
7
|
export class PermissionMetadataSeederService {
|
|
8
8
|
private readonly logger = new Logger(PermissionMetadataSeederService.name);
|
|
9
|
+
private readonly adminPermissionExclusionPrefixes = [
|
|
10
|
+
'ImportTransactionController.',
|
|
11
|
+
'ExportTemplateController.',
|
|
12
|
+
'ExportTransactionController.',
|
|
13
|
+
];
|
|
9
14
|
|
|
10
15
|
constructor(
|
|
11
16
|
// @InjectRepository(PermissionMetadata)
|
|
@@ -58,6 +63,6 @@ export class PermissionMetadataSeederService {
|
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
// Associate the Admin role with all existing permissions.
|
|
61
|
-
await this.roleService.
|
|
66
|
+
await this.roleService.addAllPermissionsToRoleExceptPrefixes("Admin", this.adminPermissionExclusionPrefixes);
|
|
62
67
|
}
|
|
63
68
|
}
|
|
@@ -1704,6 +1704,18 @@
|
|
|
1704
1704
|
"columnName": "original_file_name",
|
|
1705
1705
|
"isSystem": true
|
|
1706
1706
|
},
|
|
1707
|
+
{
|
|
1708
|
+
"name": "isPublic",
|
|
1709
|
+
"displayName": "Is Public",
|
|
1710
|
+
"type": "boolean",
|
|
1711
|
+
"required": false,
|
|
1712
|
+
"unique": false,
|
|
1713
|
+
"index": false,
|
|
1714
|
+
"private": false,
|
|
1715
|
+
"encrypt": false,
|
|
1716
|
+
"columnName": "is_public",
|
|
1717
|
+
"isSystem": true
|
|
1718
|
+
},
|
|
1707
1719
|
{
|
|
1708
1720
|
"name": "modelMetadata",
|
|
1709
1721
|
"displayName": "Model Metadata",
|
|
@@ -5306,6 +5318,9 @@
|
|
|
5306
5318
|
"FieldMetadataController.getSelectionDynamicValues",
|
|
5307
5319
|
"FieldMetadataController.getSelectionDynamicValue",
|
|
5308
5320
|
"FieldMetadataController.findFieldDefaultMetaData",
|
|
5321
|
+
"MediaController.findMany",
|
|
5322
|
+
"MediaController.findOne",
|
|
5323
|
+
"MediaController.download",
|
|
5309
5324
|
"SavedFiltersController.delete",
|
|
5310
5325
|
"SavedFiltersController.deleteMany",
|
|
5311
5326
|
"SavedFiltersController.findOne",
|
|
@@ -5321,14 +5336,6 @@
|
|
|
5321
5336
|
"ChatterMessageController.findMany",
|
|
5322
5337
|
"ChatterMessageController.markCompleted",
|
|
5323
5338
|
"ChatterMessageController.updateCustomNoteMessage",
|
|
5324
|
-
"ImportTransactionController.getImportTemplate",
|
|
5325
|
-
"ImportTransactionController.getImportInstructions",
|
|
5326
|
-
"ImportTransactionController.getImportMappingInfo",
|
|
5327
|
-
"ImportTransactionController.startImportSync",
|
|
5328
|
-
"ImportTransactionController.startImportAsync",
|
|
5329
|
-
"ImportTransactionController.exportFailedImportedImports",
|
|
5330
|
-
"ExportTemplateController.startExportSync",
|
|
5331
|
-
"ExportTemplateController.startExportAsync",
|
|
5332
5339
|
"ListOfValuesController.findMany",
|
|
5333
5340
|
"ListOfValuesController.findOne",
|
|
5334
5341
|
"SettingController.getAllSettings",
|
|
@@ -5348,6 +5355,31 @@
|
|
|
5348
5355
|
"agent:invoke"
|
|
5349
5356
|
]
|
|
5350
5357
|
},
|
|
5358
|
+
{
|
|
5359
|
+
"name": "Allowed To Import",
|
|
5360
|
+
"permissions": [
|
|
5361
|
+
"ImportTransactionController.create",
|
|
5362
|
+
"ImportTransactionController.delete",
|
|
5363
|
+
"ImportTransactionController.deleteMany",
|
|
5364
|
+
"ImportTransactionController.getImportTemplate",
|
|
5365
|
+
"ImportTransactionController.getImportInstructions",
|
|
5366
|
+
"ImportTransactionController.getImportMappingInfo",
|
|
5367
|
+
"ImportTransactionController.partialUpdate",
|
|
5368
|
+
"ImportTransactionController.startImportSync",
|
|
5369
|
+
"ImportTransactionController.startImportAsync",
|
|
5370
|
+
"ImportTransactionController.exportFailedImportedImports"
|
|
5371
|
+
]
|
|
5372
|
+
},
|
|
5373
|
+
{
|
|
5374
|
+
"name": "Allowed To Export",
|
|
5375
|
+
"permissions": [
|
|
5376
|
+
"ExportTemplateController.create",
|
|
5377
|
+
"ExportTemplateController.delete",
|
|
5378
|
+
"ExportTemplateController.findMany",
|
|
5379
|
+
"ExportTemplateController.startExportSync",
|
|
5380
|
+
"ExportTemplateController.startExportAsync"
|
|
5381
|
+
]
|
|
5382
|
+
},
|
|
5351
5383
|
{
|
|
5352
5384
|
"name": "Public",
|
|
5353
5385
|
"permissions": [
|
|
@@ -8607,16 +8639,15 @@
|
|
|
8607
8639
|
"attrs": {
|
|
8608
8640
|
"name": "roles",
|
|
8609
8641
|
"widget": "checkbox",
|
|
8610
|
-
"inlineCreateAutoSave": "
|
|
8642
|
+
"inlineCreateAutoSave": "false",
|
|
8611
8643
|
"renderModeCheckboxPreprocessor": "MenuItemMetadataRolesFormFieldPreprocessor",
|
|
8612
|
-
"inlineCreate": "
|
|
8644
|
+
"inlineCreate": "false",
|
|
8613
8645
|
"inlineCreateLayout": {
|
|
8614
8646
|
"type": "form",
|
|
8615
8647
|
"attrs": {
|
|
8616
8648
|
"name": "form-1",
|
|
8617
8649
|
"label": "User",
|
|
8618
|
-
"className": "grid"
|
|
8619
|
-
"width": "20vw"
|
|
8650
|
+
"className": "grid"
|
|
8620
8651
|
},
|
|
8621
8652
|
"children": [
|
|
8622
8653
|
{
|
|
@@ -8758,6 +8789,13 @@
|
|
|
8758
8789
|
"sortable": false
|
|
8759
8790
|
}
|
|
8760
8791
|
},
|
|
8792
|
+
{
|
|
8793
|
+
"type": "field",
|
|
8794
|
+
"attrs": {
|
|
8795
|
+
"name": "isPublic",
|
|
8796
|
+
"sortable": true
|
|
8797
|
+
}
|
|
8798
|
+
},
|
|
8761
8799
|
{
|
|
8762
8800
|
"type": "field",
|
|
8763
8801
|
"attrs": {
|
|
@@ -8853,6 +8891,14 @@
|
|
|
8853
8891
|
"isSearchable": true
|
|
8854
8892
|
}
|
|
8855
8893
|
},
|
|
8894
|
+
{
|
|
8895
|
+
"type": "field",
|
|
8896
|
+
"attrs": {
|
|
8897
|
+
"name": "isPublic",
|
|
8898
|
+
"label": "isPublic",
|
|
8899
|
+
"isSearchable": true
|
|
8900
|
+
}
|
|
8901
|
+
},
|
|
8856
8902
|
{
|
|
8857
8903
|
"type": "field",
|
|
8858
8904
|
"attrs": {
|
|
@@ -8921,6 +8967,12 @@
|
|
|
8921
8967
|
"name": "mediaStorageProviderMetadata"
|
|
8922
8968
|
}
|
|
8923
8969
|
},
|
|
8970
|
+
{
|
|
8971
|
+
"type": "field",
|
|
8972
|
+
"attrs": {
|
|
8973
|
+
"name": "isPublic"
|
|
8974
|
+
}
|
|
8975
|
+
},
|
|
8924
8976
|
{
|
|
8925
8977
|
"type": "field",
|
|
8926
8978
|
"attrs": {
|
|
@@ -14525,7 +14577,15 @@
|
|
|
14525
14577
|
"mediaStorageProviders": [
|
|
14526
14578
|
{
|
|
14527
14579
|
"name": "default-filesystem",
|
|
14528
|
-
"type": "filesystem"
|
|
14580
|
+
"type": "filesystem",
|
|
14581
|
+
"isPublic": true,
|
|
14582
|
+
"localPath": "media-files-storage"
|
|
14583
|
+
},
|
|
14584
|
+
{
|
|
14585
|
+
"name": "private-filesystem",
|
|
14586
|
+
"type": "filesystem",
|
|
14587
|
+
"isPublic": false,
|
|
14588
|
+
"localPath": "media-private-files-storage"
|
|
14529
14589
|
},
|
|
14530
14590
|
{
|
|
14531
14591
|
"name": "default-aws-s3",
|
|
@@ -14561,6 +14621,47 @@
|
|
|
14561
14621
|
}
|
|
14562
14622
|
}
|
|
14563
14623
|
}
|
|
14624
|
+
},
|
|
14625
|
+
{
|
|
14626
|
+
"name": "model:Media-role:Internal User-own",
|
|
14627
|
+
"description": "Show Media records owned by the current user",
|
|
14628
|
+
"roleUserKey": "Internal User",
|
|
14629
|
+
"modelMetadataUserKey": "media",
|
|
14630
|
+
"securityRuleConfig": {
|
|
14631
|
+
"filters": {
|
|
14632
|
+
"createdBy": {
|
|
14633
|
+
"$eq": "$activeUserId"
|
|
14634
|
+
}
|
|
14635
|
+
}
|
|
14636
|
+
}
|
|
14637
|
+
},
|
|
14638
|
+
{
|
|
14639
|
+
"name": "model:Media-role:Internal User-public",
|
|
14640
|
+
"description": "Show public Media records",
|
|
14641
|
+
"roleUserKey": "Internal User",
|
|
14642
|
+
"modelMetadataUserKey": "media",
|
|
14643
|
+
"securityRuleConfig": {
|
|
14644
|
+
"filters": {
|
|
14645
|
+
"mediaStorageProviderMetadata": {
|
|
14646
|
+
"isPublic": {
|
|
14647
|
+
"$eq": true
|
|
14648
|
+
}
|
|
14649
|
+
}
|
|
14650
|
+
}
|
|
14651
|
+
}
|
|
14652
|
+
},
|
|
14653
|
+
{
|
|
14654
|
+
"name": "model:Media-role:Admin",
|
|
14655
|
+
"description": "Show All Media records",
|
|
14656
|
+
"roleUserKey": "Admin",
|
|
14657
|
+
"modelMetadataUserKey": "media",
|
|
14658
|
+
"securityRuleConfig": {
|
|
14659
|
+
"filters": {
|
|
14660
|
+
"id": {
|
|
14661
|
+
"$ne": "0"
|
|
14662
|
+
}
|
|
14663
|
+
}
|
|
14664
|
+
}
|
|
14564
14665
|
}
|
|
14565
14666
|
],
|
|
14566
14667
|
"modelSequences": [],
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
1. Do i need to create a storeStreams method for aws service too?
|
|
2
|
+
- Handle later
|
|
3
|
+
2. queues handling -> if queues is enabled by default, i.e triggerExport(exportTransactionEntity.id).
|
|
4
|
+
- startExport should either return the data or return the transaction id
|
|
5
|
+
3. How to handle scenarios wherein, nested related exist.(do i need to only get the userkey)
|
|
6
|
+
- show the userKey
|
|
@@ -109,6 +109,26 @@ export class AuthenticationService {
|
|
|
109
109
|
});
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
private async resolveUserForPasswordSignIn(username: string, email: string) {
|
|
113
|
+
const normalizedEmail = email?.trim().toLowerCase();
|
|
114
|
+
const query = (await this.userRepository
|
|
115
|
+
.createSecurityRuleAwareQueryBuilder("u"))
|
|
116
|
+
.leftJoinAndSelect("u.roles", "roles");
|
|
117
|
+
|
|
118
|
+
if (username) {
|
|
119
|
+
query.where("u.username = :username", { username });
|
|
120
|
+
}
|
|
121
|
+
if (normalizedEmail) {
|
|
122
|
+
if (username) {
|
|
123
|
+
query.orWhere("LOWER(u.email) = :email", { email: normalizedEmail });
|
|
124
|
+
} else {
|
|
125
|
+
query.where("LOWER(u.email) = :email", { email: normalizedEmail });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return await query.getOne();
|
|
130
|
+
}
|
|
131
|
+
|
|
112
132
|
async updatePasswordDetails(user: User, newPassword: string) {
|
|
113
133
|
user.password = await this.hashingService.hash(newPassword);
|
|
114
134
|
user.passwordScheme = this.hashingService.name();
|
|
@@ -985,7 +1005,10 @@ export class AuthenticationService {
|
|
|
985
1005
|
}
|
|
986
1006
|
|
|
987
1007
|
async signIn(signInDto: SignInDto) {
|
|
988
|
-
const user = await this.
|
|
1008
|
+
const user = await this.resolveUserForPasswordSignIn(
|
|
1009
|
+
signInDto.username,
|
|
1010
|
+
signInDto.email,
|
|
1011
|
+
);
|
|
989
1012
|
if (!user) {
|
|
990
1013
|
throw new UnauthorizedException(ERROR_MESSAGES.INVALID_CREDENTIALS);
|
|
991
1014
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Injectable, Logger } from '@nestjs/common';
|
|
2
|
+
import { ConfigService } from '@nestjs/config';
|
|
3
|
+
import { JwtService } from '@nestjs/jwt';
|
|
4
|
+
import { MediaStorageProviderType } from 'src/dtos/create-media-storage-provider-metadata.dto';
|
|
5
|
+
import { MediaStorageProviderMetadata } from 'src/entities/media-storage-provider-metadata.entity';
|
|
6
|
+
import { S3FileService } from 'src/services/file';
|
|
7
|
+
import { getEffectiveS3Region } from 'src/services/media-storage.utils';
|
|
8
|
+
import { SettingService } from 'src/services/setting.service';
|
|
9
|
+
import type { SolidCoreSetting } from 'src/services/settings/default-settings-provider.service';
|
|
10
|
+
|
|
11
|
+
export const MEDIA_DOWNLOAD_TOKEN_AUDIENCE = 'media-download';
|
|
12
|
+
const DEFAULT_SIGNED_URL_EXPIRY_MINUTES = 60;
|
|
13
|
+
|
|
14
|
+
export interface MediaDownloadTokenPayload {
|
|
15
|
+
mediaId: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@Injectable()
|
|
19
|
+
export class MediaDownloadUrlService {
|
|
20
|
+
private readonly logger = new Logger(MediaDownloadUrlService.name);
|
|
21
|
+
|
|
22
|
+
constructor(
|
|
23
|
+
private readonly jwtService: JwtService,
|
|
24
|
+
private readonly s3FileService: S3FileService,
|
|
25
|
+
private readonly settingService: SettingService,
|
|
26
|
+
private readonly configService: ConfigService,
|
|
27
|
+
) { }
|
|
28
|
+
|
|
29
|
+
async resolveDownloadUrl(mediaId: number, relativeUri: string, storageProvider?: MediaStorageProviderMetadata): Promise<string> {
|
|
30
|
+
const expiryMinutes = storageProvider?.signedUrlExpiry ?? DEFAULT_SIGNED_URL_EXPIRY_MINUTES;
|
|
31
|
+
|
|
32
|
+
if ((storageProvider?.type as MediaStorageProviderType) === MediaStorageProviderType.AwsS3) {
|
|
33
|
+
if (storageProvider?.bucketName) {
|
|
34
|
+
return this.s3FileService.getUrl(
|
|
35
|
+
`${storageProvider.bucketName}:${relativeUri}`,
|
|
36
|
+
{ region: getEffectiveS3Region(this.configService, storageProvider.region), expiresIn: expiryMinutes * 60 },
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
this.logger.warn(`Media id ${mediaId}: AwsS3 provider missing bucketName; falling back to app-hosted token URL`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Filesystem (or misconfigured S3) has no native presigned-URL mechanism, so mint our
|
|
43
|
+
// own short-lived token and embed it in the download URL - the token alone authorizes
|
|
44
|
+
// the request (see MediaSignedUrlGuard), so it works in a plain <img src> with no
|
|
45
|
+
// Authorization header needed.
|
|
46
|
+
const token = await this.mintDownloadToken(mediaId, expiryMinutes);
|
|
47
|
+
const downloadPath = `/api/media/${mediaId}/download?token=${token}`;
|
|
48
|
+
const baseUrl = this.settingService.getConfigValue<SolidCoreSetting>('baseUrl');
|
|
49
|
+
return `${String(baseUrl).replace(/\/+$/, '')}${downloadPath}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async verifyDownloadToken(token: string): Promise<MediaDownloadTokenPayload> {
|
|
53
|
+
return this.jwtService.verifyAsync<MediaDownloadTokenPayload>(token, {
|
|
54
|
+
secret: this.settingService.getConfigValue<SolidCoreSetting>('secret'),
|
|
55
|
+
audience: MEDIA_DOWNLOAD_TOKEN_AUDIENCE,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private mintDownloadToken(mediaId: number, expiryMinutes: number): Promise<string> {
|
|
60
|
+
const payload: MediaDownloadTokenPayload = { mediaId };
|
|
61
|
+
return this.jwtService.signAsync(payload, {
|
|
62
|
+
secret: this.settingService.getConfigValue<SolidCoreSetting>('secret'),
|
|
63
|
+
audience: MEDIA_DOWNLOAD_TOKEN_AUDIENCE,
|
|
64
|
+
expiresIn: expiryMinutes * 60,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { ConfigService } from '@nestjs/config';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { FieldMetadata } from 'src/entities/field-metadata.entity';
|
|
4
|
+
import { MediaStorageProviderMetadata } from 'src/entities/media-storage-provider-metadata.entity';
|
|
5
|
+
import { SettingService } from 'src/services/setting.service';
|
|
6
|
+
import { DEFAULT_MEDIA_FILE_STORAGE_DIR } from 'src/services/settings/default-settings-provider.service';
|
|
7
|
+
import type { SolidCoreSetting } from 'src/services/settings/default-settings-provider.service';
|
|
8
|
+
|
|
9
|
+
export const DEFAULT_PRIVATE_MEDIA_STORAGE_DIR = 'media-private-files-storage';
|
|
10
|
+
|
|
11
|
+
export interface MediaRecordCreateInput {
|
|
12
|
+
entityId: number;
|
|
13
|
+
modelMetadataId: number;
|
|
14
|
+
mediaStorageProviderMetadataId: number;
|
|
15
|
+
fieldMetadataId: number;
|
|
16
|
+
relativeUri: string;
|
|
17
|
+
mimeType?: string;
|
|
18
|
+
fileSize?: number;
|
|
19
|
+
originalFileName?: string;
|
|
20
|
+
isPublic?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Builds the stored file name format shared by media storage implementations.
|
|
24
|
+
export function buildStoredMediaFileName(file: Pick<Express.Multer.File, 'filename' | 'originalname'>): string {
|
|
25
|
+
return `${file.filename}-${file.originalname}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Resolves the on-disk path for a media file based on settings and provider visibility.
|
|
29
|
+
export function buildDiskMediaPath(fileName: string,settingService: Pick<SettingService, 'getConfigValue'>,storageProvider?: Pick<MediaStorageProviderMetadata, 'localPath' | 'isPublic'>,): string {
|
|
30
|
+
const publicBase = settingService.getConfigValue<SolidCoreSetting>('fileStorageDir') || DEFAULT_MEDIA_FILE_STORAGE_DIR;
|
|
31
|
+
const privateBase = DEFAULT_PRIVATE_MEDIA_STORAGE_DIR;
|
|
32
|
+
const providerBase = storageProvider?.localPath || (storageProvider?.isPublic === false ? privateBase : publicBase);
|
|
33
|
+
|
|
34
|
+
if ( path.isAbsolute(fileName) || fileName.startsWith(`${publicBase}/`) || fileName.startsWith(`${privateBase}/`) || (!!storageProvider?.localPath && fileName.startsWith(`${storageProvider.localPath}/`))) {
|
|
35
|
+
return fileName;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return `${providerBase}/${fileName}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Returns the S3 region from the provider when present, otherwise falls back to app config.
|
|
42
|
+
export function getEffectiveS3Region(configService: Pick<ConfigService, 'get'>,providerRegion?: string,): string | undefined {
|
|
43
|
+
return providerRegion || configService.get<string>('S3_AWS_REGION_NAME');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Resolves the effective public/private flag for a media storage provider.
|
|
47
|
+
export function resolveMediaIsPublic(storageProvider?: Pick<MediaStorageProviderMetadata, 'isPublic'>,): boolean | undefined {
|
|
48
|
+
if (!storageProvider) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return storageProvider.isPublic !== false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Preserves an explicit media visibility value or derives it from the storage provider.
|
|
56
|
+
export function resolveStoredMediaIsPublic(currentValue: boolean | undefined,storageProvider?:Pick<MediaStorageProviderMetadata, 'isPublic'>,): boolean | undefined {
|
|
57
|
+
if (typeof currentValue === 'boolean') {
|
|
58
|
+
return currentValue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return resolveMediaIsPublic(storageProvider);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Builds the shared media record payload used before saving media metadata to the database.
|
|
65
|
+
export function buildMediaRecordCreateInput(
|
|
66
|
+
entityId: number,
|
|
67
|
+
mediaFieldMetadata: Pick<FieldMetadata, 'id' | 'model'> & { model: { id: number } },
|
|
68
|
+
storageProvider: Pick<MediaStorageProviderMetadata, 'id'>,
|
|
69
|
+
overrides: Pick<MediaRecordCreateInput, 'relativeUri'>
|
|
70
|
+
& Partial<Omit<MediaRecordCreateInput, 'entityId' | 'modelMetadataId' | 'mediaStorageProviderMetadataId' | 'fieldMetadataId' | 'relativeUri'>>,
|
|
71
|
+
): MediaRecordCreateInput {
|
|
72
|
+
return {
|
|
73
|
+
entityId,
|
|
74
|
+
modelMetadataId: mediaFieldMetadata.model.id,
|
|
75
|
+
mediaStorageProviderMetadataId: storageProvider.id,
|
|
76
|
+
fieldMetadataId: mediaFieldMetadata.id,
|
|
77
|
+
...overrides,
|
|
78
|
+
};
|
|
79
|
+
}
|