zync-nest-data-module 1.1.38 → 1.1.41
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/database/database.repository.d.ts +0 -1
- package/dist/database/database.repository.js +0 -1
- package/dist/database/database.repository.js.map +1 -1
- package/dist/database/database.scheme.d.ts +1 -1
- package/dist/database/database.scheme.js +5 -5
- package/dist/database/database.scheme.js.map +1 -1
- package/dist/database/database.transaction.d.ts +1 -1
- package/dist/database/database.transaction.js +12 -3
- package/dist/database/database.transaction.js.map +1 -1
- package/dist/database/database.utils.d.ts +2 -2
- package/dist/database/database.utils.js +42 -27
- package/dist/database/database.utils.js.map +1 -1
- package/dist/redis/redis.module.js +2 -3
- package/dist/redis/redis.module.js.map +1 -1
- package/dist/service/service.d.ts +0 -1
- package/dist/service/service.js +4 -0
- package/dist/service/service.js.map +1 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +4 -5
- package/dist/index.js.map +0 -1
- package/libs/src/app.controller.ts +0 -91
- package/libs/src/app.module.ts +0 -34
- package/libs/src/backup/backup.config.ts +0 -45
- package/libs/src/backup/backup.interface.ts +0 -21
- package/libs/src/backup/backup.module.ts +0 -11
- package/libs/src/backup/backup.service.ts +0 -274
- package/libs/src/backup/backup.worker.ts +0 -35
- package/libs/src/backup/index.ts +0 -4
- package/libs/src/base/dto.ts +0 -46
- package/libs/src/base/index.ts +0 -2
- package/libs/src/base/resolver.ts +0 -20
- package/libs/src/database/database.module.ts +0 -28
- package/libs/src/database/database.repository.ts +0 -355
- package/libs/src/database/database.scheme.ts +0 -128
- package/libs/src/database/database.service.ts +0 -75
- package/libs/src/database/database.sync.ts +0 -61
- package/libs/src/database/database.transaction.ts +0 -99
- package/libs/src/database/database.uniqueId.ts +0 -90
- package/libs/src/database/database.utils.ts +0 -99
- package/libs/src/database/index.ts +0 -8
- package/libs/src/index.ts +0 -5
- package/libs/src/main.ts +0 -52
- package/libs/src/redis/index.ts +0 -2
- package/libs/src/redis/redis.interface.ts +0 -34
- package/libs/src/redis/redis.module.ts +0 -30
- package/libs/src/redis/redis.service.ts +0 -137
- package/libs/src/service/index.ts +0 -1
- package/libs/src/service/service.ts +0 -181
- package/libs/src/test/test.controller.ts +0 -59
- package/libs/src/test/test.dto.ts +0 -41
- package/libs/src/test/test.module.ts +0 -24
- package/libs/src/test/test.redis.ts +0 -19
- package/libs/src/test/test.repository.ts +0 -44
- package/libs/src/test/test.resolver.ts +0 -35
- package/libs/src/test/test.schema.ts +0 -21
- package/libs/src/test/test.service.ts +0 -19
- package/libs/tsconfig.lib.json +0 -19
- package/tsconfig.json +0 -29
- package/update-links.js +0 -159
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { Injectable, Scope, Logger } from "@nestjs/common";
|
|
2
|
-
import { InjectConnection } from "@nestjs/mongoose";
|
|
3
|
-
import mongodb, { ClientSession, Connection } from "mongoose";
|
|
4
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
5
|
-
|
|
6
|
-
export type TransactionSession = mongodb.ClientSession;
|
|
7
|
-
|
|
8
|
-
@Injectable({ scope: Scope.DEFAULT })
|
|
9
|
-
export class TransactionManager {
|
|
10
|
-
private readonly logger = new Logger(TransactionManager.name);
|
|
11
|
-
private static readonly storage = new AsyncLocalStorage<{ session: ClientSession; sessionName?: string }>();
|
|
12
|
-
|
|
13
|
-
constructor(@InjectConnection() private readonly connection: Connection) { }
|
|
14
|
-
|
|
15
|
-
public run<T>(callback: () => Promise<T>): Promise<T> {
|
|
16
|
-
return TransactionManager.storage.run({ session: null }, callback);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
public static getCurrentSession(): ClientSession | null {
|
|
20
|
-
const store = TransactionManager.storage.getStore();
|
|
21
|
-
return store?.session || null;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
private async getSession(): Promise<ClientSession> {
|
|
25
|
-
const store = TransactionManager.storage.getStore();
|
|
26
|
-
if (!store) {
|
|
27
|
-
throw new Error("[TransactionManager] Context not initialized. Use run() method.");
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (store.session) return store.session;
|
|
31
|
-
|
|
32
|
-
store.session = await this.connection.startSession();
|
|
33
|
-
store.session.startTransaction({
|
|
34
|
-
readConcern: { level: "snapshot" },
|
|
35
|
-
writeConcern: { w: "majority" },
|
|
36
|
-
});
|
|
37
|
-
return store.session as TransactionSession;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
public async startTransaction(): Promise<TransactionSession> {
|
|
41
|
-
return await this.getSession();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
public async commitTransaction(): Promise<void> {
|
|
45
|
-
const store = TransactionManager.storage.getStore();
|
|
46
|
-
if (!store?.session) {
|
|
47
|
-
throw new Error("[TransactionManager] No session available");
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (!store.session.transaction.isActive) {
|
|
51
|
-
this.logger.warn("[TransactionManager] No active transaction to commit"); // Changed to warn
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
try {
|
|
56
|
-
await store.session.commitTransaction();
|
|
57
|
-
this.logger.log("[TransactionManager] Transaction committed successfully.");
|
|
58
|
-
} catch (error) {
|
|
59
|
-
await this.abortTransaction();
|
|
60
|
-
throw error;
|
|
61
|
-
} finally {
|
|
62
|
-
this.logger.log("[TransactionManager] Ending transaction...");
|
|
63
|
-
await this.endTransaction();
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
public async abortTransaction(): Promise<void> {
|
|
68
|
-
const store = TransactionManager.storage.getStore();
|
|
69
|
-
if (store?.session && store.session.transaction.isActive) {
|
|
70
|
-
try {
|
|
71
|
-
await store.session.abortTransaction();
|
|
72
|
-
} catch (error) {
|
|
73
|
-
} finally {
|
|
74
|
-
await this.endTransaction();
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
public async endTransaction(): Promise<void> {
|
|
80
|
-
const store = TransactionManager.storage.getStore();
|
|
81
|
-
if (store?.session) {
|
|
82
|
-
try {
|
|
83
|
-
await store.session.endSession();
|
|
84
|
-
} catch (error) { }
|
|
85
|
-
store.session = null;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
public get sessionName(): string {
|
|
90
|
-
return TransactionManager.storage.getStore()?.sessionName;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
public set sessionName(name: string) {
|
|
94
|
-
const store = TransactionManager.storage.getStore();
|
|
95
|
-
if (store) {
|
|
96
|
-
store.sessionName = name;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { Inject, Injectable, OnModuleInit } from "@nestjs/common";
|
|
2
|
-
import { getModelToken, InjectModel, Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
|
|
3
|
-
import { Document, Types } from "mongoose";
|
|
4
|
-
import { SoftDeleteModel } from "mongoose-delete";
|
|
5
|
-
import { CONNECTION_NAME } from "./database.module";
|
|
6
|
-
import { ModuleRef } from "@nestjs/core";
|
|
7
|
-
|
|
8
|
-
@Schema({ collection: "unique_ids" })
|
|
9
|
-
export class UniqueId {
|
|
10
|
-
_id: string;
|
|
11
|
-
@Prop({ unique: true })
|
|
12
|
-
key: string;
|
|
13
|
-
@Prop()
|
|
14
|
-
sequence_value: number;
|
|
15
|
-
}
|
|
16
|
-
export type UniqueIdDocument = UniqueId & Document;
|
|
17
|
-
|
|
18
|
-
export const UniqueIdSchema = SchemaFactory.createForClass(UniqueId);
|
|
19
|
-
|
|
20
|
-
export type UniqueKeyTypes = "users" | "uniqueId";
|
|
21
|
-
|
|
22
|
-
@Injectable({})
|
|
23
|
-
export class ApUniqueIdGenerator implements OnModuleInit {
|
|
24
|
-
private model: SoftDeleteModel<UniqueId>;
|
|
25
|
-
|
|
26
|
-
constructor(@Inject(CONNECTION_NAME) private readonly connName: string, private readonly moduleRef: ModuleRef) {}
|
|
27
|
-
// constructor(
|
|
28
|
-
// @InjectModel(UniqueId.name)
|
|
29
|
-
// private model: SoftDeleteModel<UniqueIdDocument>
|
|
30
|
-
// ) {}
|
|
31
|
-
onModuleInit() {
|
|
32
|
-
this.ensureModelInitialized();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
private ensureModelInitialized(): void {
|
|
36
|
-
if (!this.model) {
|
|
37
|
-
// Lazy initialization as fallback
|
|
38
|
-
this.model = this.connName
|
|
39
|
-
? this.moduleRef.get<SoftDeleteModel<UniqueId>>(getModelToken(UniqueId.name, this.connName), {
|
|
40
|
-
strict: false
|
|
41
|
-
})
|
|
42
|
-
: this.moduleRef.get<SoftDeleteModel<UniqueId>>(getModelToken(UniqueId.name), {
|
|
43
|
-
strict: false
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
if (!this.model) {
|
|
47
|
-
throw new Error(
|
|
48
|
-
`UniqueId model is not initialized. Please ensure MongooseModule.forFeature includes UniqueId schema with connection name: ${
|
|
49
|
-
this.connName || "default"
|
|
50
|
-
}`
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
public async getNextUniqueId(key: string = "uniqueId"): Promise<number> {
|
|
57
|
-
this.ensureModelInitialized();
|
|
58
|
-
|
|
59
|
-
const result = await this.model.findOneAndUpdate(
|
|
60
|
-
{ key },
|
|
61
|
-
{ $inc: { sequence_value: 1 } },
|
|
62
|
-
{ returnDocument: "after", upsert: true }
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
if (!result) {
|
|
66
|
-
throw new Error("Failed to generate unique ID");
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return result.sequence_value;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
public getObjectId(): string {
|
|
73
|
-
return new Types.ObjectId().toString();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
public async seed(): Promise<void> {
|
|
77
|
-
this.ensureModelInitialized();
|
|
78
|
-
|
|
79
|
-
const keys = [{ key: "uniqueId", sequence_value: 1001 }];
|
|
80
|
-
|
|
81
|
-
for await (const key of keys) {
|
|
82
|
-
const exist = await this.model.findOne({ key: key.key });
|
|
83
|
-
|
|
84
|
-
if (exist) {
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
await this.model.create(key);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import moment from "moment";
|
|
2
|
-
import { ObjectId } from "mongodb";
|
|
3
|
-
import * as mongoose from "mongoose";
|
|
4
|
-
import { Connection, Schema } from "mongoose";
|
|
5
|
-
|
|
6
|
-
const mapToObjectIds = <T>(query: Partial<T>): T => {
|
|
7
|
-
try {
|
|
8
|
-
const mapVal = (val: any): any => {
|
|
9
|
-
if (Array.isArray(val)) {
|
|
10
|
-
return val.map(mapVal); // Recursively map each array element
|
|
11
|
-
} else if (val && typeof val === "object" && !ObjectId.isValid(val)) {
|
|
12
|
-
return mapToObjectIds(val); // Recursively process nested objects
|
|
13
|
-
} else if (
|
|
14
|
-
val &&
|
|
15
|
-
ObjectId.isValid(val?.toString()) &&
|
|
16
|
-
new ObjectId(val?.toString())?.toString() === val
|
|
17
|
-
) {
|
|
18
|
-
return new ObjectId(val?.toString()); // Convert valid ObjectId strings
|
|
19
|
-
}
|
|
20
|
-
return val; // Return as-is if no conversion needed
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const mappedQuery: any = { ...query };
|
|
24
|
-
Object.keys(mappedQuery).forEach((key) => {
|
|
25
|
-
mappedQuery[key] = mapVal(mappedQuery[key]); // Apply transformation
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
return mappedQuery;
|
|
29
|
-
} catch (error) {
|
|
30
|
-
console.error("mapToObjectIds Error:", error);
|
|
31
|
-
// return query; // Return original query in case of error
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export const DbUtils = {
|
|
36
|
-
mapToObjectIds,
|
|
37
|
-
getOrCreateDiscriminatorModel(
|
|
38
|
-
connection: Connection = mongoose.connection,
|
|
39
|
-
baseModelName: string,
|
|
40
|
-
baseSchema: Schema,
|
|
41
|
-
discriminatorName: string,
|
|
42
|
-
discriminatorSchema: Schema
|
|
43
|
-
) {
|
|
44
|
-
const getBaseModel = (conn: Connection | typeof mongoose) =>
|
|
45
|
-
conn.models[baseModelName] || conn.model(baseModelName, baseSchema);
|
|
46
|
-
|
|
47
|
-
const BaseModel = connection
|
|
48
|
-
? getBaseModel(connection)
|
|
49
|
-
: getBaseModel(mongoose);
|
|
50
|
-
|
|
51
|
-
if (
|
|
52
|
-
!BaseModel.discriminators ||
|
|
53
|
-
!BaseModel.discriminators[discriminatorName]
|
|
54
|
-
) {
|
|
55
|
-
return BaseModel.discriminator(discriminatorName, discriminatorSchema);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return connection
|
|
59
|
-
? connection.models[discriminatorName] ||
|
|
60
|
-
connection.model(discriminatorName)
|
|
61
|
-
: mongoose.models[discriminatorName] || mongoose.model(discriminatorName);
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
export const SchemeUtil = {
|
|
66
|
-
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
|
-
toCurrentDate: (value: number | string) => {
|
|
84
|
-
if (!value) {
|
|
85
|
-
return moment().valueOf();
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (typeof value === "number" && !Number.isNaN(value)) {
|
|
89
|
-
return value;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const momentDate = moment(value);
|
|
93
|
-
if (momentDate.isValid()) {
|
|
94
|
-
return momentDate.valueOf();
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return moment().valueOf();
|
|
98
|
-
},
|
|
99
|
-
};
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export * from './database.scheme';
|
|
2
|
-
export * from './database.repository';
|
|
3
|
-
export * from './database.transaction';
|
|
4
|
-
export * from './database.module';
|
|
5
|
-
export * from './database.uniqueId';
|
|
6
|
-
export * from './database.service';
|
|
7
|
-
export * from './database.utils';
|
|
8
|
-
export * from './database.sync';
|
package/libs/src/index.ts
DELETED
package/libs/src/main.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { NestFactory } from "@nestjs/core";
|
|
2
|
-
import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger";
|
|
3
|
-
import { ZyncNestDataModule } from "./app.module";
|
|
4
|
-
|
|
5
|
-
async function bootstrap() {
|
|
6
|
-
const app = await NestFactory.create(ZyncNestDataModule);
|
|
7
|
-
|
|
8
|
-
// Enable CORS
|
|
9
|
-
app.enableCors({
|
|
10
|
-
origin: true,
|
|
11
|
-
credentials: true,
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
// Swagger configuration
|
|
15
|
-
const config = new DocumentBuilder()
|
|
16
|
-
.setTitle("Zync Nest Data Module API")
|
|
17
|
-
.setDescription("API documentation for Zync Nest Data Module")
|
|
18
|
-
.setVersion("1.0.23")
|
|
19
|
-
.addTag("Data Module", "Data Module endpoints for Zync Nest Data Module")
|
|
20
|
-
.build();
|
|
21
|
-
|
|
22
|
-
const document = SwaggerModule.createDocument(app, config);
|
|
23
|
-
SwaggerModule.setup("api", app, document, {
|
|
24
|
-
customSiteTitle: "Zync Nest Data Module API",
|
|
25
|
-
customfavIcon: "https://nestjs.com/img/logo-small.svg",
|
|
26
|
-
customJs: [
|
|
27
|
-
"https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.15.5/swagger-ui-bundle.js",
|
|
28
|
-
"https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.15.5/swagger-ui-standalone-preset.js",
|
|
29
|
-
],
|
|
30
|
-
customCssUrl: [
|
|
31
|
-
"https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.15.5/swagger-ui.min.css",
|
|
32
|
-
],
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
const port = process.env.app_port || 3000;
|
|
36
|
-
await app.listen(port);
|
|
37
|
-
|
|
38
|
-
console.log(
|
|
39
|
-
`🚀 Zync Nest Data Module server is running on: http://localhost:${port}`
|
|
40
|
-
);
|
|
41
|
-
console.log(
|
|
42
|
-
`📚 Swagger documentation available at: http://localhost:${port}/api`
|
|
43
|
-
);
|
|
44
|
-
console.log(
|
|
45
|
-
`🔍 Health check available at: http://localhost:${port}/test/health`
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
bootstrap().catch((error) => {
|
|
50
|
-
console.error("Failed to start the application:", error);
|
|
51
|
-
process.exit(1);
|
|
52
|
-
});
|
package/libs/src/redis/index.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
export interface IRedisSearchParams {
|
|
2
|
-
query?: string;
|
|
3
|
-
minScore?: number;
|
|
4
|
-
maxScore?: number;
|
|
5
|
-
keyPrefix?: string;
|
|
6
|
-
sortBy: {
|
|
7
|
-
field: string;
|
|
8
|
-
order?: "asc" | "desc";
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface IRedisPageParams extends IRedisSearchParams {
|
|
13
|
-
page: number; // 1-based page
|
|
14
|
-
pageSize: number;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface IRedisSearchResult<T> {
|
|
18
|
-
data: T[];
|
|
19
|
-
totalRecords: number;
|
|
20
|
-
page?: number;
|
|
21
|
-
totalPages?: number;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface IRedisZSetItem {
|
|
25
|
-
key: string;
|
|
26
|
-
score: number;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface IRedisSchemaField {
|
|
30
|
-
name: string;
|
|
31
|
-
type: "TEXT" | "NUMERIC" | "TAG"; // supported Redisearch types
|
|
32
|
-
sortable?: boolean; // if field can be used in SORTBY
|
|
33
|
-
noIndex?: boolean; // optional, skip indexing
|
|
34
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
// src/redis/redis.module.ts
|
|
2
|
-
import { Global, Module } from "@nestjs/common";
|
|
3
|
-
import Redis from "ioredis";
|
|
4
|
-
import { ConfigModule } from "@nestjs/config";
|
|
5
|
-
import { RedisService } from "./redis.service";
|
|
6
|
-
|
|
7
|
-
@Global()
|
|
8
|
-
@Module({
|
|
9
|
-
imports: [ConfigModule],
|
|
10
|
-
providers: [
|
|
11
|
-
{
|
|
12
|
-
provide: "REDIS_CLIENT",
|
|
13
|
-
useFactory: async () => {
|
|
14
|
-
const client = new Redis({
|
|
15
|
-
host: process.env.redis_host || "127.0.0.1",
|
|
16
|
-
port: Number(process.env.redis_port) || 6379,
|
|
17
|
-
password: process.env.redis_password || undefined,
|
|
18
|
-
db: Number(process.env.redis_database) || 0
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
client.on("connect", () => console.log("✅ Redis connected"));
|
|
22
|
-
client.on("error", (err) => console.error("❌ Redis error:", err));
|
|
23
|
-
|
|
24
|
-
return client;
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
],
|
|
28
|
-
exports: ["REDIS_CLIENT", RedisService]
|
|
29
|
-
})
|
|
30
|
-
export class RedisModule { }
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { Injectable, Logger } from "@nestjs/common";
|
|
2
|
-
import { RedisPubSub } from "graphql-redis-subscriptions";
|
|
3
|
-
import { EntityData, Client as RedisOmClient, Repository, Schema, SchemaDefinition, Search } from "redis-om";
|
|
4
|
-
import { IPageParams } from "../database/database.scheme";
|
|
5
|
-
|
|
6
|
-
@Injectable()
|
|
7
|
-
export abstract class RedisService<T extends EntityData> extends RedisPubSub {
|
|
8
|
-
private readonly logger = new Logger(RedisService.name);
|
|
9
|
-
public readonly baseKey = `${process.env.redis_prefix || "zync_redis_data"}:`;
|
|
10
|
-
private redisOmClient: RedisOmClient | null = null;
|
|
11
|
-
|
|
12
|
-
constructor() {
|
|
13
|
-
super({
|
|
14
|
-
connection: {
|
|
15
|
-
host: process.env.redis_host || "localhost",
|
|
16
|
-
port: Number(process.env.redis_port) || 6379,
|
|
17
|
-
username: process.env.redis_username || undefined,
|
|
18
|
-
password: process.env.redis_password || undefined,
|
|
19
|
-
db: Number(process.env.redis_database) || 0
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
public abstract cacheKey: string;
|
|
25
|
-
|
|
26
|
-
protected abstract buildQuery(search: Search, query: any): Search;
|
|
27
|
-
|
|
28
|
-
protected abstract mapSchema(): Record<string, any>;
|
|
29
|
-
|
|
30
|
-
protected async repository(): Promise<Repository<T>> {
|
|
31
|
-
const schema = this.createSchema(this.cacheKey, this.mapSchema());
|
|
32
|
-
const repository = await this.getRepository(schema);
|
|
33
|
-
try {
|
|
34
|
-
await repository.createIndex();
|
|
35
|
-
} catch (error) {
|
|
36
|
-
this.logger.error(`Error creating index for ${this.cacheKey}: ${error}`);
|
|
37
|
-
}
|
|
38
|
-
return repository;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
private createSchema<M extends EntityData>(name: string, fields: Record<string, any>): Schema<M> {
|
|
42
|
-
return new Schema<M>(`${this.baseKey}${name}`, fields as SchemaDefinition<M>, {
|
|
43
|
-
dataStructure: "JSON",
|
|
44
|
-
indexName: `${this.baseKey}${name}:idx`
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
private async getRedisOmClient(): Promise<RedisOmClient> {
|
|
49
|
-
if (this.redisOmClient) return this.redisOmClient;
|
|
50
|
-
|
|
51
|
-
const host = process.env.redis_host || "127.0.0.1";
|
|
52
|
-
const port = Number(process.env.redis_port) || 6379;
|
|
53
|
-
const password = process.env.redis_password;
|
|
54
|
-
const db = Number(process.env.redis_database) || 0;
|
|
55
|
-
|
|
56
|
-
const authSegment = password ? `:${password}@` : "";
|
|
57
|
-
const url = `redis://${authSegment}${host}:${port}/${db}`;
|
|
58
|
-
|
|
59
|
-
const client = new RedisOmClient();
|
|
60
|
-
await client.open(url);
|
|
61
|
-
this.redisOmClient = client;
|
|
62
|
-
return client;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
public async getRepository(schema: Schema<any>): Promise<Repository<any>> {
|
|
66
|
-
return (await this.getRedisOmClient()).fetchRepository(schema);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
public async create(data: Partial<T> | T): Promise<T> {
|
|
71
|
-
const repository = await this.repository();
|
|
72
|
-
const _id = (data as any)._id as string;
|
|
73
|
-
const existing = _id ? await this.findById(_id) : null;
|
|
74
|
-
if (existing) {
|
|
75
|
-
return await this.update(_id, data);
|
|
76
|
-
}
|
|
77
|
-
return await repository.save(JSON.parse(JSON.stringify(data)));
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
public async update(_id: string, data: Partial<T> | T): Promise<T> {
|
|
81
|
-
const repository = await this.repository();
|
|
82
|
-
const entityId = await this.getEntityId(_id);
|
|
83
|
-
|
|
84
|
-
// Fetch the entity with its internal metadata (including entityId)
|
|
85
|
-
const entity = await repository.fetch(entityId);
|
|
86
|
-
|
|
87
|
-
// Update the entity properties
|
|
88
|
-
Object.assign(entity, JSON.parse(JSON.stringify(data)));
|
|
89
|
-
|
|
90
|
-
return await repository.save(entity);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
public async delete(id: string): Promise<boolean> {
|
|
94
|
-
const repository = await this.repository();
|
|
95
|
-
await repository.remove(await this.getEntityId(id));
|
|
96
|
-
return true;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
public async findById(_id: string): Promise<T> {
|
|
100
|
-
const repository = await this.repository();
|
|
101
|
-
return await repository
|
|
102
|
-
.search()
|
|
103
|
-
.where("_id" as any)
|
|
104
|
-
.eq(_id)
|
|
105
|
-
.return.first();
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
public async getEntityId(_id: string): Promise<string> {
|
|
110
|
-
const repository = await this.repository();
|
|
111
|
-
return await repository
|
|
112
|
-
.search()
|
|
113
|
-
.where("_id" as any)
|
|
114
|
-
.eq(_id)
|
|
115
|
-
.return.firstId();
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
public async findOne(query: Partial<T>): Promise<T> {
|
|
119
|
-
const repository = await this.repository();
|
|
120
|
-
const search = this.buildQuery(repository.search(), query);
|
|
121
|
-
return (await search.return.first()) as T;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
public async find(query: Partial<T>): Promise<T[]> {
|
|
125
|
-
const repository = await this.repository();
|
|
126
|
-
const search = this.buildQuery(repository.search(), query);
|
|
127
|
-
return (await search.return.all()) as T[];
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
public async page(query: Partial<T> & IPageParams): Promise<{ totalRecords: number; data: Array<Partial<T>> }> {
|
|
131
|
-
const repository = await this.repository();
|
|
132
|
-
const search = this.buildQuery(repository.search(), query);
|
|
133
|
-
const totalRecords = await search.return.count();
|
|
134
|
-
const data = (await search.return.page(query.skip, query.take)) as T[];
|
|
135
|
-
return { totalRecords, data };
|
|
136
|
-
}
|
|
137
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./service";
|