feat(W1): status lifecycle (suspend/delete/resume) + shared effective-grants lib

- Extract calculateEffectiveGrants into n8n/shared/effective-grants.js
  (single source of truth), inlined at build time into W1/W2/W3 via
  gen_w*.js + inline_shared.js — removes the duplicated algorithm.
- W1 now provisions with a status branch:
  suspended/deleted -> disable AList user + clear role scopes (soft delete)
  active -> create/update user+role, RECOMPUTE desired scopes (never restore
  stale), re-enable
- Loop guard corrected: check AList user LIVE disabled state via
  GET /api/admin/user/get (sync_status alone breaks suspend->resume).
- W2/W3 rebuilt on shared lib (identical behaviour).
- Tests: test_effective_grants.js (15), test_w3_refactored.js (13),
  test_w1_lifecycle.js (23) — all green.
- Drop superseded w2-compute.js / w3-compute.js / test_w3.js.
This commit is contained in:
2026-08-23 04:59:43 +08:00
parent 870a702f7b
commit 3ecee3099e
18 changed files with 1216 additions and 1109 deletions
+10 -14
View File
@@ -1,19 +1,19 @@
// Builds n8n/W3-expiry-sync.json from w3-compute.js (single source of truth).
// Builds n8n/W3-expiry-sync.json — the 5-min expiry sweep.
// Single source of truth: shared/effective-grants.js (inlined) + w3-body.js.
// Run: node n8n/gen_w3.js
const fs = require('fs');
const path = require('path');
const jsCode = fs.readFileSync(path.join(__dirname, 'w3-compute.js'), 'utf8');
const { sharedSource } = require('./inline_shared');
let body = fs.readFileSync(path.join(__dirname, 'w3-body.js'), 'utf8');
body = body.replace('// __SHARED_INLINE__ (replaced by shared/effective-grants.js source at build)', sharedSource());
const workflow = {
name: 'W3 - Expiry Sync (AList Role Reconciliation)',
nodes: [
{
parameters: {
rule: {
interval: [
{ field: 'minutes', minutesInterval: 5 }
]
}
rule: { interval: [{ field: 'minutes', minutesInterval: 5 }] }
},
id: 'trigger-schedule',
name: 'Every 5 minutes',
@@ -22,7 +22,7 @@ const workflow = {
position: [0, 0]
},
{
parameters: { jsCode },
parameters: { jsCode: body },
id: 'reconcile',
name: 'Reconcile & Apply (Expiry Sweep)',
type: 'n8n-nodes-base.code',
@@ -32,11 +32,7 @@ const workflow = {
],
connections: {
'Every 5 minutes': {
main: [
[
{ node: 'Reconcile & Apply (Expiry Sweep)', type: 'main', index: 0 }
]
]
main: [[{ node: 'Reconcile & Apply (Expiry Sweep)', type: 'main', index: 0 }]]
}
},
settings: { executionOrder: 'v1' },
@@ -44,4 +40,4 @@ const workflow = {
};
fs.writeFileSync(path.join(__dirname, 'W3-expiry-sync.json'), JSON.stringify(workflow, null, 2) + '\n');
console.log('wrote W3-expiry-sync.json (' + jsCode.length + ' chars jsCode)');
console.log('wrote W3-expiry-sync.json (' + body.length + ' chars body)');