@yunsoft/yuncms-api 0.1.23 → 0.1.25
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/package.json +7 -2
- package/src/ai/service.js +18 -1
- package/src/app.js +2 -0
- package/src/audit-hooks.js +27 -0
- package/src/automations/config.js +70 -0
- package/src/automations/service.js +180 -0
- package/src/automations/worker.js +118 -0
- package/src/error-response.js +18 -0
- package/src/mcp.js +27 -2
- package/src/routes/automations.js +22 -0
- package/src/server.js +26 -28
- package/studio-dist/assets/{index-CBnagW4C.css → index-Cs3kN5GV.css} +1 -1
- package/studio-dist/assets/index-NAtDOkZk.js +11 -0
- package/studio-dist/index.html +2 -2
- package/studio-dist/assets/index-CQMpTTnA.js +0 -11
package/src/server.js
CHANGED
|
@@ -23,13 +23,16 @@ import { createApp } from './app.js';
|
|
|
23
23
|
import { AiAssistantService } from './ai/service.js';
|
|
24
24
|
import { loadOrCreateAiSettingsKey } from './ai/secret-key.js';
|
|
25
25
|
import { AiSettingsStore } from './ai/settings-store.js';
|
|
26
|
-
import {
|
|
26
|
+
import { registerInternalAudit } from './audit-hooks.js';
|
|
27
27
|
import { loadExternalAuthConfig } from './external-auth/config.js';
|
|
28
28
|
import { ExternalAuthProviderRegistry } from './external-auth/providers.js';
|
|
29
29
|
import { loadExtensionRuntime } from './extensions/runtime.js';
|
|
30
30
|
import { createMcpRouter } from './mcp.js';
|
|
31
31
|
import { McpSettingsStore } from './mcp/settings-store.js';
|
|
32
32
|
import { createAiRouter } from './routes/ai.js';
|
|
33
|
+
import { createAutomationsRouter } from './routes/automations.js';
|
|
34
|
+
import { AiAutomationsService } from './automations/service.js';
|
|
35
|
+
import { AiAutomationWorker } from './automations/worker.js';
|
|
33
36
|
|
|
34
37
|
loadEnvFileIfPresent();
|
|
35
38
|
await assertMaintenanceStartupAllowed({ cwd: process.cwd(), env: process.env });
|
|
@@ -82,30 +85,9 @@ const mailer = config.mail.host ? new SmtpMailer({
|
|
|
82
85
|
|
|
83
86
|
let server = null;
|
|
84
87
|
let extensionRuntime = null;
|
|
88
|
+
let automationWorker = null;
|
|
85
89
|
let shuttingDown = false;
|
|
86
90
|
|
|
87
|
-
function registerInternalAudit({ emitter, services }) {
|
|
88
|
-
const AuditService = services.AuditService;
|
|
89
|
-
const systemAccountability = createSystemAccountability();
|
|
90
|
-
for (const event of INTERNAL_AUDIT_EVENTS) {
|
|
91
|
-
emitter.registerAction(event, async (payload, context) => {
|
|
92
|
-
try {
|
|
93
|
-
const audit = new AuditService({ accountability: systemAccountability, database: pool, logger, requestId: context.requestId ?? null });
|
|
94
|
-
await audit.record({
|
|
95
|
-
user: context.accountability?.user ?? null,
|
|
96
|
-
action: event,
|
|
97
|
-
collection: context.collection ?? null,
|
|
98
|
-
itemKey: payload?.key ?? null,
|
|
99
|
-
requestId: context.requestId ?? null,
|
|
100
|
-
payload,
|
|
101
|
-
});
|
|
102
|
-
} catch (error) {
|
|
103
|
-
logger.error('YunCMS audit write failed after committed mutation', { event, requestId: context.requestId ?? null, code: error?.code, error });
|
|
104
|
-
}
|
|
105
|
-
}, { extensionId: 'core.audit', priority: 1000 });
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
91
|
async function start() {
|
|
110
92
|
await assertDatabaseCompatible(pool);
|
|
111
93
|
const aiKey = await loadOrCreateAiSettingsKey();
|
|
@@ -136,9 +118,17 @@ async function start() {
|
|
|
136
118
|
const aiAssistant = new AiAssistantService({ settingsStore: aiSettingsStore, logger });
|
|
137
119
|
const aiRouter = createAiRouter({ assistant: aiAssistant, settingsStore: aiSettingsStore });
|
|
138
120
|
const mcpSettingsStore = new McpSettingsStore({ database: pool });
|
|
139
|
-
const mcpRouter = createMcpRouter({ settingsStore: mcpSettingsStore, logger });
|
|
140
121
|
mailer?.setEmitter(emitter);
|
|
141
|
-
registerInternalAudit({ emitter, services });
|
|
122
|
+
registerInternalAudit({ emitter, services, database: pool, logger });
|
|
123
|
+
const automationOptions = { assistant: aiAssistant, settingsStore: aiSettingsStore, schemaCache };
|
|
124
|
+
const automations = new AiAutomationsService({ ...automationOptions, database: pool,
|
|
125
|
+
accountability: createSystemAccountability(), services, emitter, storage, logger });
|
|
126
|
+
for (const event of ['items.create', 'items.update']) {
|
|
127
|
+
emitter.registerAction(event, (payload, context) => automations.enqueue(event, payload, context), { extensionId: 'core.ai-automations' });
|
|
128
|
+
}
|
|
129
|
+
automationWorker = new AiAutomationWorker(automations);
|
|
130
|
+
const automationsRouter = createAutomationsRouter(automationOptions);
|
|
131
|
+
const mcpRouter = createMcpRouter({ settingsStore: mcpSettingsStore, logger, automationOptions });
|
|
142
132
|
|
|
143
133
|
extensionRuntime = await loadExtensionRuntime({
|
|
144
134
|
services,
|
|
@@ -165,12 +155,17 @@ async function start() {
|
|
|
165
155
|
externalAuthRegistry,
|
|
166
156
|
endpointExtensions: extensionRuntime.endpointExtensions,
|
|
167
157
|
aiRouter,
|
|
158
|
+
automationsRouter,
|
|
168
159
|
mcpRouter,
|
|
169
160
|
});
|
|
170
161
|
|
|
171
162
|
await extensionRuntime.init('app.beforeStart');
|
|
172
163
|
server = await new Promise((resolve, reject) => {
|
|
173
|
-
const listeningServer = app.listen(config.server.port, config.server.host, () => {
|
|
164
|
+
const listeningServer = app.listen(config.server.port, config.server.host, (error) => {
|
|
165
|
+
if (error) {
|
|
166
|
+
reject(error);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
174
169
|
logger.info('YunCMS API listening', { host: config.server.host, port: config.server.port });
|
|
175
170
|
resolve(listeningServer);
|
|
176
171
|
});
|
|
@@ -178,6 +173,7 @@ async function start() {
|
|
|
178
173
|
});
|
|
179
174
|
await extensionRuntime.init('app.afterStart');
|
|
180
175
|
extensionRuntime.startSchedules();
|
|
176
|
+
automationWorker.start();
|
|
181
177
|
}
|
|
182
178
|
|
|
183
179
|
async function shutdown(signal) {
|
|
@@ -189,6 +185,7 @@ async function shutdown(signal) {
|
|
|
189
185
|
process.exit(1);
|
|
190
186
|
}, 10_000);
|
|
191
187
|
forceExit.unref();
|
|
188
|
+
await automationWorker?.stop();
|
|
192
189
|
|
|
193
190
|
const schedulesStopped = await extensionRuntime?.stopSchedules({ timeoutMs: 5_000 }).catch(() => false);
|
|
194
191
|
if (schedulesStopped === false) {
|
|
@@ -218,7 +215,8 @@ for (const signal of ['SIGINT', 'SIGTERM']) {
|
|
|
218
215
|
|
|
219
216
|
start().catch(async (error) => {
|
|
220
217
|
logger.error('YunCMS API failed to start', { code: error?.code, error });
|
|
221
|
-
await
|
|
222
|
-
|
|
218
|
+
await shutdown('startup-failure').catch((cleanupError) => {
|
|
219
|
+
logger.error('YunCMS API startup cleanup failed', { code: cleanupError?.code, error: cleanupError });
|
|
220
|
+
});
|
|
223
221
|
process.exit(1);
|
|
224
222
|
});
|