@unchainedshop/platform 5.0.0-alpha.10 → 5.0.0-alpha.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -114,6 +114,9 @@ The package also exports the [built-in template resolvers](src/templates/index.t
114
114
  | `unchainedAPI` | Core modules, services, bulk importer/exporter, and options |
115
115
  | `graphqlHandler` | GraphQL Yoga server instance |
116
116
  | `db` | MongoDB database instance |
117
+ | `shutdown` | Stops the work queue, plugins, event emitter, GraphQL server, audit log and database without exiting the process; every call returns the same promise |
118
+
119
+ On `SIGTERM` / `SIGINT` the platform calls `shutdown()` and exits. Once `shutdown()` has started, these signals no longer exit the process, so exit it yourself after awaiting `shutdown()`.
117
120
 
118
121
  ## License
119
122
 
@@ -16,4 +16,5 @@ export declare const startPlatform: ({ modules, services, options, rolesOptions,
16
16
  unchainedAPI: UnchainedCore;
17
17
  graphqlHandler: any;
18
18
  db: mongodb.Db;
19
+ shutdown: () => Promise<void>;
19
20
  }>;
@@ -83,13 +83,31 @@ export const startPlatform = async ({ modules, services, options, rolesOptions,
83
83
  });
84
84
  const version = UNCHAINED_API_VERSION || npm_package_version || 'n/a';
85
85
  defaultLogger.info(`Unchained Engine running`, { version });
86
- let isCleaningUp = false;
86
+ let shutdownPromise;
87
+ const shutdown = () => {
88
+ shutdownPromise ??= (async () => {
89
+ defaultLogger.debug('Stopping Workqueue');
90
+ stopWorkqueue();
91
+ defaultLogger.debug('Shutting down plugins');
92
+ await pluginRegistry.shutdown(unchainedAPI);
93
+ defaultLogger.debug('Shutting down event emitter');
94
+ await getEmitAdapter()?.shutdown?.();
95
+ defaultLogger.debug('Stopping GraphQL server');
96
+ await graphqlHandler.dispose();
97
+ if (auditLog) {
98
+ defaultLogger.debug('Closing audit log');
99
+ await auditLog.close();
100
+ }
101
+ defaultLogger.debug('Stopping DB Connection');
102
+ await stopDb();
103
+ })();
104
+ return shutdownPromise;
105
+ };
87
106
  const cleanup = (signal) => async () => {
88
- if (isCleaningUp) {
107
+ if (shutdownPromise) {
89
108
  defaultLogger.debug('Cleanup already in progress, ignoring signal', { signal });
90
109
  return;
91
110
  }
92
- isCleaningUp = true;
93
111
  defaultLogger.debug('Starting cleanup', { signal });
94
112
  const forceExitTimeout = setTimeout(() => {
95
113
  defaultLogger.error('Cleanup timeout exceeded, forcing exit');
@@ -97,20 +115,7 @@ export const startPlatform = async ({ modules, services, options, rolesOptions,
97
115
  }, CLEANUP_FORCE_EXIT_TIMEOUT_MS);
98
116
  forceExitTimeout.unref();
99
117
  try {
100
- defaultLogger.debug('Stopping Workqueue', { signal });
101
- stopWorkqueue();
102
- defaultLogger.debug('Shutting down plugins', { signal });
103
- await pluginRegistry.shutdown(unchainedAPI);
104
- defaultLogger.debug('Shutting down event emitter', { signal });
105
- await getEmitAdapter()?.shutdown?.();
106
- defaultLogger.debug('Stopping GraphQL server', { signal });
107
- await graphqlHandler.dispose();
108
- if (auditLog) {
109
- defaultLogger.debug('Closing audit log', { signal });
110
- await auditLog.close();
111
- }
112
- defaultLogger.debug('Stopping DB Connection', { signal });
113
- await stopDb();
118
+ await shutdown();
114
119
  defaultLogger.debug(`Unchained Engine exiting gracefully`, { signal, version });
115
120
  clearTimeout(forceExitTimeout);
116
121
  process.exit(0);
@@ -138,5 +143,5 @@ export const startPlatform = async ({ modules, services, options, rolesOptions,
138
143
  });
139
144
  const end = performance.now();
140
145
  defaultLogger.debug(`Unchained Engine started in ${(end - start).toFixed(0)} ms`);
141
- return { unchainedAPI, graphqlHandler, db };
146
+ return { unchainedAPI, graphqlHandler, db, shutdown };
142
147
  };
@@ -1,14 +1,22 @@
1
1
  const { EMAIL_FROM, EMAIL_WEBSITE_NAME, EMAIL_WEBSITE_URL } = process.env;
2
- export const resolveEnrollmentStatusTemplate = async ({ enrollmentId }, context) => {
2
+ export const resolveEnrollmentStatusTemplate = async ({ enrollmentId, locale }, context) => {
3
3
  const { modules } = context;
4
4
  const enrollment = await modules.enrollments.findEnrollment({ enrollmentId });
5
5
  const user = await modules.users.findUserById(enrollment.userId);
6
- const subject = `${EMAIL_WEBSITE_NAME}: Updated Enrollment / ${enrollment.enrollmentNumber}`;
6
+ const subject = `${EMAIL_WEBSITE_NAME}: Updated Enrollment / ${enrollment.enrollmentNumber || enrollment._id}`;
7
7
  const url = `${EMAIL_WEBSITE_URL}/enrollment?_id=${enrollment._id}`;
8
+ const formatDate = (date) => new Date(date).toLocaleDateString(locale || 'en', { dateStyle: 'long' });
9
+ const scheduledDates = [
10
+ enrollment.expires && `Ends: ${formatDate(enrollment.expires)}`,
11
+ enrollment.resumeAt && `Resumes: ${formatDate(enrollment.resumeAt)}`,
12
+ enrollment.minimumCommitmentEnd &&
13
+ `Minimum commitment until: ${formatDate(enrollment.minimumCommitmentEnd)}`,
14
+ ].filter(Boolean);
8
15
  const text = `
9
16
  ${subject}\n
10
17
  \n
11
18
  Status: ${enrollment.status}
19
+ ${scheduledDates.join('\n ')}
12
20
  \n
13
21
  -----------------\n
14
22
  Show: ${url}\n
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@unchainedshop/platform",
3
3
  "description": "Complete e-commerce platform bundle for the Unchained Engine",
4
- "version": "5.0.0-alpha.10",
4
+ "version": "5.0.0-alpha.11",
5
5
  "files": [
6
6
  "lib",
7
7
  "bin"
@@ -45,14 +45,14 @@
45
45
  "npm": ">=10.0.0"
46
46
  },
47
47
  "dependencies": {
48
- "@unchainedshop/api": "^5.0.0-alpha.10",
49
- "@unchainedshop/core": "^5.0.0-alpha.10",
50
- "@unchainedshop/events": "^5.0.0-alpha.10",
51
- "@unchainedshop/logger": "^5.0.0-alpha.10",
52
- "@unchainedshop/mongodb": "^5.0.0-alpha.10",
53
- "@unchainedshop/plugins": "^5.0.0-alpha.10",
54
- "@unchainedshop/roles": "^5.0.0-alpha.10",
55
- "@unchainedshop/utils": "^5.0.0-alpha.10"
48
+ "@unchainedshop/api": "^5.0.0-alpha.11",
49
+ "@unchainedshop/core": "^5.0.0-alpha.11",
50
+ "@unchainedshop/events": "^5.0.0-alpha.11",
51
+ "@unchainedshop/logger": "^5.0.0-alpha.11",
52
+ "@unchainedshop/mongodb": "^5.0.0-alpha.11",
53
+ "@unchainedshop/plugins": "^5.0.0-alpha.11",
54
+ "@unchainedshop/roles": "^5.0.0-alpha.11",
55
+ "@unchainedshop/utils": "^5.0.0-alpha.11"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@types/node": "^26.2.0",