fix(n8n): AList user/update requires username; hooks -> container-local; deploy script

- w1/w3-body: disable calls now send username + assert code===200
  (v3.63 validator rejects {id,disabled} with 400 -> silent no-disable)
- regenerated W1/W3 json, deployed live via REST API, activeVersionId re-aligned
- NocoDB hooks repointed to http://n8n:5678 (old auto.hoelee.com chain loses
  events on VPS stalls; W2 hook path was also 404 since creation)
- deploy_w1_w3.mjs: reusable PUT deploy via n8n API key from SECRETS.md
- PROJECT_STATE: e2e verification results + updated next steps
This commit is contained in:
2026-08-25 05:17:46 +08:00
parent fd62775fcd
commit 773fd3f2d5
7 changed files with 71 additions and 19 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+28
View File
@@ -0,0 +1,28 @@
// Deploy regenerated W1..W4 workflows to live n8n via public API.
// Usage: N8N_API_KEY=<key> N8N_BASE=http://192.168.137.2:5678 node deploy_w1_w3.mjs
// Key lives in repo-root SECRETS.md (never hardcode here).
import { readFile } from "node:fs/promises";
const N8N = process.env.N8N_BASE || "http://192.168.137.2:5678";
const KEY = process.env.N8N_API_KEY;
if (!KEY) { console.error("N8N_API_KEY env required (see SECRETS.md)"); process.exit(1); }
const H = { "X-N8N-API-KEY": KEY, "Content-Type": "application/json" };
const TARGETS = [
{ file: "W1-customer-provision.json", id: "gU2ee9Eg5w1KzB5p" },
{ file: "W3-expiry-sync.json", id: "zlEfjj5eK5IfILtk" },
];
for (const t of TARGETS) {
const wf = JSON.parse(await readFile(t.file, "utf-8"));
delete wf.pinData;
delete wf.active;
delete wf.id;
const r = await fetch(`${N8N}/api/v1/workflows/${t.id}`, {
method: "PUT", headers: H, body: JSON.stringify(wf),
});
const txt = await r.text();
console.log(t.file, "PUT", r.status, txt.slice(0, 120));
if (r.status !== 200) process.exit(1);
}
console.log("DEPLOY OK — remember to recheck workflow_entity.activeVersionId alignment");
+4 -1
View File
@@ -153,7 +153,10 @@ if (status !== 'active') {
}
const actions = [];
if (alistUserId) {
await alistReq(ctx, 'POST', '/api/admin/user/update', { id: alistUserId, disabled: true }, token);
// AList v3.63 /user/update REQUIRES username (validation: Username required).
// Sending {id,disabled} alone -> 400 -> silent no-disable (found live 2026-08-25).
const disRes = await alistReq(ctx, 'POST', '/api/admin/user/update', { id: alistUserId, username, disabled: true }, token);
if (!disRes || disRes.code !== 200) throw new Error('AList disable failed: ' + JSON.stringify(disRes));
actions.push('disable_user');
}
if (alistRoleId) {
+5 -2
View File
@@ -56,8 +56,11 @@ for (const cust of customers) {
applied.push('role_scopes');
}
if (shouldDisable) {
if (!DRY_RUN && cust.alist_user_id)
await alistReq(ctx, 'POST', '/api/admin/user/update', { id: cust.alist_user_id, disabled: true }, token);
// AList v3.63 /user/update REQUIRES username — {id,disabled} alone = 400 silent no-op (2026-08-25).
if (!DRY_RUN && cust.alist_user_id) {
const disRes = await alistReq(ctx, 'POST', '/api/admin/user/update', { id: cust.alist_user_id, username: cust.username, disabled: true }, token);
if (!disRes || disRes.code !== 200) throw new Error('AList disable failed: ' + JSON.stringify(disRes));
}
applied.push('disable');
}
if (applied.length) {