Commit Graph
20 Commits
Author SHA1 Message Date
hoelee 29ff66de88 docs(PROJECT_STATE): sync handoff to final state (status 5-value 定稿, W4 next)
- Fix stale '待决策 status 语义' section (now 定稿 as 5 account-level statuses)
- Rewrite 下一步 to reflect: features done + tested, remaining = reimport W1
  to n8n for real acceptance, then W4 reconciliation, then AccessGrants design
- Correct test count (W1 = 10 scenarios, not 26 assertions)
- Add ARCHITECTURE/PERMISSIONS/ADMIN_GUIDE to project structure
- Fix lifecycle value suspend_or_delete -> deactivate
- Drop obsolete Load Env template (now in gen_w1.js)
w3-verified
2026-08-23 07:50:25 +08:00
hoelee 03af9df3e4 docs(ADMIN_GUIDE): rewrite for non-technical admins
- Remove all n8n/backend-logic mentions; NocoDB-only instructions.
- Add required-field markers + fill order for each table.
- Add Purchases table section (record customer purchase, expected fields).
- Plain language, no jargon. Add status quick-reference table.
2026-08-23 07:41:45 +08:00
hoelee 80eb2b71be docs: architecture, permissions, admin guide + refresh schema/workflows
- New ARCHITECTURE.md (3-layer model + data flow + lifecycle matrix)
- New PERMISSIONS.md (grant calculation rules + enforcement)
- New ADMIN_GUIDE.md (華文极简:Customer/CustomerProduct 授权 + Product/Resource 管理)
- Rewrite N8N_WORKFLOWS.md from WF1-6 (obsolete) to actual W1/W2/W3
- Update DATABASE_SCHEMA.md: fix status enum (drop 'disabled'), add 5-status semantics
- Update README.md doc index
2026-08-23 07:30:41 +08:00
hoelee 89b769cfd9 feat(W1): full 5-status account lifecycle (pending/active/suspended/expired/deleted)
- Non-active statuses (pending/suspended/expired/deleted) now uniformly:
  disable AList user + clear role scopes (soft, keeps user/role/CP/history);
  no-op when the customer has no AList user yet (pure NocoDB label).
- Resume transitions (X -> active) recompute effective grants from current
  CustomerProducts + expiry, never restore stale scopes.
- businessChanged() now also treats after-insert (new row, no previous) as a
  business change so fresh customers are processed on first webhook.
- Lifecycle test covers the full matrix: 10 scenarios, all green.
- AccessGrants still not materialized (deferred to W4 per decision).
- Customers.status stays account-level, outside permission calc.
2026-08-23 07:17:45 +08:00
hoelee 21e3a3b45f fix(shared): link-field where query must use (customer.Id,eq,N) not (customer,eq,N)
- calculateEffectiveGrants queried CustomerProducts/AccessGrants with
  (customer,eq,N), which matches the link field's DISPLAY NAME and always
  returns 0 rows -> desiredPaths always [] -> W1/W2/W3 write empty scopes.
- Regression from the shared-lib refactor (3ecee30): W2 had already fixed
  this in 36a6b3b but the shared extract reverted to the old syntax.
- Correct syntax (customer.Id,eq,N) verified against live NocoDB for
  customer 6 -> /Class/Health, /Class/Other, /Template/Music.
- Updated all three test mocks to match the corrected where clause.
2026-08-23 06:55:04 +08:00
hoelee 9bcdfc2049 fix(W1): self-write-back suppression via old/new field comparison
- Replace brittle sync_status==='synced' loop guard with NocoDB v2
  old/new row comparison (previous_rows vs rows).
- businessChanged() = business field move OR sync_required set; echo
  (bookkeeping-only delta) -> skip_writeback_echo, zero AList mutation.
- writeBack() value-change guard: GET live record, PATCH only fields
  whose value actually changes (no-change -> no PATCH -> no webhook).
- sync_required Checkbox honored as force-reprocess flag, cleared on write-back.
- Extract-id node passes previousRow/newRow through Load Env to lifecycle.
- W1 lifecycle test now covers echo suppression (26 assertions).
- Customers.status still NOT consumed by permission calc (TBD semantics).
- W2/W3 effective-grants semantics unchanged.
2026-08-23 06:43:01 +08:00
hoelee 3ecee3099e 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.
2026-08-23 04:59:43 +08:00
hoelee 870a702f7b docs: clean up double-redaction artifacts in credential placeholders v0.3-core-sync-working 2026-08-23 04:38:16 +08:00
hoelee 2de82ff3af fix(W2/W3): expiry boundary — use <= today (expiry on today counts as expired)
Root cause: expires_at == today was NOT filtered (used '< today'), so a
product expiring ON today kept its resources in desired scopes -> no diff
-> no removal. Changed to '<='. Applied to W3 (CP + grant loops) and W2
(active filter). Also W2 previously had NO expiry filter on active CPs at
all.
2026-08-23 03:48:44 +08:00
hoelee 7f12804f47 fix(W3): remove incorrect 'Bearer' prefix in Authorization header
AList expects bare token (not 'Bearer <token>'). With 'Bearer' prefix,
all role/get calls returned non-data responses -> currentPaths degraded
to [] -> W3 kept re-applying same scopes every run (false toAdd) and
never detected expired scopes (false toRemove). This is the root cause
of W3 not showing hoe's expiry removal.
2026-08-23 03:08:59 +08:00
hoelee 266a3442c8 fix(W2): graceful skip when customer link not yet filled (no crash)
When CP record is drafted with product set before customer,
the webhook fires with customerId=null -> now returns
skipWriteBack=true (routed to No-op) instead of throwing.
Record re-fires webhook properly once customer field is set.
2026-08-23 02:41:54 +08:00
hoelee a907a85a57 feat(W3): expiry sync / full reconciliation sweep (schedule */5) + design + tested compute
- Single Code-node sweep: desired = CP-active paths UNION active AccessGrants;
  expiry excluded -> role/update rewrite; optional auto-disable (env-gated, default off)
- disable guard: only when had scopes before (never fresh empty-role customer)
- loop guard: no NocoDB Customers.status flip (avoids W1 re-provision loop)
- test_w3.js mock-verified both AUTO_DISABLE modes; gen_w3.js builds JSON from w3-compute.js
2026-08-23 01:58:58 +08:00
hoelee 543f4a7885 docs: add credentials quick-reference (local use only; redact before pushing to Gitea) 2026-08-23 00:48:15 +08:00
hoelee 7ff0fa1120 docs: update PROJECT_STATE with session bug fixes; add alist-serve-resource skill 2026-08-23 00:42:47 +08:00
hoelee 87a120431f fix(W1): Load Env use $env (not process.env) — matches live n8n working version 2026-08-23 00:40:03 +08:00
hoelee 3f3306ba46 fix(W2): query active CPs via customer.Id (not customer display name) — was returning 0 rows -> empty scopes 2026-08-23 00:34:16 +08:00
hoelee 813ca6a2b6 docs: reinforce base_path=/ rule as ops invariant in API_INTEGRATION 2026-08-23 00:10:45 +08:00
hoelee 7518f8c32e fix(W1): base_path hardcode to / (was /Class causing JoinPath double-prefix -> 403) 2026-08-23 00:10:26 +08:00
hoelee 160169e6f2 Fix W1/W2 webhook loop (skip-if-already-synced guard) + fix W2 link API alist_path bug 2026-08-22 22:34:27 +08:00
hoelee 71acb88078 Phase 1-3: AList stock deployment, NocoDB 7-table schema, W1/W2 n8n workflows + docs 2026-08-22 19:26:06 +08:00