@vritti/api-sdk 0.2.3 → 0.2.5
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/dist/index.cjs +1692 -850
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +189 -109
- package/dist/index.d.ts +189 -109
- package/dist/index.js +1634 -793
- package/dist/index.js.map +1 -1
- package/dist/xlsx.cjs +88 -0
- package/dist/xlsx.cjs.map +1 -0
- package/dist/xlsx.d.cts +6 -0
- package/dist/xlsx.d.ts +6 -0
- package/dist/xlsx.js +61 -0
- package/dist/xlsx.js.map +1 -0
- package/package.json +15 -4
package/dist/index.js
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
2
5
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
6
|
+
var __copyProps = (to, from, except, desc2) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc2 = __getOwnPropDesc(from, key)) || desc2.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
3
15
|
|
|
4
16
|
// src/auth/auth-config.module.ts
|
|
5
17
|
import { Global as Global2, Module as Module2 } from "@nestjs/common";
|
|
@@ -251,7 +263,10 @@ var defaultConfig = {
|
|
|
251
263
|
guard: {
|
|
252
264
|
tenantHeaderName: "x-tenant-id",
|
|
253
265
|
authHeaderName: "authorization",
|
|
254
|
-
tokenPrefix: "Bearer"
|
|
266
|
+
tokenPrefix: "Bearer",
|
|
267
|
+
defaultSessionTypes: [
|
|
268
|
+
"CLOUD"
|
|
269
|
+
]
|
|
255
270
|
}
|
|
256
271
|
};
|
|
257
272
|
var currentConfig = {
|
|
@@ -439,10 +454,10 @@ import { ConfigService } from "@nestjs/config";
|
|
|
439
454
|
import { Reflector } from "@nestjs/core";
|
|
440
455
|
import { JwtService } from "@nestjs/jwt";
|
|
441
456
|
|
|
442
|
-
// src/auth/decorators/
|
|
457
|
+
// src/auth/decorators/require-session.decorator.ts
|
|
443
458
|
import { SetMetadata } from "@nestjs/common";
|
|
444
|
-
var
|
|
445
|
-
var
|
|
459
|
+
var REQUIRE_SESSION_KEY = "requiredSessionTypes";
|
|
460
|
+
var RequireSession = /* @__PURE__ */ __name((...types) => SetMetadata(REQUIRE_SESSION_KEY, types), "RequireSession");
|
|
446
461
|
|
|
447
462
|
// src/auth/decorators/skip-csrf.decorator.ts
|
|
448
463
|
import { SetMetadata as SetMetadata2 } from "@nestjs/common";
|
|
@@ -506,17 +521,13 @@ var VrittiAuthGuard = class _VrittiAuthGuard {
|
|
|
506
521
|
if (isPublic) {
|
|
507
522
|
return true;
|
|
508
523
|
}
|
|
509
|
-
const
|
|
510
|
-
context.getHandler(),
|
|
511
|
-
context.getClass()
|
|
512
|
-
]);
|
|
513
|
-
const isReset = this.reflector.getAllAndOverride(RESET_KEY, [
|
|
524
|
+
const requiredSessionTypes = this.reflector.getAllAndOverride(REQUIRE_SESSION_KEY, [
|
|
514
525
|
context.getHandler(),
|
|
515
526
|
context.getClass()
|
|
516
527
|
]);
|
|
517
528
|
const isSseEndpoint = this.reflector.get(SSE_METADATA, context.getHandler());
|
|
518
529
|
if (isSseEndpoint) {
|
|
519
|
-
return this.handleSseAuth(request,
|
|
530
|
+
return this.handleSseAuth(request, requiredSessionTypes);
|
|
520
531
|
}
|
|
521
532
|
try {
|
|
522
533
|
const accessToken = this.requestService.getAccessToken();
|
|
@@ -528,14 +539,10 @@ var VrittiAuthGuard = class _VrittiAuthGuard {
|
|
|
528
539
|
throw new UnauthorizedException2("Invalid token type");
|
|
529
540
|
}
|
|
530
541
|
this.validateRefreshTokenBinding(decodedAccessToken);
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
throw new UnauthorizedException2("This endpoint requires a reset session");
|
|
536
|
-
}
|
|
537
|
-
if (!isOnboarding && !isReset && (decodedAccessToken.sessionType === "ONBOARDING" || decodedAccessToken.sessionType === "RESET")) {
|
|
538
|
-
throw new UnauthorizedException2(`${decodedAccessToken.sessionType} sessions cannot access this endpoint`);
|
|
542
|
+
const sessionType = decodedAccessToken.sessionType;
|
|
543
|
+
const allowed = requiredSessionTypes ?? getConfig().guard.defaultSessionTypes;
|
|
544
|
+
if (!allowed.includes(sessionType)) {
|
|
545
|
+
throw new UnauthorizedException2(`${sessionType} sessions cannot access this endpoint`);
|
|
539
546
|
}
|
|
540
547
|
request.sessionInfo = {
|
|
541
548
|
userId: decodedAccessToken.userId,
|
|
@@ -584,7 +591,7 @@ var VrittiAuthGuard = class _VrittiAuthGuard {
|
|
|
584
591
|
}
|
|
585
592
|
}
|
|
586
593
|
// Authenticates SSE connections using the refresh token httpOnly cookie
|
|
587
|
-
handleSseAuth(request,
|
|
594
|
+
handleSseAuth(request, requiredSessionTypes) {
|
|
588
595
|
const refreshToken = this.requestService.getRefreshToken();
|
|
589
596
|
if (!refreshToken) {
|
|
590
597
|
throw new UnauthorizedException2("Authentication required");
|
|
@@ -598,8 +605,9 @@ var VrittiAuthGuard = class _VrittiAuthGuard {
|
|
|
598
605
|
if (decoded.tokenType !== "refresh") {
|
|
599
606
|
throw new UnauthorizedException2("Invalid token type");
|
|
600
607
|
}
|
|
601
|
-
|
|
602
|
-
|
|
608
|
+
const allowed = requiredSessionTypes ?? getConfig().guard.defaultSessionTypes;
|
|
609
|
+
if (!allowed.includes(decoded.sessionType)) {
|
|
610
|
+
throw new UnauthorizedException2(`${decoded.sessionType} sessions cannot access this endpoint`);
|
|
603
611
|
}
|
|
604
612
|
request.sessionInfo = {
|
|
605
613
|
userId: decoded.userId,
|
|
@@ -861,13 +869,9 @@ var CookieDomain = createParamDecorator2((_data, ctx) => {
|
|
|
861
869
|
return domain.endsWith(`.${baseDomain}`) ? domain : baseDomain;
|
|
862
870
|
});
|
|
863
871
|
|
|
864
|
-
// src/auth/decorators/onboarding.decorator.ts
|
|
865
|
-
import { SetMetadata as SetMetadata3 } from "@nestjs/common";
|
|
866
|
-
var Onboarding = /* @__PURE__ */ __name(() => SetMetadata3("isOnboarding", true), "Onboarding");
|
|
867
|
-
|
|
868
872
|
// src/auth/decorators/public.decorator.ts
|
|
869
|
-
import { SetMetadata as
|
|
870
|
-
var Public = /* @__PURE__ */ __name(() =>
|
|
873
|
+
import { SetMetadata as SetMetadata3 } from "@nestjs/common";
|
|
874
|
+
var Public = /* @__PURE__ */ __name(() => SetMetadata3("isPublic", true), "Public");
|
|
871
875
|
|
|
872
876
|
// src/auth/decorators/refresh-cookie-options.decorator.ts
|
|
873
877
|
import { createParamDecorator as createParamDecorator3 } from "@nestjs/common";
|
|
@@ -889,9 +893,27 @@ var RefreshTokenCookie = createParamDecorator4((_data, ctx) => {
|
|
|
889
893
|
return cookies[config.cookie.refreshCookieName];
|
|
890
894
|
});
|
|
891
895
|
|
|
892
|
-
// src/auth/decorators/
|
|
896
|
+
// src/auth/decorators/subdomain.decorator.ts
|
|
893
897
|
import { createParamDecorator as createParamDecorator5 } from "@nestjs/common";
|
|
894
|
-
var
|
|
898
|
+
var Subdomain = createParamDecorator5((_data, ctx) => {
|
|
899
|
+
const request = ctx.switchToHttp().getRequest();
|
|
900
|
+
const origin = request.headers.origin;
|
|
901
|
+
if (origin) {
|
|
902
|
+
try {
|
|
903
|
+
const url = new URL(origin);
|
|
904
|
+
return url.hostname.split(".")[0];
|
|
905
|
+
} catch {
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
const forwarded = request.headers["x-forwarded-host"];
|
|
909
|
+
const host = Array.isArray(forwarded) ? forwarded[0] : forwarded;
|
|
910
|
+
if (host) return host.split(".")[0];
|
|
911
|
+
return void 0;
|
|
912
|
+
});
|
|
913
|
+
|
|
914
|
+
// src/auth/decorators/session-data.decorator.ts
|
|
915
|
+
import { createParamDecorator as createParamDecorator6 } from "@nestjs/common";
|
|
916
|
+
var SessionData = createParamDecorator6((_data, ctx) => {
|
|
895
917
|
const request = ctx.switchToHttp().getRequest();
|
|
896
918
|
const sessionInfo = request.sessionInfo;
|
|
897
919
|
if (!sessionInfo?.sessionId) {
|
|
@@ -905,8 +927,8 @@ var SessionData = createParamDecorator5((_data, ctx) => {
|
|
|
905
927
|
});
|
|
906
928
|
|
|
907
929
|
// src/auth/decorators/user-id.decorator.ts
|
|
908
|
-
import { createParamDecorator as
|
|
909
|
-
var UserId =
|
|
930
|
+
import { createParamDecorator as createParamDecorator7 } from "@nestjs/common";
|
|
931
|
+
var UserId = createParamDecorator7((_data, ctx) => {
|
|
910
932
|
const request = ctx.switchToHttp().getRequest();
|
|
911
933
|
const sessionInfo = request.sessionInfo;
|
|
912
934
|
if (!sessionInfo?.userId) {
|
|
@@ -1129,7 +1151,6 @@ var DATABASE_MODULE_OPTIONS = Symbol("DATABASE_MODULE_OPTIONS");
|
|
|
1129
1151
|
|
|
1130
1152
|
// src/database/services/primary-database.service.ts
|
|
1131
1153
|
import { Inject as Inject3, Injectable as Injectable6, InternalServerErrorException as InternalServerErrorException2, Logger as Logger6 } from "@nestjs/common";
|
|
1132
|
-
import { eq, or } from "drizzle-orm";
|
|
1133
1154
|
import { drizzle } from "drizzle-orm/node-postgres";
|
|
1134
1155
|
import { Pool } from "pg";
|
|
1135
1156
|
function _ts_decorate9(decorators, target, key, desc2) {
|
|
@@ -1157,11 +1178,8 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
|
1157
1178
|
logger = new Logger6(_PrimaryDatabaseService.name);
|
|
1158
1179
|
pool = null;
|
|
1159
1180
|
db = null;
|
|
1160
|
-
tenantConfigCache = /* @__PURE__ */ new Map();
|
|
1161
|
-
cacheTTL;
|
|
1162
1181
|
constructor(options) {
|
|
1163
1182
|
this.options = options;
|
|
1164
|
-
this.cacheTTL = options.connectionCacheTTL || 3e5;
|
|
1165
1183
|
}
|
|
1166
1184
|
async onModuleInit() {
|
|
1167
1185
|
if (this.options.primaryDb) {
|
|
@@ -1171,10 +1189,20 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
|
1171
1189
|
// Initializes connection to primary database using Drizzle
|
|
1172
1190
|
async initializeDrizzleClient() {
|
|
1173
1191
|
try {
|
|
1174
|
-
const
|
|
1192
|
+
const { host, port = 5432, username, password, database, schema, sslMode = "require" } = this.options.primaryDb;
|
|
1175
1193
|
this.pool = new Pool({
|
|
1176
|
-
|
|
1177
|
-
|
|
1194
|
+
host,
|
|
1195
|
+
port,
|
|
1196
|
+
user: username,
|
|
1197
|
+
password,
|
|
1198
|
+
database,
|
|
1199
|
+
max: this.options.maxConnections || 10,
|
|
1200
|
+
ssl: sslMode === "disable" ? false : {
|
|
1201
|
+
rejectUnauthorized: sslMode !== "no-verify"
|
|
1202
|
+
},
|
|
1203
|
+
...schema && {
|
|
1204
|
+
options: `-csearch_path=${schema}`
|
|
1205
|
+
}
|
|
1178
1206
|
});
|
|
1179
1207
|
this.logger.debug(`Schema keys passed to drizzle: [${Object.keys(this.options.drizzleSchema || {}).join(", ")}]`);
|
|
1180
1208
|
this.logger.debug(`Relations keys passed to drizzle: [${Object.keys(this.options.drizzleRelations || {}).join(", ")}]`);
|
|
@@ -1185,110 +1213,13 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
|
1185
1213
|
});
|
|
1186
1214
|
this.logger.debug(`Drizzle query keys after init: [${Object.keys(this.db.query || {}).join(", ")}]`);
|
|
1187
1215
|
await this.pool.query("SELECT 1");
|
|
1188
|
-
this.logger.log(
|
|
1216
|
+
this.logger.log(`Connected to primary database (schema: ${schema ?? "public"})`);
|
|
1189
1217
|
} catch (error) {
|
|
1190
1218
|
this.logger.error("Failed to connect to primary database", error);
|
|
1191
|
-
throw new InternalServerErrorException2("Failed to initialize
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
// Builds the PostgreSQL connection URL from primary database config properties
|
|
1195
|
-
buildPrimaryDbUrl() {
|
|
1196
|
-
if (!this.options.primaryDb) {
|
|
1197
|
-
throw new Error("Primary database configuration not provided");
|
|
1198
|
-
}
|
|
1199
|
-
const { host, port = 5432, username, password, database, schema = "public", sslMode = "require" } = this.options.primaryDb;
|
|
1200
|
-
let url = `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}`;
|
|
1201
|
-
const params = new URLSearchParams();
|
|
1202
|
-
if (schema) {
|
|
1203
|
-
params.set("schema", schema);
|
|
1204
|
-
}
|
|
1205
|
-
params.set("sslmode", sslMode);
|
|
1206
|
-
const queryString = params.toString();
|
|
1207
|
-
if (queryString) {
|
|
1208
|
-
url += `?${queryString}`;
|
|
1209
|
-
}
|
|
1210
|
-
this.logger.debug(`Primary DB connection URL: ${this.maskPassword(url)}`);
|
|
1211
|
-
return url;
|
|
1212
|
-
}
|
|
1213
|
-
// Masks password in connection URL for safe logging
|
|
1214
|
-
maskPassword(url) {
|
|
1215
|
-
return url.replace(/:([^@]+)@/, ":****@");
|
|
1216
|
-
}
|
|
1217
|
-
// Retrieves tenant configuration by ID or subdomain, with in-memory caching
|
|
1218
|
-
async getTenantInfo(tenantIdentifier) {
|
|
1219
|
-
const cached = this.tenantConfigCache.get(tenantIdentifier);
|
|
1220
|
-
if (cached) {
|
|
1221
|
-
this.logger.debug(`Cache hit for tenant: ${tenantIdentifier}`);
|
|
1222
|
-
return cached;
|
|
1223
|
-
}
|
|
1224
|
-
try {
|
|
1225
|
-
if (!this.db) {
|
|
1226
|
-
throw new Error("Primary database client not initialized");
|
|
1227
|
-
}
|
|
1228
|
-
this.logger.debug(`Querying primary database for tenant: ${tenantIdentifier}`);
|
|
1229
|
-
const schema = this.options.drizzleSchema;
|
|
1230
|
-
const { tenants, tenantDatabaseConfigs } = schema;
|
|
1231
|
-
const result = await this.db.select().from(tenants).leftJoin(tenantDatabaseConfigs, eq(tenants.id, tenantDatabaseConfigs.tenantId)).where(or(eq(tenants.id, tenantIdentifier), eq(tenants.subdomain, tenantIdentifier))).limit(1);
|
|
1232
|
-
if (!result.length) {
|
|
1233
|
-
this.logger.warn(`Tenant not found: ${tenantIdentifier}`);
|
|
1234
|
-
return null;
|
|
1235
|
-
}
|
|
1236
|
-
const row = result[0];
|
|
1237
|
-
const tenant = row.tenants;
|
|
1238
|
-
const config = row.tenant_database_configs;
|
|
1239
|
-
if (tenant.status !== "ACTIVE") {
|
|
1240
|
-
this.logger.warn(`Tenant not active: ${tenantIdentifier}`);
|
|
1241
|
-
return null;
|
|
1242
|
-
}
|
|
1243
|
-
const info = {
|
|
1244
|
-
id: tenant.id,
|
|
1245
|
-
subdomain: tenant.subdomain,
|
|
1246
|
-
type: tenant.dbType,
|
|
1247
|
-
status: tenant.status,
|
|
1248
|
-
// For SHARED tenants: schema name
|
|
1249
|
-
schemaName: config?.dbSchema || void 0,
|
|
1250
|
-
// For DEDICATED tenants: database configuration from TenantDatabaseConfig table
|
|
1251
|
-
databaseName: config?.dbName || void 0,
|
|
1252
|
-
databaseHost: config?.dbHost || void 0,
|
|
1253
|
-
databasePort: config?.dbPort || void 0,
|
|
1254
|
-
databaseUsername: config?.dbUsername ? this.decrypt(config.dbUsername) : void 0,
|
|
1255
|
-
databasePassword: config?.dbPassword ? this.decrypt(config.dbPassword) : void 0,
|
|
1256
|
-
databaseSslMode: config?.dbSslMode || void 0,
|
|
1257
|
-
connectionPoolSize: config?.connectionPoolSize || void 0
|
|
1258
|
-
};
|
|
1259
|
-
this.cacheInfo(info);
|
|
1260
|
-
return info;
|
|
1261
|
-
} catch (error) {
|
|
1262
|
-
this.logger.error(`Failed to fetch tenant info: ${tenantIdentifier}`, error);
|
|
1263
|
-
throw new InternalServerErrorException2("Failed to resolve tenant");
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
// Caches tenant info by both ID and subdomain with TTL expiration
|
|
1267
|
-
cacheInfo(info) {
|
|
1268
|
-
this.tenantConfigCache.set(info.id, info);
|
|
1269
|
-
this.tenantConfigCache.set(info.subdomain, info);
|
|
1270
|
-
setTimeout(() => {
|
|
1271
|
-
this.tenantConfigCache.delete(info.id);
|
|
1272
|
-
this.tenantConfigCache.delete(info.subdomain);
|
|
1273
|
-
this.logger.debug(`Cache expired for tenant: ${info.subdomain}`);
|
|
1274
|
-
}, this.cacheTTL);
|
|
1275
|
-
}
|
|
1276
|
-
// Clears cached tenant info for the given ID or subdomain
|
|
1277
|
-
clearTenantCache(tenantIdentifier) {
|
|
1278
|
-
const config = this.tenantConfigCache.get(tenantIdentifier);
|
|
1279
|
-
if (config) {
|
|
1280
|
-
this.tenantConfigCache.delete(config.id);
|
|
1281
|
-
this.tenantConfigCache.delete(config.subdomain);
|
|
1282
|
-
this.logger.log(`Cleared cache for tenant: ${tenantIdentifier}`);
|
|
1219
|
+
throw new InternalServerErrorException2("Failed to initialize database connection");
|
|
1283
1220
|
}
|
|
1284
1221
|
}
|
|
1285
|
-
//
|
|
1286
|
-
clearAllCaches() {
|
|
1287
|
-
const size = this.tenantConfigCache.size;
|
|
1288
|
-
this.tenantConfigCache.clear();
|
|
1289
|
-
this.logger.log(`Cleared ${size} cached tenant configs`);
|
|
1290
|
-
}
|
|
1291
|
-
// Returns the initialized Drizzle client, throwing if not yet initialized
|
|
1222
|
+
// Returns the initialized Drizzle client
|
|
1292
1223
|
get drizzleClient() {
|
|
1293
1224
|
if (!this.db) {
|
|
1294
1225
|
throw new Error("Primary database client not initialized");
|
|
@@ -1299,10 +1230,6 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
|
1299
1230
|
get schema() {
|
|
1300
1231
|
return this.options.drizzleSchema;
|
|
1301
1232
|
}
|
|
1302
|
-
// Decrypts a database credential value (placeholder for actual decryption)
|
|
1303
|
-
decrypt(encrypted) {
|
|
1304
|
-
return encrypted;
|
|
1305
|
-
}
|
|
1306
1233
|
async onModuleDestroy() {
|
|
1307
1234
|
if (this.pool) {
|
|
1308
1235
|
await this.pool.end();
|
|
@@ -1319,8 +1246,7 @@ PrimaryDatabaseService = _ts_decorate9([
|
|
|
1319
1246
|
])
|
|
1320
1247
|
], PrimaryDatabaseService);
|
|
1321
1248
|
|
|
1322
|
-
// src/database/
|
|
1323
|
-
import { Injectable as Injectable7, Scope as Scope3, UnauthorizedException as UnauthorizedException3 } from "@nestjs/common";
|
|
1249
|
+
// src/database/database.module.ts
|
|
1324
1250
|
function _ts_decorate10(decorators, target, key, desc2) {
|
|
1325
1251
|
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
1326
1252
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc2);
|
|
@@ -1328,52 +1254,63 @@ function _ts_decorate10(decorators, target, key, desc2) {
|
|
|
1328
1254
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1329
1255
|
}
|
|
1330
1256
|
__name(_ts_decorate10, "_ts_decorate");
|
|
1331
|
-
var
|
|
1257
|
+
var DatabaseModule = class _DatabaseModule {
|
|
1332
1258
|
static {
|
|
1333
|
-
__name(this, "
|
|
1334
|
-
}
|
|
1335
|
-
tenantInfo = null;
|
|
1336
|
-
// Sets tenant info for this request, throwing if already set to prevent overwrites
|
|
1337
|
-
setTenant(tenantInfo) {
|
|
1338
|
-
if (this.tenantInfo) {
|
|
1339
|
-
throw new Error("Tenant context already set for this request");
|
|
1340
|
-
}
|
|
1341
|
-
this.tenantInfo = tenantInfo;
|
|
1342
|
-
}
|
|
1343
|
-
// Returns the tenant info for this request, throwing if context is not set
|
|
1344
|
-
getTenant() {
|
|
1345
|
-
if (!this.tenantInfo) {
|
|
1346
|
-
throw new UnauthorizedException3("Tenant context not set");
|
|
1347
|
-
}
|
|
1348
|
-
return this.tenantInfo;
|
|
1349
|
-
}
|
|
1350
|
-
// Returns true if tenant context has been set for this request
|
|
1351
|
-
hasTenant() {
|
|
1352
|
-
return this.tenantInfo !== null;
|
|
1353
|
-
}
|
|
1354
|
-
// Clears the tenant context (useful for RabbitMQ message handler cleanup)
|
|
1355
|
-
clearTenant() {
|
|
1356
|
-
this.tenantInfo = null;
|
|
1357
|
-
}
|
|
1358
|
-
// Returns the tenant ID or null if context is not set
|
|
1359
|
-
getTenantIdSafe() {
|
|
1360
|
-
return this.tenantInfo?.id ?? null;
|
|
1259
|
+
__name(this, "DatabaseModule");
|
|
1361
1260
|
}
|
|
1362
|
-
//
|
|
1363
|
-
|
|
1364
|
-
|
|
1261
|
+
// Configures the database module with a single primary connection
|
|
1262
|
+
static forServer(options) {
|
|
1263
|
+
const asyncProvider = {
|
|
1264
|
+
provide: DATABASE_MODULE_OPTIONS,
|
|
1265
|
+
useFactory: options.useFactory,
|
|
1266
|
+
inject: options.inject || []
|
|
1267
|
+
};
|
|
1268
|
+
return {
|
|
1269
|
+
module: _DatabaseModule,
|
|
1270
|
+
imports: [
|
|
1271
|
+
RequestModule
|
|
1272
|
+
],
|
|
1273
|
+
providers: [
|
|
1274
|
+
{
|
|
1275
|
+
provide: Reflector3,
|
|
1276
|
+
useClass: Reflector3
|
|
1277
|
+
},
|
|
1278
|
+
asyncProvider,
|
|
1279
|
+
PrimaryDatabaseService
|
|
1280
|
+
],
|
|
1281
|
+
exports: [
|
|
1282
|
+
PrimaryDatabaseService,
|
|
1283
|
+
asyncProvider
|
|
1284
|
+
]
|
|
1285
|
+
};
|
|
1365
1286
|
}
|
|
1366
1287
|
};
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1288
|
+
DatabaseModule = _ts_decorate10([
|
|
1289
|
+
Global3(),
|
|
1290
|
+
Module4({})
|
|
1291
|
+
], DatabaseModule);
|
|
1292
|
+
|
|
1293
|
+
// src/decorators/uploaded-file.decorator.ts
|
|
1294
|
+
import { createParamDecorator as createParamDecorator8 } from "@nestjs/common";
|
|
1295
|
+
var UploadedFile = createParamDecorator8(async (_data, ctx) => {
|
|
1296
|
+
const request = ctx.switchToHttp().getRequest();
|
|
1297
|
+
const file = await request.file();
|
|
1298
|
+
if (!file) {
|
|
1299
|
+
throw new BadRequestException({
|
|
1300
|
+
label: "File Required",
|
|
1301
|
+
detail: "Please attach a file to your request."
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
const buffer = await file.toBuffer();
|
|
1305
|
+
return {
|
|
1306
|
+
buffer,
|
|
1307
|
+
filename: file.filename,
|
|
1308
|
+
mimetype: file.mimetype
|
|
1309
|
+
};
|
|
1310
|
+
});
|
|
1372
1311
|
|
|
1373
|
-
// src/database/
|
|
1374
|
-
import {
|
|
1375
|
-
import { drizzle as drizzle2 } from "drizzle-orm/node-postgres";
|
|
1376
|
-
import { Pool as Pool2 } from "pg";
|
|
1312
|
+
// src/database/dto/create-response.dto.ts
|
|
1313
|
+
import { ApiProperty } from "@nestjs/swagger";
|
|
1377
1314
|
function _ts_decorate11(decorators, target, key, desc2) {
|
|
1378
1315
|
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
1379
1316
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc2);
|
|
@@ -1385,161 +1322,33 @@ function _ts_metadata7(k, v) {
|
|
|
1385
1322
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1386
1323
|
}
|
|
1387
1324
|
__name(_ts_metadata7, "_ts_metadata");
|
|
1388
|
-
|
|
1389
|
-
return function(target, key) {
|
|
1390
|
-
decorator(target, key, paramIndex);
|
|
1391
|
-
};
|
|
1392
|
-
}
|
|
1393
|
-
__name(_ts_param4, "_ts_param");
|
|
1394
|
-
var TenantDatabaseService = class _TenantDatabaseService {
|
|
1325
|
+
var CreateResponseDto = class {
|
|
1395
1326
|
static {
|
|
1396
|
-
__name(this, "
|
|
1397
|
-
}
|
|
1398
|
-
options;
|
|
1399
|
-
tenantContext;
|
|
1400
|
-
logger = new Logger7(_TenantDatabaseService.name);
|
|
1401
|
-
clients = /* @__PURE__ */ new Map();
|
|
1402
|
-
clientLastUsed = /* @__PURE__ */ new Map();
|
|
1403
|
-
cleanupInterval;
|
|
1404
|
-
constructor(options, tenantContext) {
|
|
1405
|
-
this.options = options;
|
|
1406
|
-
this.tenantContext = tenantContext;
|
|
1407
|
-
this.startConnectionCleaner();
|
|
1408
|
-
}
|
|
1409
|
-
// Returns the Drizzle client scoped to the current tenant's database
|
|
1410
|
-
get drizzleClient() {
|
|
1411
|
-
return this.getDbClient();
|
|
1412
|
-
}
|
|
1413
|
-
// Returns the Drizzle schema passed in module options
|
|
1414
|
-
get schema() {
|
|
1415
|
-
return this.options.drizzleSchema;
|
|
1416
|
-
}
|
|
1417
|
-
// Returns a cached or new Drizzle client for the current tenant context
|
|
1418
|
-
getDbClient() {
|
|
1419
|
-
const tenant = this.tenantContext.getTenant();
|
|
1420
|
-
const cacheKey = this.buildCacheKey(tenant);
|
|
1421
|
-
const existing = this.clients.get(cacheKey);
|
|
1422
|
-
if (existing) {
|
|
1423
|
-
this.clientLastUsed.set(cacheKey, Date.now());
|
|
1424
|
-
this.logger.debug(`Reusing cached connection: ${cacheKey}`);
|
|
1425
|
-
return existing.db;
|
|
1426
|
-
}
|
|
1427
|
-
this.logger.log(`Creating new database connection: ${cacheKey}`);
|
|
1428
|
-
const connection = this.createDbClientSync(tenant);
|
|
1429
|
-
this.clients.set(cacheKey, connection);
|
|
1430
|
-
this.clientLastUsed.set(cacheKey, Date.now());
|
|
1431
|
-
return connection.db;
|
|
1432
|
-
}
|
|
1433
|
-
// Creates a new pool and Drizzle client for the given tenant
|
|
1434
|
-
createDbClientSync(tenant) {
|
|
1435
|
-
try {
|
|
1436
|
-
const databaseUrl = this.buildTenantDbUrl(tenant);
|
|
1437
|
-
const pool = new Pool2({
|
|
1438
|
-
connectionString: databaseUrl,
|
|
1439
|
-
max: tenant.connectionPoolSize || this.options.maxConnections || 10
|
|
1440
|
-
});
|
|
1441
|
-
const db = drizzle2({
|
|
1442
|
-
client: pool,
|
|
1443
|
-
schema: this.options.drizzleSchema
|
|
1444
|
-
});
|
|
1445
|
-
this.logger.log(`Connected to database for tenant: ${tenant.subdomain}`);
|
|
1446
|
-
return {
|
|
1447
|
-
pool,
|
|
1448
|
-
db
|
|
1449
|
-
};
|
|
1450
|
-
} catch (error) {
|
|
1451
|
-
this.logger.error(`Failed to create database connection for tenant: ${tenant.subdomain}`, error);
|
|
1452
|
-
throw new InternalServerErrorException3("Failed to connect to tenant database");
|
|
1453
|
-
}
|
|
1454
|
-
}
|
|
1455
|
-
// Builds the PostgreSQL connection URL for a dedicated tenant database
|
|
1456
|
-
buildTenantDbUrl(tenant) {
|
|
1457
|
-
const { databaseHost, databasePort, databaseName, databaseUsername, databasePassword, databaseSslMode } = tenant;
|
|
1458
|
-
if (!databaseHost || !databaseName || !databaseUsername) {
|
|
1459
|
-
throw new Error(`Tenant ${tenant.subdomain} missing database configuration`);
|
|
1460
|
-
}
|
|
1461
|
-
const port = databasePort || 5432;
|
|
1462
|
-
const sslMode = databaseSslMode || "require";
|
|
1463
|
-
const connectionUrl = `postgresql://${databaseUsername}:${encodeURIComponent(databasePassword || "")}@${databaseHost}:${port}/${databaseName}?sslmode=${sslMode}`;
|
|
1464
|
-
this.logger.debug(`Tenant connection URL: ${this.maskPassword(connectionUrl)}`);
|
|
1465
|
-
return connectionUrl;
|
|
1466
|
-
}
|
|
1467
|
-
// Builds a cache key for connection pooling from tenant database coordinates
|
|
1468
|
-
buildCacheKey(tenant) {
|
|
1469
|
-
return `${tenant.type}:${tenant.databaseName}@${tenant.databaseHost}`;
|
|
1470
|
-
}
|
|
1471
|
-
// Starts a periodic interval to close idle database connections
|
|
1472
|
-
startConnectionCleaner() {
|
|
1473
|
-
const interval = this.options.connectionCacheTTL || 3e5;
|
|
1474
|
-
this.cleanupInterval = setInterval(() => {
|
|
1475
|
-
this.cleanupIdleConnections();
|
|
1476
|
-
}, interval);
|
|
1477
|
-
this.logger.log(`Connection cleanup scheduled every ${interval / 1e3} seconds`);
|
|
1478
|
-
}
|
|
1479
|
-
// Closes and removes connections that have been idle beyond the TTL
|
|
1480
|
-
async cleanupIdleConnections() {
|
|
1481
|
-
const now = Date.now();
|
|
1482
|
-
const maxIdle = this.options.connectionCacheTTL || 3e5;
|
|
1483
|
-
let cleaned = 0;
|
|
1484
|
-
for (const [key, lastUsed] of this.clientLastUsed.entries()) {
|
|
1485
|
-
if (now - lastUsed > maxIdle) {
|
|
1486
|
-
const connection = this.clients.get(key);
|
|
1487
|
-
if (connection) {
|
|
1488
|
-
try {
|
|
1489
|
-
await connection.pool.end();
|
|
1490
|
-
this.logger.debug(`Cleaned up idle connection: ${key}`);
|
|
1491
|
-
} catch (error) {
|
|
1492
|
-
this.logger.error(`Error disconnecting idle client: ${key}`, error);
|
|
1493
|
-
}
|
|
1494
|
-
this.clients.delete(key);
|
|
1495
|
-
this.clientLastUsed.delete(key);
|
|
1496
|
-
cleaned++;
|
|
1497
|
-
}
|
|
1498
|
-
}
|
|
1499
|
-
}
|
|
1500
|
-
if (cleaned > 0) {
|
|
1501
|
-
this.logger.log(`Cleaned up ${cleaned} idle connections`);
|
|
1502
|
-
}
|
|
1503
|
-
}
|
|
1504
|
-
// Returns the current number of active pooled connections and their tenant keys
|
|
1505
|
-
getPoolStats() {
|
|
1506
|
-
return {
|
|
1507
|
-
activeConnections: this.clients.size,
|
|
1508
|
-
tenants: Array.from(this.clients.keys())
|
|
1509
|
-
};
|
|
1510
|
-
}
|
|
1511
|
-
// Masks password in connection URL for safe logging
|
|
1512
|
-
maskPassword(url) {
|
|
1513
|
-
return url.replace(/:([^@]+)@/, ":****@");
|
|
1514
|
-
}
|
|
1515
|
-
async onModuleDestroy() {
|
|
1516
|
-
if (this.cleanupInterval) {
|
|
1517
|
-
clearInterval(this.cleanupInterval);
|
|
1518
|
-
}
|
|
1519
|
-
this.logger.log(`Disconnecting ${this.clients.size} database connections`);
|
|
1520
|
-
const disconnectPromises = Array.from(this.clients.entries()).map(async ([key, connection]) => {
|
|
1521
|
-
try {
|
|
1522
|
-
await connection.pool.end();
|
|
1523
|
-
this.logger.debug(`Disconnected: ${key}`);
|
|
1524
|
-
} catch (error) {
|
|
1525
|
-
this.logger.error(`Error disconnecting client: ${key}`, error);
|
|
1526
|
-
}
|
|
1527
|
-
});
|
|
1528
|
-
await Promise.all(disconnectPromises);
|
|
1529
|
-
this.logger.log("All database connections closed");
|
|
1327
|
+
__name(this, "CreateResponseDto");
|
|
1530
1328
|
}
|
|
1329
|
+
success;
|
|
1330
|
+
message;
|
|
1331
|
+
data;
|
|
1531
1332
|
};
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
_ts_metadata7("design:
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1333
|
+
_ts_decorate11([
|
|
1334
|
+
ApiProperty({
|
|
1335
|
+
example: true
|
|
1336
|
+
}),
|
|
1337
|
+
_ts_metadata7("design:type", Boolean)
|
|
1338
|
+
], CreateResponseDto.prototype, "success", void 0);
|
|
1339
|
+
_ts_decorate11([
|
|
1340
|
+
ApiProperty({
|
|
1341
|
+
example: "Resource created successfully"
|
|
1342
|
+
}),
|
|
1343
|
+
_ts_metadata7("design:type", String)
|
|
1344
|
+
], CreateResponseDto.prototype, "message", void 0);
|
|
1345
|
+
_ts_decorate11([
|
|
1346
|
+
ApiProperty(),
|
|
1347
|
+
_ts_metadata7("design:type", typeof T === "undefined" ? Object : T)
|
|
1348
|
+
], CreateResponseDto.prototype, "data", void 0);
|
|
1541
1349
|
|
|
1542
|
-
// src/database/
|
|
1350
|
+
// src/database/dto/import-response.dto.ts
|
|
1351
|
+
import { ApiProperty as ApiProperty2, ApiPropertyOptional } from "@nestjs/swagger";
|
|
1543
1352
|
function _ts_decorate12(decorators, target, key, desc2) {
|
|
1544
1353
|
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
1545
1354
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc2);
|
|
@@ -1547,69 +1356,133 @@ function _ts_decorate12(decorators, target, key, desc2) {
|
|
|
1547
1356
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1548
1357
|
}
|
|
1549
1358
|
__name(_ts_decorate12, "_ts_decorate");
|
|
1550
|
-
|
|
1359
|
+
function _ts_metadata8(k, v) {
|
|
1360
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1361
|
+
}
|
|
1362
|
+
__name(_ts_metadata8, "_ts_metadata");
|
|
1363
|
+
var ValidatedRowDto = class {
|
|
1551
1364
|
static {
|
|
1552
|
-
__name(this, "
|
|
1553
|
-
}
|
|
1554
|
-
// Configures the module for gateway/HTTP mode with TenantContextInterceptor
|
|
1555
|
-
static forServer(options) {
|
|
1556
|
-
return _DatabaseModule.createDynamicModule(options, "server");
|
|
1365
|
+
__name(this, "ValidatedRowDto");
|
|
1557
1366
|
}
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1367
|
+
index;
|
|
1368
|
+
data;
|
|
1369
|
+
valid;
|
|
1370
|
+
errors;
|
|
1371
|
+
};
|
|
1372
|
+
_ts_decorate12([
|
|
1373
|
+
ApiProperty2({
|
|
1374
|
+
example: 1
|
|
1375
|
+
}),
|
|
1376
|
+
_ts_metadata8("design:type", Number)
|
|
1377
|
+
], ValidatedRowDto.prototype, "index", void 0);
|
|
1378
|
+
_ts_decorate12([
|
|
1379
|
+
ApiProperty2({
|
|
1380
|
+
example: {
|
|
1381
|
+
code: "products",
|
|
1382
|
+
name: "Products"
|
|
1383
|
+
}
|
|
1384
|
+
}),
|
|
1385
|
+
_ts_metadata8("design:type", typeof Record === "undefined" ? Object : Record)
|
|
1386
|
+
], ValidatedRowDto.prototype, "data", void 0);
|
|
1387
|
+
_ts_decorate12([
|
|
1388
|
+
ApiProperty2({
|
|
1389
|
+
example: true
|
|
1390
|
+
}),
|
|
1391
|
+
_ts_metadata8("design:type", Boolean)
|
|
1392
|
+
], ValidatedRowDto.prototype, "valid", void 0);
|
|
1393
|
+
_ts_decorate12([
|
|
1394
|
+
ApiProperty2({
|
|
1395
|
+
example: [
|
|
1396
|
+
"Code already exists"
|
|
1397
|
+
]
|
|
1398
|
+
}),
|
|
1399
|
+
_ts_metadata8("design:type", Array)
|
|
1400
|
+
], ValidatedRowDto.prototype, "errors", void 0);
|
|
1401
|
+
var ImportSummaryDto = class {
|
|
1402
|
+
static {
|
|
1403
|
+
__name(this, "ImportSummaryDto");
|
|
1593
1404
|
}
|
|
1405
|
+
total;
|
|
1406
|
+
valid;
|
|
1407
|
+
invalid;
|
|
1594
1408
|
};
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1409
|
+
_ts_decorate12([
|
|
1410
|
+
ApiProperty2({
|
|
1411
|
+
example: 10
|
|
1412
|
+
}),
|
|
1413
|
+
_ts_metadata8("design:type", Number)
|
|
1414
|
+
], ImportSummaryDto.prototype, "total", void 0);
|
|
1415
|
+
_ts_decorate12([
|
|
1416
|
+
ApiProperty2({
|
|
1417
|
+
example: 8
|
|
1418
|
+
}),
|
|
1419
|
+
_ts_metadata8("design:type", Number)
|
|
1420
|
+
], ImportSummaryDto.prototype, "valid", void 0);
|
|
1421
|
+
_ts_decorate12([
|
|
1422
|
+
ApiProperty2({
|
|
1423
|
+
example: 2
|
|
1424
|
+
}),
|
|
1425
|
+
_ts_metadata8("design:type", Number)
|
|
1426
|
+
], ImportSummaryDto.prototype, "invalid", void 0);
|
|
1427
|
+
var ImportResponseDto = class {
|
|
1428
|
+
static {
|
|
1429
|
+
__name(this, "ImportResponseDto");
|
|
1607
1430
|
}
|
|
1608
|
-
|
|
1609
|
-
|
|
1431
|
+
success;
|
|
1432
|
+
message;
|
|
1433
|
+
created;
|
|
1434
|
+
updated;
|
|
1435
|
+
skipped;
|
|
1436
|
+
rows;
|
|
1437
|
+
summary;
|
|
1438
|
+
};
|
|
1439
|
+
_ts_decorate12([
|
|
1440
|
+
ApiProperty2({
|
|
1441
|
+
example: true
|
|
1442
|
+
}),
|
|
1443
|
+
_ts_metadata8("design:type", Boolean)
|
|
1444
|
+
], ImportResponseDto.prototype, "success", void 0);
|
|
1445
|
+
_ts_decorate12([
|
|
1446
|
+
ApiProperty2({
|
|
1447
|
+
example: "Import complete."
|
|
1448
|
+
}),
|
|
1449
|
+
_ts_metadata8("design:type", String)
|
|
1450
|
+
], ImportResponseDto.prototype, "message", void 0);
|
|
1451
|
+
_ts_decorate12([
|
|
1452
|
+
ApiPropertyOptional({
|
|
1453
|
+
example: 3
|
|
1454
|
+
}),
|
|
1455
|
+
_ts_metadata8("design:type", Number)
|
|
1456
|
+
], ImportResponseDto.prototype, "created", void 0);
|
|
1457
|
+
_ts_decorate12([
|
|
1458
|
+
ApiPropertyOptional({
|
|
1459
|
+
example: 2
|
|
1460
|
+
}),
|
|
1461
|
+
_ts_metadata8("design:type", Number)
|
|
1462
|
+
], ImportResponseDto.prototype, "updated", void 0);
|
|
1463
|
+
_ts_decorate12([
|
|
1464
|
+
ApiPropertyOptional({
|
|
1465
|
+
example: 1
|
|
1466
|
+
}),
|
|
1467
|
+
_ts_metadata8("design:type", Number)
|
|
1468
|
+
], ImportResponseDto.prototype, "skipped", void 0);
|
|
1469
|
+
_ts_decorate12([
|
|
1470
|
+
ApiPropertyOptional({
|
|
1471
|
+
type: [
|
|
1472
|
+
ValidatedRowDto
|
|
1473
|
+
]
|
|
1474
|
+
}),
|
|
1475
|
+
_ts_metadata8("design:type", Array)
|
|
1476
|
+
], ImportResponseDto.prototype, "rows", void 0);
|
|
1477
|
+
_ts_decorate12([
|
|
1478
|
+
ApiPropertyOptional({
|
|
1479
|
+
type: ImportSummaryDto
|
|
1480
|
+
}),
|
|
1481
|
+
_ts_metadata8("design:type", typeof ImportSummaryDto === "undefined" ? Object : ImportSummaryDto)
|
|
1482
|
+
], ImportResponseDto.prototype, "summary", void 0);
|
|
1610
1483
|
|
|
1611
1484
|
// src/database/dto/select-options-query.dto.ts
|
|
1612
|
-
import { ApiPropertyOptional } from "@nestjs/swagger";
|
|
1485
|
+
import { ApiPropertyOptional as ApiPropertyOptional2 } from "@nestjs/swagger";
|
|
1613
1486
|
import { Type } from "class-transformer";
|
|
1614
1487
|
import { IsInt, IsOptional, IsString, Min } from "class-validator";
|
|
1615
1488
|
function _ts_decorate13(decorators, target, key, desc2) {
|
|
@@ -1619,10 +1492,10 @@ function _ts_decorate13(decorators, target, key, desc2) {
|
|
|
1619
1492
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1620
1493
|
}
|
|
1621
1494
|
__name(_ts_decorate13, "_ts_decorate");
|
|
1622
|
-
function
|
|
1495
|
+
function _ts_metadata9(k, v) {
|
|
1623
1496
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1624
1497
|
}
|
|
1625
|
-
__name(
|
|
1498
|
+
__name(_ts_metadata9, "_ts_metadata");
|
|
1626
1499
|
var SelectOptionsQueryDto = class {
|
|
1627
1500
|
static {
|
|
1628
1501
|
__name(this, "SelectOptionsQueryDto");
|
|
@@ -1638,16 +1511,16 @@ var SelectOptionsQueryDto = class {
|
|
|
1638
1511
|
groupIdKey;
|
|
1639
1512
|
};
|
|
1640
1513
|
_ts_decorate13([
|
|
1641
|
-
|
|
1514
|
+
ApiPropertyOptional2({
|
|
1642
1515
|
description: "Search term to filter by label",
|
|
1643
1516
|
example: "united"
|
|
1644
1517
|
}),
|
|
1645
1518
|
IsOptional(),
|
|
1646
1519
|
IsString(),
|
|
1647
|
-
|
|
1520
|
+
_ts_metadata9("design:type", String)
|
|
1648
1521
|
], SelectOptionsQueryDto.prototype, "search", void 0);
|
|
1649
1522
|
_ts_decorate13([
|
|
1650
|
-
|
|
1523
|
+
ApiPropertyOptional2({
|
|
1651
1524
|
description: "Maximum number of results",
|
|
1652
1525
|
example: 20,
|
|
1653
1526
|
default: 20
|
|
@@ -1656,10 +1529,10 @@ _ts_decorate13([
|
|
|
1656
1529
|
Type(() => Number),
|
|
1657
1530
|
IsInt(),
|
|
1658
1531
|
Min(1),
|
|
1659
|
-
|
|
1532
|
+
_ts_metadata9("design:type", Number)
|
|
1660
1533
|
], SelectOptionsQueryDto.prototype, "limit", void 0);
|
|
1661
1534
|
_ts_decorate13([
|
|
1662
|
-
|
|
1535
|
+
ApiPropertyOptional2({
|
|
1663
1536
|
description: "Number of results to skip",
|
|
1664
1537
|
example: 0,
|
|
1665
1538
|
default: 0
|
|
@@ -1668,67 +1541,67 @@ _ts_decorate13([
|
|
|
1668
1541
|
Type(() => Number),
|
|
1669
1542
|
IsInt(),
|
|
1670
1543
|
Min(0),
|
|
1671
|
-
|
|
1544
|
+
_ts_metadata9("design:type", Number)
|
|
1672
1545
|
], SelectOptionsQueryDto.prototype, "offset", void 0);
|
|
1673
1546
|
_ts_decorate13([
|
|
1674
|
-
|
|
1547
|
+
ApiPropertyOptional2({
|
|
1675
1548
|
description: "Comma-separated values to fetch specific options",
|
|
1676
1549
|
example: "1,2,3"
|
|
1677
1550
|
}),
|
|
1678
1551
|
IsOptional(),
|
|
1679
1552
|
IsString(),
|
|
1680
|
-
|
|
1553
|
+
_ts_metadata9("design:type", String)
|
|
1681
1554
|
], SelectOptionsQueryDto.prototype, "values", void 0);
|
|
1682
1555
|
_ts_decorate13([
|
|
1683
|
-
|
|
1556
|
+
ApiPropertyOptional2({
|
|
1684
1557
|
description: "Comma-separated IDs to exclude from results (already selected)",
|
|
1685
1558
|
example: "5,10"
|
|
1686
1559
|
}),
|
|
1687
1560
|
IsOptional(),
|
|
1688
1561
|
IsString(),
|
|
1689
|
-
|
|
1562
|
+
_ts_metadata9("design:type", String)
|
|
1690
1563
|
], SelectOptionsQueryDto.prototype, "excludeIds", void 0);
|
|
1691
1564
|
_ts_decorate13([
|
|
1692
|
-
|
|
1565
|
+
ApiPropertyOptional2({
|
|
1693
1566
|
description: "Column name for option value",
|
|
1694
1567
|
example: "id",
|
|
1695
1568
|
default: "id"
|
|
1696
1569
|
}),
|
|
1697
1570
|
IsOptional(),
|
|
1698
1571
|
IsString(),
|
|
1699
|
-
|
|
1572
|
+
_ts_metadata9("design:type", String)
|
|
1700
1573
|
], SelectOptionsQueryDto.prototype, "valueKey", void 0);
|
|
1701
1574
|
_ts_decorate13([
|
|
1702
|
-
|
|
1575
|
+
ApiPropertyOptional2({
|
|
1703
1576
|
description: "Column name for option label",
|
|
1704
1577
|
example: "name",
|
|
1705
1578
|
default: "name"
|
|
1706
1579
|
}),
|
|
1707
1580
|
IsOptional(),
|
|
1708
1581
|
IsString(),
|
|
1709
|
-
|
|
1582
|
+
_ts_metadata9("design:type", String)
|
|
1710
1583
|
], SelectOptionsQueryDto.prototype, "labelKey", void 0);
|
|
1711
1584
|
_ts_decorate13([
|
|
1712
|
-
|
|
1585
|
+
ApiPropertyOptional2({
|
|
1713
1586
|
description: "Column name for option description",
|
|
1714
1587
|
example: "description"
|
|
1715
1588
|
}),
|
|
1716
1589
|
IsOptional(),
|
|
1717
1590
|
IsString(),
|
|
1718
|
-
|
|
1591
|
+
_ts_metadata9("design:type", String)
|
|
1719
1592
|
], SelectOptionsQueryDto.prototype, "descriptionKey", void 0);
|
|
1720
1593
|
_ts_decorate13([
|
|
1721
|
-
|
|
1594
|
+
ApiPropertyOptional2({
|
|
1722
1595
|
description: "Column name for group ID",
|
|
1723
1596
|
example: "regionId"
|
|
1724
1597
|
}),
|
|
1725
1598
|
IsOptional(),
|
|
1726
1599
|
IsString(),
|
|
1727
|
-
|
|
1600
|
+
_ts_metadata9("design:type", String)
|
|
1728
1601
|
], SelectOptionsQueryDto.prototype, "groupIdKey", void 0);
|
|
1729
1602
|
|
|
1730
1603
|
// src/database/dto/success-response.dto.ts
|
|
1731
|
-
import { ApiProperty } from "@nestjs/swagger";
|
|
1604
|
+
import { ApiProperty as ApiProperty3 } from "@nestjs/swagger";
|
|
1732
1605
|
import { IsBoolean, IsNotEmpty, IsString as IsString2 } from "class-validator";
|
|
1733
1606
|
function _ts_decorate14(decorators, target, key, desc2) {
|
|
1734
1607
|
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
@@ -1737,10 +1610,10 @@ function _ts_decorate14(decorators, target, key, desc2) {
|
|
|
1737
1610
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1738
1611
|
}
|
|
1739
1612
|
__name(_ts_decorate14, "_ts_decorate");
|
|
1740
|
-
function
|
|
1613
|
+
function _ts_metadata10(k, v) {
|
|
1741
1614
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1742
1615
|
}
|
|
1743
|
-
__name(
|
|
1616
|
+
__name(_ts_metadata10, "_ts_metadata");
|
|
1744
1617
|
var SuccessResponseDto = class {
|
|
1745
1618
|
static {
|
|
1746
1619
|
__name(this, "SuccessResponseDto");
|
|
@@ -1749,24 +1622,24 @@ var SuccessResponseDto = class {
|
|
|
1749
1622
|
message;
|
|
1750
1623
|
};
|
|
1751
1624
|
_ts_decorate14([
|
|
1752
|
-
|
|
1625
|
+
ApiProperty3({
|
|
1753
1626
|
example: true
|
|
1754
1627
|
}),
|
|
1755
1628
|
IsNotEmpty(),
|
|
1756
1629
|
IsBoolean(),
|
|
1757
|
-
|
|
1630
|
+
_ts_metadata10("design:type", Boolean)
|
|
1758
1631
|
], SuccessResponseDto.prototype, "success", void 0);
|
|
1759
1632
|
_ts_decorate14([
|
|
1760
|
-
|
|
1633
|
+
ApiProperty3({
|
|
1761
1634
|
example: "Operation completed successfully"
|
|
1762
1635
|
}),
|
|
1763
1636
|
IsNotEmpty(),
|
|
1764
1637
|
IsString2(),
|
|
1765
|
-
|
|
1638
|
+
_ts_metadata10("design:type", String)
|
|
1766
1639
|
], SuccessResponseDto.prototype, "message", void 0);
|
|
1767
1640
|
|
|
1768
1641
|
// src/database/dto/table-response.dto.ts
|
|
1769
|
-
import { ApiProperty as
|
|
1642
|
+
import { ApiProperty as ApiProperty4, ApiPropertyOptional as ApiPropertyOptional3 } from "@nestjs/swagger";
|
|
1770
1643
|
function _ts_decorate15(decorators, target, key, desc2) {
|
|
1771
1644
|
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
1772
1645
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc2);
|
|
@@ -1774,10 +1647,10 @@ function _ts_decorate15(decorators, target, key, desc2) {
|
|
|
1774
1647
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1775
1648
|
}
|
|
1776
1649
|
__name(_ts_decorate15, "_ts_decorate");
|
|
1777
|
-
function
|
|
1650
|
+
function _ts_metadata11(k, v) {
|
|
1778
1651
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1779
1652
|
}
|
|
1780
|
-
__name(
|
|
1653
|
+
__name(_ts_metadata11, "_ts_metadata");
|
|
1781
1654
|
var TableResponseDto = class {
|
|
1782
1655
|
static {
|
|
1783
1656
|
__name(this, "TableResponseDto");
|
|
@@ -1788,26 +1661,26 @@ var TableResponseDto = class {
|
|
|
1788
1661
|
activeViewId;
|
|
1789
1662
|
};
|
|
1790
1663
|
_ts_decorate15([
|
|
1791
|
-
|
|
1792
|
-
|
|
1664
|
+
ApiProperty4(),
|
|
1665
|
+
_ts_metadata11("design:type", Array)
|
|
1793
1666
|
], TableResponseDto.prototype, "result", void 0);
|
|
1794
1667
|
_ts_decorate15([
|
|
1795
|
-
|
|
1796
|
-
|
|
1668
|
+
ApiProperty4(),
|
|
1669
|
+
_ts_metadata11("design:type", Number)
|
|
1797
1670
|
], TableResponseDto.prototype, "count", void 0);
|
|
1798
1671
|
_ts_decorate15([
|
|
1799
|
-
|
|
1800
|
-
|
|
1672
|
+
ApiProperty4(),
|
|
1673
|
+
_ts_metadata11("design:type", typeof TableViewState === "undefined" ? Object : TableViewState)
|
|
1801
1674
|
], TableResponseDto.prototype, "state", void 0);
|
|
1802
1675
|
_ts_decorate15([
|
|
1803
|
-
|
|
1676
|
+
ApiPropertyOptional3({
|
|
1804
1677
|
nullable: true
|
|
1805
1678
|
}),
|
|
1806
|
-
|
|
1679
|
+
_ts_metadata11("design:type", Object)
|
|
1807
1680
|
], TableResponseDto.prototype, "activeViewId", void 0);
|
|
1808
1681
|
|
|
1809
1682
|
// src/database/filter/filter.processor.ts
|
|
1810
|
-
import { and, asc, desc, eq
|
|
1683
|
+
import { and, asc, desc, eq, gt, gte, ilike, lt, lte, ne, notIlike, or } from "drizzle-orm";
|
|
1811
1684
|
var FilterProcessor = class {
|
|
1812
1685
|
static {
|
|
1813
1686
|
__name(this, "FilterProcessor");
|
|
@@ -1825,10 +1698,10 @@ var FilterProcessor = class {
|
|
|
1825
1698
|
switch (f.operator) {
|
|
1826
1699
|
case "equals":
|
|
1827
1700
|
if (def.type === "boolean") return [
|
|
1828
|
-
|
|
1701
|
+
eq(col, val === "true" || val === 1)
|
|
1829
1702
|
];
|
|
1830
1703
|
return [
|
|
1831
|
-
|
|
1704
|
+
eq(col, val)
|
|
1832
1705
|
];
|
|
1833
1706
|
case "notEquals":
|
|
1834
1707
|
if (def.type === "boolean") return [
|
|
@@ -1872,7 +1745,7 @@ var FilterProcessor = class {
|
|
|
1872
1745
|
if (!search?.value) return void 0;
|
|
1873
1746
|
if (search.columnId === "all") {
|
|
1874
1747
|
const conditions = Object.values(fieldMap).filter((def2) => "column" in def2 && def2.type === "string").map((def2) => ilike(def2.column, `%${search.value}%`));
|
|
1875
|
-
return conditions.length ?
|
|
1748
|
+
return conditions.length ? or(...conditions) : void 0;
|
|
1876
1749
|
}
|
|
1877
1750
|
const def = fieldMap[search.columnId];
|
|
1878
1751
|
if (!def || !("column" in def)) return void 0;
|
|
@@ -1891,8 +1764,8 @@ var FilterProcessor = class {
|
|
|
1891
1764
|
};
|
|
1892
1765
|
|
|
1893
1766
|
// src/database/repositories/primary-base.repository.ts
|
|
1894
|
-
import { Logger as
|
|
1895
|
-
import { and as and2, asc as asc2, eq as
|
|
1767
|
+
import { Logger as Logger7 } from "@nestjs/common";
|
|
1768
|
+
import { and as and2, asc as asc2, eq as eq2, getTableName, ilike as ilike2, inArray, notInArray, sql } from "drizzle-orm";
|
|
1896
1769
|
function snakeToCamel(str) {
|
|
1897
1770
|
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
1898
1771
|
}
|
|
@@ -1923,14 +1796,15 @@ var PrimaryBaseRepository = class {
|
|
|
1923
1796
|
this.table = table;
|
|
1924
1797
|
const dbTableName = getTableName(table);
|
|
1925
1798
|
this.tableName = snakeToCamel(dbTableName);
|
|
1926
|
-
this.logger = new
|
|
1799
|
+
this.logger = new Logger7(this.constructor.name);
|
|
1927
1800
|
this.logger.debug(`Initialized ${this.constructor.name}`);
|
|
1928
1801
|
this.logger.debug(`Table name: '${dbTableName}' -> query key: '${this.tableName}'`);
|
|
1929
1802
|
}
|
|
1930
1803
|
// Creates a new record and returns it
|
|
1931
|
-
async create(data) {
|
|
1804
|
+
async create(data, tx) {
|
|
1932
1805
|
this.logger.log("Creating record");
|
|
1933
|
-
const
|
|
1806
|
+
const db = tx ?? this.db;
|
|
1807
|
+
const results = await db.insert(this.table).values(data).returning();
|
|
1934
1808
|
const record = results[0];
|
|
1935
1809
|
if (!record) throw new Error(`${this.tableName}: database operation returned no record`);
|
|
1936
1810
|
return record;
|
|
@@ -1956,16 +1830,35 @@ var PrimaryBaseRepository = class {
|
|
|
1956
1830
|
this.logger.debug("Finding multiple records");
|
|
1957
1831
|
return this.model.findMany(options);
|
|
1958
1832
|
}
|
|
1959
|
-
// Builds a select query with optional custom fields,
|
|
1833
|
+
// Builds a select query with optional custom fields, joins, filter, grouping, ordering, and pagination
|
|
1960
1834
|
buildSelectQuery(options) {
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1835
|
+
let query = (options?.select ? this.db.select(options.select).from(this.table) : this.db.select().from(this.table)).$dynamic();
|
|
1836
|
+
if (options?.leftJoin) {
|
|
1837
|
+
query = query.leftJoin(options.leftJoin.table, options.leftJoin.on);
|
|
1838
|
+
}
|
|
1839
|
+
if (options?.leftJoins) {
|
|
1840
|
+
for (const join of options.leftJoins) {
|
|
1841
|
+
query = query.leftJoin(join.table, join.on);
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
if (options?.where) {
|
|
1845
|
+
query = query.where(options.where);
|
|
1846
|
+
}
|
|
1847
|
+
if (options?.groupBy?.length) {
|
|
1848
|
+
query = query.groupBy(...options.groupBy);
|
|
1849
|
+
}
|
|
1850
|
+
if (options?.orderBy?.length) {
|
|
1851
|
+
query = query.orderBy(...options.orderBy);
|
|
1852
|
+
}
|
|
1853
|
+
if (options?.limit) {
|
|
1854
|
+
query = query.limit(options.limit);
|
|
1855
|
+
}
|
|
1856
|
+
if (options?.offset) {
|
|
1857
|
+
query = query.offset(options.offset);
|
|
1858
|
+
}
|
|
1859
|
+
return query;
|
|
1860
|
+
}
|
|
1861
|
+
// Returns paginated result and total count, with optional custom select, LEFT JOINs, GROUP BY, and ordering
|
|
1969
1862
|
async findAllAndCount(options) {
|
|
1970
1863
|
const [count, result] = await Promise.all([
|
|
1971
1864
|
this.count(options?.where),
|
|
@@ -1977,37 +1870,41 @@ var PrimaryBaseRepository = class {
|
|
|
1977
1870
|
};
|
|
1978
1871
|
}
|
|
1979
1872
|
// Updates a record by ID and returns the updated record
|
|
1980
|
-
async update(id, data) {
|
|
1873
|
+
async update(id, data, tx) {
|
|
1981
1874
|
this.logger.log(`Updating record with ID: ${id}`);
|
|
1875
|
+
const db = tx ?? this.db;
|
|
1982
1876
|
const idColumn = this.table.id;
|
|
1983
1877
|
if (!idColumn) throw new Error(`Table '${this.tableName}' has no 'id' column`);
|
|
1984
|
-
const results = await
|
|
1878
|
+
const results = await db.update(this.table).set(data).where(eq2(idColumn, id)).returning();
|
|
1985
1879
|
const record = results[0];
|
|
1986
1880
|
if (!record) throw new Error(`${this.tableName}: database operation returned no record`);
|
|
1987
1881
|
return record;
|
|
1988
1882
|
}
|
|
1989
1883
|
// Updates all records matching the SQL condition and returns the affected count
|
|
1990
|
-
async updateMany(where, data) {
|
|
1884
|
+
async updateMany(where, data, tx) {
|
|
1991
1885
|
this.logger.log("Updating multiple records");
|
|
1992
|
-
const
|
|
1886
|
+
const db = tx ?? this.db;
|
|
1887
|
+
const result = await db.update(this.table).set(data).where(where);
|
|
1993
1888
|
return {
|
|
1994
1889
|
count: result.rowCount ?? 0
|
|
1995
1890
|
};
|
|
1996
1891
|
}
|
|
1997
1892
|
// Deletes a record by ID and returns the deleted record
|
|
1998
|
-
async delete(id) {
|
|
1893
|
+
async delete(id, tx) {
|
|
1999
1894
|
this.logger.log(`Deleting record with ID: ${id}`);
|
|
1895
|
+
const db = tx ?? this.db;
|
|
2000
1896
|
const idColumn = this.table.id;
|
|
2001
1897
|
if (!idColumn) throw new Error(`Table '${this.tableName}' has no 'id' column`);
|
|
2002
|
-
const results = await
|
|
1898
|
+
const results = await db.delete(this.table).where(eq2(idColumn, id)).returning();
|
|
2003
1899
|
const record = results[0];
|
|
2004
1900
|
if (!record) throw new Error(`${this.tableName}: database operation returned no record`);
|
|
2005
1901
|
return record;
|
|
2006
1902
|
}
|
|
2007
1903
|
// Deletes all records matching the SQL condition and returns the affected count
|
|
2008
|
-
async deleteMany(where) {
|
|
1904
|
+
async deleteMany(where, tx) {
|
|
2009
1905
|
this.logger.log("Deleting multiple records");
|
|
2010
|
-
const
|
|
1906
|
+
const db = tx ?? this.db;
|
|
1907
|
+
const result = await db.delete(this.table).where(where);
|
|
2011
1908
|
return {
|
|
2012
1909
|
count: result.rowCount ?? 0
|
|
2013
1910
|
};
|
|
@@ -2029,9 +1926,14 @@ var PrimaryBaseRepository = class {
|
|
|
2029
1926
|
const count = await this.count(where);
|
|
2030
1927
|
return count > 0;
|
|
2031
1928
|
}
|
|
1929
|
+
// Executes the callback within a database transaction
|
|
1930
|
+
async transaction(callback) {
|
|
1931
|
+
return this.db.transaction(callback);
|
|
1932
|
+
}
|
|
2032
1933
|
// Finds records formatted as select dropdown options with optional search, pagination, and grouping
|
|
2033
1934
|
async findForSelect(config) {
|
|
2034
1935
|
this.logger.debug("Finding records for select dropdown");
|
|
1936
|
+
const selectFn = config.distinct ? this.db.selectDistinct.bind(this.db) : this.db.select.bind(this.db);
|
|
2035
1937
|
const parsedValues = typeof config.values === "string" ? config.values.split(",").map((v) => v.trim()).filter(Boolean) : config.values;
|
|
2036
1938
|
const parsedExcludeIds = typeof config.excludeIds === "string" ? config.excludeIds.split(",").map((v) => v.trim()).filter(Boolean) : config.excludeIds ?? [];
|
|
2037
1939
|
const tableColumns = this.table;
|
|
@@ -2059,7 +1961,7 @@ var PrimaryBaseRepository = class {
|
|
|
2059
1961
|
const groupIdCol = tableColumns[config.groupId];
|
|
2060
1962
|
if (groupIdCol) selectCols.groupId = groupIdCol;
|
|
2061
1963
|
}
|
|
2062
|
-
let valuesQuery =
|
|
1964
|
+
let valuesQuery = selectFn(selectCols).from(this.table).$dynamic();
|
|
2063
1965
|
if (config.joins) {
|
|
2064
1966
|
for (const join of config.joins) {
|
|
2065
1967
|
if (join.type === "inner") {
|
|
@@ -2108,7 +2010,7 @@ var PrimaryBaseRepository = class {
|
|
|
2108
2010
|
for (const [field, val] of Object.entries(config.where)) {
|
|
2109
2011
|
const column = tableColumns[field];
|
|
2110
2012
|
if (column) {
|
|
2111
|
-
conditions.push(
|
|
2013
|
+
conditions.push(eq2(column, val));
|
|
2112
2014
|
}
|
|
2113
2015
|
}
|
|
2114
2016
|
}
|
|
@@ -2119,7 +2021,7 @@ var PrimaryBaseRepository = class {
|
|
|
2119
2021
|
const orderByCol = orderByKey ? tableColumns[orderByKey] ?? labelCol : labelCol;
|
|
2120
2022
|
const limit = Number(config.limit) || 20;
|
|
2121
2023
|
const offset = Number(config.offset) || 0;
|
|
2122
|
-
let query =
|
|
2024
|
+
let query = selectFn(selectFields).from(this.table).$dynamic();
|
|
2123
2025
|
if (config.joins) {
|
|
2124
2026
|
for (const join of config.joins) {
|
|
2125
2027
|
if (join.type === "inner") {
|
|
@@ -2180,241 +2082,13 @@ var PrimaryBaseRepository = class {
|
|
|
2180
2082
|
}
|
|
2181
2083
|
};
|
|
2182
2084
|
|
|
2183
|
-
// src/database/repositories/tenant-base.repository.ts
|
|
2184
|
-
import { Logger as Logger9 } from "@nestjs/common";
|
|
2185
|
-
import { and as and3, asc as asc3, eq as eq4, getTableName as getTableName2, ilike as ilike3, inArray as inArray2, notInArray as notInArray2, sql as sql2 } from "drizzle-orm";
|
|
2186
|
-
var TenantBaseRepository = class {
|
|
2187
|
-
static {
|
|
2188
|
-
__name(this, "TenantBaseRepository");
|
|
2189
|
-
}
|
|
2190
|
-
database;
|
|
2191
|
-
table;
|
|
2192
|
-
logger;
|
|
2193
|
-
tableName;
|
|
2194
|
-
get db() {
|
|
2195
|
-
return this.database.drizzleClient;
|
|
2196
|
-
}
|
|
2197
|
-
get model() {
|
|
2198
|
-
return this.database.drizzleClient.query[this.tableName];
|
|
2199
|
-
}
|
|
2200
|
-
constructor(database, table) {
|
|
2201
|
-
this.database = database;
|
|
2202
|
-
this.table = table;
|
|
2203
|
-
this.tableName = getTableName2(table);
|
|
2204
|
-
this.logger = new Logger9(this.constructor.name);
|
|
2205
|
-
this.logger.debug(`Initialized ${this.constructor.name}`);
|
|
2206
|
-
}
|
|
2207
|
-
// Creates a new record and returns it
|
|
2208
|
-
async create(data) {
|
|
2209
|
-
this.logger.log("Creating record");
|
|
2210
|
-
const results = await this.db.insert(this.table).values(data).returning();
|
|
2211
|
-
const record = results[0];
|
|
2212
|
-
if (!record) throw new Error(`${this.tableName}: database operation returned no record`);
|
|
2213
|
-
return record;
|
|
2214
|
-
}
|
|
2215
|
-
// Finds a single record by primary key ID
|
|
2216
|
-
async findById(id) {
|
|
2217
|
-
this.logger.debug(`Finding record by ID: ${id}`);
|
|
2218
|
-
const idColumn = this.table.id;
|
|
2219
|
-
if (!idColumn) throw new Error(`Table '${this.tableName}' has no 'id' column`);
|
|
2220
|
-
const results = await this.db.select().from(this.table).where(eq4(idColumn, id)).limit(1);
|
|
2221
|
-
return results[0] ?? null;
|
|
2222
|
-
}
|
|
2223
|
-
// Finds a single record matching the given SQL condition
|
|
2224
|
-
async findOne(where) {
|
|
2225
|
-
this.logger.debug("Finding record with custom query");
|
|
2226
|
-
const results = await this.db.select().from(this.table).where(where).limit(1);
|
|
2227
|
-
return results[0] ?? null;
|
|
2228
|
-
}
|
|
2229
|
-
// Finds multiple records with optional SQL filtering, ordering, and pagination
|
|
2230
|
-
async findMany(options) {
|
|
2231
|
-
this.logger.debug("Finding multiple records");
|
|
2232
|
-
let query = this.db.select().from(this.table).$dynamic();
|
|
2233
|
-
if (options?.where) {
|
|
2234
|
-
query = query.where(options.where);
|
|
2235
|
-
}
|
|
2236
|
-
if (options?.orderBy) {
|
|
2237
|
-
query = query.orderBy(options.orderBy);
|
|
2238
|
-
}
|
|
2239
|
-
if (options?.limit) {
|
|
2240
|
-
query = query.limit(options.limit);
|
|
2241
|
-
}
|
|
2242
|
-
if (options?.offset) {
|
|
2243
|
-
query = query.offset(options.offset);
|
|
2244
|
-
}
|
|
2245
|
-
return await query;
|
|
2246
|
-
}
|
|
2247
|
-
// Updates a record by ID and returns the updated record
|
|
2248
|
-
async update(id, data) {
|
|
2249
|
-
this.logger.log(`Updating record with ID: ${id}`);
|
|
2250
|
-
const idColumn = this.table.id;
|
|
2251
|
-
if (!idColumn) throw new Error(`Table '${this.tableName}' has no 'id' column`);
|
|
2252
|
-
const results = await this.db.update(this.table).set(data).where(eq4(idColumn, id)).returning();
|
|
2253
|
-
const record = results[0];
|
|
2254
|
-
if (!record) throw new Error(`${this.tableName}: database operation returned no record`);
|
|
2255
|
-
return record;
|
|
2256
|
-
}
|
|
2257
|
-
// Updates all records matching the SQL condition and returns the affected count
|
|
2258
|
-
async updateMany(where, data) {
|
|
2259
|
-
this.logger.log("Updating multiple records");
|
|
2260
|
-
const result = await this.db.update(this.table).set(data).where(where);
|
|
2261
|
-
return {
|
|
2262
|
-
count: result.rowCount ?? 0
|
|
2263
|
-
};
|
|
2264
|
-
}
|
|
2265
|
-
// Deletes a record by ID and returns the deleted record
|
|
2266
|
-
async delete(id) {
|
|
2267
|
-
this.logger.log(`Deleting record with ID: ${id}`);
|
|
2268
|
-
const idColumn = this.table.id;
|
|
2269
|
-
if (!idColumn) throw new Error(`Table '${this.tableName}' has no 'id' column`);
|
|
2270
|
-
const results = await this.db.delete(this.table).where(eq4(idColumn, id)).returning();
|
|
2271
|
-
const record = results[0];
|
|
2272
|
-
if (!record) throw new Error(`${this.tableName}: database operation returned no record`);
|
|
2273
|
-
return record;
|
|
2274
|
-
}
|
|
2275
|
-
// Deletes all records matching the SQL condition and returns the affected count
|
|
2276
|
-
async deleteMany(where) {
|
|
2277
|
-
this.logger.log("Deleting multiple records");
|
|
2278
|
-
const result = await this.db.delete(this.table).where(where);
|
|
2279
|
-
return {
|
|
2280
|
-
count: result.rowCount ?? 0
|
|
2281
|
-
};
|
|
2282
|
-
}
|
|
2283
|
-
// Counts records matching the optional SQL condition
|
|
2284
|
-
async count(where) {
|
|
2285
|
-
this.logger.debug("Counting records");
|
|
2286
|
-
let query = this.db.select({
|
|
2287
|
-
count: sql2`count(*)::int`
|
|
2288
|
-
}).from(this.table).$dynamic();
|
|
2289
|
-
if (where) {
|
|
2290
|
-
query = query.where(where);
|
|
2291
|
-
}
|
|
2292
|
-
const results = await query;
|
|
2293
|
-
return results[0].count;
|
|
2294
|
-
}
|
|
2295
|
-
// Returns true if at least one record matches the SQL condition
|
|
2296
|
-
async exists(where) {
|
|
2297
|
-
const count = await this.count(where);
|
|
2298
|
-
return count > 0;
|
|
2299
|
-
}
|
|
2300
|
-
// Finds records formatted as select dropdown options with optional search, pagination, and grouping
|
|
2301
|
-
async findForSelect(config) {
|
|
2302
|
-
this.logger.debug("Finding records for select dropdown");
|
|
2303
|
-
const parsedValues = typeof config.values === "string" ? config.values.split(",").map((v) => v.trim()).filter(Boolean) : config.values;
|
|
2304
|
-
const parsedExcludeIds = typeof config.excludeIds === "string" ? config.excludeIds.split(",").map((v) => v.trim()).filter(Boolean) : config.excludeIds ?? [];
|
|
2305
|
-
const tableColumns = this.table;
|
|
2306
|
-
const valueCol = tableColumns[config.value];
|
|
2307
|
-
if (!valueCol) throw new Error(`Column '${config.value}' not found in table '${this.tableName}'`);
|
|
2308
|
-
const labelCol = tableColumns[config.label];
|
|
2309
|
-
if (!labelCol) throw new Error(`Column '${config.label}' not found in table '${this.tableName}'`);
|
|
2310
|
-
if (parsedValues && parsedValues.length > 0) {
|
|
2311
|
-
const selectCols = {
|
|
2312
|
-
value: valueCol,
|
|
2313
|
-
label: labelCol
|
|
2314
|
-
};
|
|
2315
|
-
if (config.groupId) {
|
|
2316
|
-
const groupIdCol = tableColumns[config.groupId];
|
|
2317
|
-
if (groupIdCol) selectCols.groupId = groupIdCol;
|
|
2318
|
-
}
|
|
2319
|
-
const rows2 = await this.db.select(selectCols).from(this.table).where(inArray2(valueCol, parsedValues));
|
|
2320
|
-
return {
|
|
2321
|
-
options: rows2.map((row) => ({
|
|
2322
|
-
value: row.value,
|
|
2323
|
-
label: String(row.label),
|
|
2324
|
-
...config.groupId && row.groupId != null ? {
|
|
2325
|
-
groupId: row.groupId
|
|
2326
|
-
} : {}
|
|
2327
|
-
})),
|
|
2328
|
-
hasMore: false,
|
|
2329
|
-
...config.groups ? {
|
|
2330
|
-
groups: config.groups
|
|
2331
|
-
} : {}
|
|
2332
|
-
};
|
|
2333
|
-
}
|
|
2334
|
-
const selectFields = {
|
|
2335
|
-
value: valueCol,
|
|
2336
|
-
label: labelCol,
|
|
2337
|
-
totalCount: sql2`count(*) over()`.mapWith(Number)
|
|
2338
|
-
};
|
|
2339
|
-
if (config.groupId) {
|
|
2340
|
-
const groupIdCol = tableColumns[config.groupId];
|
|
2341
|
-
if (groupIdCol) selectFields.groupId = groupIdCol;
|
|
2342
|
-
}
|
|
2343
|
-
const conditions = [];
|
|
2344
|
-
if (config.search) {
|
|
2345
|
-
conditions.push(ilike3(labelCol, `%${config.search}%`));
|
|
2346
|
-
}
|
|
2347
|
-
if (parsedExcludeIds.length > 0) {
|
|
2348
|
-
conditions.push(notInArray2(valueCol, parsedExcludeIds));
|
|
2349
|
-
}
|
|
2350
|
-
if (config.where) {
|
|
2351
|
-
for (const [field, val] of Object.entries(config.where)) {
|
|
2352
|
-
const column = tableColumns[field];
|
|
2353
|
-
if (column) {
|
|
2354
|
-
conditions.push(eq4(column, val));
|
|
2355
|
-
}
|
|
2356
|
-
}
|
|
2357
|
-
}
|
|
2358
|
-
const orderByKey = config.orderBy ? Object.keys(config.orderBy)[0] : void 0;
|
|
2359
|
-
const orderByCol = orderByKey ? tableColumns[orderByKey] ?? labelCol : labelCol;
|
|
2360
|
-
const limit = Number(config.limit) || 20;
|
|
2361
|
-
const offset = Number(config.offset) || 0;
|
|
2362
|
-
let query = this.db.select(selectFields).from(this.table).$dynamic();
|
|
2363
|
-
if (conditions.length > 0) {
|
|
2364
|
-
query = query.where(conditions.length === 1 ? conditions[0] : and3(...conditions));
|
|
2365
|
-
}
|
|
2366
|
-
const orderClauses = [];
|
|
2367
|
-
if (config.groupId) {
|
|
2368
|
-
const groupIdCol = tableColumns[config.groupId];
|
|
2369
|
-
if (groupIdCol) orderClauses.push(asc3(groupIdCol));
|
|
2370
|
-
}
|
|
2371
|
-
orderClauses.push(asc3(orderByCol));
|
|
2372
|
-
query = query.orderBy(...orderClauses).limit(limit).offset(offset);
|
|
2373
|
-
const rows = await query;
|
|
2374
|
-
const totalCount = rows.length > 0 ? rows[0].totalCount : 0;
|
|
2375
|
-
const options = rows.map((row) => ({
|
|
2376
|
-
value: row.value,
|
|
2377
|
-
label: String(row.label),
|
|
2378
|
-
...config.groupId && row.groupId != null ? {
|
|
2379
|
-
groupId: row.groupId
|
|
2380
|
-
} : {}
|
|
2381
|
-
}));
|
|
2382
|
-
let resolvedGroups = config.groups;
|
|
2383
|
-
if (config.groupTable && config.groupId) {
|
|
2384
|
-
const groupTableColumns = config.groupTable;
|
|
2385
|
-
const groupIdKey = config.groupIdKey ?? "id";
|
|
2386
|
-
const groupNameKey = config.groupLabelKey ?? "name";
|
|
2387
|
-
const groupIdCol = groupTableColumns[groupIdKey];
|
|
2388
|
-
if (!groupIdCol) throw new Error(`Column '${groupIdKey}' not found in group table`);
|
|
2389
|
-
const groupNameCol = groupTableColumns[groupNameKey];
|
|
2390
|
-
if (!groupNameCol) throw new Error(`Column '${groupNameKey}' not found in group table`);
|
|
2391
|
-
const groupRows = await this.db.select({
|
|
2392
|
-
id: groupIdCol,
|
|
2393
|
-
name: groupNameCol
|
|
2394
|
-
}).from(config.groupTable).orderBy(asc3(groupNameCol));
|
|
2395
|
-
resolvedGroups = groupRows.map((r) => ({
|
|
2396
|
-
id: r.id,
|
|
2397
|
-
name: String(r.name)
|
|
2398
|
-
}));
|
|
2399
|
-
}
|
|
2400
|
-
return {
|
|
2401
|
-
options,
|
|
2402
|
-
hasMore: offset + limit < totalCount,
|
|
2403
|
-
totalCount,
|
|
2404
|
-
...resolvedGroups ? {
|
|
2405
|
-
groups: resolvedGroups
|
|
2406
|
-
} : {}
|
|
2407
|
-
};
|
|
2408
|
-
}
|
|
2409
|
-
};
|
|
2410
|
-
|
|
2411
2085
|
// src/email/email.module.ts
|
|
2412
2086
|
import { Global as Global4, Module as Module5 } from "@nestjs/common";
|
|
2413
2087
|
import { ConfigModule as ConfigModule3 } from "@nestjs/config";
|
|
2414
2088
|
|
|
2415
2089
|
// src/email/email.service.ts
|
|
2416
2090
|
import { BrevoClient, BrevoError, BrevoTimeoutError } from "@getbrevo/brevo";
|
|
2417
|
-
import { Injectable as
|
|
2091
|
+
import { Injectable as Injectable7, Logger as Logger8 } from "@nestjs/common";
|
|
2418
2092
|
import { ConfigService as ConfigService5 } from "@nestjs/config";
|
|
2419
2093
|
function _ts_decorate16(decorators, target, key, desc2) {
|
|
2420
2094
|
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
@@ -2423,16 +2097,16 @@ function _ts_decorate16(decorators, target, key, desc2) {
|
|
|
2423
2097
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2424
2098
|
}
|
|
2425
2099
|
__name(_ts_decorate16, "_ts_decorate");
|
|
2426
|
-
function
|
|
2100
|
+
function _ts_metadata12(k, v) {
|
|
2427
2101
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2428
2102
|
}
|
|
2429
|
-
__name(
|
|
2103
|
+
__name(_ts_metadata12, "_ts_metadata");
|
|
2430
2104
|
var EmailService = class _EmailService {
|
|
2431
2105
|
static {
|
|
2432
2106
|
__name(this, "EmailService");
|
|
2433
2107
|
}
|
|
2434
2108
|
configService;
|
|
2435
|
-
logger = new
|
|
2109
|
+
logger = new Logger8(_EmailService.name);
|
|
2436
2110
|
brevoClient;
|
|
2437
2111
|
senderEmail;
|
|
2438
2112
|
senderName;
|
|
@@ -2865,44 +2539,133 @@ This is an automated message, please do not reply.
|
|
|
2865
2539
|
});
|
|
2866
2540
|
this.logger.log(`Email revert confirmation sent to ${email}`);
|
|
2867
2541
|
}
|
|
2868
|
-
//
|
|
2869
|
-
async
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2542
|
+
// Sends an invite email to a new portal user with their set-password link
|
|
2543
|
+
async sendInviteEmail(params) {
|
|
2544
|
+
const { to, name, inviteUrl } = params;
|
|
2545
|
+
const subject = "You have been invited to Vritti AI";
|
|
2546
|
+
const htmlContent = `
|
|
2547
|
+
<!DOCTYPE html>
|
|
2548
|
+
<html>
|
|
2549
|
+
<head>
|
|
2550
|
+
<meta charset="UTF-8">
|
|
2551
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2552
|
+
</head>
|
|
2553
|
+
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f5f5f5;">
|
|
2554
|
+
<table role="presentation" style="width: 100%; border-collapse: collapse;">
|
|
2555
|
+
<tr>
|
|
2556
|
+
<td style="padding: 40px 20px;">
|
|
2557
|
+
<table role="presentation" style="max-width: 600px; margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
|
2558
|
+
<!-- Header -->
|
|
2559
|
+
<tr>
|
|
2560
|
+
<td style="padding: 40px 40px 20px; text-align: center; border-bottom: 1px solid #e0e0e0;">
|
|
2561
|
+
<h1 style="margin: 0; color: #1a1a1a; font-size: 24px; font-weight: 600;">You're Invited</h1>
|
|
2562
|
+
</td>
|
|
2563
|
+
</tr>
|
|
2564
|
+
|
|
2565
|
+
<!-- Content -->
|
|
2566
|
+
<tr>
|
|
2567
|
+
<td style="padding: 40px;">
|
|
2568
|
+
<p style="margin: 0 0 20px; color: #333333; font-size: 16px; line-height: 1.6;">
|
|
2569
|
+
Hello <strong>${name}</strong>,
|
|
2570
|
+
</p>
|
|
2571
|
+
<p style="margin: 0 0 30px; color: #333333; font-size: 16px; line-height: 1.6;">
|
|
2572
|
+
You have been invited to join Vritti AI. Click the button below to set your password and get started.
|
|
2573
|
+
</p>
|
|
2574
|
+
|
|
2575
|
+
<div style="text-align: center; margin: 30px 0;">
|
|
2576
|
+
<a href="${inviteUrl}" style="display: inline-block; padding: 14px 32px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: #ffffff; text-decoration: none; border-radius: 6px; font-weight: 600; font-size: 16px;">
|
|
2577
|
+
Set Your Password
|
|
2578
|
+
</a>
|
|
2579
|
+
</div>
|
|
2580
|
+
|
|
2581
|
+
<p style="margin: 30px 0 0; color: #666666; font-size: 14px; line-height: 1.6;">
|
|
2582
|
+
If you did not expect this invitation, you can safely ignore this email.
|
|
2583
|
+
</p>
|
|
2584
|
+
</td>
|
|
2585
|
+
</tr>
|
|
2586
|
+
|
|
2587
|
+
<!-- Footer -->
|
|
2588
|
+
<tr>
|
|
2589
|
+
<td style="padding: 30px 40px; border-top: 1px solid #e0e0e0; text-align: center;">
|
|
2590
|
+
<p style="margin: 0; color: #999999; font-size: 12px; line-height: 1.5;">
|
|
2591
|
+
Vritti AI Cloud - Cloud Management Platform
|
|
2592
|
+
</p>
|
|
2593
|
+
<p style="margin: 8px 0 0; color: #999999; font-size: 12px; line-height: 1.5;">
|
|
2594
|
+
This is an automated message, please do not reply.
|
|
2595
|
+
</p>
|
|
2596
|
+
</td>
|
|
2597
|
+
</tr>
|
|
2598
|
+
</table>
|
|
2599
|
+
</td>
|
|
2600
|
+
</tr>
|
|
2601
|
+
</table>
|
|
2602
|
+
</body>
|
|
2603
|
+
</html>
|
|
2604
|
+
`;
|
|
2605
|
+
const textContent = `
|
|
2606
|
+
Hello ${name},
|
|
2607
|
+
|
|
2608
|
+
You have been invited to join Vritti AI. Visit the link below to set your password and get started:
|
|
2609
|
+
|
|
2610
|
+
${inviteUrl}
|
|
2611
|
+
|
|
2612
|
+
If you did not expect this invitation, you can safely ignore this email.
|
|
2613
|
+
|
|
2614
|
+
---
|
|
2615
|
+
Vritti AI Cloud - Cloud Management Platform
|
|
2616
|
+
This is an automated message, please do not reply.
|
|
2617
|
+
`.trim();
|
|
2618
|
+
await this.sendEmail({
|
|
2619
|
+
to: [
|
|
2620
|
+
{
|
|
2621
|
+
email: to,
|
|
2622
|
+
name
|
|
2623
|
+
}
|
|
2624
|
+
],
|
|
2625
|
+
subject,
|
|
2626
|
+
htmlContent,
|
|
2627
|
+
textContent
|
|
2628
|
+
});
|
|
2629
|
+
this.logger.log(`Invite email sent to ${to}`);
|
|
2630
|
+
}
|
|
2631
|
+
// Verifies Brevo API connectivity — a 400 response means the API is reachable
|
|
2632
|
+
async verifyConnection() {
|
|
2633
|
+
try {
|
|
2634
|
+
await this.brevoClient.transactionalEmails.sendTransacEmail({
|
|
2635
|
+
sender: {
|
|
2636
|
+
email: this.senderEmail,
|
|
2637
|
+
name: this.senderName
|
|
2638
|
+
},
|
|
2639
|
+
to: [
|
|
2640
|
+
{
|
|
2641
|
+
email: this.senderEmail
|
|
2642
|
+
}
|
|
2643
|
+
],
|
|
2644
|
+
subject: "Connection Test",
|
|
2645
|
+
htmlContent: "<p>Test</p>"
|
|
2646
|
+
});
|
|
2647
|
+
return true;
|
|
2648
|
+
} catch (err) {
|
|
2649
|
+
if (err instanceof BrevoError && err.statusCode === 400) {
|
|
2650
|
+
return true;
|
|
2651
|
+
}
|
|
2652
|
+
this.logger.error("Brevo connection verification failed:", err);
|
|
2653
|
+
return false;
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
// Sends a transactional email via Brevo — retries handled internally by BrevoClient
|
|
2657
|
+
async sendEmail(emailData) {
|
|
2658
|
+
try {
|
|
2659
|
+
const result = await this.brevoClient.transactionalEmails.sendTransacEmail({
|
|
2660
|
+
sender: {
|
|
2661
|
+
email: this.senderEmail,
|
|
2662
|
+
name: this.senderName
|
|
2663
|
+
},
|
|
2664
|
+
to: emailData.to,
|
|
2665
|
+
subject: emailData.subject,
|
|
2666
|
+
htmlContent: emailData.htmlContent,
|
|
2667
|
+
textContent: emailData.textContent
|
|
2668
|
+
});
|
|
2906
2669
|
this.logger.debug(`Email sent successfully. Message ID: ${result.messageId}`);
|
|
2907
2670
|
} catch (err) {
|
|
2908
2671
|
if (err instanceof BrevoTimeoutError) {
|
|
@@ -2930,9 +2693,9 @@ This is an automated message, please do not reply.
|
|
|
2930
2693
|
}
|
|
2931
2694
|
};
|
|
2932
2695
|
EmailService = _ts_decorate16([
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2696
|
+
Injectable7(),
|
|
2697
|
+
_ts_metadata12("design:type", Function),
|
|
2698
|
+
_ts_metadata12("design:paramtypes", [
|
|
2936
2699
|
typeof ConfigService5 === "undefined" ? Object : ConfigService5
|
|
2937
2700
|
])
|
|
2938
2701
|
], EmailService);
|
|
@@ -2966,7 +2729,7 @@ EmailModule = _ts_decorate17([
|
|
|
2966
2729
|
], EmailModule);
|
|
2967
2730
|
|
|
2968
2731
|
// src/filters/http-exception.filter.ts
|
|
2969
|
-
import { Catch,
|
|
2732
|
+
import { Catch, HttpStatus as HttpStatus19, Logger as Logger9 } from "@nestjs/common";
|
|
2970
2733
|
function _ts_decorate18(decorators, target, key, desc2) {
|
|
2971
2734
|
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
2972
2735
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc2);
|
|
@@ -2986,7 +2749,7 @@ var HttpExceptionFilter = class _HttpExceptionFilter {
|
|
|
2986
2749
|
static {
|
|
2987
2750
|
__name(this, "HttpExceptionFilter");
|
|
2988
2751
|
}
|
|
2989
|
-
logger = new
|
|
2752
|
+
logger = new Logger9(_HttpExceptionFilter.name);
|
|
2990
2753
|
catch(exception, host) {
|
|
2991
2754
|
const ctx = host.switchToHttp();
|
|
2992
2755
|
const response = ctx.getResponse();
|
|
@@ -2996,7 +2759,7 @@ var HttpExceptionFilter = class _HttpExceptionFilter {
|
|
|
2996
2759
|
let label;
|
|
2997
2760
|
let detail = "Internal server error";
|
|
2998
2761
|
let errors = [];
|
|
2999
|
-
if (exception
|
|
2762
|
+
if (this.isHttpException(exception)) {
|
|
3000
2763
|
status = exception.getStatus();
|
|
3001
2764
|
const exceptionResponse = exception.getResponse();
|
|
3002
2765
|
if (typeof exceptionResponse === "object" && exceptionResponse !== null) {
|
|
@@ -3049,6 +2812,10 @@ var HttpExceptionFilter = class _HttpExceptionFilter {
|
|
|
3049
2812
|
};
|
|
3050
2813
|
response.header("Content-Type", "application/problem+json").status(status).send(problemDetails);
|
|
3051
2814
|
}
|
|
2815
|
+
// Duck-type check for HttpException — avoids instanceof failing across pnpm package instances
|
|
2816
|
+
isHttpException(error) {
|
|
2817
|
+
return error instanceof Error && typeof error.getStatus === "function" && typeof error.getResponse === "function";
|
|
2818
|
+
}
|
|
3052
2819
|
// Duck-type check for AxiosError without importing axios
|
|
3053
2820
|
isAxiosError(error) {
|
|
3054
2821
|
return error instanceof Error && error.isAxiosError === true;
|
|
@@ -3059,11 +2826,11 @@ HttpExceptionFilter = _ts_decorate18([
|
|
|
3059
2826
|
], HttpExceptionFilter);
|
|
3060
2827
|
|
|
3061
2828
|
// src/logger/interceptors/http-logger.interceptor.ts
|
|
3062
|
-
import { Injectable as
|
|
2829
|
+
import { Injectable as Injectable9, Optional as Optional2 } from "@nestjs/common";
|
|
3063
2830
|
import { catchError, tap } from "rxjs/operators";
|
|
3064
2831
|
|
|
3065
2832
|
// src/logger/services/logger.service.ts
|
|
3066
|
-
import { Injectable as
|
|
2833
|
+
import { Injectable as Injectable8, Optional } from "@nestjs/common";
|
|
3067
2834
|
import { createLogger, format, transports } from "winston";
|
|
3068
2835
|
import DailyRotateFile from "winston-daily-rotate-file";
|
|
3069
2836
|
|
|
@@ -3108,16 +2875,16 @@ function _ts_decorate19(decorators, target, key, desc2) {
|
|
|
3108
2875
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
3109
2876
|
}
|
|
3110
2877
|
__name(_ts_decorate19, "_ts_decorate");
|
|
3111
|
-
function
|
|
2878
|
+
function _ts_metadata13(k, v) {
|
|
3112
2879
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
3113
2880
|
}
|
|
3114
|
-
__name(
|
|
3115
|
-
function
|
|
2881
|
+
__name(_ts_metadata13, "_ts_metadata");
|
|
2882
|
+
function _ts_param4(paramIndex, decorator) {
|
|
3116
2883
|
return function(target, key) {
|
|
3117
2884
|
decorator(target, key, paramIndex);
|
|
3118
2885
|
};
|
|
3119
2886
|
}
|
|
3120
|
-
__name(
|
|
2887
|
+
__name(_ts_param4, "_ts_param");
|
|
3121
2888
|
var LoggerService = class _LoggerService {
|
|
3122
2889
|
static {
|
|
3123
2890
|
__name(this, "LoggerService");
|
|
@@ -3157,9 +2924,9 @@ var LoggerService = class _LoggerService {
|
|
|
3157
2924
|
}) : new transports.Console({
|
|
3158
2925
|
level,
|
|
3159
2926
|
format: format.combine(...baseFormatters, format.printf((info) => {
|
|
3160
|
-
const { timestamp, level: level2, message, context, correlationId, trace } = info;
|
|
2927
|
+
const { timestamp: timestamp2, level: level2, message, context, correlationId, trace } = info;
|
|
3161
2928
|
const parts = [
|
|
3162
|
-
|
|
2929
|
+
timestamp2,
|
|
3163
2930
|
level2.toUpperCase().padEnd(7),
|
|
3164
2931
|
correlationId ? `[${correlationId.toString().slice(-6)}]` : "",
|
|
3165
2932
|
context ? `[${context}]` : "",
|
|
@@ -3310,11 +3077,11 @@ ${trace}`;
|
|
|
3310
3077
|
}
|
|
3311
3078
|
};
|
|
3312
3079
|
LoggerService = _ts_decorate19([
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3080
|
+
Injectable8(),
|
|
3081
|
+
_ts_param4(0, Optional()),
|
|
3082
|
+
_ts_param4(1, Optional()),
|
|
3083
|
+
_ts_metadata13("design:type", Function),
|
|
3084
|
+
_ts_metadata13("design:paramtypes", [
|
|
3318
3085
|
typeof LoggerModuleOptions === "undefined" ? Object : LoggerModuleOptions,
|
|
3319
3086
|
typeof Logger === "undefined" ? Object : Logger
|
|
3320
3087
|
])
|
|
@@ -3328,16 +3095,16 @@ function _ts_decorate20(decorators, target, key, desc2) {
|
|
|
3328
3095
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
3329
3096
|
}
|
|
3330
3097
|
__name(_ts_decorate20, "_ts_decorate");
|
|
3331
|
-
function
|
|
3098
|
+
function _ts_metadata14(k, v) {
|
|
3332
3099
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
3333
3100
|
}
|
|
3334
|
-
__name(
|
|
3335
|
-
function
|
|
3101
|
+
__name(_ts_metadata14, "_ts_metadata");
|
|
3102
|
+
function _ts_param5(paramIndex, decorator) {
|
|
3336
3103
|
return function(target, key) {
|
|
3337
3104
|
decorator(target, key, paramIndex);
|
|
3338
3105
|
};
|
|
3339
3106
|
}
|
|
3340
|
-
__name(
|
|
3107
|
+
__name(_ts_param5, "_ts_param");
|
|
3341
3108
|
var HttpLoggerInterceptor = class {
|
|
3342
3109
|
static {
|
|
3343
3110
|
__name(this, "HttpLoggerInterceptor");
|
|
@@ -3441,20 +3208,20 @@ var HttpLoggerInterceptor = class {
|
|
|
3441
3208
|
}
|
|
3442
3209
|
};
|
|
3443
3210
|
HttpLoggerInterceptor = _ts_decorate20([
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3211
|
+
Injectable9(),
|
|
3212
|
+
_ts_param5(1, Optional2()),
|
|
3213
|
+
_ts_metadata14("design:type", Function),
|
|
3214
|
+
_ts_metadata14("design:paramtypes", [
|
|
3448
3215
|
typeof LoggerService === "undefined" ? Object : LoggerService,
|
|
3449
3216
|
typeof HttpLoggerOptions === "undefined" ? Object : HttpLoggerOptions
|
|
3450
3217
|
])
|
|
3451
3218
|
], HttpLoggerInterceptor);
|
|
3452
3219
|
|
|
3453
3220
|
// src/logger/logger.module.ts
|
|
3454
|
-
import { Global as Global5, Logger as
|
|
3221
|
+
import { Global as Global5, Logger as Logger10, Module as Module6 } from "@nestjs/common";
|
|
3455
3222
|
|
|
3456
3223
|
// src/logger/middleware/correlation-id.middleware.ts
|
|
3457
|
-
import { Injectable as
|
|
3224
|
+
import { Injectable as Injectable10 } from "@nestjs/common";
|
|
3458
3225
|
function _ts_decorate21(decorators, target, key, desc2) {
|
|
3459
3226
|
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
3460
3227
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc2);
|
|
@@ -3462,10 +3229,10 @@ function _ts_decorate21(decorators, target, key, desc2) {
|
|
|
3462
3229
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
3463
3230
|
}
|
|
3464
3231
|
__name(_ts_decorate21, "_ts_decorate");
|
|
3465
|
-
function
|
|
3232
|
+
function _ts_metadata15(k, v) {
|
|
3466
3233
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
3467
3234
|
}
|
|
3468
|
-
__name(
|
|
3235
|
+
__name(_ts_metadata15, "_ts_metadata");
|
|
3469
3236
|
var CorrelationIdMiddleware = class {
|
|
3470
3237
|
static {
|
|
3471
3238
|
__name(this, "CorrelationIdMiddleware");
|
|
@@ -3503,9 +3270,9 @@ var CorrelationIdMiddleware = class {
|
|
|
3503
3270
|
}
|
|
3504
3271
|
};
|
|
3505
3272
|
CorrelationIdMiddleware = _ts_decorate21([
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3273
|
+
Injectable10(),
|
|
3274
|
+
_ts_metadata15("design:type", Function),
|
|
3275
|
+
_ts_metadata15("design:paramtypes", [
|
|
3509
3276
|
typeof CorrelationIdMiddlewareOptions === "undefined" ? Object : CorrelationIdMiddlewareOptions
|
|
3510
3277
|
])
|
|
3511
3278
|
], CorrelationIdMiddleware);
|
|
@@ -3594,9 +3361,9 @@ function mergeWithDefaults(options = {}) {
|
|
|
3594
3361
|
__name(mergeWithDefaults, "mergeWithDefaults");
|
|
3595
3362
|
function createDefaultLoggerProvider(options) {
|
|
3596
3363
|
return {
|
|
3597
|
-
provide:
|
|
3364
|
+
provide: Logger10,
|
|
3598
3365
|
useFactory: /* @__PURE__ */ __name(() => {
|
|
3599
|
-
const logger = new
|
|
3366
|
+
const logger = new Logger10();
|
|
3600
3367
|
if (options.level) {
|
|
3601
3368
|
const levels = getLevelsUpTo(options.level);
|
|
3602
3369
|
logger.setLogLevels?.(levels);
|
|
@@ -3626,7 +3393,7 @@ function createLoggerProviders(options = {}) {
|
|
|
3626
3393
|
inject: [
|
|
3627
3394
|
LOGGER_MODULE_OPTIONS,
|
|
3628
3395
|
{
|
|
3629
|
-
token:
|
|
3396
|
+
token: Logger10,
|
|
3630
3397
|
optional: true
|
|
3631
3398
|
}
|
|
3632
3399
|
]
|
|
@@ -3705,10 +3472,10 @@ var LoggerModule = class _LoggerModule {
|
|
|
3705
3472
|
...asyncProviders,
|
|
3706
3473
|
// Default logger provider
|
|
3707
3474
|
{
|
|
3708
|
-
provide:
|
|
3475
|
+
provide: Logger10,
|
|
3709
3476
|
useFactory: /* @__PURE__ */ __name((opts) => {
|
|
3710
3477
|
if (opts.provider === "default") {
|
|
3711
|
-
const logger = new
|
|
3478
|
+
const logger = new Logger10();
|
|
3712
3479
|
if (opts.level) {
|
|
3713
3480
|
const levels = getLevelsUpTo(opts.level);
|
|
3714
3481
|
logger.setLogLevels?.(levels);
|
|
@@ -3730,7 +3497,7 @@ var LoggerModule = class _LoggerModule {
|
|
|
3730
3497
|
inject: [
|
|
3731
3498
|
LOGGER_MODULE_OPTIONS,
|
|
3732
3499
|
{
|
|
3733
|
-
token:
|
|
3500
|
+
token: Logger10,
|
|
3734
3501
|
optional: true
|
|
3735
3502
|
}
|
|
3736
3503
|
]
|
|
@@ -3856,7 +3623,7 @@ function ApiHealthCheck() {
|
|
|
3856
3623
|
__name(ApiHealthCheck, "ApiHealthCheck");
|
|
3857
3624
|
|
|
3858
3625
|
// src/root/services/app.service.ts
|
|
3859
|
-
import { Injectable as
|
|
3626
|
+
import { Injectable as Injectable11 } from "@nestjs/common";
|
|
3860
3627
|
function _ts_decorate23(decorators, target, key, desc2) {
|
|
3861
3628
|
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
3862
3629
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc2);
|
|
@@ -3874,7 +3641,7 @@ var AppService = class {
|
|
|
3874
3641
|
}
|
|
3875
3642
|
};
|
|
3876
3643
|
AppService = _ts_decorate23([
|
|
3877
|
-
|
|
3644
|
+
Injectable11()
|
|
3878
3645
|
], AppService);
|
|
3879
3646
|
|
|
3880
3647
|
// src/root/controllers/app.controller.ts
|
|
@@ -3885,10 +3652,10 @@ function _ts_decorate24(decorators, target, key, desc2) {
|
|
|
3885
3652
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
3886
3653
|
}
|
|
3887
3654
|
__name(_ts_decorate24, "_ts_decorate");
|
|
3888
|
-
function
|
|
3655
|
+
function _ts_metadata16(k, v) {
|
|
3889
3656
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
3890
3657
|
}
|
|
3891
|
-
__name(
|
|
3658
|
+
__name(_ts_metadata16, "_ts_metadata");
|
|
3892
3659
|
var AppController = class {
|
|
3893
3660
|
static {
|
|
3894
3661
|
__name(this, "AppController");
|
|
@@ -3906,15 +3673,15 @@ _ts_decorate24([
|
|
|
3906
3673
|
Get(),
|
|
3907
3674
|
Public(),
|
|
3908
3675
|
ApiHealthCheck(),
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3676
|
+
_ts_metadata16("design:type", Function),
|
|
3677
|
+
_ts_metadata16("design:paramtypes", []),
|
|
3678
|
+
_ts_metadata16("design:returntype", String)
|
|
3912
3679
|
], AppController.prototype, "getHello", null);
|
|
3913
3680
|
AppController = _ts_decorate24([
|
|
3914
3681
|
ApiTags("Health"),
|
|
3915
3682
|
Controller(),
|
|
3916
|
-
|
|
3917
|
-
|
|
3683
|
+
_ts_metadata16("design:type", Function),
|
|
3684
|
+
_ts_metadata16("design:paramtypes", [
|
|
3918
3685
|
typeof AppService === "undefined" ? Object : AppService
|
|
3919
3686
|
])
|
|
3920
3687
|
], AppController);
|
|
@@ -3958,16 +3725,16 @@ function _ts_decorate25(decorators, target, key, desc2) {
|
|
|
3958
3725
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
3959
3726
|
}
|
|
3960
3727
|
__name(_ts_decorate25, "_ts_decorate");
|
|
3961
|
-
function
|
|
3728
|
+
function _ts_metadata17(k, v) {
|
|
3962
3729
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
3963
3730
|
}
|
|
3964
|
-
__name(
|
|
3965
|
-
function
|
|
3731
|
+
__name(_ts_metadata17, "_ts_metadata");
|
|
3732
|
+
function _ts_param6(paramIndex, decorator) {
|
|
3966
3733
|
return function(target, key) {
|
|
3967
3734
|
decorator(target, key, paramIndex);
|
|
3968
3735
|
};
|
|
3969
3736
|
}
|
|
3970
|
-
__name(
|
|
3737
|
+
__name(_ts_param6, "_ts_param");
|
|
3971
3738
|
var CsrfController = class {
|
|
3972
3739
|
static {
|
|
3973
3740
|
__name(this, "CsrfController");
|
|
@@ -3985,14 +3752,14 @@ _ts_decorate25([
|
|
|
3985
3752
|
Public(),
|
|
3986
3753
|
HttpCode(HttpStatus20.OK),
|
|
3987
3754
|
ApiGetCsrfToken(),
|
|
3988
|
-
|
|
3755
|
+
_ts_param6(0, Res({
|
|
3989
3756
|
passthrough: true
|
|
3990
3757
|
})),
|
|
3991
|
-
|
|
3992
|
-
|
|
3758
|
+
_ts_metadata17("design:type", Function),
|
|
3759
|
+
_ts_metadata17("design:paramtypes", [
|
|
3993
3760
|
typeof FastifyReply === "undefined" ? Object : FastifyReply
|
|
3994
3761
|
]),
|
|
3995
|
-
|
|
3762
|
+
_ts_metadata17("design:returntype", Object)
|
|
3996
3763
|
], CsrfController.prototype, "getToken", null);
|
|
3997
3764
|
CsrfController = _ts_decorate25([
|
|
3998
3765
|
ApiTags2("CSRF"),
|
|
@@ -4233,6 +4000,1067 @@ function normalizePhoneNumber(phone) {
|
|
|
4233
4000
|
return phone.startsWith("+") ? phone : `+${phone}`;
|
|
4234
4001
|
}
|
|
4235
4002
|
__name(normalizePhoneNumber, "normalizePhoneNumber");
|
|
4003
|
+
|
|
4004
|
+
// src/data-table/data-table.module.ts
|
|
4005
|
+
import { Module as Module8 } from "@nestjs/common";
|
|
4006
|
+
import { ConfigModule as ConfigModule4 } from "@nestjs/config";
|
|
4007
|
+
|
|
4008
|
+
// src/data-table/data-table.constants.ts
|
|
4009
|
+
var DATA_TABLE_VIEWS_TABLE = Symbol("DATA_TABLE_VIEWS_TABLE");
|
|
4010
|
+
|
|
4011
|
+
// src/data-table/state/controllers/data-table-state.controller.ts
|
|
4012
|
+
import { Body, Controller as Controller3, HttpCode as HttpCode2, HttpStatus as HttpStatus21, Logger as Logger12, Post } from "@nestjs/common";
|
|
4013
|
+
import { ApiBearerAuth, ApiTags as ApiTags3 } from "@nestjs/swagger";
|
|
4014
|
+
|
|
4015
|
+
// src/data-table/state/docs/data-table-state.docs.ts
|
|
4016
|
+
import { applyDecorators as applyDecorators3 } from "@nestjs/common";
|
|
4017
|
+
import { ApiBody, ApiOperation as ApiOperation3, ApiResponse as ApiResponse3 } from "@nestjs/swagger";
|
|
4018
|
+
|
|
4019
|
+
// src/data-table/state/dto/request/upsert-data-table-state.dto.ts
|
|
4020
|
+
import { ApiProperty as ApiProperty5, ApiPropertyOptional as ApiPropertyOptional4 } from "@nestjs/swagger";
|
|
4021
|
+
import { IsObject, IsOptional as IsOptional2, IsString as IsString3, IsUUID, MaxLength } from "class-validator";
|
|
4022
|
+
function _ts_decorate27(decorators, target, key, desc2) {
|
|
4023
|
+
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
4024
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc2);
|
|
4025
|
+
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;
|
|
4026
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
4027
|
+
}
|
|
4028
|
+
__name(_ts_decorate27, "_ts_decorate");
|
|
4029
|
+
function _ts_metadata18(k, v) {
|
|
4030
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
4031
|
+
}
|
|
4032
|
+
__name(_ts_metadata18, "_ts_metadata");
|
|
4033
|
+
var UpsertDataTableStateDto = class {
|
|
4034
|
+
static {
|
|
4035
|
+
__name(this, "UpsertDataTableStateDto");
|
|
4036
|
+
}
|
|
4037
|
+
tableSlug;
|
|
4038
|
+
state;
|
|
4039
|
+
activeViewId;
|
|
4040
|
+
};
|
|
4041
|
+
_ts_decorate27([
|
|
4042
|
+
ApiProperty5({
|
|
4043
|
+
description: "Unique slug identifying the table",
|
|
4044
|
+
example: "cloud-providers"
|
|
4045
|
+
}),
|
|
4046
|
+
IsString3(),
|
|
4047
|
+
MaxLength(100),
|
|
4048
|
+
_ts_metadata18("design:type", String)
|
|
4049
|
+
], UpsertDataTableStateDto.prototype, "tableSlug", void 0);
|
|
4050
|
+
_ts_decorate27([
|
|
4051
|
+
ApiProperty5({
|
|
4052
|
+
description: "Full table view state including filters, sort, and column visibility"
|
|
4053
|
+
}),
|
|
4054
|
+
IsObject(),
|
|
4055
|
+
_ts_metadata18("design:type", typeof TableViewState === "undefined" ? Object : TableViewState)
|
|
4056
|
+
], UpsertDataTableStateDto.prototype, "state", void 0);
|
|
4057
|
+
_ts_decorate27([
|
|
4058
|
+
ApiPropertyOptional4(),
|
|
4059
|
+
IsOptional2(),
|
|
4060
|
+
IsUUID(),
|
|
4061
|
+
_ts_metadata18("design:type", Object)
|
|
4062
|
+
], UpsertDataTableStateDto.prototype, "activeViewId", void 0);
|
|
4063
|
+
|
|
4064
|
+
// src/data-table/state/docs/data-table-state.docs.ts
|
|
4065
|
+
function ApiUpsertDataTableState() {
|
|
4066
|
+
return applyDecorators3(ApiOperation3({
|
|
4067
|
+
summary: "Save live table state",
|
|
4068
|
+
description: "Stores the current filter, sort, and column visibility state in Redis cache. Called on filter Apply and sort column click. State expires after TABLE_STATE_CACHE_TTL seconds."
|
|
4069
|
+
}), ApiBody({
|
|
4070
|
+
type: UpsertDataTableStateDto
|
|
4071
|
+
}), ApiResponse3({
|
|
4072
|
+
status: 200,
|
|
4073
|
+
description: "Live state cached."
|
|
4074
|
+
}), ApiResponse3({
|
|
4075
|
+
status: 400,
|
|
4076
|
+
description: "Invalid request body."
|
|
4077
|
+
}), ApiResponse3({
|
|
4078
|
+
status: 401,
|
|
4079
|
+
description: "Unauthorized."
|
|
4080
|
+
}));
|
|
4081
|
+
}
|
|
4082
|
+
__name(ApiUpsertDataTableState, "ApiUpsertDataTableState");
|
|
4083
|
+
|
|
4084
|
+
// src/data-table/state/services/data-table-state.service.ts
|
|
4085
|
+
import { Injectable as Injectable12, Logger as Logger11 } from "@nestjs/common";
|
|
4086
|
+
import { ConfigService as ConfigService6 } from "@nestjs/config";
|
|
4087
|
+
function _ts_decorate28(decorators, target, key, desc2) {
|
|
4088
|
+
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
4089
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc2);
|
|
4090
|
+
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;
|
|
4091
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
4092
|
+
}
|
|
4093
|
+
__name(_ts_decorate28, "_ts_decorate");
|
|
4094
|
+
function _ts_metadata19(k, v) {
|
|
4095
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
4096
|
+
}
|
|
4097
|
+
__name(_ts_metadata19, "_ts_metadata");
|
|
4098
|
+
var EMPTY_TABLE_STATE = {
|
|
4099
|
+
filters: [],
|
|
4100
|
+
sort: [],
|
|
4101
|
+
columnVisibility: {},
|
|
4102
|
+
columnOrder: [],
|
|
4103
|
+
columnSizing: {},
|
|
4104
|
+
columnPinning: {
|
|
4105
|
+
left: [],
|
|
4106
|
+
right: []
|
|
4107
|
+
},
|
|
4108
|
+
lockedColumnSizing: false,
|
|
4109
|
+
density: "normal",
|
|
4110
|
+
filterOrder: [],
|
|
4111
|
+
filterVisibility: {},
|
|
4112
|
+
pagination: {
|
|
4113
|
+
limit: 20,
|
|
4114
|
+
offset: 0
|
|
4115
|
+
}
|
|
4116
|
+
};
|
|
4117
|
+
var DataTableStateService = class _DataTableStateService {
|
|
4118
|
+
static {
|
|
4119
|
+
__name(this, "DataTableStateService");
|
|
4120
|
+
}
|
|
4121
|
+
cacheService;
|
|
4122
|
+
configService;
|
|
4123
|
+
logger = new Logger11(_DataTableStateService.name);
|
|
4124
|
+
constructor(cacheService, configService) {
|
|
4125
|
+
this.cacheService = cacheService;
|
|
4126
|
+
this.configService = configService;
|
|
4127
|
+
}
|
|
4128
|
+
// Returns configured TTL for live table state in seconds, defaulting to 3600 (1h)
|
|
4129
|
+
get stateTtl() {
|
|
4130
|
+
return this.configService.get("TABLE_STATE_CACHE_TTL") ?? 3600;
|
|
4131
|
+
}
|
|
4132
|
+
// Saves live table state and active view ID to Redis; DB is not written
|
|
4133
|
+
async upsertCurrentState(userId, dto) {
|
|
4134
|
+
const key = `dt:${userId}:${dto.tableSlug}`;
|
|
4135
|
+
await this.cacheService.set(key, {
|
|
4136
|
+
state: dto.state,
|
|
4137
|
+
activeViewId: dto.activeViewId ?? null
|
|
4138
|
+
}, this.stateTtl);
|
|
4139
|
+
this.logger.log(`Cached live state for user: ${userId}, table: ${dto.tableSlug}`);
|
|
4140
|
+
}
|
|
4141
|
+
// Returns live table state and active view ID from Redis; returns empty state on miss — no DB query
|
|
4142
|
+
async getCurrentState(userId, tableSlug) {
|
|
4143
|
+
const key = `dt:${userId}:${tableSlug}`;
|
|
4144
|
+
const cached = await this.cacheService.get(key);
|
|
4145
|
+
return cached ?? {
|
|
4146
|
+
state: EMPTY_TABLE_STATE,
|
|
4147
|
+
activeViewId: null
|
|
4148
|
+
};
|
|
4149
|
+
}
|
|
4150
|
+
};
|
|
4151
|
+
DataTableStateService = _ts_decorate28([
|
|
4152
|
+
Injectable12(),
|
|
4153
|
+
_ts_metadata19("design:type", Function),
|
|
4154
|
+
_ts_metadata19("design:paramtypes", [
|
|
4155
|
+
typeof CacheService === "undefined" ? Object : CacheService,
|
|
4156
|
+
typeof ConfigService6 === "undefined" ? Object : ConfigService6
|
|
4157
|
+
])
|
|
4158
|
+
], DataTableStateService);
|
|
4159
|
+
|
|
4160
|
+
// src/data-table/state/controllers/data-table-state.controller.ts
|
|
4161
|
+
function _ts_decorate29(decorators, target, key, desc2) {
|
|
4162
|
+
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
4163
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc2);
|
|
4164
|
+
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;
|
|
4165
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
4166
|
+
}
|
|
4167
|
+
__name(_ts_decorate29, "_ts_decorate");
|
|
4168
|
+
function _ts_metadata20(k, v) {
|
|
4169
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
4170
|
+
}
|
|
4171
|
+
__name(_ts_metadata20, "_ts_metadata");
|
|
4172
|
+
function _ts_param7(paramIndex, decorator) {
|
|
4173
|
+
return function(target, key) {
|
|
4174
|
+
decorator(target, key, paramIndex);
|
|
4175
|
+
};
|
|
4176
|
+
}
|
|
4177
|
+
__name(_ts_param7, "_ts_param");
|
|
4178
|
+
var DataTableStateController = class _DataTableStateController {
|
|
4179
|
+
static {
|
|
4180
|
+
__name(this, "DataTableStateController");
|
|
4181
|
+
}
|
|
4182
|
+
dataTableStateService;
|
|
4183
|
+
logger = new Logger12(_DataTableStateController.name);
|
|
4184
|
+
constructor(dataTableStateService) {
|
|
4185
|
+
this.dataTableStateService = dataTableStateService;
|
|
4186
|
+
}
|
|
4187
|
+
// Saves live table state to Redis cache for the authenticated user's table
|
|
4188
|
+
upsertCurrentState(userId, dto) {
|
|
4189
|
+
this.logger.log(`POST /table-states - User: ${userId}, table: ${dto.tableSlug}`);
|
|
4190
|
+
return this.dataTableStateService.upsertCurrentState(userId, dto);
|
|
4191
|
+
}
|
|
4192
|
+
};
|
|
4193
|
+
_ts_decorate29([
|
|
4194
|
+
Post(),
|
|
4195
|
+
HttpCode2(HttpStatus21.OK),
|
|
4196
|
+
ApiUpsertDataTableState(),
|
|
4197
|
+
_ts_param7(0, UserId()),
|
|
4198
|
+
_ts_param7(1, Body()),
|
|
4199
|
+
_ts_metadata20("design:type", Function),
|
|
4200
|
+
_ts_metadata20("design:paramtypes", [
|
|
4201
|
+
String,
|
|
4202
|
+
typeof UpsertDataTableStateDto === "undefined" ? Object : UpsertDataTableStateDto
|
|
4203
|
+
]),
|
|
4204
|
+
_ts_metadata20("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
4205
|
+
], DataTableStateController.prototype, "upsertCurrentState", null);
|
|
4206
|
+
DataTableStateController = _ts_decorate29([
|
|
4207
|
+
ApiTags3("Table States"),
|
|
4208
|
+
ApiBearerAuth(),
|
|
4209
|
+
RequireSession("CLOUD", "ADMIN"),
|
|
4210
|
+
Controller3("table-states"),
|
|
4211
|
+
_ts_metadata20("design:type", Function),
|
|
4212
|
+
_ts_metadata20("design:paramtypes", [
|
|
4213
|
+
typeof DataTableStateService === "undefined" ? Object : DataTableStateService
|
|
4214
|
+
])
|
|
4215
|
+
], DataTableStateController);
|
|
4216
|
+
|
|
4217
|
+
// src/data-table/views/controllers/data-table-views.controller.ts
|
|
4218
|
+
import { Body as Body2, Controller as Controller4, Delete, Get as Get3, HttpCode as HttpCode3, HttpStatus as HttpStatus22, Logger as Logger14, Param, Patch, Post as Post2, Query } from "@nestjs/common";
|
|
4219
|
+
import { ApiBearerAuth as ApiBearerAuth2, ApiTags as ApiTags4 } from "@nestjs/swagger";
|
|
4220
|
+
|
|
4221
|
+
// src/data-table/views/docs/data-table-views.docs.ts
|
|
4222
|
+
import { applyDecorators as applyDecorators4 } from "@nestjs/common";
|
|
4223
|
+
import { ApiBody as ApiBody2, ApiOperation as ApiOperation4, ApiParam, ApiQuery, ApiResponse as ApiResponse4 } from "@nestjs/swagger";
|
|
4224
|
+
|
|
4225
|
+
// src/data-table/views/dto/entity/data-table-view.dto.ts
|
|
4226
|
+
import { ApiProperty as ApiProperty6, ApiPropertyOptional as ApiPropertyOptional5 } from "@nestjs/swagger";
|
|
4227
|
+
function _ts_decorate30(decorators, target, key, desc2) {
|
|
4228
|
+
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
4229
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc2);
|
|
4230
|
+
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;
|
|
4231
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
4232
|
+
}
|
|
4233
|
+
__name(_ts_decorate30, "_ts_decorate");
|
|
4234
|
+
function _ts_metadata21(k, v) {
|
|
4235
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
4236
|
+
}
|
|
4237
|
+
__name(_ts_metadata21, "_ts_metadata");
|
|
4238
|
+
var DataTableViewDto = class _DataTableViewDto {
|
|
4239
|
+
static {
|
|
4240
|
+
__name(this, "DataTableViewDto");
|
|
4241
|
+
}
|
|
4242
|
+
id;
|
|
4243
|
+
name;
|
|
4244
|
+
tableSlug;
|
|
4245
|
+
state;
|
|
4246
|
+
isShared;
|
|
4247
|
+
isOwn;
|
|
4248
|
+
createdAt;
|
|
4249
|
+
updatedAt;
|
|
4250
|
+
// Creates a response DTO from a DataTableView entity, computing isOwn by comparing userId
|
|
4251
|
+
static from(view, userId) {
|
|
4252
|
+
const dto = new _DataTableViewDto();
|
|
4253
|
+
dto.id = view.id;
|
|
4254
|
+
dto.name = view.name ?? null;
|
|
4255
|
+
dto.tableSlug = view.tableSlug;
|
|
4256
|
+
dto.state = view.state;
|
|
4257
|
+
dto.isShared = view.isShared;
|
|
4258
|
+
dto.isOwn = view.userId === userId;
|
|
4259
|
+
dto.createdAt = view.createdAt;
|
|
4260
|
+
dto.updatedAt = view.updatedAt ?? null;
|
|
4261
|
+
return dto;
|
|
4262
|
+
}
|
|
4263
|
+
};
|
|
4264
|
+
_ts_decorate30([
|
|
4265
|
+
ApiProperty6({
|
|
4266
|
+
description: "View unique identifier"
|
|
4267
|
+
}),
|
|
4268
|
+
_ts_metadata21("design:type", String)
|
|
4269
|
+
], DataTableViewDto.prototype, "id", void 0);
|
|
4270
|
+
_ts_decorate30([
|
|
4271
|
+
ApiPropertyOptional5({
|
|
4272
|
+
description: "Display name of the view",
|
|
4273
|
+
nullable: true
|
|
4274
|
+
}),
|
|
4275
|
+
_ts_metadata21("design:type", Object)
|
|
4276
|
+
], DataTableViewDto.prototype, "name", void 0);
|
|
4277
|
+
_ts_decorate30([
|
|
4278
|
+
ApiProperty6({
|
|
4279
|
+
description: "Slug of the table this view belongs to",
|
|
4280
|
+
example: "cloud-providers"
|
|
4281
|
+
}),
|
|
4282
|
+
_ts_metadata21("design:type", String)
|
|
4283
|
+
], DataTableViewDto.prototype, "tableSlug", void 0);
|
|
4284
|
+
_ts_decorate30([
|
|
4285
|
+
ApiProperty6({
|
|
4286
|
+
description: "Stored filter, sort, and column visibility state"
|
|
4287
|
+
}),
|
|
4288
|
+
_ts_metadata21("design:type", typeof TableViewState === "undefined" ? Object : TableViewState)
|
|
4289
|
+
], DataTableViewDto.prototype, "state", void 0);
|
|
4290
|
+
_ts_decorate30([
|
|
4291
|
+
ApiProperty6({
|
|
4292
|
+
description: "Whether the view is visible to all users",
|
|
4293
|
+
example: false
|
|
4294
|
+
}),
|
|
4295
|
+
_ts_metadata21("design:type", Boolean)
|
|
4296
|
+
], DataTableViewDto.prototype, "isShared", void 0);
|
|
4297
|
+
_ts_decorate30([
|
|
4298
|
+
ApiProperty6({
|
|
4299
|
+
description: "Whether the requesting user owns this view",
|
|
4300
|
+
example: true
|
|
4301
|
+
}),
|
|
4302
|
+
_ts_metadata21("design:type", Boolean)
|
|
4303
|
+
], DataTableViewDto.prototype, "isOwn", void 0);
|
|
4304
|
+
_ts_decorate30([
|
|
4305
|
+
ApiProperty6({
|
|
4306
|
+
description: "Creation timestamp"
|
|
4307
|
+
}),
|
|
4308
|
+
_ts_metadata21("design:type", typeof Date === "undefined" ? Object : Date)
|
|
4309
|
+
], DataTableViewDto.prototype, "createdAt", void 0);
|
|
4310
|
+
_ts_decorate30([
|
|
4311
|
+
ApiPropertyOptional5({
|
|
4312
|
+
description: "Last updated timestamp",
|
|
4313
|
+
nullable: true
|
|
4314
|
+
}),
|
|
4315
|
+
_ts_metadata21("design:type", Object)
|
|
4316
|
+
], DataTableViewDto.prototype, "updatedAt", void 0);
|
|
4317
|
+
|
|
4318
|
+
// src/data-table/views/dto/request/create-data-table-view.dto.ts
|
|
4319
|
+
import { ApiProperty as ApiProperty7, ApiPropertyOptional as ApiPropertyOptional6 } from "@nestjs/swagger";
|
|
4320
|
+
import { IsBoolean as IsBoolean2, IsObject as IsObject2, IsOptional as IsOptional3, IsString as IsString4, MaxLength as MaxLength2 } from "class-validator";
|
|
4321
|
+
function _ts_decorate31(decorators, target, key, desc2) {
|
|
4322
|
+
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
4323
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc2);
|
|
4324
|
+
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;
|
|
4325
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
4326
|
+
}
|
|
4327
|
+
__name(_ts_decorate31, "_ts_decorate");
|
|
4328
|
+
function _ts_metadata22(k, v) {
|
|
4329
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
4330
|
+
}
|
|
4331
|
+
__name(_ts_metadata22, "_ts_metadata");
|
|
4332
|
+
var CreateDataTableViewDto = class {
|
|
4333
|
+
static {
|
|
4334
|
+
__name(this, "CreateDataTableViewDto");
|
|
4335
|
+
}
|
|
4336
|
+
name;
|
|
4337
|
+
tableSlug;
|
|
4338
|
+
state;
|
|
4339
|
+
isShared;
|
|
4340
|
+
};
|
|
4341
|
+
_ts_decorate31([
|
|
4342
|
+
ApiProperty7({
|
|
4343
|
+
description: "Display name for the saved view",
|
|
4344
|
+
example: "AWS Only"
|
|
4345
|
+
}),
|
|
4346
|
+
IsString4(),
|
|
4347
|
+
MaxLength2(100),
|
|
4348
|
+
_ts_metadata22("design:type", String)
|
|
4349
|
+
], CreateDataTableViewDto.prototype, "name", void 0);
|
|
4350
|
+
_ts_decorate31([
|
|
4351
|
+
ApiProperty7({
|
|
4352
|
+
description: "Unique slug identifying the table",
|
|
4353
|
+
example: "cloud-providers"
|
|
4354
|
+
}),
|
|
4355
|
+
IsString4(),
|
|
4356
|
+
MaxLength2(100),
|
|
4357
|
+
_ts_metadata22("design:type", String)
|
|
4358
|
+
], CreateDataTableViewDto.prototype, "tableSlug", void 0);
|
|
4359
|
+
_ts_decorate31([
|
|
4360
|
+
ApiProperty7({
|
|
4361
|
+
description: "Full table view state including filters, sort, and column visibility"
|
|
4362
|
+
}),
|
|
4363
|
+
IsObject2(),
|
|
4364
|
+
_ts_metadata22("design:type", typeof TableViewState === "undefined" ? Object : TableViewState)
|
|
4365
|
+
], CreateDataTableViewDto.prototype, "state", void 0);
|
|
4366
|
+
_ts_decorate31([
|
|
4367
|
+
ApiPropertyOptional6({
|
|
4368
|
+
description: "Whether this view is visible to all users",
|
|
4369
|
+
example: false
|
|
4370
|
+
}),
|
|
4371
|
+
IsBoolean2(),
|
|
4372
|
+
IsOptional3(),
|
|
4373
|
+
_ts_metadata22("design:type", Boolean)
|
|
4374
|
+
], CreateDataTableViewDto.prototype, "isShared", void 0);
|
|
4375
|
+
|
|
4376
|
+
// src/data-table/views/dto/request/rename-data-table-view.dto.ts
|
|
4377
|
+
import { ApiProperty as ApiProperty8 } from "@nestjs/swagger";
|
|
4378
|
+
import { IsString as IsString5, MaxLength as MaxLength3, MinLength } from "class-validator";
|
|
4379
|
+
function _ts_decorate32(decorators, target, key, desc2) {
|
|
4380
|
+
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
4381
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc2);
|
|
4382
|
+
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;
|
|
4383
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
4384
|
+
}
|
|
4385
|
+
__name(_ts_decorate32, "_ts_decorate");
|
|
4386
|
+
function _ts_metadata23(k, v) {
|
|
4387
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
4388
|
+
}
|
|
4389
|
+
__name(_ts_metadata23, "_ts_metadata");
|
|
4390
|
+
var RenameDataTableViewDto = class {
|
|
4391
|
+
static {
|
|
4392
|
+
__name(this, "RenameDataTableViewDto");
|
|
4393
|
+
}
|
|
4394
|
+
name;
|
|
4395
|
+
};
|
|
4396
|
+
_ts_decorate32([
|
|
4397
|
+
ApiProperty8({
|
|
4398
|
+
description: "New display name for the view",
|
|
4399
|
+
example: "AWS Only"
|
|
4400
|
+
}),
|
|
4401
|
+
IsString5(),
|
|
4402
|
+
MinLength(1),
|
|
4403
|
+
MaxLength3(100),
|
|
4404
|
+
_ts_metadata23("design:type", String)
|
|
4405
|
+
], RenameDataTableViewDto.prototype, "name", void 0);
|
|
4406
|
+
|
|
4407
|
+
// src/data-table/views/dto/request/toggle-share-data-table-view.dto.ts
|
|
4408
|
+
import { ApiProperty as ApiProperty9 } from "@nestjs/swagger";
|
|
4409
|
+
import { IsBoolean as IsBoolean3 } from "class-validator";
|
|
4410
|
+
function _ts_decorate33(decorators, target, key, desc2) {
|
|
4411
|
+
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
4412
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc2);
|
|
4413
|
+
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;
|
|
4414
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
4415
|
+
}
|
|
4416
|
+
__name(_ts_decorate33, "_ts_decorate");
|
|
4417
|
+
function _ts_metadata24(k, v) {
|
|
4418
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
4419
|
+
}
|
|
4420
|
+
__name(_ts_metadata24, "_ts_metadata");
|
|
4421
|
+
var ToggleShareDataTableViewDto = class {
|
|
4422
|
+
static {
|
|
4423
|
+
__name(this, "ToggleShareDataTableViewDto");
|
|
4424
|
+
}
|
|
4425
|
+
isShared;
|
|
4426
|
+
};
|
|
4427
|
+
_ts_decorate33([
|
|
4428
|
+
ApiProperty9({
|
|
4429
|
+
description: "Whether the view should be visible to all users",
|
|
4430
|
+
example: true
|
|
4431
|
+
}),
|
|
4432
|
+
IsBoolean3(),
|
|
4433
|
+
_ts_metadata24("design:type", Boolean)
|
|
4434
|
+
], ToggleShareDataTableViewDto.prototype, "isShared", void 0);
|
|
4435
|
+
|
|
4436
|
+
// src/data-table/views/dto/request/update-data-table-view.dto.ts
|
|
4437
|
+
import { ApiProperty as ApiProperty10 } from "@nestjs/swagger";
|
|
4438
|
+
import { IsObject as IsObject3 } from "class-validator";
|
|
4439
|
+
function _ts_decorate34(decorators, target, key, desc2) {
|
|
4440
|
+
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
4441
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc2);
|
|
4442
|
+
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;
|
|
4443
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
4444
|
+
}
|
|
4445
|
+
__name(_ts_decorate34, "_ts_decorate");
|
|
4446
|
+
function _ts_metadata25(k, v) {
|
|
4447
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
4448
|
+
}
|
|
4449
|
+
__name(_ts_metadata25, "_ts_metadata");
|
|
4450
|
+
var UpdateDataTableViewDto = class {
|
|
4451
|
+
static {
|
|
4452
|
+
__name(this, "UpdateDataTableViewDto");
|
|
4453
|
+
}
|
|
4454
|
+
state;
|
|
4455
|
+
};
|
|
4456
|
+
_ts_decorate34([
|
|
4457
|
+
ApiProperty10({
|
|
4458
|
+
description: "Updated filter, sort, and column visibility state"
|
|
4459
|
+
}),
|
|
4460
|
+
IsObject3(),
|
|
4461
|
+
_ts_metadata25("design:type", typeof TableViewState === "undefined" ? Object : TableViewState)
|
|
4462
|
+
], UpdateDataTableViewDto.prototype, "state", void 0);
|
|
4463
|
+
|
|
4464
|
+
// src/data-table/views/docs/data-table-views.docs.ts
|
|
4465
|
+
function ApiListDataTableViews() {
|
|
4466
|
+
return applyDecorators4(ApiOperation4({
|
|
4467
|
+
summary: "List named table views",
|
|
4468
|
+
description: "Returns the authenticated user's own named views plus all shared views for the given table slug."
|
|
4469
|
+
}), ApiQuery({
|
|
4470
|
+
name: "tableSlug",
|
|
4471
|
+
description: "Slug of the table to fetch views for",
|
|
4472
|
+
example: "cloud-providers",
|
|
4473
|
+
required: true
|
|
4474
|
+
}), ApiResponse4({
|
|
4475
|
+
status: 200,
|
|
4476
|
+
description: "Named views retrieved.",
|
|
4477
|
+
type: [
|
|
4478
|
+
DataTableViewDto
|
|
4479
|
+
]
|
|
4480
|
+
}), ApiResponse4({
|
|
4481
|
+
status: 401,
|
|
4482
|
+
description: "Unauthorized."
|
|
4483
|
+
}));
|
|
4484
|
+
}
|
|
4485
|
+
__name(ApiListDataTableViews, "ApiListDataTableViews");
|
|
4486
|
+
function ApiCreateDataTableView() {
|
|
4487
|
+
return applyDecorators4(ApiOperation4({
|
|
4488
|
+
summary: "Create named table view",
|
|
4489
|
+
description: "Saves the current table state as a named view snapshot. The name must be unique per user+table."
|
|
4490
|
+
}), ApiBody2({
|
|
4491
|
+
type: CreateDataTableViewDto
|
|
4492
|
+
}), ApiResponse4({
|
|
4493
|
+
status: 201,
|
|
4494
|
+
description: "Named view created.",
|
|
4495
|
+
type: DataTableViewDto
|
|
4496
|
+
}), ApiResponse4({
|
|
4497
|
+
status: 400,
|
|
4498
|
+
description: "Invalid request body."
|
|
4499
|
+
}), ApiResponse4({
|
|
4500
|
+
status: 401,
|
|
4501
|
+
description: "Unauthorized."
|
|
4502
|
+
}));
|
|
4503
|
+
}
|
|
4504
|
+
__name(ApiCreateDataTableView, "ApiCreateDataTableView");
|
|
4505
|
+
function ApiUpdateDataTableView() {
|
|
4506
|
+
return applyDecorators4(ApiOperation4({
|
|
4507
|
+
summary: "Update named table view",
|
|
4508
|
+
description: "Updates the state of an existing named view. Only the owner can update."
|
|
4509
|
+
}), ApiParam({
|
|
4510
|
+
name: "id",
|
|
4511
|
+
description: "UUID of the table view to update"
|
|
4512
|
+
}), ApiBody2({
|
|
4513
|
+
type: UpdateDataTableViewDto
|
|
4514
|
+
}), ApiResponse4({
|
|
4515
|
+
status: 200,
|
|
4516
|
+
description: "View updated.",
|
|
4517
|
+
type: DataTableViewDto
|
|
4518
|
+
}), ApiResponse4({
|
|
4519
|
+
status: 400,
|
|
4520
|
+
description: "Validation failed or not owned by caller."
|
|
4521
|
+
}), ApiResponse4({
|
|
4522
|
+
status: 401,
|
|
4523
|
+
description: "Unauthorized."
|
|
4524
|
+
}), ApiResponse4({
|
|
4525
|
+
status: 404,
|
|
4526
|
+
description: "View not found."
|
|
4527
|
+
}));
|
|
4528
|
+
}
|
|
4529
|
+
__name(ApiUpdateDataTableView, "ApiUpdateDataTableView");
|
|
4530
|
+
function ApiRenameDataTableView() {
|
|
4531
|
+
return applyDecorators4(ApiOperation4({
|
|
4532
|
+
summary: "Rename a named table view",
|
|
4533
|
+
description: "Updates the display name of an existing view. The new name must be unique per user+table. Only the owner can rename."
|
|
4534
|
+
}), ApiParam({
|
|
4535
|
+
name: "id",
|
|
4536
|
+
description: "UUID of the table view to rename"
|
|
4537
|
+
}), ApiBody2({
|
|
4538
|
+
type: RenameDataTableViewDto
|
|
4539
|
+
}), ApiResponse4({
|
|
4540
|
+
status: 200,
|
|
4541
|
+
description: "View renamed.",
|
|
4542
|
+
type: DataTableViewDto
|
|
4543
|
+
}), ApiResponse4({
|
|
4544
|
+
status: 400,
|
|
4545
|
+
description: "Not owned by caller."
|
|
4546
|
+
}), ApiResponse4({
|
|
4547
|
+
status: 401,
|
|
4548
|
+
description: "Unauthorized."
|
|
4549
|
+
}), ApiResponse4({
|
|
4550
|
+
status: 404,
|
|
4551
|
+
description: "View not found."
|
|
4552
|
+
}), ApiResponse4({
|
|
4553
|
+
status: 409,
|
|
4554
|
+
description: "A view with this name already exists."
|
|
4555
|
+
}));
|
|
4556
|
+
}
|
|
4557
|
+
__name(ApiRenameDataTableView, "ApiRenameDataTableView");
|
|
4558
|
+
function ApiToggleShareDataTableView() {
|
|
4559
|
+
return applyDecorators4(ApiOperation4({
|
|
4560
|
+
summary: "Toggle view sharing",
|
|
4561
|
+
description: "Makes a view visible to all users (shared) or restricts it to the owner only (private). Only the owner can toggle sharing."
|
|
4562
|
+
}), ApiParam({
|
|
4563
|
+
name: "id",
|
|
4564
|
+
description: "UUID of the table view"
|
|
4565
|
+
}), ApiBody2({
|
|
4566
|
+
type: ToggleShareDataTableViewDto
|
|
4567
|
+
}), ApiResponse4({
|
|
4568
|
+
status: 200,
|
|
4569
|
+
description: "Sharing status updated.",
|
|
4570
|
+
type: DataTableViewDto
|
|
4571
|
+
}), ApiResponse4({
|
|
4572
|
+
status: 400,
|
|
4573
|
+
description: "Not owned by caller."
|
|
4574
|
+
}), ApiResponse4({
|
|
4575
|
+
status: 401,
|
|
4576
|
+
description: "Unauthorized."
|
|
4577
|
+
}), ApiResponse4({
|
|
4578
|
+
status: 404,
|
|
4579
|
+
description: "View not found."
|
|
4580
|
+
}));
|
|
4581
|
+
}
|
|
4582
|
+
__name(ApiToggleShareDataTableView, "ApiToggleShareDataTableView");
|
|
4583
|
+
function ApiDeleteDataTableView() {
|
|
4584
|
+
return applyDecorators4(ApiOperation4({
|
|
4585
|
+
summary: "Delete named table view",
|
|
4586
|
+
description: "Permanently deletes a named view. Only the owner can delete their own views."
|
|
4587
|
+
}), ApiParam({
|
|
4588
|
+
name: "id",
|
|
4589
|
+
description: "UUID of the table view to delete"
|
|
4590
|
+
}), ApiResponse4({
|
|
4591
|
+
status: 200,
|
|
4592
|
+
description: "View deleted.",
|
|
4593
|
+
type: DataTableViewDto
|
|
4594
|
+
}), ApiResponse4({
|
|
4595
|
+
status: 400,
|
|
4596
|
+
description: "Not owned by caller."
|
|
4597
|
+
}), ApiResponse4({
|
|
4598
|
+
status: 401,
|
|
4599
|
+
description: "Unauthorized."
|
|
4600
|
+
}), ApiResponse4({
|
|
4601
|
+
status: 404,
|
|
4602
|
+
description: "View not found."
|
|
4603
|
+
}));
|
|
4604
|
+
}
|
|
4605
|
+
__name(ApiDeleteDataTableView, "ApiDeleteDataTableView");
|
|
4606
|
+
|
|
4607
|
+
// src/data-table/views/services/data-table-views.service.ts
|
|
4608
|
+
import { createHash as createHash2 } from "crypto";
|
|
4609
|
+
import { Injectable as Injectable14, Logger as Logger13 } from "@nestjs/common";
|
|
4610
|
+
import { ConfigService as ConfigService7 } from "@nestjs/config";
|
|
4611
|
+
|
|
4612
|
+
// src/data-table/views/repositories/data-table-views.repository.ts
|
|
4613
|
+
import { Inject as Inject4, Injectable as Injectable13 } from "@nestjs/common";
|
|
4614
|
+
import { and as and3, eq as eq3 } from "drizzle-orm";
|
|
4615
|
+
function _ts_decorate35(decorators, target, key, desc2) {
|
|
4616
|
+
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
4617
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc2);
|
|
4618
|
+
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;
|
|
4619
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
4620
|
+
}
|
|
4621
|
+
__name(_ts_decorate35, "_ts_decorate");
|
|
4622
|
+
function _ts_metadata26(k, v) {
|
|
4623
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
4624
|
+
}
|
|
4625
|
+
__name(_ts_metadata26, "_ts_metadata");
|
|
4626
|
+
function _ts_param8(paramIndex, decorator) {
|
|
4627
|
+
return function(target, key) {
|
|
4628
|
+
decorator(target, key, paramIndex);
|
|
4629
|
+
};
|
|
4630
|
+
}
|
|
4631
|
+
__name(_ts_param8, "_ts_param");
|
|
4632
|
+
var NAMED_VIEWS_LIMIT = 100;
|
|
4633
|
+
var DataTableViewsRepository = class extends PrimaryBaseRepository {
|
|
4634
|
+
static {
|
|
4635
|
+
__name(this, "DataTableViewsRepository");
|
|
4636
|
+
}
|
|
4637
|
+
constructor(database, table) {
|
|
4638
|
+
super(database, table);
|
|
4639
|
+
}
|
|
4640
|
+
// Returns personal (non-shared) named views owned by the user for a given table
|
|
4641
|
+
async findPersonalViewsBySlug(userId, tableSlug) {
|
|
4642
|
+
const t = this.table;
|
|
4643
|
+
return this.db.select().from(this.table).where(and3(eq3(t.tableSlug, tableSlug), eq3(t.userId, userId), eq3(t.isShared, false))).orderBy(t.createdAt).limit(NAMED_VIEWS_LIMIT);
|
|
4644
|
+
}
|
|
4645
|
+
// Returns all shared named views for a given table — visible to all users
|
|
4646
|
+
async findSharedViewsBySlug(tableSlug) {
|
|
4647
|
+
const t = this.table;
|
|
4648
|
+
return this.db.select().from(this.table).where(and3(eq3(t.tableSlug, tableSlug), eq3(t.isShared, true))).orderBy(t.createdAt).limit(NAMED_VIEWS_LIMIT);
|
|
4649
|
+
}
|
|
4650
|
+
};
|
|
4651
|
+
DataTableViewsRepository = _ts_decorate35([
|
|
4652
|
+
Injectable13(),
|
|
4653
|
+
_ts_param8(1, Inject4(DATA_TABLE_VIEWS_TABLE)),
|
|
4654
|
+
_ts_metadata26("design:type", Function),
|
|
4655
|
+
_ts_metadata26("design:paramtypes", [
|
|
4656
|
+
typeof PrimaryDatabaseService === "undefined" ? Object : PrimaryDatabaseService,
|
|
4657
|
+
typeof PgTable === "undefined" ? Object : PgTable
|
|
4658
|
+
])
|
|
4659
|
+
], DataTableViewsRepository);
|
|
4660
|
+
|
|
4661
|
+
// src/data-table/views/services/data-table-views.service.ts
|
|
4662
|
+
function _ts_decorate36(decorators, target, key, desc2) {
|
|
4663
|
+
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
4664
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc2);
|
|
4665
|
+
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;
|
|
4666
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
4667
|
+
}
|
|
4668
|
+
__name(_ts_decorate36, "_ts_decorate");
|
|
4669
|
+
function _ts_metadata27(k, v) {
|
|
4670
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
4671
|
+
}
|
|
4672
|
+
__name(_ts_metadata27, "_ts_metadata");
|
|
4673
|
+
function computeChecksum(value) {
|
|
4674
|
+
return createHash2("sha256").update(JSON.stringify(value)).digest("hex");
|
|
4675
|
+
}
|
|
4676
|
+
__name(computeChecksum, "computeChecksum");
|
|
4677
|
+
var DataTableViewsService = class _DataTableViewsService {
|
|
4678
|
+
static {
|
|
4679
|
+
__name(this, "DataTableViewsService");
|
|
4680
|
+
}
|
|
4681
|
+
dataTableViewsRepository;
|
|
4682
|
+
cacheService;
|
|
4683
|
+
configService;
|
|
4684
|
+
logger = new Logger13(_DataTableViewsService.name);
|
|
4685
|
+
constructor(dataTableViewsRepository, cacheService, configService) {
|
|
4686
|
+
this.dataTableViewsRepository = dataTableViewsRepository;
|
|
4687
|
+
this.cacheService = cacheService;
|
|
4688
|
+
this.configService = configService;
|
|
4689
|
+
}
|
|
4690
|
+
// Builds the Redis key for a user's personal (non-shared) views for a given table
|
|
4691
|
+
personalViewsKey(userId, tableSlug) {
|
|
4692
|
+
return `views:personal:${userId}:${tableSlug}`;
|
|
4693
|
+
}
|
|
4694
|
+
// Builds the Redis key for all shared views for a given table — same key for all users
|
|
4695
|
+
sharedViewsKey(tableSlug) {
|
|
4696
|
+
return `views:shared:${tableSlug}`;
|
|
4697
|
+
}
|
|
4698
|
+
// Returns configured TTL for named views in seconds, defaulting to 86400 (24h)
|
|
4699
|
+
get viewsTtl() {
|
|
4700
|
+
return this.configService.get("TABLE_VIEWS_CACHE_TTL") ?? 86400;
|
|
4701
|
+
}
|
|
4702
|
+
// Fetches personal views from cache; falls back to DB and warms cache on miss
|
|
4703
|
+
async getOrCachePersonalViews(userId, tableSlug) {
|
|
4704
|
+
const key = this.personalViewsKey(userId, tableSlug);
|
|
4705
|
+
const cached = await this.cacheService.get(key);
|
|
4706
|
+
if (cached) {
|
|
4707
|
+
this.logger.debug(`Cache hit for personal views: user=${userId}, table=${tableSlug}`);
|
|
4708
|
+
return cached;
|
|
4709
|
+
}
|
|
4710
|
+
const rows = await this.dataTableViewsRepository.findPersonalViewsBySlug(userId, tableSlug);
|
|
4711
|
+
await this.cacheService.set(key, rows, this.viewsTtl);
|
|
4712
|
+
return rows;
|
|
4713
|
+
}
|
|
4714
|
+
// Fetches shared views from cache; falls back to DB and warms cache on miss
|
|
4715
|
+
async getOrCacheSharedViews(tableSlug) {
|
|
4716
|
+
const key = this.sharedViewsKey(tableSlug);
|
|
4717
|
+
const cached = await this.cacheService.get(key);
|
|
4718
|
+
if (cached) {
|
|
4719
|
+
this.logger.debug(`Cache hit for shared views: table=${tableSlug}`);
|
|
4720
|
+
return cached;
|
|
4721
|
+
}
|
|
4722
|
+
const rows = await this.dataTableViewsRepository.findSharedViewsBySlug(tableSlug);
|
|
4723
|
+
await this.cacheService.set(key, rows, this.viewsTtl);
|
|
4724
|
+
return rows;
|
|
4725
|
+
}
|
|
4726
|
+
// Deletes personal and/or shared cache keys based on which pools the mutation affects
|
|
4727
|
+
async invalidateViewsCache(userId, tableSlug, affectsPersonal, affectsShared) {
|
|
4728
|
+
const toDelete = [];
|
|
4729
|
+
if (affectsPersonal) toDelete.push(this.personalViewsKey(userId, tableSlug));
|
|
4730
|
+
if (affectsShared) toDelete.push(this.sharedViewsKey(tableSlug));
|
|
4731
|
+
if (toDelete.length > 0) await this.cacheService.del(...toDelete);
|
|
4732
|
+
}
|
|
4733
|
+
// Returns personal + shared named views — each pool fetched from cache or DB in parallel
|
|
4734
|
+
async findViews(userId, tableSlug) {
|
|
4735
|
+
const [personalRows, sharedRows] = await Promise.all([
|
|
4736
|
+
this.getOrCachePersonalViews(userId, tableSlug),
|
|
4737
|
+
this.getOrCacheSharedViews(tableSlug)
|
|
4738
|
+
]);
|
|
4739
|
+
return [
|
|
4740
|
+
...personalRows,
|
|
4741
|
+
...sharedRows
|
|
4742
|
+
].map((row) => DataTableViewDto.from(row, userId));
|
|
4743
|
+
}
|
|
4744
|
+
// Creates a named snapshot and invalidates the relevant cache pool
|
|
4745
|
+
async createView(userId, dto) {
|
|
4746
|
+
const view = await this.dataTableViewsRepository.create({
|
|
4747
|
+
userId,
|
|
4748
|
+
tableSlug: dto.tableSlug,
|
|
4749
|
+
name: dto.name,
|
|
4750
|
+
state: dto.state,
|
|
4751
|
+
isShared: dto.isShared ?? false
|
|
4752
|
+
});
|
|
4753
|
+
this.logger.log(`Created view "${dto.name}" for user: ${userId}, table: ${dto.tableSlug}`);
|
|
4754
|
+
const isShared = dto.isShared ?? false;
|
|
4755
|
+
await this.invalidateViewsCache(userId, dto.tableSlug, !isShared, isShared);
|
|
4756
|
+
return DataTableViewDto.from(view, userId);
|
|
4757
|
+
}
|
|
4758
|
+
// Updates the state of a named view — skips DB write if state is unchanged
|
|
4759
|
+
async updateView(userId, id, dto) {
|
|
4760
|
+
const view = await this.dataTableViewsRepository.findById(id);
|
|
4761
|
+
if (!view) throw new NotFoundException("Table view not found.");
|
|
4762
|
+
if (view.userId !== userId) throw new BadRequestException("You do not have permission to update this view.");
|
|
4763
|
+
if (computeChecksum(dto.state) === computeChecksum(view.state)) {
|
|
4764
|
+
this.logger.log(`State unchanged for view ${id} \u2014 skipping DB write`);
|
|
4765
|
+
return DataTableViewDto.from(view, userId);
|
|
4766
|
+
}
|
|
4767
|
+
const updated = await this.dataTableViewsRepository.update(id, {
|
|
4768
|
+
state: dto.state
|
|
4769
|
+
});
|
|
4770
|
+
this.logger.log(`Updated state for view ${id}, user: ${userId}`);
|
|
4771
|
+
await this.invalidateViewsCache(userId, view.tableSlug, !view.isShared, view.isShared);
|
|
4772
|
+
return DataTableViewDto.from(updated, userId);
|
|
4773
|
+
}
|
|
4774
|
+
// Toggles the sharing status of a named view — updates both personal and shared cache
|
|
4775
|
+
async toggleShareView(userId, id, isShared) {
|
|
4776
|
+
const view = await this.dataTableViewsRepository.findById(id);
|
|
4777
|
+
if (!view) throw new NotFoundException("Table view not found.");
|
|
4778
|
+
if (view.userId !== userId) throw new BadRequestException("You do not have permission to share this view.");
|
|
4779
|
+
const updated = await this.dataTableViewsRepository.update(id, {
|
|
4780
|
+
isShared
|
|
4781
|
+
});
|
|
4782
|
+
this.logger.log(`Set isShared=${isShared} for view ${id}, user: ${userId}`);
|
|
4783
|
+
await this.invalidateViewsCache(userId, view.tableSlug, true, true);
|
|
4784
|
+
return DataTableViewDto.from(updated, userId);
|
|
4785
|
+
}
|
|
4786
|
+
// Renames a named view — enforces unique name per user+table, invalidates personal cache
|
|
4787
|
+
async renameView(userId, id, name) {
|
|
4788
|
+
const view = await this.dataTableViewsRepository.findById(id);
|
|
4789
|
+
if (!view) throw new NotFoundException("Table view not found.");
|
|
4790
|
+
if (view.userId !== userId) throw new BadRequestException("You do not have permission to rename this view.");
|
|
4791
|
+
const existing = await this.dataTableViewsRepository.findOne({
|
|
4792
|
+
userId,
|
|
4793
|
+
tableSlug: view.tableSlug,
|
|
4794
|
+
name,
|
|
4795
|
+
isShared: false
|
|
4796
|
+
});
|
|
4797
|
+
if (existing && existing.id !== id) {
|
|
4798
|
+
throw new ConflictException({
|
|
4799
|
+
label: "Name Already Taken",
|
|
4800
|
+
detail: "A view with this name already exists for this table.",
|
|
4801
|
+
errors: [
|
|
4802
|
+
{
|
|
4803
|
+
field: "name",
|
|
4804
|
+
message: "Name already taken"
|
|
4805
|
+
}
|
|
4806
|
+
]
|
|
4807
|
+
});
|
|
4808
|
+
}
|
|
4809
|
+
const updated = await this.dataTableViewsRepository.update(id, {
|
|
4810
|
+
name
|
|
4811
|
+
});
|
|
4812
|
+
this.logger.log(`Renamed view ${id} to "${name}" for user: ${userId}`);
|
|
4813
|
+
await this.invalidateViewsCache(userId, view.tableSlug, !view.isShared, view.isShared);
|
|
4814
|
+
return DataTableViewDto.from(updated, userId);
|
|
4815
|
+
}
|
|
4816
|
+
// Deletes a named view and invalidates the relevant cache pool
|
|
4817
|
+
async deleteView(userId, id) {
|
|
4818
|
+
const view = await this.dataTableViewsRepository.findById(id);
|
|
4819
|
+
if (!view) throw new NotFoundException("Table view not found.");
|
|
4820
|
+
if (view.userId !== userId) throw new BadRequestException("You do not have permission to delete this view.");
|
|
4821
|
+
await this.dataTableViewsRepository.delete(id);
|
|
4822
|
+
this.logger.log(`Deleted view ${id} for user: ${userId}`);
|
|
4823
|
+
await this.invalidateViewsCache(userId, view.tableSlug, !view.isShared, view.isShared);
|
|
4824
|
+
return DataTableViewDto.from(view, userId);
|
|
4825
|
+
}
|
|
4826
|
+
};
|
|
4827
|
+
DataTableViewsService = _ts_decorate36([
|
|
4828
|
+
Injectable14(),
|
|
4829
|
+
_ts_metadata27("design:type", Function),
|
|
4830
|
+
_ts_metadata27("design:paramtypes", [
|
|
4831
|
+
typeof DataTableViewsRepository === "undefined" ? Object : DataTableViewsRepository,
|
|
4832
|
+
typeof CacheService === "undefined" ? Object : CacheService,
|
|
4833
|
+
typeof ConfigService7 === "undefined" ? Object : ConfigService7
|
|
4834
|
+
])
|
|
4835
|
+
], DataTableViewsService);
|
|
4836
|
+
|
|
4837
|
+
// src/data-table/views/controllers/data-table-views.controller.ts
|
|
4838
|
+
function _ts_decorate37(decorators, target, key, desc2) {
|
|
4839
|
+
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
4840
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc2);
|
|
4841
|
+
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;
|
|
4842
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
4843
|
+
}
|
|
4844
|
+
__name(_ts_decorate37, "_ts_decorate");
|
|
4845
|
+
function _ts_metadata28(k, v) {
|
|
4846
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
4847
|
+
}
|
|
4848
|
+
__name(_ts_metadata28, "_ts_metadata");
|
|
4849
|
+
function _ts_param9(paramIndex, decorator) {
|
|
4850
|
+
return function(target, key) {
|
|
4851
|
+
decorator(target, key, paramIndex);
|
|
4852
|
+
};
|
|
4853
|
+
}
|
|
4854
|
+
__name(_ts_param9, "_ts_param");
|
|
4855
|
+
var DataTableViewsController = class _DataTableViewsController {
|
|
4856
|
+
static {
|
|
4857
|
+
__name(this, "DataTableViewsController");
|
|
4858
|
+
}
|
|
4859
|
+
dataTableViewsService;
|
|
4860
|
+
logger = new Logger14(_DataTableViewsController.name);
|
|
4861
|
+
constructor(dataTableViewsService) {
|
|
4862
|
+
this.dataTableViewsService = dataTableViewsService;
|
|
4863
|
+
}
|
|
4864
|
+
// Returns all named views for the given table — own plus shared
|
|
4865
|
+
findViews(userId, tableSlug) {
|
|
4866
|
+
this.logger.log(`GET /table-views?tableSlug=${tableSlug} - User: ${userId}`);
|
|
4867
|
+
return this.dataTableViewsService.findViews(userId, tableSlug);
|
|
4868
|
+
}
|
|
4869
|
+
// Creates a named snapshot of the current table state
|
|
4870
|
+
createView(userId, dto) {
|
|
4871
|
+
this.logger.log(`POST /table-views - User: ${userId}, table: ${dto.tableSlug}`);
|
|
4872
|
+
return this.dataTableViewsService.createView(userId, dto);
|
|
4873
|
+
}
|
|
4874
|
+
// Updates state of an existing named view
|
|
4875
|
+
updateView(userId, id, dto) {
|
|
4876
|
+
this.logger.log(`PATCH /table-views/${id} - User: ${userId}`);
|
|
4877
|
+
return this.dataTableViewsService.updateView(userId, id, dto);
|
|
4878
|
+
}
|
|
4879
|
+
// Renames an existing named view — enforces unique name per user+table
|
|
4880
|
+
renameView(userId, id, dto) {
|
|
4881
|
+
this.logger.log(`PATCH /table-views/${id}/rename - User: ${userId}`);
|
|
4882
|
+
return this.dataTableViewsService.renameView(userId, id, dto.name);
|
|
4883
|
+
}
|
|
4884
|
+
// Toggles sharing visibility of a named view
|
|
4885
|
+
toggleShareView(userId, id, dto) {
|
|
4886
|
+
this.logger.log(`PATCH /table-views/${id}/share - User: ${userId}`);
|
|
4887
|
+
return this.dataTableViewsService.toggleShareView(userId, id, dto.isShared);
|
|
4888
|
+
}
|
|
4889
|
+
// Deletes a named view owned by the authenticated user
|
|
4890
|
+
deleteView(userId, id) {
|
|
4891
|
+
this.logger.log(`DELETE /table-views/${id} - User: ${userId}`);
|
|
4892
|
+
return this.dataTableViewsService.deleteView(userId, id);
|
|
4893
|
+
}
|
|
4894
|
+
};
|
|
4895
|
+
_ts_decorate37([
|
|
4896
|
+
Get3(),
|
|
4897
|
+
ApiListDataTableViews(),
|
|
4898
|
+
_ts_param9(0, UserId()),
|
|
4899
|
+
_ts_param9(1, Query("tableSlug")),
|
|
4900
|
+
_ts_metadata28("design:type", Function),
|
|
4901
|
+
_ts_metadata28("design:paramtypes", [
|
|
4902
|
+
String,
|
|
4903
|
+
String
|
|
4904
|
+
]),
|
|
4905
|
+
_ts_metadata28("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
4906
|
+
], DataTableViewsController.prototype, "findViews", null);
|
|
4907
|
+
_ts_decorate37([
|
|
4908
|
+
Post2(),
|
|
4909
|
+
HttpCode3(HttpStatus22.CREATED),
|
|
4910
|
+
ApiCreateDataTableView(),
|
|
4911
|
+
_ts_param9(0, UserId()),
|
|
4912
|
+
_ts_param9(1, Body2()),
|
|
4913
|
+
_ts_metadata28("design:type", Function),
|
|
4914
|
+
_ts_metadata28("design:paramtypes", [
|
|
4915
|
+
String,
|
|
4916
|
+
typeof CreateDataTableViewDto === "undefined" ? Object : CreateDataTableViewDto
|
|
4917
|
+
]),
|
|
4918
|
+
_ts_metadata28("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
4919
|
+
], DataTableViewsController.prototype, "createView", null);
|
|
4920
|
+
_ts_decorate37([
|
|
4921
|
+
Patch(":id"),
|
|
4922
|
+
ApiUpdateDataTableView(),
|
|
4923
|
+
_ts_param9(0, UserId()),
|
|
4924
|
+
_ts_param9(1, Param("id")),
|
|
4925
|
+
_ts_param9(2, Body2()),
|
|
4926
|
+
_ts_metadata28("design:type", Function),
|
|
4927
|
+
_ts_metadata28("design:paramtypes", [
|
|
4928
|
+
String,
|
|
4929
|
+
String,
|
|
4930
|
+
typeof UpdateDataTableViewDto === "undefined" ? Object : UpdateDataTableViewDto
|
|
4931
|
+
]),
|
|
4932
|
+
_ts_metadata28("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
4933
|
+
], DataTableViewsController.prototype, "updateView", null);
|
|
4934
|
+
_ts_decorate37([
|
|
4935
|
+
Patch(":id/rename"),
|
|
4936
|
+
ApiRenameDataTableView(),
|
|
4937
|
+
_ts_param9(0, UserId()),
|
|
4938
|
+
_ts_param9(1, Param("id")),
|
|
4939
|
+
_ts_param9(2, Body2()),
|
|
4940
|
+
_ts_metadata28("design:type", Function),
|
|
4941
|
+
_ts_metadata28("design:paramtypes", [
|
|
4942
|
+
String,
|
|
4943
|
+
String,
|
|
4944
|
+
typeof RenameDataTableViewDto === "undefined" ? Object : RenameDataTableViewDto
|
|
4945
|
+
]),
|
|
4946
|
+
_ts_metadata28("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
4947
|
+
], DataTableViewsController.prototype, "renameView", null);
|
|
4948
|
+
_ts_decorate37([
|
|
4949
|
+
Patch(":id/share"),
|
|
4950
|
+
ApiToggleShareDataTableView(),
|
|
4951
|
+
_ts_param9(0, UserId()),
|
|
4952
|
+
_ts_param9(1, Param("id")),
|
|
4953
|
+
_ts_param9(2, Body2()),
|
|
4954
|
+
_ts_metadata28("design:type", Function),
|
|
4955
|
+
_ts_metadata28("design:paramtypes", [
|
|
4956
|
+
String,
|
|
4957
|
+
String,
|
|
4958
|
+
typeof ToggleShareDataTableViewDto === "undefined" ? Object : ToggleShareDataTableViewDto
|
|
4959
|
+
]),
|
|
4960
|
+
_ts_metadata28("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
4961
|
+
], DataTableViewsController.prototype, "toggleShareView", null);
|
|
4962
|
+
_ts_decorate37([
|
|
4963
|
+
Delete(":id"),
|
|
4964
|
+
ApiDeleteDataTableView(),
|
|
4965
|
+
_ts_param9(0, UserId()),
|
|
4966
|
+
_ts_param9(1, Param("id")),
|
|
4967
|
+
_ts_metadata28("design:type", Function),
|
|
4968
|
+
_ts_metadata28("design:paramtypes", [
|
|
4969
|
+
String,
|
|
4970
|
+
String
|
|
4971
|
+
]),
|
|
4972
|
+
_ts_metadata28("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
4973
|
+
], DataTableViewsController.prototype, "deleteView", null);
|
|
4974
|
+
DataTableViewsController = _ts_decorate37([
|
|
4975
|
+
ApiTags4("Table Views"),
|
|
4976
|
+
ApiBearerAuth2(),
|
|
4977
|
+
RequireSession("CLOUD", "ADMIN"),
|
|
4978
|
+
Controller4("table-views"),
|
|
4979
|
+
_ts_metadata28("design:type", Function),
|
|
4980
|
+
_ts_metadata28("design:paramtypes", [
|
|
4981
|
+
typeof DataTableViewsService === "undefined" ? Object : DataTableViewsService
|
|
4982
|
+
])
|
|
4983
|
+
], DataTableViewsController);
|
|
4984
|
+
|
|
4985
|
+
// src/data-table/data-table.module.ts
|
|
4986
|
+
function _ts_decorate38(decorators, target, key, desc2) {
|
|
4987
|
+
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
4988
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc2);
|
|
4989
|
+
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;
|
|
4990
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
4991
|
+
}
|
|
4992
|
+
__name(_ts_decorate38, "_ts_decorate");
|
|
4993
|
+
var DataTableModule = class _DataTableModule {
|
|
4994
|
+
static {
|
|
4995
|
+
__name(this, "DataTableModule");
|
|
4996
|
+
}
|
|
4997
|
+
static forRoot(options) {
|
|
4998
|
+
return {
|
|
4999
|
+
global: true,
|
|
5000
|
+
module: _DataTableModule,
|
|
5001
|
+
imports: [
|
|
5002
|
+
ConfigModule4,
|
|
5003
|
+
CacheModule
|
|
5004
|
+
],
|
|
5005
|
+
controllers: [
|
|
5006
|
+
DataTableStateController,
|
|
5007
|
+
DataTableViewsController
|
|
5008
|
+
],
|
|
5009
|
+
providers: [
|
|
5010
|
+
{
|
|
5011
|
+
provide: DATA_TABLE_VIEWS_TABLE,
|
|
5012
|
+
useValue: options.tableViews
|
|
5013
|
+
},
|
|
5014
|
+
DataTableViewsService,
|
|
5015
|
+
DataTableViewsRepository,
|
|
5016
|
+
DataTableStateService
|
|
5017
|
+
],
|
|
5018
|
+
exports: [
|
|
5019
|
+
DataTableViewsService,
|
|
5020
|
+
DataTableStateService
|
|
5021
|
+
]
|
|
5022
|
+
};
|
|
5023
|
+
}
|
|
5024
|
+
};
|
|
5025
|
+
DataTableModule = _ts_decorate38([
|
|
5026
|
+
Module8({})
|
|
5027
|
+
], DataTableModule);
|
|
5028
|
+
|
|
5029
|
+
// src/drizzle-pg-core.ts
|
|
5030
|
+
var drizzle_pg_core_exports = {};
|
|
5031
|
+
__reExport(drizzle_pg_core_exports, pg_core_star);
|
|
5032
|
+
import * as pg_core_star from "drizzle-orm/pg-core";
|
|
5033
|
+
|
|
5034
|
+
// src/data-table/schema/data-table-views.table.ts
|
|
5035
|
+
function dataTableViewsColumns() {
|
|
5036
|
+
return {
|
|
5037
|
+
id: (0, drizzle_pg_core_exports.uuid)("id").primaryKey().defaultRandom(),
|
|
5038
|
+
userId: (0, drizzle_pg_core_exports.uuid)("user_id").notNull(),
|
|
5039
|
+
tableSlug: (0, drizzle_pg_core_exports.varchar)("table_slug", {
|
|
5040
|
+
length: 100
|
|
5041
|
+
}).notNull(),
|
|
5042
|
+
name: (0, drizzle_pg_core_exports.varchar)("name", {
|
|
5043
|
+
length: 100
|
|
5044
|
+
}).notNull(),
|
|
5045
|
+
state: (0, drizzle_pg_core_exports.jsonb)("state").notNull().$type(),
|
|
5046
|
+
isShared: (0, drizzle_pg_core_exports.boolean)("is_shared").notNull().default(false),
|
|
5047
|
+
createdAt: (0, drizzle_pg_core_exports.timestamp)("created_at", {
|
|
5048
|
+
withTimezone: true
|
|
5049
|
+
}).notNull().defaultNow(),
|
|
5050
|
+
updatedAt: (0, drizzle_pg_core_exports.timestamp)("updated_at", {
|
|
5051
|
+
withTimezone: true
|
|
5052
|
+
}).$onUpdate(() => /* @__PURE__ */ new Date())
|
|
5053
|
+
};
|
|
5054
|
+
}
|
|
5055
|
+
__name(dataTableViewsColumns, "dataTableViewsColumns");
|
|
5056
|
+
function dataTableViewsIndexes(table) {
|
|
5057
|
+
return [
|
|
5058
|
+
(0, drizzle_pg_core_exports.index)("table_views_user_table_idx").on(table.userId, table.tableSlug),
|
|
5059
|
+
(0, drizzle_pg_core_exports.index)("table_views_shared_slug_idx").on(table.tableSlug, table.isShared),
|
|
5060
|
+
(0, drizzle_pg_core_exports.uniqueIndex)("table_views_user_table_name_shared_unique").on(table.userId, table.tableSlug, table.name, table.isShared)
|
|
5061
|
+
];
|
|
5062
|
+
}
|
|
5063
|
+
__name(dataTableViewsIndexes, "dataTableViewsIndexes");
|
|
4236
5064
|
export {
|
|
4237
5065
|
AccessToken,
|
|
4238
5066
|
AuthConfigModule,
|
|
@@ -4244,7 +5072,14 @@ export {
|
|
|
4244
5072
|
ConflictException,
|
|
4245
5073
|
CookieDomain,
|
|
4246
5074
|
CorrelationIdMiddleware,
|
|
5075
|
+
CreateDataTableViewDto,
|
|
5076
|
+
CreateResponseDto,
|
|
5077
|
+
DATA_TABLE_VIEWS_TABLE,
|
|
4247
5078
|
DEFAULT_CORRELATION_HEADER,
|
|
5079
|
+
DataTableModule,
|
|
5080
|
+
DataTableStateService,
|
|
5081
|
+
DataTableViewDto,
|
|
5082
|
+
DataTableViewsService,
|
|
4248
5083
|
DatabaseModule,
|
|
4249
5084
|
EmailModule,
|
|
4250
5085
|
EmailService,
|
|
@@ -4254,6 +5089,8 @@ export {
|
|
|
4254
5089
|
HttpExceptionFilter,
|
|
4255
5090
|
HttpLoggerInterceptor,
|
|
4256
5091
|
HttpProblemException,
|
|
5092
|
+
ImportResponseDto,
|
|
5093
|
+
ImportSummaryDto,
|
|
4257
5094
|
InternalServerErrorException,
|
|
4258
5095
|
JwtAuthService,
|
|
4259
5096
|
LOGGER_MODULE_OPTIONS,
|
|
@@ -4263,40 +5100,44 @@ export {
|
|
|
4263
5100
|
NotAcceptableException,
|
|
4264
5101
|
NotFoundException,
|
|
4265
5102
|
NotImplementedException,
|
|
4266
|
-
Onboarding,
|
|
4267
5103
|
PayloadTooLargeException,
|
|
4268
5104
|
PrimaryBaseRepository,
|
|
4269
5105
|
PrimaryDatabaseService,
|
|
4270
5106
|
Public,
|
|
4271
|
-
|
|
5107
|
+
REQUIRE_SESSION_KEY,
|
|
4272
5108
|
RedisCacheProvider,
|
|
4273
5109
|
RefreshCookieOptions,
|
|
4274
5110
|
RefreshTokenCookie,
|
|
5111
|
+
RenameDataTableViewDto,
|
|
4275
5112
|
RequestTimeoutException,
|
|
4276
|
-
|
|
5113
|
+
RequireSession,
|
|
4277
5114
|
RootModule,
|
|
4278
5115
|
SKIP_CSRF_KEY,
|
|
4279
5116
|
SelectOptionsQueryDto,
|
|
4280
5117
|
ServiceUnavailableException,
|
|
4281
5118
|
SessionData,
|
|
4282
5119
|
SkipCsrf,
|
|
5120
|
+
Subdomain,
|
|
4283
5121
|
SuccessResponseDto,
|
|
4284
5122
|
TableResponseDto,
|
|
4285
|
-
|
|
4286
|
-
TenantBaseRepository,
|
|
4287
|
-
TenantContextService,
|
|
4288
|
-
TenantDatabaseService,
|
|
5123
|
+
ToggleShareDataTableViewDto,
|
|
4289
5124
|
TokenType,
|
|
4290
5125
|
TooManyRequestsException,
|
|
4291
5126
|
UnauthorizedException,
|
|
4292
5127
|
UnprocessableEntityException,
|
|
4293
5128
|
UnsupportedMediaTypeException,
|
|
5129
|
+
UpdateDataTableViewDto,
|
|
5130
|
+
UploadedFile,
|
|
5131
|
+
UpsertDataTableStateDto,
|
|
4294
5132
|
UserId,
|
|
5133
|
+
ValidatedRowDto,
|
|
4295
5134
|
ValidationException,
|
|
4296
5135
|
VrittiAuthGuard,
|
|
4297
5136
|
addCorrelationIdToResponse,
|
|
4298
5137
|
configureApiSdk,
|
|
4299
5138
|
correlationStorage,
|
|
5139
|
+
dataTableViewsColumns,
|
|
5140
|
+
dataTableViewsIndexes,
|
|
4300
5141
|
defineConfig,
|
|
4301
5142
|
extractCountryFromPhone,
|
|
4302
5143
|
generateCorrelationId,
|