@flusys/nestjs-localization 4.0.0-rc
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/README.md +870 -0
- package/cjs/config/index.js +19 -0
- package/cjs/config/localization.constants.js +20 -0
- package/cjs/config/message-keys.js +77 -0
- package/cjs/controllers/index.js +20 -0
- package/cjs/controllers/language.controller.js +188 -0
- package/cjs/controllers/translation-key.controller.js +174 -0
- package/cjs/controllers/translation.controller.js +254 -0
- package/cjs/dtos/index.js +20 -0
- package/cjs/dtos/language.dto.js +205 -0
- package/cjs/dtos/translation-key.dto.js +233 -0
- package/cjs/dtos/translation.dto.js +298 -0
- package/cjs/entities/index.js +42 -0
- package/cjs/entities/language.entity.js +126 -0
- package/cjs/entities/translation-key.entity.js +125 -0
- package/cjs/entities/translation.entity.js +120 -0
- package/cjs/index.js +24 -0
- package/cjs/interfaces/index.js +21 -0
- package/cjs/interfaces/language.interface.js +4 -0
- package/cjs/interfaces/localization-module-options.interface.js +4 -0
- package/cjs/interfaces/translation-key.interface.js +4 -0
- package/cjs/interfaces/translation.interface.js +4 -0
- package/cjs/modules/index.js +18 -0
- package/cjs/modules/localization.module.js +115 -0
- package/cjs/services/index.js +22 -0
- package/cjs/services/language.service.js +108 -0
- package/cjs/services/localization-config.service.js +66 -0
- package/cjs/services/localization-datasource.provider.js +122 -0
- package/cjs/services/translation-key.service.js +184 -0
- package/cjs/services/translation.service.js +222 -0
- package/config/index.d.ts +2 -0
- package/config/localization.constants.d.ts +2 -0
- package/config/message-keys.d.ts +96 -0
- package/controllers/index.d.ts +3 -0
- package/controllers/language.controller.d.ts +25 -0
- package/controllers/translation-key.controller.d.ts +22 -0
- package/controllers/translation.controller.d.ts +25 -0
- package/dtos/index.d.ts +3 -0
- package/dtos/language.dto.d.ts +24 -0
- package/dtos/translation-key.dto.d.ts +29 -0
- package/dtos/translation.dto.d.ts +42 -0
- package/entities/index.d.ts +8 -0
- package/entities/language.entity.d.ts +13 -0
- package/entities/translation-key.entity.d.ts +13 -0
- package/entities/translation.entity.d.ts +12 -0
- package/fesm/config/index.js +2 -0
- package/fesm/config/localization.constants.js +2 -0
- package/fesm/config/message-keys.js +54 -0
- package/fesm/controllers/index.js +3 -0
- package/fesm/controllers/language.controller.js +178 -0
- package/fesm/controllers/translation-key.controller.js +164 -0
- package/fesm/controllers/translation.controller.js +244 -0
- package/fesm/dtos/index.js +3 -0
- package/fesm/dtos/language.dto.js +184 -0
- package/fesm/dtos/translation-key.dto.js +209 -0
- package/fesm/dtos/translation.dto.js +265 -0
- package/fesm/entities/index.js +14 -0
- package/fesm/entities/language.entity.js +116 -0
- package/fesm/entities/translation-key.entity.js +115 -0
- package/fesm/entities/translation.entity.js +110 -0
- package/fesm/index.js +7 -0
- package/fesm/interfaces/index.js +4 -0
- package/fesm/interfaces/language.interface.js +1 -0
- package/fesm/interfaces/localization-module-options.interface.js +1 -0
- package/fesm/interfaces/translation-key.interface.js +1 -0
- package/fesm/interfaces/translation.interface.js +1 -0
- package/fesm/modules/index.js +1 -0
- package/fesm/modules/localization.module.js +105 -0
- package/fesm/services/index.js +5 -0
- package/fesm/services/language.service.js +98 -0
- package/fesm/services/localization-config.service.js +56 -0
- package/fesm/services/localization-datasource.provider.js +112 -0
- package/fesm/services/translation-key.service.js +174 -0
- package/fesm/services/translation.service.js +212 -0
- package/index.d.ts +7 -0
- package/interfaces/index.d.ts +4 -0
- package/interfaces/language.interface.d.ts +10 -0
- package/interfaces/localization-module-options.interface.d.ts +21 -0
- package/interfaces/translation-key.interface.d.ts +11 -0
- package/interfaces/translation.interface.d.ts +13 -0
- package/modules/index.d.ts +1 -0
- package/modules/localization.module.d.ts +8 -0
- package/package.json +80 -0
- package/services/index.d.ts +5 -0
- package/services/language.service.d.ts +18 -0
- package/services/localization-config.service.d.ts +11 -0
- package/services/localization-datasource.provider.d.ts +16 -0
- package/services/translation-key.service.d.ts +23 -0
- package/services/translation.service.d.ts +30 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
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;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
}
|
|
7
|
+
import { CacheModule, UtilsModule } from '@flusys/nestjs-shared/modules';
|
|
8
|
+
import { Module } from '@nestjs/common';
|
|
9
|
+
import { LOCALIZATION_MODULE_OPTIONS } from '../config/localization.constants';
|
|
10
|
+
import { LanguageController, TranslationKeyController, TranslationController } from '../controllers';
|
|
11
|
+
import { LanguageService, LocalizationConfigService, LocalizationDataSourceProvider, TranslationKeyService, TranslationService } from '../services';
|
|
12
|
+
const CONTROLLERS = [
|
|
13
|
+
LanguageController,
|
|
14
|
+
TranslationKeyController,
|
|
15
|
+
TranslationController
|
|
16
|
+
];
|
|
17
|
+
const EXPORTED_SERVICES = [
|
|
18
|
+
LocalizationConfigService,
|
|
19
|
+
LocalizationDataSourceProvider,
|
|
20
|
+
LanguageService,
|
|
21
|
+
TranslationKeyService,
|
|
22
|
+
TranslationService
|
|
23
|
+
];
|
|
24
|
+
export class LocalizationModule {
|
|
25
|
+
static forRoot(options) {
|
|
26
|
+
return {
|
|
27
|
+
module: LocalizationModule,
|
|
28
|
+
global: options.global ?? false,
|
|
29
|
+
imports: [
|
|
30
|
+
CacheModule,
|
|
31
|
+
UtilsModule
|
|
32
|
+
],
|
|
33
|
+
controllers: options.includeController !== false ? CONTROLLERS : [],
|
|
34
|
+
providers: this.buildProviders(options),
|
|
35
|
+
exports: EXPORTED_SERVICES
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
static forRootAsync(options) {
|
|
39
|
+
return {
|
|
40
|
+
module: LocalizationModule,
|
|
41
|
+
global: options.global ?? false,
|
|
42
|
+
imports: [
|
|
43
|
+
...options.imports || [],
|
|
44
|
+
CacheModule,
|
|
45
|
+
UtilsModule
|
|
46
|
+
],
|
|
47
|
+
controllers: options.includeController !== false ? CONTROLLERS : [],
|
|
48
|
+
providers: [
|
|
49
|
+
...this.createAsyncProviders(options),
|
|
50
|
+
...this.buildProviders()
|
|
51
|
+
],
|
|
52
|
+
exports: EXPORTED_SERVICES
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
static buildProviders(options) {
|
|
56
|
+
const providers = [
|
|
57
|
+
...EXPORTED_SERVICES
|
|
58
|
+
];
|
|
59
|
+
if (options) {
|
|
60
|
+
providers.unshift({
|
|
61
|
+
provide: LOCALIZATION_MODULE_OPTIONS,
|
|
62
|
+
useValue: options
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return providers;
|
|
66
|
+
}
|
|
67
|
+
static createAsyncProviders(options) {
|
|
68
|
+
if (options.useFactory) {
|
|
69
|
+
return [
|
|
70
|
+
{
|
|
71
|
+
provide: LOCALIZATION_MODULE_OPTIONS,
|
|
72
|
+
useFactory: async (...args)=>({
|
|
73
|
+
...options,
|
|
74
|
+
config: await options.useFactory(...args)
|
|
75
|
+
}),
|
|
76
|
+
inject: options.inject || []
|
|
77
|
+
}
|
|
78
|
+
];
|
|
79
|
+
}
|
|
80
|
+
const factoryClass = options.useClass || options.useExisting;
|
|
81
|
+
if (!factoryClass) return [];
|
|
82
|
+
const providers = [
|
|
83
|
+
{
|
|
84
|
+
provide: LOCALIZATION_MODULE_OPTIONS,
|
|
85
|
+
useFactory: async (factory)=>({
|
|
86
|
+
...options,
|
|
87
|
+
config: await factory.createLocalizationOptions()
|
|
88
|
+
}),
|
|
89
|
+
inject: [
|
|
90
|
+
factoryClass
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
];
|
|
94
|
+
if (options.useClass) {
|
|
95
|
+
providers.push({
|
|
96
|
+
provide: options.useClass,
|
|
97
|
+
useClass: options.useClass
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return providers;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
LocalizationModule = _ts_decorate([
|
|
104
|
+
Module({})
|
|
105
|
+
], LocalizationModule);
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
+
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;
|
|
18
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
+
}
|
|
20
|
+
function _ts_metadata(k, v) {
|
|
21
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
|
+
}
|
|
23
|
+
function _ts_param(paramIndex, decorator) {
|
|
24
|
+
return function(target, key) {
|
|
25
|
+
decorator(target, key, paramIndex);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
import { HybridCache, RequestScopedApiService } from '@flusys/nestjs-shared/classes';
|
|
29
|
+
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
30
|
+
import { Inject, Injectable, Scope } from '@nestjs/common';
|
|
31
|
+
import { IsNull } from 'typeorm';
|
|
32
|
+
import { Language } from '../entities';
|
|
33
|
+
import { LocalizationDataSourceProvider } from './localization-datasource.provider';
|
|
34
|
+
export class LanguageService extends RequestScopedApiService {
|
|
35
|
+
resolveEntity() {
|
|
36
|
+
return Language;
|
|
37
|
+
}
|
|
38
|
+
getDataSourceProvider() {
|
|
39
|
+
return this.dataSourceProvider;
|
|
40
|
+
}
|
|
41
|
+
async getDefaultLanguage() {
|
|
42
|
+
await this.ensureRepositoryInitialized();
|
|
43
|
+
return this.repository.findOne({
|
|
44
|
+
where: {
|
|
45
|
+
isDefault: true,
|
|
46
|
+
deletedAt: IsNull()
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
async getActiveLanguages() {
|
|
51
|
+
await this.ensureRepositoryInitialized();
|
|
52
|
+
return this.repository.find({
|
|
53
|
+
where: {
|
|
54
|
+
isActive: true,
|
|
55
|
+
deletedAt: IsNull()
|
|
56
|
+
},
|
|
57
|
+
order: {
|
|
58
|
+
serial: 'ASC',
|
|
59
|
+
name: 'ASC'
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
async setAsDefault(id) {
|
|
64
|
+
await this.ensureRepositoryInitialized();
|
|
65
|
+
await this.repository.update({
|
|
66
|
+
isDefault: true
|
|
67
|
+
}, {
|
|
68
|
+
isDefault: false
|
|
69
|
+
});
|
|
70
|
+
await this.repository.update({
|
|
71
|
+
id
|
|
72
|
+
}, {
|
|
73
|
+
isDefault: true
|
|
74
|
+
});
|
|
75
|
+
return this.repository.findOne({
|
|
76
|
+
where: {
|
|
77
|
+
id
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
constructor(cacheManager, utilsService, dataSourceProvider){
|
|
82
|
+
super('language', null, cacheManager, utilsService, LanguageService.name, true, 'localization'), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "dataSourceProvider", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.dataSourceProvider = dataSourceProvider;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
LanguageService = _ts_decorate([
|
|
86
|
+
Injectable({
|
|
87
|
+
scope: Scope.REQUEST
|
|
88
|
+
}),
|
|
89
|
+
_ts_param(0, Inject('CACHE_INSTANCE')),
|
|
90
|
+
_ts_param(1, Inject(UtilsService)),
|
|
91
|
+
_ts_param(2, Inject(LocalizationDataSourceProvider)),
|
|
92
|
+
_ts_metadata("design:type", Function),
|
|
93
|
+
_ts_metadata("design:paramtypes", [
|
|
94
|
+
typeof HybridCache === "undefined" ? Object : HybridCache,
|
|
95
|
+
typeof UtilsService === "undefined" ? Object : UtilsService,
|
|
96
|
+
typeof LocalizationDataSourceProvider === "undefined" ? Object : LocalizationDataSourceProvider
|
|
97
|
+
])
|
|
98
|
+
], LanguageService);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
+
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;
|
|
18
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
+
}
|
|
20
|
+
function _ts_metadata(k, v) {
|
|
21
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
|
+
}
|
|
23
|
+
function _ts_param(paramIndex, decorator) {
|
|
24
|
+
return function(target, key) {
|
|
25
|
+
decorator(target, key, paramIndex);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
29
|
+
import { LOCALIZATION_MODULE_OPTIONS } from '../config/localization.constants';
|
|
30
|
+
import { LocalizationModuleOptions } from '../interfaces';
|
|
31
|
+
export class LocalizationConfigService {
|
|
32
|
+
isCompanyFeatureEnabled() {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
getDatabaseMode() {
|
|
36
|
+
return this.options.bootstrapAppConfig?.databaseMode ?? 'single';
|
|
37
|
+
}
|
|
38
|
+
isMultiTenant() {
|
|
39
|
+
return this.getDatabaseMode() === 'multi-tenant';
|
|
40
|
+
}
|
|
41
|
+
getOptions() {
|
|
42
|
+
return this.options;
|
|
43
|
+
}
|
|
44
|
+
constructor(options){
|
|
45
|
+
_define_property(this, "options", void 0);
|
|
46
|
+
this.options = options;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
LocalizationConfigService = _ts_decorate([
|
|
50
|
+
Injectable(),
|
|
51
|
+
_ts_param(0, Inject(LOCALIZATION_MODULE_OPTIONS)),
|
|
52
|
+
_ts_metadata("design:type", Function),
|
|
53
|
+
_ts_metadata("design:paramtypes", [
|
|
54
|
+
typeof LocalizationModuleOptions === "undefined" ? Object : LocalizationModuleOptions
|
|
55
|
+
])
|
|
56
|
+
], LocalizationConfigService);
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
+
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;
|
|
18
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
+
}
|
|
20
|
+
function _ts_metadata(k, v) {
|
|
21
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
|
+
}
|
|
23
|
+
function _ts_param(paramIndex, decorator) {
|
|
24
|
+
return function(target, key) {
|
|
25
|
+
decorator(target, key, paramIndex);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
import { MultiTenantDataSourceService } from '@flusys/nestjs-shared/modules';
|
|
29
|
+
import { SYSTEM_MESSAGES } from '@flusys/nestjs-shared/constants';
|
|
30
|
+
import { Inject, Injectable, InternalServerErrorException, Optional, Scope } from '@nestjs/common';
|
|
31
|
+
import { REQUEST } from '@nestjs/core';
|
|
32
|
+
import { Request } from 'express';
|
|
33
|
+
import { getLocalizationEntities } from '../entities';
|
|
34
|
+
import { LocalizationConfigService } from './localization-config.service';
|
|
35
|
+
export class LocalizationDataSourceProvider extends MultiTenantDataSourceService {
|
|
36
|
+
static buildParentOptions(options) {
|
|
37
|
+
return {
|
|
38
|
+
bootstrapAppConfig: options.bootstrapAppConfig,
|
|
39
|
+
defaultDatabaseConfig: options.config?.defaultDatabaseConfig,
|
|
40
|
+
tenantDefaultDatabaseConfig: options.config?.tenantDefaultDatabaseConfig,
|
|
41
|
+
tenants: options.config?.tenants
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async createDataSourceFromConfig(config) {
|
|
45
|
+
const entities = getLocalizationEntities();
|
|
46
|
+
return super.createDataSourceFromConfig(config, entities);
|
|
47
|
+
}
|
|
48
|
+
async getSingleDataSource() {
|
|
49
|
+
if (LocalizationDataSourceProvider.singleDataSource?.isInitialized) {
|
|
50
|
+
return LocalizationDataSourceProvider.singleDataSource;
|
|
51
|
+
}
|
|
52
|
+
if (LocalizationDataSourceProvider.singleConnectionLock) {
|
|
53
|
+
return LocalizationDataSourceProvider.singleConnectionLock;
|
|
54
|
+
}
|
|
55
|
+
const config = this.getDefaultDatabaseConfig();
|
|
56
|
+
if (!config) {
|
|
57
|
+
throw new InternalServerErrorException({
|
|
58
|
+
message: 'No database config available. Provide defaultDatabaseConfig or tenantDefaultDatabaseConfig.',
|
|
59
|
+
messageKey: SYSTEM_MESSAGES.DATABASE_CONFIG_NOT_AVAILABLE
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
const connectionPromise = this.createDataSourceFromConfig(config);
|
|
63
|
+
LocalizationDataSourceProvider.singleConnectionLock = connectionPromise;
|
|
64
|
+
try {
|
|
65
|
+
const dataSource = await connectionPromise;
|
|
66
|
+
LocalizationDataSourceProvider.singleDataSource = dataSource;
|
|
67
|
+
return dataSource;
|
|
68
|
+
} finally{
|
|
69
|
+
LocalizationDataSourceProvider.singleConnectionLock = null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async getOrCreateTenantConnection(tenant) {
|
|
73
|
+
const existing = LocalizationDataSourceProvider.tenantConnections.get(tenant.id);
|
|
74
|
+
if (existing?.isInitialized) {
|
|
75
|
+
return existing;
|
|
76
|
+
}
|
|
77
|
+
const pendingConnection = LocalizationDataSourceProvider.connectionLocks.get(tenant.id);
|
|
78
|
+
if (pendingConnection) {
|
|
79
|
+
return pendingConnection;
|
|
80
|
+
}
|
|
81
|
+
const config = this.buildTenantDatabaseConfig(tenant);
|
|
82
|
+
const connectionPromise = this.createDataSourceFromConfig(config);
|
|
83
|
+
LocalizationDataSourceProvider.connectionLocks.set(tenant.id, connectionPromise);
|
|
84
|
+
try {
|
|
85
|
+
const dataSource = await connectionPromise;
|
|
86
|
+
LocalizationDataSourceProvider.tenantConnections.set(tenant.id, dataSource);
|
|
87
|
+
return dataSource;
|
|
88
|
+
} finally{
|
|
89
|
+
LocalizationDataSourceProvider.connectionLocks.delete(tenant.id);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
constructor(configService, request){
|
|
93
|
+
super(LocalizationDataSourceProvider.buildParentOptions(configService.getOptions()), request);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
_define_property(LocalizationDataSourceProvider, "tenantConnections", new Map());
|
|
97
|
+
_define_property(LocalizationDataSourceProvider, "singleDataSource", null);
|
|
98
|
+
_define_property(LocalizationDataSourceProvider, "connectionLocks", new Map());
|
|
99
|
+
_define_property(LocalizationDataSourceProvider, "singleConnectionLock", null);
|
|
100
|
+
LocalizationDataSourceProvider = _ts_decorate([
|
|
101
|
+
Injectable({
|
|
102
|
+
scope: Scope.REQUEST
|
|
103
|
+
}),
|
|
104
|
+
_ts_param(0, Inject(LocalizationConfigService)),
|
|
105
|
+
_ts_param(1, Optional()),
|
|
106
|
+
_ts_param(1, Inject(REQUEST)),
|
|
107
|
+
_ts_metadata("design:type", Function),
|
|
108
|
+
_ts_metadata("design:paramtypes", [
|
|
109
|
+
typeof LocalizationConfigService === "undefined" ? Object : LocalizationConfigService,
|
|
110
|
+
typeof Request === "undefined" ? Object : Request
|
|
111
|
+
])
|
|
112
|
+
], LocalizationDataSourceProvider);
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
+
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;
|
|
18
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
+
}
|
|
20
|
+
function _ts_metadata(k, v) {
|
|
21
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
|
+
}
|
|
23
|
+
function _ts_param(paramIndex, decorator) {
|
|
24
|
+
return function(target, key) {
|
|
25
|
+
decorator(target, key, paramIndex);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
import { HybridCache, RequestScopedApiService } from '@flusys/nestjs-shared/classes';
|
|
29
|
+
import { TRANSLATION_KEY_MESSAGES } from '../config';
|
|
30
|
+
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
31
|
+
import { BadRequestException, ForbiddenException, Inject, Injectable, Scope } from '@nestjs/common';
|
|
32
|
+
import { In, IsNull } from 'typeorm';
|
|
33
|
+
import { TranslationKey } from '../entities';
|
|
34
|
+
import { LocalizationDataSourceProvider } from './localization-datasource.provider';
|
|
35
|
+
export class TranslationKeyService extends RequestScopedApiService {
|
|
36
|
+
resolveEntity() {
|
|
37
|
+
return TranslationKey;
|
|
38
|
+
}
|
|
39
|
+
getDataSourceProvider() {
|
|
40
|
+
return this.dataSourceProvider;
|
|
41
|
+
}
|
|
42
|
+
async beforeInsertOperation(dto, _user, _queryRunner) {
|
|
43
|
+
const dtos = Array.isArray(dto) ? dto : [
|
|
44
|
+
dto
|
|
45
|
+
];
|
|
46
|
+
await this.validateNoDuplicateKeys(dtos.map((d)=>({
|
|
47
|
+
module: d.module,
|
|
48
|
+
key: d.key
|
|
49
|
+
})));
|
|
50
|
+
}
|
|
51
|
+
async beforeUpdateOperation(dto, _user, _queryRunner) {
|
|
52
|
+
const dtos = Array.isArray(dto) ? dto : [
|
|
53
|
+
dto
|
|
54
|
+
];
|
|
55
|
+
const ids = dtos.map((d)=>d.id);
|
|
56
|
+
await this.ensureRepositoryInitialized();
|
|
57
|
+
const existingKeys = await this.repository.find({
|
|
58
|
+
where: {
|
|
59
|
+
id: In(ids),
|
|
60
|
+
deletedAt: IsNull()
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
// Check if any readonly keys are being updated with key/module changes
|
|
64
|
+
for (const d of dtos){
|
|
65
|
+
const existing = existingKeys.find((e)=>e.id === d.id);
|
|
66
|
+
if (existing?.isReadonly && (d.key || d.module)) {
|
|
67
|
+
throw new ForbiddenException({
|
|
68
|
+
message: `Cannot modify key or module of readonly translation key "${existing.key}"`,
|
|
69
|
+
messageKey: TRANSLATION_KEY_MESSAGES.READONLY_UPDATE_FORBIDDEN,
|
|
70
|
+
messageParams: {
|
|
71
|
+
key: existing.key
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Validate no duplicates for keys being changed
|
|
77
|
+
const keysToValidate = dtos.filter((d)=>d.key || d.module).map((d)=>{
|
|
78
|
+
const existing = existingKeys.find((e)=>e.id === d.id);
|
|
79
|
+
return {
|
|
80
|
+
module: d.module ?? existing?.module ?? '',
|
|
81
|
+
key: d.key ?? existing?.key ?? '',
|
|
82
|
+
excludeId: d.id
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
if (keysToValidate.length > 0) {
|
|
86
|
+
await this.validateNoDuplicateKeys(keysToValidate);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async beforeDeleteOperation(option, _user, _queryRunner) {
|
|
90
|
+
const ids = Array.isArray(option.id) ? option.id : [
|
|
91
|
+
option.id
|
|
92
|
+
];
|
|
93
|
+
await this.ensureRepositoryInitialized();
|
|
94
|
+
const readonlyKeys = await this.repository.find({
|
|
95
|
+
where: {
|
|
96
|
+
id: In(ids),
|
|
97
|
+
isReadonly: true,
|
|
98
|
+
deletedAt: IsNull()
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
if (readonlyKeys.length > 0) {
|
|
102
|
+
const keys = readonlyKeys.map((k)=>k.key).join(', ');
|
|
103
|
+
throw new ForbiddenException({
|
|
104
|
+
message: `Cannot delete readonly translation keys: ${keys}`,
|
|
105
|
+
messageKey: TRANSLATION_KEY_MESSAGES.READONLY_DELETE_FORBIDDEN,
|
|
106
|
+
messageParams: {
|
|
107
|
+
keys
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async findByModule(module) {
|
|
113
|
+
await this.ensureRepositoryInitialized();
|
|
114
|
+
return this.repository.find({
|
|
115
|
+
where: {
|
|
116
|
+
module,
|
|
117
|
+
isActive: true,
|
|
118
|
+
deletedAt: IsNull()
|
|
119
|
+
},
|
|
120
|
+
order: {
|
|
121
|
+
serial: 'ASC',
|
|
122
|
+
key: 'ASC'
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
async getModules() {
|
|
127
|
+
await this.ensureRepositoryInitialized();
|
|
128
|
+
const result = await this.repository.createQueryBuilder('tk').select('DISTINCT tk.module', 'module').where('tk.deleted_at IS NULL').getRawMany();
|
|
129
|
+
return result.map((r)=>r.module);
|
|
130
|
+
}
|
|
131
|
+
async validateNoDuplicateKeys(keys) {
|
|
132
|
+
await this.ensureRepositoryInitialized();
|
|
133
|
+
for (const { module, key, excludeId } of keys){
|
|
134
|
+
const query = this.repository.createQueryBuilder('tk').where('tk.module = :module', {
|
|
135
|
+
module
|
|
136
|
+
}).andWhere('tk.key = :key', {
|
|
137
|
+
key
|
|
138
|
+
}).andWhere('tk.deleted_at IS NULL');
|
|
139
|
+
if (excludeId) {
|
|
140
|
+
query.andWhere('tk.id != :excludeId', {
|
|
141
|
+
excludeId
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
const existing = await query.getOne();
|
|
145
|
+
if (existing) {
|
|
146
|
+
throw new BadRequestException({
|
|
147
|
+
message: `Translation key "${key}" already exists in module "${module}"`,
|
|
148
|
+
messageKey: TRANSLATION_KEY_MESSAGES.DUPLICATE_KEY_IN_MODULE,
|
|
149
|
+
messageParams: {
|
|
150
|
+
key,
|
|
151
|
+
module
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
constructor(cacheManager, utilsService, dataSourceProvider){
|
|
158
|
+
super('translationKey', null, cacheManager, utilsService, TranslationKeyService.name, true, 'localization'), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "dataSourceProvider", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.dataSourceProvider = dataSourceProvider;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
TranslationKeyService = _ts_decorate([
|
|
162
|
+
Injectable({
|
|
163
|
+
scope: Scope.REQUEST
|
|
164
|
+
}),
|
|
165
|
+
_ts_param(0, Inject('CACHE_INSTANCE')),
|
|
166
|
+
_ts_param(1, Inject(UtilsService)),
|
|
167
|
+
_ts_param(2, Inject(LocalizationDataSourceProvider)),
|
|
168
|
+
_ts_metadata("design:type", Function),
|
|
169
|
+
_ts_metadata("design:paramtypes", [
|
|
170
|
+
typeof HybridCache === "undefined" ? Object : HybridCache,
|
|
171
|
+
typeof UtilsService === "undefined" ? Object : UtilsService,
|
|
172
|
+
typeof LocalizationDataSourceProvider === "undefined" ? Object : LocalizationDataSourceProvider
|
|
173
|
+
])
|
|
174
|
+
], TranslationKeyService);
|