@coopenomics/notifications 2025.11.8
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/.cursor/rules/notifications.mdc +20 -0
- package/.env-example +2 -0
- package/README.md +218 -0
- package/build.config.ts +13 -0
- package/dist/index.cjs +3027 -0
- package/dist/index.d.cts +792 -0
- package/dist/index.d.mts +792 -0
- package/dist/index.d.ts +792 -0
- package/dist/index.mjs +3016 -0
- package/dist/sync/novu-sync.service.d.ts +30 -0
- package/dist/sync/novu-sync.service.js +128 -0
- package/dist/sync/sync-runner.d.ts +4 -0
- package/dist/sync/sync-runner.js +130 -0
- package/dist/utils/role-utils.d.ts +8 -0
- package/dist/utils/role-utils.js +18 -0
- package/dist/workflows/incoming-transfer/incoming-transfer-workflow.d.ts +3 -0
- package/dist/workflows/incoming-transfer/incoming-transfer-workflow.js +16 -0
- package/dist/workflows/incoming-transfer/index.d.ts +3 -0
- package/dist/workflows/incoming-transfer/index.js +2 -0
- package/dist/workflows/incoming-transfer/types.d.ts +12 -0
- package/dist/workflows/incoming-transfer/types.js +5 -0
- package/dist/workflows/new-agenda-item/index.d.ts +3 -0
- package/dist/workflows/new-agenda-item/index.js +2 -0
- package/dist/workflows/new-agenda-item/new-agenda-item-workflow.d.ts +3 -0
- package/dist/workflows/new-agenda-item/new-agenda-item-workflow.js +16 -0
- package/dist/workflows/new-agenda-item/types.d.ts +27 -0
- package/dist/workflows/new-agenda-item/types.js +10 -0
- package/dist/workflows/role-based-workflows.d.ts +21 -0
- package/dist/workflows/role-based-workflows.js +65 -0
- package/package.json +51 -0
- package/src/base/defaults.ts +66 -0
- package/src/base/workflow-builder.ts +128 -0
- package/src/index.ts +9 -0
- package/src/sync/README.md +54 -0
- package/src/sync/novu-sync.service.ts +246 -0
- package/src/sync/sync-runner.ts +154 -0
- package/src/types/index.ts +99 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/slugify/builtinReplacements.ts +2065 -0
- package/src/utils/slugify/slugify.ts +284 -0
- package/src/utils/slugify/transliterate.ts +48 -0
- package/src/workflows/approval-request/index.ts +52 -0
- package/src/workflows/approval-response/index.ts +54 -0
- package/src/workflows/decision-approved/index.ts +50 -0
- package/src/workflows/email-verification/index.ts +35 -0
- package/src/workflows/incoming-transfer/index.ts +43 -0
- package/src/workflows/index.ts +74 -0
- package/src/workflows/invite/index.ts +34 -0
- package/src/workflows/meet-ended/index.ts +51 -0
- package/src/workflows/meet-initial/index.ts +53 -0
- package/src/workflows/meet-reminder-end/index.ts +52 -0
- package/src/workflows/meet-reminder-start/index.ts +51 -0
- package/src/workflows/meet-restart/index.ts +53 -0
- package/src/workflows/meet-started/index.ts +51 -0
- package/src/workflows/new-agenda-item/index.ts +51 -0
- package/src/workflows/new-deposit-payment-request/index.ts +50 -0
- package/src/workflows/new-initial-payment-request/index.ts +50 -0
- package/src/workflows/payment-cancelled/index.ts +51 -0
- package/src/workflows/payment-paid/index.ts +50 -0
- package/src/workflows/reset-key/index.ts +36 -0
- package/src/workflows/server-provisioned/index.ts +45 -0
- package/src/workflows/welcome/index.ts +43 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
3
|
+
import {
|
|
4
|
+
WorkflowDefinition,
|
|
5
|
+
BaseWorkflowPayload,
|
|
6
|
+
WorkflowStep,
|
|
7
|
+
PayloadSchema,
|
|
8
|
+
NovuWorkflowData,
|
|
9
|
+
NovuOrigin
|
|
10
|
+
} from '../types';
|
|
11
|
+
import { createDefaultPreferences } from './defaults';
|
|
12
|
+
|
|
13
|
+
export class WorkflowBuilder<T extends BaseWorkflowPayload> {
|
|
14
|
+
private _name: string = '';
|
|
15
|
+
private _workflowId: string = '';
|
|
16
|
+
private _description?: string;
|
|
17
|
+
private _steps: WorkflowStep[] = [];
|
|
18
|
+
private _payloadZodSchema?: z.ZodSchema<T>;
|
|
19
|
+
private _origin?: NovuOrigin;
|
|
20
|
+
private _tags?: string[];
|
|
21
|
+
|
|
22
|
+
static create<T extends BaseWorkflowPayload>(): WorkflowBuilder<T> {
|
|
23
|
+
return new WorkflowBuilder<T>();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
name(name: string): this {
|
|
27
|
+
this._name = name;
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
workflowId(id: string): this {
|
|
32
|
+
this._workflowId = id;
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
description(description: string): this {
|
|
37
|
+
this._description = description;
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
origin(origin: NovuOrigin): this {
|
|
42
|
+
this._origin = origin;
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
tags(tags: string[]): this {
|
|
47
|
+
this._tags = tags;
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
addStep(step: WorkflowStep): this {
|
|
52
|
+
this._steps.push(step);
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
addSteps(steps: WorkflowStep[]): this {
|
|
57
|
+
this._steps.push(...steps);
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
payloadSchema(schema: z.ZodSchema<T>): this {
|
|
62
|
+
this._payloadZodSchema = schema;
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
build(): WorkflowDefinition<T> {
|
|
67
|
+
if (!this._name) throw new Error('Workflow name is required');
|
|
68
|
+
if (!this._workflowId) throw new Error('Workflow ID is required');
|
|
69
|
+
if (!this._payloadZodSchema) throw new Error('Payload schema is required');
|
|
70
|
+
|
|
71
|
+
// ΠΡΠ΅ΠΎΠ±ΡΠ°Π·ΡΠ΅ΠΌ Zod ΡΡ
Π΅ΠΌΡ Π² JSON Schema Π΄Π»Ρ Novu
|
|
72
|
+
const jsonSchema = zodToJsonSchema(this._payloadZodSchema);
|
|
73
|
+
|
|
74
|
+
// ΠΡΠ°Π²ΠΈΠ»ΡΠ½Π°Ρ ΡΠΈΠΏΠΈΠ·Π°ΡΠΈΡ: zodToJsonSchema ΠΌΠΎΠΆΠ΅Ρ Π²Π΅ΡΠ½ΡΡΡ ΡΠ°Π·Π½ΡΠ΅ ΡΠΈΠΏΡ ΡΡ
Π΅ΠΌ
|
|
75
|
+
const payloadSchema: PayloadSchema = {
|
|
76
|
+
type: (jsonSchema as any).type || 'object',
|
|
77
|
+
properties: (jsonSchema as any).properties || {},
|
|
78
|
+
required: Array.isArray((jsonSchema as any).required) ? (jsonSchema as any).required : [],
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const workflow: WorkflowDefinition<T> = {
|
|
82
|
+
name: this._name,
|
|
83
|
+
workflowId: this._workflowId,
|
|
84
|
+
description: this._description,
|
|
85
|
+
payloadSchema,
|
|
86
|
+
steps: this._steps,
|
|
87
|
+
preferences: createDefaultPreferences(),
|
|
88
|
+
payloadZodSchema: this._payloadZodSchema,
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// ΠΠΎΠ±Π°Π²Π»ΡΠ΅ΠΌ origin ΡΠΎΠ»ΡΠΊΠΎ Π΅ΡΠ»ΠΈ ΠΎΠ½ ΡΠΊΠ°Π·Π°Π½
|
|
92
|
+
if (this._origin) {
|
|
93
|
+
workflow.origin = this._origin;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ΠΠΎΠ±Π°Π²Π»ΡΠ΅ΠΌ tags ΡΠΎΠ»ΡΠΊΠΎ Π΅ΡΠ»ΠΈ ΠΎΠ½ΠΈ ΡΠΊΠ°Π·Π°Π½Ρ
|
|
97
|
+
if (this._tags) {
|
|
98
|
+
workflow.tags = this._tags;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return workflow;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ΠΠΎΠ½Π²Π΅ΡΡΠ°ΡΠΈΡ Π² ΡΠΎΡΠΌΠ°Ρ Π΄Π»Ρ Novu API
|
|
105
|
+
toNovuData(): NovuWorkflowData {
|
|
106
|
+
const workflow = this.build();
|
|
107
|
+
const novuData: NovuWorkflowData = {
|
|
108
|
+
name: workflow.name,
|
|
109
|
+
workflowId: workflow.workflowId,
|
|
110
|
+
description: workflow.description,
|
|
111
|
+
payloadSchema: workflow.payloadSchema,
|
|
112
|
+
steps: workflow.steps,
|
|
113
|
+
preferences: workflow.preferences,
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// ΠΠΎΠ±Π°Π²Π»ΡΠ΅ΠΌ origin ΡΠΎΠ»ΡΠΊΠΎ Π΅ΡΠ»ΠΈ ΠΎΠ½ ΡΠΊΠ°Π·Π°Π½
|
|
117
|
+
if (workflow.origin) {
|
|
118
|
+
novuData.origin = workflow.origin;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ΠΠΎΠ±Π°Π²Π»ΡΠ΅ΠΌ tags ΡΠΎΠ»ΡΠΊΠΎ Π΅ΡΠ»ΠΈ ΠΎΠ½ΠΈ ΡΠΊΠ°Π·Π°Π½Ρ
|
|
122
|
+
if (workflow.tags) {
|
|
123
|
+
novuData.tags = workflow.tags;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return novuData;
|
|
127
|
+
}
|
|
128
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// ΠΠΊΡΠΏΠΎΡΡΠΈΡΡΠ΅ΠΌ ΡΠΈΠΏΡ
|
|
2
|
+
export * as Types from './types';
|
|
3
|
+
|
|
4
|
+
// ΠΠΊΡΠΏΠΎΡΡΠΈΡΡΠ΅ΠΌ Π±Π°Π·ΠΎΠ²ΡΠ΅ ΡΡΠΈΠ»ΠΈΡΡ
|
|
5
|
+
export * from './base/defaults';
|
|
6
|
+
export { WorkflowBuilder } from './base/workflow-builder';
|
|
7
|
+
|
|
8
|
+
// ΠΠΊΡΠΏΠΎΡΡΠΈΡΡΠ΅ΠΌ Π²ΡΠ΅ Π²ΠΎΡΠΊΡΠ»ΠΎΡ
|
|
9
|
+
export * as Workflows from './workflows';
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Π‘ΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΡ Novu Workflows
|
|
2
|
+
|
|
3
|
+
ΠΡΠ° ΠΏΠ°ΠΏΠΊΠ° ΡΠΎΠ΄Π΅ΡΠΆΠΈΡ ΡΠ΅ΡΠ²Π΅ΡΠ½ΡΡ Π»ΠΎΠ³ΠΈΠΊΡ Π΄Π»Ρ ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΠΈ Π²ΠΎΡΠΊΡΠ»ΠΎΡ Ρ Novu API.
|
|
4
|
+
|
|
5
|
+
## Π€Π°ΠΉΠ»Ρ
|
|
6
|
+
|
|
7
|
+
- `novu-sync.service.ts` - Π‘Π΅ΡΠ²ΠΈΡ Π΄Π»Ρ ΡΠ°Π±ΠΎΡΡ Ρ Novu API
|
|
8
|
+
- `sync-runner.ts` - Π‘ΠΊΡΠΈΠΏΡ Π΄Π»Ρ Π·Π°ΠΏΡΡΠΊΠ° ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΠΈ
|
|
9
|
+
- `README.md` - ΠΡΠΎΡ ΡΠ°ΠΉΠ»
|
|
10
|
+
|
|
11
|
+
## ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅
|
|
12
|
+
|
|
13
|
+
### Production ΡΠ΅ΠΆΠΈΠΌ (ΠΎΠ΄ΠΈΠ½ ΡΠ°Π·)
|
|
14
|
+
```bash
|
|
15
|
+
pnpm run sync
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Development ΡΠ΅ΠΆΠΈΠΌ (watch)
|
|
19
|
+
```bash
|
|
20
|
+
pnpm run sync:dev
|
|
21
|
+
# ΠΈΠ»ΠΈ
|
|
22
|
+
pnpm run sync:watch
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## ΠΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ
|
|
26
|
+
|
|
27
|
+
Π’ΡΠ΅Π±ΡΡΡΡΡ ΡΠ»Π΅Π΄ΡΡΡΠΈΠ΅ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅:
|
|
28
|
+
- `NOVU_API_KEY` - API ΠΊΠ»ΡΡ Π΄Π»Ρ Novu
|
|
29
|
+
- `NOVU_API_URL` - URL Novu API (ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ: https://api.novu.co)
|
|
30
|
+
|
|
31
|
+
## Π Π΅ΠΆΠΈΠΌΡ ΡΠ°Π±ΠΎΡΡ
|
|
32
|
+
|
|
33
|
+
### Production
|
|
34
|
+
- ΠΠ°ΠΏΡΡΠΊΠ°Π΅ΡΡΡ ΠΎΠ΄ΠΈΠ½ ΡΠ°Π·
|
|
35
|
+
- Π‘ΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·ΠΈΡΡΠ΅Ρ Π²ΡΠ΅ Π²ΠΎΡΠΊΡΠ»ΠΎΡ
|
|
36
|
+
- ΠΠ°Π²Π΅ΡΡΠ°Π΅ΡΡΡ ΠΏΠΎΡΠ»Π΅ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΡ
|
|
37
|
+
|
|
38
|
+
### Development
|
|
39
|
+
- ΠΠ°ΠΏΡΡΠΊΠ°Π΅ΡΡΡ ΠΈ ΠΎΡΡΠ°Π΅ΡΡΡ Π°ΠΊΡΠΈΠ²Π½ΡΠΌ
|
|
40
|
+
- ΠΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π΅Ρ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ Π² ΠΏΠ°ΠΏΠΊΠ°Ρ
`workflows/` ΠΈ `types/`
|
|
41
|
+
- ΠΠ²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ ΠΏΠ΅ΡΠ΅Π·Π°ΠΏΡΡΠΊΠ°Π΅Ρ ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΡ ΠΏΡΠΈ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡΡ
|
|
42
|
+
- ΠΠ΅Π±Π°ΡΠ½Ρ 1 ΡΠ΅ΠΊΡΠ½Π΄Π° Π΄Π»Ρ ΠΈΠ·Π±Π΅ΠΆΠ°Π½ΠΈΡ ΠΌΠ½ΠΎΠΆΠ΅ΡΡΠ²Π΅Π½Π½ΡΡ
Π·Π°ΠΏΡΡΠΊΠΎΠ²
|
|
43
|
+
|
|
44
|
+
## ΠΠ°ΠΊ ΡΠ°Π±ΠΎΡΠ°Π΅Ρ
|
|
45
|
+
|
|
46
|
+
1. ΠΠ°Π³ΡΡΠΆΠ°Π΅Ρ Π²ΡΠ΅ Π²ΠΎΡΠΊΡΠ»ΠΎΡ ΠΈΠ· `../workflows/index.ts`
|
|
47
|
+
2. ΠΠ»Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ Π²ΠΎΡΠΊΡΠ»ΠΎΡ:
|
|
48
|
+
- ΠΡΠΎΠ²Π΅ΡΡΠ΅Ρ ΡΡΡΠ΅ΡΡΠ²ΠΎΠ²Π°Π½ΠΈΠ΅ Π² Novu API
|
|
49
|
+
- Π‘ΠΎΠ·Π΄Π°Π΅Ρ Π½ΠΎΠ²ΡΠΉ ΠΈΠ»ΠΈ ΠΎΠ±Π½ΠΎΠ²Π»ΡΠ΅Ρ ΡΡΡΠ΅ΡΡΠ²ΡΡΡΠΈΠΉ
|
|
50
|
+
3. ΠΠΎΠ³ΠΈΡΡΠ΅Ρ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΡ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΉ
|
|
51
|
+
|
|
52
|
+
## ΠΠ°ΠΆΠ½ΠΎ
|
|
53
|
+
|
|
54
|
+
β οΈ ΠΡΠΎΡ ΠΌΠΎΠ΄ΡΠ»Ρ ΠΏΡΠ΅Π΄Π½Π°Π·Π½Π°ΡΠ΅Π½ ΡΠΎΠ»ΡΠΊΠΎ Π΄Π»Ρ ΡΠ΅ΡΠ²Π΅ΡΠ½ΠΎΠ³ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ ΠΈ Π½Π΅ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΠΈΠΌΠΏΠΎΡΡΠΈΡΠΎΠ²Π°ΡΡΡΡ Π½Π° ΡΡΠΎΠ½ΡΠ΅Π½Π΄Π΅!
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import axios, { AxiosInstance } from 'axios';
|
|
2
|
+
import {
|
|
3
|
+
Types,
|
|
4
|
+
Workflows
|
|
5
|
+
} from '../index';
|
|
6
|
+
|
|
7
|
+
export interface NovuSyncConfig {
|
|
8
|
+
apiKey: string;
|
|
9
|
+
apiUrl: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class NovuSyncService {
|
|
13
|
+
private readonly client: AxiosInstance;
|
|
14
|
+
private readonly config: NovuSyncConfig;
|
|
15
|
+
|
|
16
|
+
constructor(config: NovuSyncConfig) {
|
|
17
|
+
this.config = config;
|
|
18
|
+
|
|
19
|
+
if (!this.config.apiKey) {
|
|
20
|
+
throw new Error('NOVU_API_KEY is required');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!this.config.apiUrl) {
|
|
24
|
+
throw new Error('NOVU_API_URL is required');
|
|
25
|
+
}
|
|
26
|
+
this.client = axios.create({
|
|
27
|
+
baseURL: this.config.apiUrl,
|
|
28
|
+
headers: {
|
|
29
|
+
'Authorization': `ApiKey ${this.config.apiKey}`,
|
|
30
|
+
'Content-Type': 'application/json',
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* ΠΠΎΠ»ΡΡΠΈΡΡ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ ΠΎ Π²ΠΎΡΠΊΡΠ»ΠΎΡ
|
|
37
|
+
*/
|
|
38
|
+
async getWorkflow(workflowId: string): Promise<any> {
|
|
39
|
+
try {
|
|
40
|
+
const response = await this.client.get(`/v2/workflows/${workflowId}`);
|
|
41
|
+
return response.data;
|
|
42
|
+
} catch (error: any) {
|
|
43
|
+
if (error.response?.status === 404) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* ΠΠΎΠ»ΡΡΠΈΡΡ ΡΠΏΠΈΡΠΎΠΊ Π²ΡΠ΅Ρ
Π²ΠΎΡΠΊΡΠ»ΠΎΡ
|
|
52
|
+
*/
|
|
53
|
+
async getAllWorkflows(): Promise<any[]> {
|
|
54
|
+
try {
|
|
55
|
+
const response = await this.client.get('/v2/workflows', {
|
|
56
|
+
params: {
|
|
57
|
+
limit: 10000
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return response.data.data.workflows || [];
|
|
62
|
+
} catch (error: any) {
|
|
63
|
+
console.error('ΠΡΠΈΠ±ΠΊΠ° ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΡ ΡΠΏΠΈΡΠΊΠ° Π²ΠΎΡΠΊΡΠ»ΠΎΡ:', console.dir(error.response?.data || error.message, {depth: null}));
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Π‘ΠΎΠ·Π΄Π°ΡΡ Π½ΠΎΠ²ΡΠΉ Π²ΠΎΡΠΊΡΠ»ΠΎΡ
|
|
70
|
+
*/
|
|
71
|
+
async createWorkflow(data: Types.NovuWorkflowData): Promise<any> {
|
|
72
|
+
try {
|
|
73
|
+
// ΠΠ»Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΠΠ ΠΏΠ΅ΡΠ΅Π΄Π°Π΅ΠΌ origin (ΠΊΠ°ΠΊ Π² testFramework2.ts)
|
|
74
|
+
const createData = { ...data };
|
|
75
|
+
|
|
76
|
+
delete createData.origin;
|
|
77
|
+
|
|
78
|
+
const response = await this.client.post('/v2/workflows', createData);
|
|
79
|
+
return response.data;
|
|
80
|
+
} catch (error: any) {
|
|
81
|
+
console.error(`ΠΡΠΈΠ±ΠΊΠ° ΡΠΎΠ·Π΄Π°Π½ΠΈΡ Π²ΠΎΡΠΊΡΠ»ΠΎΡ ${data.workflowId}:`, error.response?.data || error.message);
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* ΠΠ±Π½ΠΎΠ²ΠΈΡΡ ΡΡΡΠ΅ΡΡΠ²ΡΡΡΠΈΠΉ Π²ΠΎΡΠΊΡΠ»ΠΎΡ
|
|
88
|
+
*/
|
|
89
|
+
async updateWorkflow(workflowId: string, data: Types.NovuWorkflowData): Promise<any> {
|
|
90
|
+
try {
|
|
91
|
+
// ΠΠ»Ρ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΡ ΠΠ‘ΠΠΠΠ ΠΏΠ΅ΡΠ΅Π΄Π°Π΅ΠΌ origin: "external" (ΠΊΠ°ΠΊ Π² testFramework2.ts)
|
|
92
|
+
const updateData = { ...data, origin: 'novu-cloud' as const };
|
|
93
|
+
const response = await this.client.put(`/v2/workflows/${workflowId}`, updateData);
|
|
94
|
+
// console.log('response', response.data);
|
|
95
|
+
return response.data;
|
|
96
|
+
} catch (error: any) {
|
|
97
|
+
console.error(`ΠΡΠΈΠ±ΠΊΠ° ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΡ Π²ΠΎΡΠΊΡΠ»ΠΎΡ ${workflowId}:`, error.response?.data || error.message);
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Π£Π΄Π°Π»ΠΈΡΡ Π²ΠΎΡΠΊΡΠ»ΠΎΡ ΠΏΠΎ ID
|
|
104
|
+
*/
|
|
105
|
+
async deleteWorkflow(workflowId: string): Promise<void> {
|
|
106
|
+
try {
|
|
107
|
+
await this.client.delete(`/v2/workflows/${workflowId}`);
|
|
108
|
+
console.log(`Π£Π΄Π°Π»Π΅Π½ Π²ΠΎΡΠΊΡΠ»ΠΎΡ: ${workflowId}`);
|
|
109
|
+
} catch (error: any) {
|
|
110
|
+
if (error.response?.status === 404) {
|
|
111
|
+
console.log(`ΠΠΎΡΠΊΡΠ»ΠΎΡ ${workflowId} Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½ (ΡΠΆΠ΅ ΡΠ΄Π°Π»Π΅Π½)`);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
console.error(`ΠΡΠΈΠ±ΠΊΠ° ΡΠ΄Π°Π»Π΅Π½ΠΈΡ Π²ΠΎΡΠΊΡΠ»ΠΎΡ ${workflowId}:`, error.response?.data || error.message);
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Π‘ΠΎΠ·Π΄Π°ΡΡ ΠΈΠ»ΠΈ ΠΎΠ±Π½ΠΎΠ²ΠΈΡΡ Π²ΠΎΡΠΊΡΠ»ΠΎΡ (upsert)
|
|
121
|
+
*/
|
|
122
|
+
async upsertWorkflow(workflow: Types.WorkflowDefinition): Promise<any> {
|
|
123
|
+
try {
|
|
124
|
+
console.log(`ΠΡΠΎΠ²Π΅ΡΡΠ΅ΠΌ Π²ΠΎΡΠΊΡΠ»ΠΎΡ: ${workflow.workflowId}`);
|
|
125
|
+
|
|
126
|
+
const existingWorkflow = await this.getWorkflow(workflow.workflowId);
|
|
127
|
+
|
|
128
|
+
const novuData: Types.NovuWorkflowData = {
|
|
129
|
+
name: workflow.name,
|
|
130
|
+
workflowId: workflow.workflowId,
|
|
131
|
+
description: workflow.description,
|
|
132
|
+
payloadSchema: workflow.payloadSchema,
|
|
133
|
+
steps: workflow.steps,
|
|
134
|
+
preferences: workflow.preferences,
|
|
135
|
+
tags: workflow.tags,
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
if (existingWorkflow) {
|
|
139
|
+
console.log(`ΠΠ±Π½ΠΎΠ²Π»ΡΠ΅ΠΌ Π²ΠΎΡΠΊΡΠ»ΠΎΡ: ${workflow.workflowId}`);
|
|
140
|
+
return await this.updateWorkflow(workflow.workflowId, novuData);
|
|
141
|
+
} else {
|
|
142
|
+
console.log(`Π‘ΠΎΠ·Π΄Π°ΡΠΌ Π²ΠΎΡΠΊΡΠ»ΠΎΡ: ${workflow.workflowId}`);
|
|
143
|
+
return await this.createWorkflow(novuData);
|
|
144
|
+
}
|
|
145
|
+
} catch (error: any) {
|
|
146
|
+
console.error(`ΠΡΠΈΠ±ΠΊΠ° upsert Π²ΠΎΡΠΊΡΠ»ΠΎΡ ${workflow.workflowId}:`, error.message);
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Π£Π΄Π°Π»ΠΈΡΡ Π²ΡΠ΅ ΡΡΡΠ΅ΡΡΠ²ΡΡΡΠΈΠ΅ Π²ΠΎΡΠΊΡΠ»ΠΎΡ
|
|
153
|
+
*/
|
|
154
|
+
async deleteAllWorkflows(): Promise<void> {
|
|
155
|
+
console.log('ΠΠΎΠ»ΡΡΠ°Π΅ΠΌ ΡΠΏΠΈΡΠΎΠΊ Π²ΡΠ΅Ρ
Π²ΠΎΡΠΊΡΠ»ΠΎΡ Π΄Π»Ρ ΡΠ΄Π°Π»Π΅Π½ΠΈΡ...');
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
const workflows = await this.getAllWorkflows();
|
|
159
|
+
console.log(`ΠΠ°ΠΉΠ΄Π΅Π½ΠΎ ${workflows.length} Π²ΠΎΡΠΊΡΠ»ΠΎΡ Π΄Π»Ρ ΡΠ΄Π°Π»Π΅Π½ΠΈΡ`);
|
|
160
|
+
|
|
161
|
+
if (workflows.length === 0) {
|
|
162
|
+
console.log('ΠΠ΅Ρ Π²ΠΎΡΠΊΡΠ»ΠΎΡ Π΄Π»Ρ ΡΠ΄Π°Π»Π΅Π½ΠΈΡ');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const errors: string[] = [];
|
|
167
|
+
let deletedCount = 0;
|
|
168
|
+
|
|
169
|
+
for (const workflow of workflows) {
|
|
170
|
+
try {
|
|
171
|
+
await this.deleteWorkflow(workflow.workflowId || workflow._id);
|
|
172
|
+
deletedCount++;
|
|
173
|
+
} catch (error: any) {
|
|
174
|
+
const errorMessage = `ΠΡΠΈΠ±ΠΊΠ° ΡΠ΄Π°Π»Π΅Π½ΠΈΡ Π²ΠΎΡΠΊΡΠ»ΠΎΡ ${workflow.workflowId || workflow._id}: ${error.message}`;
|
|
175
|
+
console.error(`β ${errorMessage}`);
|
|
176
|
+
errors.push(errorMessage);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
console.log(`\nΠ Π΅Π·ΡΠ»ΡΡΠ°Ρ ΡΠ΄Π°Π»Π΅Π½ΠΈΡ:`);
|
|
181
|
+
console.log(`β
Π£Π΄Π°Π»Π΅Π½ΠΎ: ${deletedCount}`);
|
|
182
|
+
console.log(`β ΠΡΠΈΠ±ΠΊΠΈ: ${errors.length}`);
|
|
183
|
+
|
|
184
|
+
if (errors.length > 0) {
|
|
185
|
+
console.log(`\nΠ‘ΠΏΠΈΡΠΎΠΊ ΠΎΡΠΈΠ±ΠΎΠΊ ΡΠ΄Π°Π»Π΅Π½ΠΈΡ:`);
|
|
186
|
+
errors.forEach((error, index) => {
|
|
187
|
+
console.log(`${index + 1}. ${error}`);
|
|
188
|
+
});
|
|
189
|
+
throw new Error(`Π£Π΄Π°Π»Π΅Π½ΠΈΠ΅ Π·Π°Π²Π΅ΡΡΠΈΠ»ΠΎΡΡ Ρ ΠΎΡΠΈΠ±ΠΊΠ°ΠΌΠΈ: ${errors.length} ΠΈΠ· ${workflows.length} Π²ΠΎΡΠΊΡΠ»ΠΎΡ`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
console.log('β
ΠΡΠ΅ ΡΡΡΠ΅ΡΡΠ²ΡΡΡΠΈΠ΅ Π²ΠΎΡΠΊΡΠ»ΠΎΡ ΡΠ΄Π°Π»Π΅Π½Ρ ΡΡΠΏΠ΅ΡΠ½ΠΎ');
|
|
193
|
+
} catch (error: any) {
|
|
194
|
+
console.error('β ΠΡΠΈΡΠΈΡΠ΅ΡΠΊΠ°Ρ ΠΎΡΠΈΠ±ΠΊΠ° ΠΏΡΠΈ ΡΠ΄Π°Π»Π΅Π½ΠΈΠΈ Π²ΠΎΡΠΊΡΠ»ΠΎΡ:', error.message);
|
|
195
|
+
throw error;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Π‘ΠΎΠ·Π΄Π°ΡΡ ΠΈΠ»ΠΈ ΠΎΠ±Π½ΠΎΠ²ΠΈΡΡ Π²ΡΠ΅ Π²ΠΎΡΠΊΡΠ»ΠΎΡ (Ρ ΠΏΡΠ΅Π΄Π²Π°ΡΠΈΡΠ΅Π»ΡΠ½ΡΠΌ ΡΠ΄Π°Π»Π΅Π½ΠΈΠ΅ΠΌ ΡΡΡΠ΅ΡΡΠ²ΡΡΡΠΈΡ
)
|
|
201
|
+
*/
|
|
202
|
+
async upsertAllWorkflows(): Promise<void> {
|
|
203
|
+
console.log('π ΠΠ°ΡΠΈΠ½Π°Π΅ΠΌ ΠΏΠΎΠ»Π½ΡΡ ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΡ Π²ΠΎΡΠΊΡΠ»ΠΎΡ...');
|
|
204
|
+
|
|
205
|
+
// Π¨Π°Π³ 1: Π£Π΄Π°Π»ΡΠ΅ΠΌ Π²ΡΠ΅ ΡΡΡΠ΅ΡΡΠ²ΡΡΡΠΈΠ΅ Π²ΠΎΡΠΊΡΠ»ΠΎΡ
|
|
206
|
+
// try {
|
|
207
|
+
// await this.deleteAllWorkflows();
|
|
208
|
+
// } catch (error: any) {
|
|
209
|
+
// console.error('β ΠΡΠΈΠ±ΠΊΠ° ΠΏΡΠΈ ΡΠ΄Π°Π»Π΅Π½ΠΈΠΈ ΡΡΡΠ΅ΡΡΠ²ΡΡΡΠΈΡ
Π²ΠΎΡΠΊΡΠ»ΠΎΡ:', error.message);
|
|
210
|
+
// throw error;
|
|
211
|
+
// }
|
|
212
|
+
|
|
213
|
+
// Π¨Π°Π³ 2: Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ Π½ΠΎΠ²ΡΠ΅ Π²ΠΎΡΠΊΡΠ»ΠΎΡ
|
|
214
|
+
console.log(`\nπ ΠΠ°ΡΠΈΠ½Π°Π΅ΠΌ ΡΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ${Workflows.allWorkflows.length} Π½ΠΎΠ²ΡΡ
Π²ΠΎΡΠΊΡΠ»ΠΎΡ...`);
|
|
215
|
+
|
|
216
|
+
const errors: string[] = [];
|
|
217
|
+
let successCount = 0;
|
|
218
|
+
|
|
219
|
+
for (const workflow of Workflows.allWorkflows) {
|
|
220
|
+
try {
|
|
221
|
+
await this.upsertWorkflow(workflow);
|
|
222
|
+
console.log(`β ΠΠΎΡΠΊΡΠ»ΠΎΡ ${workflow.workflowId} ΡΡΠΏΠ΅ΡΠ½ΠΎ ΡΠΎΠ·Π΄Π°Π½`);
|
|
223
|
+
successCount++;
|
|
224
|
+
} catch (error: any) {
|
|
225
|
+
const errorMessage = `ΠΡΠΈΠ±ΠΊΠ° ΡΠΎΠ·Π΄Π°Π½ΠΈΡ Π²ΠΎΡΠΊΡΠ»ΠΎΡ ${workflow.workflowId}: ${error.message}`;
|
|
226
|
+
console.error(`β ${errorMessage}`);
|
|
227
|
+
errors.push(errorMessage);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
console.log(`\nΠ Π΅Π·ΡΠ»ΡΡΠ°Ρ ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΠΈ:`);
|
|
232
|
+
console.log(`β
Π£ΡΠΏΠ΅ΡΠ½ΠΎ ΡΠΎΠ·Π΄Π°Π½ΠΎ: ${successCount}`);
|
|
233
|
+
console.log(`β ΠΡΠΈΠ±ΠΊΠΈ: ${errors.length}`);
|
|
234
|
+
|
|
235
|
+
if (errors.length > 0) {
|
|
236
|
+
console.log(`\nΠ‘ΠΏΠΈΡΠΎΠΊ ΠΎΡΠΈΠ±ΠΎΠΊ:`);
|
|
237
|
+
errors.forEach((error, index) => {
|
|
238
|
+
console.log(`${index + 1}. ${error}`);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
throw new Error(`Π‘ΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΡ Π·Π°Π²Π΅ΡΡΠΈΠ»Π°ΡΡ Ρ ΠΎΡΠΈΠ±ΠΊΠ°ΠΌΠΈ: ${errors.length} ΠΈΠ· ${Workflows.allWorkflows.length} Π²ΠΎΡΠΊΡΠ»ΠΎΡ`);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
console.log('β
ΠΡΠ΅ Π²ΠΎΡΠΊΡΠ»ΠΎΡ ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·ΠΈΡΠΎΠ²Π°Π½Ρ ΡΡΠΏΠ΅ΡΠ½ΠΎ');
|
|
245
|
+
}
|
|
246
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { NovuSyncService } from './novu-sync.service';
|
|
4
|
+
import { existsSync } from 'fs';
|
|
5
|
+
import { join, dirname } from 'path';
|
|
6
|
+
import { watch } from 'chokidar';
|
|
7
|
+
import dotenv from 'dotenv';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
|
|
10
|
+
dotenv.config();
|
|
11
|
+
|
|
12
|
+
// ΠΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ ΠΈΠ· ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ
ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ
|
|
13
|
+
const config = {
|
|
14
|
+
apiKey: process.env.NOVU_API_KEY || '',
|
|
15
|
+
apiUrl: process.env.NOVU_API_URL || '',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
async function runSync() {
|
|
19
|
+
console.log('π ΠΠ°ΠΏΡΡΠΊ ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΠΈ Π²ΠΎΡΠΊΡΠ»ΠΎΡ...');
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
// ΠΡΠΎΠ²Π΅ΡΡΠ΅ΠΌ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ
|
|
23
|
+
if (!config.apiKey) {
|
|
24
|
+
throw new Error('β NOVU_API_KEY Π½Π΅ ΡΡΡΠ°Π½ΠΎΠ²Π»Π΅Π½ Π² ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ
ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!config.apiUrl) {
|
|
28
|
+
throw new Error('β NOVU_API_URL Π½Π΅ ΡΡΡΠ°Π½ΠΎΠ²Π»Π΅Π½ Π² ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ
ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const syncService = new NovuSyncService(config);
|
|
32
|
+
await syncService.upsertAllWorkflows();
|
|
33
|
+
console.log('β
Π‘ΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΡ Π·Π°Π²Π΅ΡΡΠ΅Π½Π° ΡΡΠΏΠ΅ΡΠ½ΠΎ');
|
|
34
|
+
return true;
|
|
35
|
+
} catch (error: any) {
|
|
36
|
+
console.error('β ΠΡΠΈΠ±ΠΊΠ° ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΠΈ:', error.message);
|
|
37
|
+
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function main() {
|
|
43
|
+
const args = process.argv.slice(2);
|
|
44
|
+
const isDev = args.includes('--dev') || process.env.NODE_ENV === 'development';
|
|
45
|
+
|
|
46
|
+
if (isDev) {
|
|
47
|
+
console.log('π‘ Π Π΅ΠΆΠΈΠΌ ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΊΠΈ: ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π΅ΠΌ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ...');
|
|
48
|
+
|
|
49
|
+
// ΠΠ°ΠΏΡΡΠΊΠ°Π΅ΠΌ ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΡ ΡΡΠ°Π·Ρ
|
|
50
|
+
const initialSyncSuccess = await runSync();
|
|
51
|
+
|
|
52
|
+
if (!initialSyncSuccess) {
|
|
53
|
+
console.error('β ΠΠ΅ΡΠ²ΠΎΠ½Π°ΡΠ°Π»ΡΠ½Π°Ρ ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΡ Π½Π΅ ΡΠ΄Π°Π»Π°ΡΡ');
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ΠΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π΅ΠΌ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ Π² ΡΠ°ΠΉΠ»Π°Ρ
Π²ΠΎΡΠΊΡΠ»ΠΎΡ ΠΈ ΡΠΈΠΏΠΎΠ²
|
|
58
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
59
|
+
const workflowsPath = join(__dirname, '../workflows');
|
|
60
|
+
const typesPath = join(__dirname, '../types');
|
|
61
|
+
|
|
62
|
+
const watchPaths = [];
|
|
63
|
+
if (existsSync(workflowsPath)) {
|
|
64
|
+
watchPaths.push(workflowsPath);
|
|
65
|
+
}
|
|
66
|
+
if (existsSync(typesPath)) {
|
|
67
|
+
watchPaths.push(typesPath);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (watchPaths.length > 0) {
|
|
71
|
+
console.log('π ΠΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π΅ΠΌ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ Π²:', watchPaths.join(', '));
|
|
72
|
+
|
|
73
|
+
let syncTimeout: NodeJS.Timeout | null = null;
|
|
74
|
+
|
|
75
|
+
const watcher = watch(watchPaths, {
|
|
76
|
+
ignored: /node_modules/,
|
|
77
|
+
persistent: true,
|
|
78
|
+
ignoreInitial: true,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
watcher.on('change', (path: string) => {
|
|
82
|
+
console.log(`π ΠΠ·ΠΌΠ΅Π½Π΅Π½ ΡΠ°ΠΉΠ»: ${path}`);
|
|
83
|
+
|
|
84
|
+
// ΠΠ΅Π±Π°ΡΠ½Ρ Π΄Π»Ρ ΠΈΠ·Π±Π΅ΠΆΠ°Π½ΠΈΡ ΠΌΠ½ΠΎΠΆΠ΅ΡΡΠ²Π΅Π½Π½ΡΡ
Π·Π°ΠΏΡΡΠΊΠΎΠ²
|
|
85
|
+
if (syncTimeout) {
|
|
86
|
+
clearTimeout(syncTimeout);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
syncTimeout = setTimeout(async () => {
|
|
90
|
+
console.log('β° ΠΠ°ΠΏΡΡΠΊΠ°Π΅ΠΌ ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΡ...');
|
|
91
|
+
const success = await runSync();
|
|
92
|
+
if (!success) {
|
|
93
|
+
console.error('β οΈ Π‘ΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΡ Π½Π΅ ΡΠ΄Π°Π»Π°ΡΡ, Π½ΠΎ ΠΏΡΠΎΠ΄ΠΎΠ»ΠΆΠ°Π΅ΠΌ ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΠ΅...');
|
|
94
|
+
}
|
|
95
|
+
}, 1000);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
watcher.on('add', (path: string) => {
|
|
99
|
+
console.log(`β ΠΠΎΠ±Π°Π²Π»Π΅Π½ ΡΠ°ΠΉΠ»: ${path}`);
|
|
100
|
+
if (syncTimeout) {
|
|
101
|
+
clearTimeout(syncTimeout);
|
|
102
|
+
}
|
|
103
|
+
syncTimeout = setTimeout(async () => {
|
|
104
|
+
console.log('β° ΠΠ°ΠΏΡΡΠΊΠ°Π΅ΠΌ ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΡ...');
|
|
105
|
+
const success = await runSync();
|
|
106
|
+
if (!success) {
|
|
107
|
+
console.error('β οΈ Π‘ΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΡ Π½Π΅ ΡΠ΄Π°Π»Π°ΡΡ, Π½ΠΎ ΠΏΡΠΎΠ΄ΠΎΠ»ΠΆΠ°Π΅ΠΌ ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΠ΅...');
|
|
108
|
+
}
|
|
109
|
+
}, 1000);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
watcher.on('unlink', (path: string) => {
|
|
113
|
+
console.log(`β Π£Π΄Π°Π»Π΅Π½ ΡΠ°ΠΉΠ»: ${path}`);
|
|
114
|
+
if (syncTimeout) {
|
|
115
|
+
clearTimeout(syncTimeout);
|
|
116
|
+
}
|
|
117
|
+
syncTimeout = setTimeout(async () => {
|
|
118
|
+
console.log('β° ΠΠ°ΠΏΡΡΠΊΠ°Π΅ΠΌ ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΡ...');
|
|
119
|
+
const success = await runSync();
|
|
120
|
+
if (!success) {
|
|
121
|
+
console.error('β οΈ Π‘ΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΡ Π½Π΅ ΡΠ΄Π°Π»Π°ΡΡ, Π½ΠΎ ΠΏΡΠΎΠ΄ΠΎΠ»ΠΆΠ°Π΅ΠΌ ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΠ΅...');
|
|
122
|
+
}
|
|
123
|
+
}, 1000);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
console.log('π‘ ΠΠ°ΠΆΠΌΠΈΡΠ΅ Ctrl+C Π΄Π»Ρ Π²ΡΡ
ΠΎΠ΄Π°');
|
|
127
|
+
|
|
128
|
+
// ΠΠ΅ΡΠΆΠΈΠΌ ΠΏΡΠΎΡΠ΅ΡΡ ΠΆΠΈΠ²ΡΠΌ
|
|
129
|
+
process.on('SIGINT', () => {
|
|
130
|
+
console.log('\nπ ΠΡΡ
ΠΎΠ΄ ΠΈΠ· ΡΠ΅ΠΆΠΈΠΌΠ° ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΊΠΈ');
|
|
131
|
+
watcher.close();
|
|
132
|
+
process.exit(0);
|
|
133
|
+
});
|
|
134
|
+
} else {
|
|
135
|
+
console.log('β οΈ ΠΠ°ΠΏΠΊΠΈ Π΄Π»Ρ ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΡ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½Ρ');
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
} else {
|
|
140
|
+
// Production ΡΠ΅ΠΆΠΈΠΌ - Π·Π°ΠΏΡΡΠΊΠ°Π΅ΠΌ ΠΎΠ΄ΠΈΠ½ ΡΠ°Π·
|
|
141
|
+
const success = await runSync();
|
|
142
|
+
process.exit(success ? 0 : 1);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ΠΡΠΎΠ²Π΅ΡΡΠ΅ΠΌ, Π·Π°ΠΏΡΡΠ΅Π½ Π»ΠΈ ΡΠ°ΠΉΠ» ΠΊΠ°ΠΊ ΠΎΡΠ½ΠΎΠ²Π½ΠΎΠΉ ΠΌΠΎΠ΄ΡΠ»Ρ
|
|
147
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
148
|
+
main().catch((error) => {
|
|
149
|
+
console.error('β ΠΡΠΈΡΠΈΡΠ΅ΡΠΊΠ°Ρ ΠΎΡΠΈΠ±ΠΊΠ°:', error);
|
|
150
|
+
process.exit(1);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export { runSync, NovuSyncService };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
// ΠΠ°Π·ΠΎΠ²ΡΠ΅ ΡΠΈΠΏΡ Π΄Π»Ρ ΠΊΠ°Π½Π°Π»ΠΎΠ² ΡΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠΉ
|
|
4
|
+
export interface ChannelConfig {
|
|
5
|
+
enabled: boolean;
|
|
6
|
+
readOnly?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ChannelsConfig {
|
|
10
|
+
email: ChannelConfig;
|
|
11
|
+
sms: ChannelConfig;
|
|
12
|
+
in_app: ChannelConfig;
|
|
13
|
+
push: ChannelConfig;
|
|
14
|
+
chat: ChannelConfig;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface PreferencesConfig {
|
|
18
|
+
user: {
|
|
19
|
+
all: ChannelConfig;
|
|
20
|
+
channels: ChannelsConfig;
|
|
21
|
+
};
|
|
22
|
+
workflow: {
|
|
23
|
+
all: ChannelConfig;
|
|
24
|
+
channels: ChannelsConfig;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ΠΠ°Π·ΠΎΠ²ΡΠ΅ ΡΠΈΠΏΡ Π΄Π»Ρ ΡΠ°Π³ΠΎΠ² Π²ΠΎΡΠΊΡΠ»ΠΎΡ
|
|
29
|
+
export interface StepControlValues {
|
|
30
|
+
subject?: string;
|
|
31
|
+
body?: string;
|
|
32
|
+
title?: string;
|
|
33
|
+
content?: string;
|
|
34
|
+
avatar?: string;
|
|
35
|
+
editorType?: 'html' | 'text';
|
|
36
|
+
[key: string]: any;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface WorkflowStep {
|
|
40
|
+
name: string;
|
|
41
|
+
type: 'email' | 'sms' | 'in_app' | 'push' | 'chat' | 'delay' | 'digest';
|
|
42
|
+
controlValues: StepControlValues;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ΠΠ°Π·ΠΎΠ²ΡΠΉ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡ Π΄Π»Ρ payload Π²ΠΎΡΠΊΡΠ»ΠΎΡ
|
|
46
|
+
export interface BaseWorkflowPayload {
|
|
47
|
+
[key: string]: any;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ΠΠ½ΡΠ΅ΡΡΠ΅ΠΉΡ Π΄Π»Ρ ΡΡ
Π΅ΠΌΡ payload (JSON Schema)
|
|
51
|
+
export interface PayloadSchema {
|
|
52
|
+
type: string;
|
|
53
|
+
properties: Record<string, any>;
|
|
54
|
+
required: string[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ΠΠΎΠΏΡΡΡΠΈΠΌΡΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΡ Π΄Π»Ρ origin Π² Novu
|
|
58
|
+
export type NovuOrigin = 'novu-cloud' | 'novu-cloud-v1' | 'external';
|
|
59
|
+
|
|
60
|
+
// ΠΠ»Π°Π²Π½ΡΠΉ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡ Π΄Π»Ρ Π²ΠΎΡΠΊΡΠ»ΠΎΡ
|
|
61
|
+
export interface WorkflowDefinition<T extends BaseWorkflowPayload = BaseWorkflowPayload> {
|
|
62
|
+
name: string;
|
|
63
|
+
workflowId: string;
|
|
64
|
+
description?: string;
|
|
65
|
+
payloadSchema: PayloadSchema;
|
|
66
|
+
steps: WorkflowStep[];
|
|
67
|
+
preferences: PreferencesConfig;
|
|
68
|
+
origin?: NovuOrigin; // ΠΠ΅Π»Π°Π΅ΠΌ optional
|
|
69
|
+
tags?: string[]; // Π’Π΅Π³ΠΈ Π΄Π»Ρ Π³ΡΡΠΏΠΏΠΈΡΠΎΠ²ΠΊΠΈ Π²ΠΎΡΠΊΡΠ»ΠΎΡ
|
|
70
|
+
// Π’ΠΈΠΏΠΈΠ·ΠΈΡΠΎΠ²Π°Π½Π½Π°Ρ ΡΡ
Π΅ΠΌΠ° Π΄Π»Ρ Π²Π°Π»ΠΈΠ΄Π°ΡΠΈΠΈ payload
|
|
71
|
+
payloadZodSchema: z.ZodSchema<T>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ΠΠ½ΡΠ΅ΡΡΠ΅ΠΉΡ Π΄Π»Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ Π²ΠΎΡΠΊΡΠ»ΠΎΡ Π² Novu (Π±Π΅Π· origin Π΄Π»Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ)
|
|
75
|
+
export interface NovuWorkflowData {
|
|
76
|
+
name: string;
|
|
77
|
+
workflowId: string;
|
|
78
|
+
description?: string;
|
|
79
|
+
payloadSchema: PayloadSchema;
|
|
80
|
+
steps: WorkflowStep[];
|
|
81
|
+
preferences: PreferencesConfig;
|
|
82
|
+
origin?: NovuOrigin; // Optional - ΡΠΎΠ»ΡΠΊΠΎ Π΄Π»Ρ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠΉ
|
|
83
|
+
tags?: string[]; // Π’Π΅Π³ΠΈ Π΄Π»Ρ Π³ΡΡΠΏΠΏΠΈΡΠΎΠ²ΠΊΠΈ Π²ΠΎΡΠΊΡΠ»ΠΎΡ
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ΠΠ½ΡΠ΅ΡΡΠ΅ΠΉΡ Π΄Π»Ρ ΡΡΠΈΠ³Π³Π΅ΡΠ° Π²ΠΎΡΠΊΡΠ»ΠΎΡ
|
|
87
|
+
export interface WorkflowTriggerData<T extends BaseWorkflowPayload> {
|
|
88
|
+
workflowId: string;
|
|
89
|
+
subscriberId: string;
|
|
90
|
+
payload: T;
|
|
91
|
+
actor?: {
|
|
92
|
+
subscriberId: string;
|
|
93
|
+
email?: string;
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Π£ΡΠΈΠ»ΠΈΡΠ°ΡΠ½ΡΠ΅ ΡΠΈΠΏΡ
|
|
98
|
+
export type WorkflowStepType = WorkflowStep['type'];
|
|
99
|
+
export type ChannelType = keyof ChannelsConfig;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { slugify } from './slugify/slugify';
|