exflower-license-server 1.0.5 → 1.0.6

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/admin/index.html CHANGED
@@ -322,7 +322,6 @@
322
322
  <label>类型</label>
323
323
  <select id="tmpl-type">
324
324
  <option value="agent">Agent</option>
325
- <option value="crew">Crew</option>
326
325
  <option value="excard">ExCard</option>
327
326
  <option value="workflow">Workflow</option>
328
327
  </select>
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "exflower-license-server",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "ExFlower License Server - 在线授权验证服务",
5
5
  "main": "index.js",
6
6
  "bin": {
7
- "exflower-license-server": "./index.js"
7
+ "exflower-license-server": "index.js"
8
8
  },
9
9
  "files": [
10
10
  "index.js",
package/routes.js CHANGED
@@ -109,6 +109,7 @@ function setupRoutes(app) {
109
109
  max_agents: company.max_agents,
110
110
  max_crews: company.max_crews
111
111
  },
112
+ active_instances: activeInstances,
112
113
  expires_at: company.expires_at
113
114
  });
114
115
  });
@@ -326,14 +327,42 @@ function setupRoutes(app) {
326
327
  res.json({ success: true, templates });
327
328
  });
328
329
 
330
+ // ── Template Relationship Helpers ──
331
+ function _extractWorkflowExcardRefs(contentJson) {
332
+ try {
333
+ const data = typeof contentJson === 'string' ? JSON.parse(contentJson) : contentJson;
334
+ const steps = data.steps || [];
335
+ const refs = new Set();
336
+ for (const step of steps) {
337
+ const eid = step.excardId || step.excard_id;
338
+ if (eid) refs.add(eid);
339
+ }
340
+ return Array.from(refs);
341
+ } catch {
342
+ return [];
343
+ }
344
+ }
345
+
346
+ function _findWorkflowRefsToExcard(excardId) {
347
+ const allWorkflows = queryAll("SELECT id, name, content_json FROM templates WHERE type = 'workflow'");
348
+ const refs = [];
349
+ for (const wf of allWorkflows) {
350
+ const ids = _extractWorkflowExcardRefs(wf.content_json);
351
+ if (ids.includes(excardId)) {
352
+ refs.push({ id: wf.id, name: wf.name });
353
+ }
354
+ }
355
+ return refs;
356
+ }
357
+
329
358
  app.post('/api/admin/templates', requireAdmin, (req, res) => {
330
359
  const { company_id, type, name, description, content_json, content_md, tags, version } = req.body;
331
360
 
332
361
  if (!type || !name || !content_json) {
333
362
  return res.status(400).json({ success: false, message: 'type, name, content_json are required' });
334
363
  }
335
- if (!['agent', 'crew', 'excard', 'workflow', 'doc'].includes(type)) {
336
- return res.status(400).json({ success: false, message: 'type must be agent, crew, excard, workflow, or doc' });
364
+ if (!['agent', 'excard', 'workflow', 'doc'].includes(type)) {
365
+ return res.status(400).json({ success: false, message: 'type must be agent, excard, workflow, or doc' });
337
366
  }
338
367
 
339
368
  // If company_id is provided, verify it exists
@@ -344,6 +373,20 @@ function setupRoutes(app) {
344
373
  }
345
374
  }
346
375
 
376
+ // Validate workflow excard references
377
+ if (type === 'workflow') {
378
+ const excardRefs = _extractWorkflowExcardRefs(content_json);
379
+ for (const eid of excardRefs) {
380
+ const exists = queryGet('SELECT id FROM templates WHERE id = ? AND type = ?', [eid, 'excard']);
381
+ if (!exists) {
382
+ return res.status(400).json({
383
+ success: false,
384
+ message: `Workflow references excard template '${eid}' which does not exist in the template library`
385
+ });
386
+ }
387
+ }
388
+ }
389
+
347
390
  const id = generateId();
348
391
  queryRun(
349
392
  `INSERT INTO templates (id, company_id, type, name, description, content_json, content_md, tags, version, created_at, updated_at)
@@ -369,6 +412,22 @@ function setupRoutes(app) {
369
412
  if (!template) return res.status(404).json({ success: false, message: 'Template not found' });
370
413
 
371
414
  const { company_id, type, name, description, content_json, content_md, tags, version, is_public } = req.body;
415
+
416
+ // Validate workflow excard references when content_json is updated
417
+ const isWorkflow = (type !== undefined ? type : template.type) === 'workflow';
418
+ if (isWorkflow && content_json !== undefined) {
419
+ const excardRefs = _extractWorkflowExcardRefs(content_json);
420
+ for (const eid of excardRefs) {
421
+ const exists = queryGet('SELECT id FROM templates WHERE id = ? AND type = ?', [eid, 'excard']);
422
+ if (!exists) {
423
+ return res.status(400).json({
424
+ success: false,
425
+ message: `Workflow references excard template '${eid}' which does not exist in the template library`
426
+ });
427
+ }
428
+ }
429
+ }
430
+
372
431
  const updates = [];
373
432
  const params = [];
374
433
 
@@ -393,6 +452,20 @@ function setupRoutes(app) {
393
452
  });
394
453
 
395
454
  app.delete('/api/admin/templates/:id', requireAdmin, (req, res) => {
455
+ const template = queryGet('SELECT * FROM templates WHERE id = ?', [req.params.id]);
456
+ if (!template) return res.status(404).json({ success: false, message: 'Template not found' });
457
+
458
+ // Prevent deleting excard templates referenced by workflow templates
459
+ if (template.type === 'excard') {
460
+ const refs = _findWorkflowRefsToExcard(template.id);
461
+ if (refs.length > 0) {
462
+ return res.status(400).json({
463
+ success: false,
464
+ message: `Cannot delete: this ExCard template is referenced by ${refs.length} workflow template(s): ${refs.map(r => `'${r.name}'`).join(', ')}`
465
+ });
466
+ }
467
+ }
468
+
396
469
  queryRun('DELETE FROM templates WHERE id = ?', [req.params.id]);
397
470
  res.json({ success: true });
398
471
  });