zync-nest-data-module 1.1.38 → 1.1.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/redis/redis.module.js +2 -3
  2. package/dist/redis/redis.module.js.map +1 -1
  3. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  4. package/package.json +4 -5
  5. package/dist/index.js.map +0 -1
  6. package/libs/src/app.controller.ts +0 -91
  7. package/libs/src/app.module.ts +0 -34
  8. package/libs/src/backup/backup.config.ts +0 -45
  9. package/libs/src/backup/backup.interface.ts +0 -21
  10. package/libs/src/backup/backup.module.ts +0 -11
  11. package/libs/src/backup/backup.service.ts +0 -274
  12. package/libs/src/backup/backup.worker.ts +0 -35
  13. package/libs/src/backup/index.ts +0 -4
  14. package/libs/src/base/dto.ts +0 -46
  15. package/libs/src/base/index.ts +0 -2
  16. package/libs/src/base/resolver.ts +0 -20
  17. package/libs/src/database/database.module.ts +0 -28
  18. package/libs/src/database/database.repository.ts +0 -355
  19. package/libs/src/database/database.scheme.ts +0 -128
  20. package/libs/src/database/database.service.ts +0 -75
  21. package/libs/src/database/database.sync.ts +0 -61
  22. package/libs/src/database/database.transaction.ts +0 -99
  23. package/libs/src/database/database.uniqueId.ts +0 -90
  24. package/libs/src/database/database.utils.ts +0 -99
  25. package/libs/src/database/index.ts +0 -8
  26. package/libs/src/index.ts +0 -5
  27. package/libs/src/main.ts +0 -52
  28. package/libs/src/redis/index.ts +0 -2
  29. package/libs/src/redis/redis.interface.ts +0 -34
  30. package/libs/src/redis/redis.module.ts +0 -30
  31. package/libs/src/redis/redis.service.ts +0 -137
  32. package/libs/src/service/index.ts +0 -1
  33. package/libs/src/service/service.ts +0 -181
  34. package/libs/src/test/test.controller.ts +0 -59
  35. package/libs/src/test/test.dto.ts +0 -41
  36. package/libs/src/test/test.module.ts +0 -24
  37. package/libs/src/test/test.redis.ts +0 -19
  38. package/libs/src/test/test.repository.ts +0 -44
  39. package/libs/src/test/test.resolver.ts +0 -35
  40. package/libs/src/test/test.schema.ts +0 -21
  41. package/libs/src/test/test.service.ts +0 -19
  42. package/libs/tsconfig.lib.json +0 -19
  43. package/tsconfig.json +0 -29
  44. package/update-links.js +0 -159
@@ -1,355 +0,0 @@
1
- import { Inject } from "@nestjs/common";
2
- import { InjectModel } from "@nestjs/mongoose";
3
- import moment from "moment";
4
- import { Document, FilterQuery, PipelineStage, Schema, Types } from "mongoose";
5
- import { SoftDeleteModel } from "mongoose-delete";
6
- import { handlePageFacet, handlePageResult, IPageParams, IPageResult } from "./database.scheme";
7
- import { TransactionManager, TransactionSession } from "./database.transaction";
8
- import { ApUniqueIdGenerator, UniqueKeyTypes } from "./database.uniqueId";
9
- import { DbUtils } from "./database.utils";
10
-
11
- export interface IRefInput<T> {
12
- prefix?: string;
13
- key?: UniqueKeyTypes;
14
- filter?: FilterQuery<T>;
15
- }
16
-
17
- export abstract class AbstractBaseRepository<T extends Omit<Document<any, any, any>, "delete">> {
18
- private _session: TransactionSession = null;
19
-
20
- @Inject(ApUniqueIdGenerator)
21
- private uniqueIdGenerator: ApUniqueIdGenerator;
22
-
23
- constructor(
24
- @InjectModel("MODEL_NAME")
25
- protected dbModel: SoftDeleteModel<T>
26
- ) { }
27
-
28
- protected abstract buildQuery(query: Partial<T>): any;
29
-
30
- protected abstract mapData?(data: any, isCreate: boolean): Promise<T>;
31
-
32
- public get session(): TransactionSession {
33
- return TransactionManager.getCurrentSession();
34
- }
35
-
36
- private get hasSession(): boolean {
37
- const session = this.session;
38
- return !!session && !session.hasEnded;
39
- }
40
-
41
- protected mapIds(query: Partial<T>): any {
42
- return DbUtils.mapToObjectIds<T>(query);
43
- }
44
-
45
- public async create(data: Partial<T> | T): Promise<T> {
46
- return await this._create(data);
47
- }
48
-
49
- public async findById(id: string): Promise<T> {
50
- return await this._findById(id);
51
- }
52
-
53
- public async findOne(query: FilterQuery<T>): Promise<T> {
54
- return await this._findOne(query);
55
- }
56
-
57
- public async find(query: FilterQuery<T>): Promise<T[]> {
58
- return await this._find(query);
59
- }
60
-
61
- public async update(id: string, data: Partial<T>): Promise<T> {
62
- return this._update(id, data);
63
- }
64
-
65
- public async updateMany(query: Partial<T>, model: Partial<T>): Promise<void> {
66
- await this._updateMany(query, model);
67
- }
68
-
69
- public async delete(id: string): Promise<void> {
70
- await this._delete(id);
71
- }
72
-
73
- public async deleteMany(query: Partial<T>): Promise<void> {
74
- await this._deleteMany(query);
75
- }
76
-
77
- public async aggregate(pipeline?: PipelineStage[]): Promise<T[]> {
78
- if (!this.hasSession) return await this.dbModel.aggregate(pipeline).session(this.session);
79
-
80
- return await this.dbModel.aggregate(pipeline).session(this.session);
81
- }
82
-
83
- public async page(page: IPageParams): Promise<IPageResult<T>> {
84
- return await this._page(page);
85
- }
86
-
87
- public async findLast(query: FilterQuery<T>): Promise<T> {
88
- return await this._findLast(query);
89
- }
90
-
91
- public async count(query?: FilterQuery<T>): Promise<number> {
92
- return this._count(query);
93
- }
94
-
95
- protected async _count(query: FilterQuery<T>): Promise<number> {
96
- query = this.mapIds(query) as FilterQuery<T>;
97
-
98
- return this.dbModel.countDocuments(query as FilterQuery<T>);
99
- }
100
-
101
- protected async _create(data: Partial<T> | T): Promise<T> {
102
- const payload: any = await this._mapData(data, true);
103
-
104
- payload.ref = payload.ref || (await this.generateRef());
105
-
106
- payload._id = payload._id ? new Types.ObjectId(payload._id?.toString()) : this.getObjectId();
107
-
108
- const created = new this.dbModel(payload);
109
-
110
- if (!this.hasSession) {
111
- const saved = await created.save();
112
-
113
- return await this.findById(saved._id);
114
- }
115
-
116
- const saved = await created.save({ session: this.session });
117
-
118
- return await this.findById(saved._id);
119
- }
120
-
121
- protected async _findById(id: string): Promise<T> {
122
- const findQuery = this.dbModel.findById(new Types.ObjectId(id?.toString()));
123
-
124
- if (this.hasSession) {
125
- findQuery.session(this.session);
126
- }
127
-
128
- return findQuery.exec().then((res) => (res as any)?._doc || res);
129
- }
130
-
131
- protected async _findOne(query: FilterQuery<T>): Promise<T> {
132
- query = this.mapIds(query) as FilterQuery<T>;
133
-
134
- const findQuery = this.dbModel.findOne(query as FilterQuery<T>);
135
-
136
- if (this.hasSession) {
137
- findQuery.session(this.session);
138
- }
139
-
140
- return findQuery.exec().then((res) => (res as any)?._doc || res);
141
- }
142
-
143
- protected async _find(query: FilterQuery<T>): Promise<T[]> {
144
- if (query._id) {
145
- query._id = new Types.ObjectId(query._id);
146
- }
147
-
148
- query = this.mapIds(query) as FilterQuery<T>;
149
-
150
- const findQuery = this.dbModel.find(query as FilterQuery<T>).sort({ createdAt: -1 });
151
-
152
- if (this.hasSession) {
153
- findQuery.session(this.session);
154
- }
155
-
156
- return findQuery.exec().then((res) => (res as any)?._doc || res);
157
- }
158
-
159
- protected async _update(id: string, data: Partial<T>): Promise<T> {
160
- delete data._id;
161
-
162
- const payload = await this._mapData(data, false);
163
-
164
- await this.dbModel
165
- .updateOne(
166
- { _id: new Types.ObjectId(id) },
167
- { $set: payload },
168
- {
169
- ...(this.hasSession && { session: this.session })
170
- }
171
- )
172
- .exec();
173
-
174
- return await this.findById(id);
175
- }
176
-
177
- private async _mapData(data: Partial<T>, isNew: boolean): Promise<Partial<T>> {
178
- const payload: any = await this.mapData(data, isNew);
179
-
180
- payload.documentNo = payload.documentNo || (await this.generateRef());
181
- payload.documentDate = payload.documentDate ? moment(payload.documentDate).valueOf() : moment().valueOf();
182
-
183
- if (isNew) {
184
- // payload.createdBy = payload.createdBy || this.contextUser?._id;
185
- payload.ref = payload.ref || (await this.generateRef());
186
- payload.createdAt = payload.createdAt ? moment(payload.createdAt).valueOf() : moment().valueOf();
187
- } else {
188
- // payload.updatedBy = payload.updatedBy || this.contextUser?._id;
189
- payload.updatedAt = payload.updatedAt ? moment(payload.updatedAt).valueOf() : moment().valueOf();
190
- }
191
-
192
- return this.mapIds(payload);
193
- }
194
-
195
- protected async _updateMany(query: Partial<T>, model: Partial<T>): Promise<void> {
196
- delete model._id;
197
-
198
- query = this.mapIds(query) as FilterQuery<T>;
199
-
200
- model = this.mapIds(model);
201
-
202
- await this.dbModel
203
- .updateMany(
204
- query as FilterQuery<T>,
205
- {
206
- ...model,
207
- updatedAt: Date.now()
208
- },
209
- this.hasSession ? { new: true, session: this.session } : {}
210
- )
211
- .exec();
212
- }
213
-
214
- protected async _delete(id: string): Promise<void> {
215
- const deleteQuery = this.dbModel.findByIdAndDelete(new Types.ObjectId(id));
216
-
217
- if (this.hasSession) {
218
- deleteQuery.session(this.session);
219
- }
220
-
221
- await deleteQuery.exec();
222
- }
223
-
224
- protected async _deleteMany(query: Partial<T>): Promise<void> {
225
- query = this.mapIds(query) as FilterQuery<T>;
226
-
227
- const deleteQuery = this.dbModel.deleteMany(query as FilterQuery<T>);
228
-
229
- if (this.hasSession) {
230
- deleteQuery.session(this.session);
231
- }
232
-
233
- await deleteQuery.exec();
234
- }
235
-
236
- protected async _findLast(query: FilterQuery<T>): Promise<T> {
237
- query = this.mapIds(query) as FilterQuery<T>;
238
-
239
- const findQuery = this.dbModel
240
- .find(query as FilterQuery<T>)
241
- .limit(1)
242
- .sort({ createdAt: -1 });
243
-
244
- if (this.hasSession) {
245
- findQuery.session(this.session);
246
- }
247
-
248
- const result = await findQuery.exec();
249
-
250
- return result.length === 0 ? null : result[0];
251
- }
252
-
253
- protected async _page(page: IPageParams, $lookups?: any[]): Promise<IPageResult<T>> {
254
- const query = this.buildQuery(page as any);
255
- return this.aggregate([
256
- ...(!!$lookups?.length ? $lookups : []),
257
- ...(Array.isArray(query) ? query : [query]),
258
- { $sort: { createdAt: -1 } },
259
- { ...handlePageFacet(page) }
260
- ]).then(handlePageResult<T>);
261
- }
262
-
263
- public schemaKeysQuery(
264
- schema: Schema,
265
- options: {
266
- query: Partial<
267
- T & {
268
- fromDate?: number;
269
- toDate?: number;
270
- ref?: string;
271
- keyword?: string;
272
- }
273
- >;
274
- searchKeys?: string[];
275
- ignoreKeys?: string[];
276
- dateKey?: string;
277
- },
278
- addMatchCondition: (condition: any) => void
279
- ) {
280
- const { query, ignoreKeys } = options;
281
-
282
- Object.keys(schema.obj).forEach((key) => {
283
- if (query[key] && !ignoreKeys?.includes(key)) {
284
- addMatchCondition({
285
- [key]: schema.path(key).instance === "Mixed" ? this.toObjectId(query[key]) : query[key]
286
- });
287
- }
288
- });
289
-
290
- if (query?.keyword && options.searchKeys?.length) {
291
- const keywordValue = query?.keyword;
292
- const globalSearchConditions = options.searchKeys?.map((field) => ({
293
- [field]: { $regex: keywordValue, $options: "i" }
294
- }));
295
-
296
- if (globalSearchConditions.length > 0) {
297
- addMatchCondition({ $or: globalSearchConditions });
298
- }
299
- }
300
-
301
- if (query?.fromDate && query?.toDate) {
302
- addMatchCondition({
303
- [options.dateKey || "createdAt"]: {
304
- $gte: moment(query.fromDate).startOf("day").valueOf(),
305
- $lte: moment(query.toDate).endOf("day").valueOf()
306
- }
307
- });
308
- }
309
-
310
- if (query?.ref) {
311
- addMatchCondition({
312
- ref: { $regex: query.ref?.toUpperCase(), $options: "i" }
313
- });
314
- }
315
- }
316
-
317
- public cleanupQuery(andConditions: any[]) {
318
- // If no conditions, match all
319
- if (andConditions.length === 0) {
320
- return { $match: {} };
321
- }
322
-
323
- // If only one condition, avoid unnecessary $and
324
- if (andConditions.length === 1) {
325
- return { $match: andConditions[0] };
326
- }
327
-
328
- // Otherwise, use $and
329
- return { $match: { $and: andConditions } };
330
- }
331
-
332
- public getObjectId(): string {
333
- return this.uniqueIdGenerator.getObjectId();
334
- }
335
-
336
- public toObjectId(value: string | any): Types.ObjectId {
337
- return new Types.ObjectId(value?.toString());
338
- }
339
-
340
- public async generateUniqueId(key: UniqueKeyTypes): Promise<number> {
341
- return await this.uniqueIdGenerator.getNextUniqueId(key);
342
- }
343
-
344
- public async generateRef(ref?: IRefInput<T>): Promise<string> {
345
- const key = ref?.key || "uniqueId";
346
- const prefix = ref?.prefix;
347
- try {
348
- const newRefNumber = await this.uniqueIdGenerator.getNextUniqueId(key);
349
- return prefix ? `${prefix}-${newRefNumber}` : `${newRefNumber}`;
350
- } catch (error) {
351
- console.error("Error generating reference:", error);
352
- throw new Error("Failed to generate reference");
353
- }
354
- }
355
- }
@@ -1,128 +0,0 @@
1
- import { Prop } from "@nestjs/mongoose";
2
- import moment from "moment";
3
- import mongoose, { Types } from "mongoose";
4
-
5
- export class BaseSchema {
6
- @Prop({ set: (val) => BaseSchema.toObjectId(val) })
7
- _id: Types.ObjectId;
8
- @Prop({ required: true, unique: true })
9
- ref: string;
10
- @Prop({ set: (val) => BaseSchema.toObjectId(val) })
11
- branchId: Types.ObjectId;
12
- @Prop({ set: (val) => moment(val).valueOf() })
13
- createdAt: number;
14
- @Prop({ set: (val) => new Types.ObjectId(val) })
15
- createdBy: Types.ObjectId;
16
- @Prop({ set: (val) => moment(val).valueOf() })
17
- updatedAt: number;
18
- @Prop({ set: (val) => new Types.ObjectId(val) })
19
- updatedBy: Types.ObjectId;
20
- @Prop({ set: (val) => moment(val).valueOf() })
21
- deletedAt: number;
22
- @Prop({ set: (val) => new Types.ObjectId(val) })
23
- deletedBy: Types.ObjectId;
24
- @Prop({ set: (val) => moment(val).valueOf() })
25
- deleted: boolean;
26
-
27
- @Prop({ set: (val) => moment(val).valueOf() })
28
- documentDate: number;
29
-
30
- @Prop({
31
- validate: {
32
- validator: function (v) {
33
- // If empty, skip unique validation (allow multiple empty)
34
- return (
35
- v === undefined ||
36
- v === null ||
37
- v === "" ||
38
- this.model(this.constructor.modelName)
39
- .countDocuments({
40
- documentNumber: v,
41
- _id: { $ne: BaseSchema.toObjectId(this._id) },
42
- })
43
- .then((count) => count === 0)
44
- );
45
- },
46
- message: "documentNumber must be unique if not empty",
47
- },
48
- })
49
- documentNumber: string;
50
-
51
- @Prop({ default: false })
52
- canDelete: boolean;
53
-
54
- @Prop({ default: false })
55
- canUpdate: boolean;
56
-
57
- @Prop({ set: (val) => (val === "" ? undefined : new Types.ObjectId(val)) })
58
- storeId: Types.ObjectId;
59
-
60
- @Prop({ set: (val) => new Types.ObjectId(val) })
61
- companyId: Types.ObjectId;
62
-
63
- @Prop({ set: (val) => new Types.ObjectId(val) })
64
- employeeId: Types.ObjectId;
65
-
66
- static toObjectId(value: string) {
67
- try {
68
- if (Array.isArray(value)) {
69
- return value.map((v) =>
70
- mongoose.Types.ObjectId.isValid(v)
71
- ? new mongoose.Types.ObjectId(v)
72
- : v
73
- );
74
- } else if (mongoose.Types.ObjectId.isValid(value)) {
75
- return new mongoose.Types.ObjectId(value);
76
- } else if (typeof value === "string") {
77
- return new mongoose.Types.ObjectId(value);
78
- }
79
- } catch (error) {
80
- return value;
81
- }
82
- }
83
-
84
- static toUnixTimestamp(value: number | string) {
85
- if (!value) {
86
- return moment().valueOf();
87
- }
88
-
89
- if (typeof value === "number" && !Number.isNaN(value)) {
90
- return value;
91
- }
92
-
93
- const momentDate = moment(value);
94
- if (momentDate.isValid()) {
95
- return momentDate.valueOf();
96
- }
97
-
98
- return moment().valueOf();
99
- }
100
- }
101
-
102
- export interface IPageParams extends BaseSchema {
103
- skip: number;
104
- take: number;
105
- }
106
-
107
- export interface IPageResult<T> {
108
- totalRecords: number;
109
- data: Array<T>;
110
- }
111
-
112
- export const handlePageFacet = (page: any, $lookups?: any[]) => {
113
- return {
114
- $facet: {
115
- data: [{ $skip: Number(page.skip) }, { $limit: Number(page.take) }, ...($lookups || [])],
116
- totalRecords: [{ $count: "count" }],
117
- },
118
- };
119
- };
120
-
121
- export const handlePageResult = <T>(res: any): IPageResult<T> => {
122
- let rs = res[0] as any;
123
- if (rs.totalRecords.length)
124
- rs = { ...rs, totalRecords: rs.totalRecords[0].count };
125
- else rs = { ...rs, totalRecords: 0 };
126
-
127
- return rs;
128
- };
@@ -1,75 +0,0 @@
1
- import { Injectable, Logger } from "@nestjs/common";
2
- import mongoose, { Connection } from "mongoose";
3
-
4
- @Injectable()
5
- export class DatabaseService {
6
- private readonly logger = new Logger(DatabaseService.name);
7
- private readonly connections: Map<string, Promise<Connection>> = new Map();
8
- private _connectionId: string = process.env.mongodb_default_db_name;
9
-
10
- /**
11
- * Create or retrieve a connection for a given DB name (company/module)
12
- */
13
- public async setConnection(companyId?: string): Promise<Connection> {
14
- const dbName = companyId || process.env.mongodb_default_db_name;
15
-
16
- // ✅ Reuse existing pending/established connection
17
- if (this.connections.has(dbName)) {
18
- return this.connections.get(dbName);
19
- }
20
-
21
- const mongoUri = process.env.mongodb_url?.replace("{{db_name}}", dbName);
22
- if (!mongoUri) {
23
- throw new Error("mongodb_url not defined in environment variables");
24
- }
25
-
26
- this.logger.log(`Connecting to MongoDB: ${dbName}`);
27
-
28
- // ✅ Store promise immediately to prevent race conditions
29
- const connectionPromise = mongoose.createConnection(mongoUri).asPromise();
30
-
31
- this.connections.set(dbName, connectionPromise);
32
-
33
- // ✅ Wait until fully connected
34
- const connection = await connectionPromise;
35
-
36
- connection.on("connected", () =>
37
- this.logger.log(`✅ MongoDB connected: ${dbName}`)
38
- );
39
- connection.on("disconnected", () => {
40
- this.logger.warn(`⚠️ MongoDB disconnected: ${dbName}`);
41
- this.connections.delete(dbName);
42
- });
43
- connection.on("error", (err) => {
44
- this.logger.error(`❌ MongoDB error on ${dbName}: ${err.message}`);
45
- this.connections.delete(dbName);
46
- });
47
-
48
- return connection;
49
- }
50
-
51
- /**
52
- * Retrieve an existing connection, or create one if missing
53
- */
54
- public async getConnection(companyId?: string): Promise<Connection> {
55
- const dbName = companyId || this._connectionId;
56
-
57
- if (this.connections.has(dbName)) {
58
- return this.connections.get(dbName);
59
- }
60
-
61
- return this.setConnection(dbName);
62
- }
63
-
64
- /**
65
- * Gracefully close all open connections
66
- */
67
- public async closeAll(): Promise<void> {
68
- for (const [dbName, connPromise] of this.connections.entries()) {
69
- const conn = await connPromise;
70
- await conn.close();
71
- this.logger.log(`Closed MongoDB connection: ${dbName}`);
72
- }
73
- this.connections.clear();
74
- }
75
- }
@@ -1,61 +0,0 @@
1
- import { config } from "dotenv";
2
- import { MongoClient, ChangeStreamDocument, Document, WithId } from "mongodb";
3
- config();
4
-
5
- interface RealtimeSyncOptions {
6
- sourceUri: string;
7
- targetUri: string;
8
- collection: string;
9
- sourceClient: MongoClient;
10
- targetClient: MongoClient;
11
- }
12
-
13
- export async function realtimeSync(options: RealtimeSyncOptions): Promise<void> {
14
- const { sourceUri, targetUri, collection, sourceClient, targetClient } = options;
15
- // const sourceClient = new MongoClient(sourceUri);
16
- // const targetClient = new MongoClient(targetUri);
17
-
18
- await sourceClient.connect();
19
- await targetClient.connect();
20
-
21
- // Extract DB name from connection string
22
- const getDbName = (uri: string) => {
23
- const match = uri.match(/\/(\w+)(\?|$)/);
24
- return match ? match[1] : undefined;
25
- };
26
- const sourceDB = getDbName(sourceUri);
27
- const targetDB = getDbName(targetUri);
28
-
29
- console.log(`🔗 Connected to source DB: ${sourceDB}`);
30
- console.log(`🔗 Connected to target DB: ${targetDB}`);
31
-
32
- if (!sourceDB || !targetDB) {
33
- throw new Error("Database name must be specified in both connection strings.");
34
- }
35
-
36
- const source = sourceClient.db(sourceDB).collection(collection);
37
- const target = targetClient.db(targetDB).collection(collection);
38
-
39
- const changeStream = source.watch([], { fullDocument: "updateLookup" });
40
-
41
- console.log(`📡 Watching changes on ${sourceDB}.${collection}...`);
42
-
43
- changeStream.on("change", async (change: ChangeStreamDocument<WithId<Document>>) => {
44
- const { operationType, fullDocument, documentKey }: any = change;
45
-
46
- try {
47
- if (operationType === "insert" && fullDocument) {
48
- // Use replaceOne with upsert to handle duplicates gracefully
49
- await target.replaceOne({ _id: fullDocument._id }, fullDocument, { upsert: true });
50
- } else if ((operationType === "update" || operationType === "replace") && fullDocument) {
51
- await target.replaceOne({ _id: documentKey._id }, fullDocument, { upsert: true });
52
- } else if (operationType === "delete") {
53
- await target.deleteOne({ _id: documentKey._id });
54
- }
55
-
56
- console.log(`🔄 Synced ${operationType} for _id=${documentKey._id}`);
57
- } catch (error) {
58
- console.error(`❌ Error syncing ${operationType} for _id=${documentKey._id}:`, error);
59
- }
60
- });
61
- }