111 lines
3.5 KiB
JavaScript
111 lines
3.5 KiB
JavaScript
// W2 core computation: effective desired grants + diff vs AList.
|
|
// Uses this.helpers.httpRequest (n8n Code node) for all HTTP.
|
|
// Config read via process.env (n8n $env proxy is unreliable for custom vars).
|
|
const NC = process.env.NOCODB_URL;
|
|
const NC_TOKEN = process.env.NOCODB_TOKEN;
|
|
const ALIST = process.env.ALIST_URL;
|
|
const T_CUSTPROD = process.env.NOCODB_TABLE_CUSTOMERPRODUCTS;
|
|
const T_PROD = process.env.NOCODB_TABLE_PRODUCTS;
|
|
const T_CUST = process.env.NOCODB_TABLE_CUSTOMERS;
|
|
const LNK_PROD_RES = process.env.NOCODB_LINK_PROD_RESOURCES; // c4ko4x8kwi16swg
|
|
|
|
async function ncGet(path) {
|
|
return await this.helpers.httpRequest({
|
|
method: 'GET',
|
|
url: NC + path,
|
|
headers: { 'xc-token': NC_TOKEN },
|
|
json: true,
|
|
});
|
|
}
|
|
async function alistReq(method, path, body, token) {
|
|
return await this.helpers.httpRequest({
|
|
method,
|
|
url: ALIST + path,
|
|
headers: token ? { Authorization: token } : {},
|
|
body,
|
|
json: true,
|
|
});
|
|
}
|
|
function linkId(v) {
|
|
if (v == null) return null;
|
|
if (typeof v === 'object' && !Array.isArray(v)) return v.Id ?? v.id ?? null;
|
|
if (Array.isArray(v)) return v[0]?.Id ?? v[0]?.id ?? null;
|
|
return v;
|
|
}
|
|
|
|
const custProdId = $json.customerProductId;
|
|
|
|
// 1. re-fetch the CP record
|
|
const cp = await ncGet(`/api/v2/tables/${T_CUSTPROD}/records/${custProdId}`);
|
|
const customerId = linkId(cp.customer);
|
|
if (!customerId) throw new Error('CP has no customer link');
|
|
|
|
// 2. load all active CPs for this customer
|
|
const cps = await ncGet(`/api/v2/tables/${T_CUSTPROD}/records?where=(customer,eq,${customerId})&limit=1000`);
|
|
const active = (cps.list || []).filter(r => {
|
|
if (r.status && r.status !== 'active') return false;
|
|
return true;
|
|
});
|
|
|
|
// 3. build resource path -> max expires_at
|
|
const effective = {};
|
|
for (const c of active) {
|
|
const pid = linkId(c.product);
|
|
if (!pid) continue;
|
|
const resList = await ncGet(`/api/v2/tables/${T_PROD}/links/${LNK_PROD_RES}/records/${pid}`);
|
|
const exp = c.expires_at; // 'YYYY-MM-DD'
|
|
for (const res of (resList.list || [])) {
|
|
const path = res.alist_path;
|
|
if (!path) continue;
|
|
const cur = effective[path];
|
|
if (!cur || !cur.expires || (exp && exp > cur.expires)) {
|
|
effective[path] = { expires: exp || null, permission: 0 };
|
|
}
|
|
}
|
|
}
|
|
|
|
// 4. admin login
|
|
const login = await alistReq('POST', '/api/auth/login', {
|
|
username: process.env.ALIST_ADMIN_USERNAME,
|
|
password: process.env.ALIST_ADMIN_PASSWORD,
|
|
}, null);
|
|
const token = login?.data?.token;
|
|
if (!token) throw new Error('AList login failed');
|
|
|
|
// customer's role id
|
|
const cust = await ncGet(`/api/v2/tables/${T_CUST}/records/${customerId}`);
|
|
const roleId = cust.alist_role_id;
|
|
|
|
let currentScopes = [];
|
|
if (roleId) {
|
|
const role = await alistReq('GET', `/api/admin/role/get?id=${roleId}`, null, token);
|
|
currentScopes = role?.data?.permission_scopes ?? [];
|
|
}
|
|
|
|
const desiredPaths = Object.keys(effective);
|
|
const currentPaths = currentScopes.map(s => s.path);
|
|
const toAdd = desiredPaths.filter(p => !currentPaths.includes(p));
|
|
const toRemove = currentPaths.filter(p => !desiredPaths.includes(p));
|
|
|
|
let roleName = null, roleDesc = null;
|
|
if (roleId) {
|
|
const role = await alistReq('GET', `/api/admin/role/get?id=${roleId}`, null, token);
|
|
roleName = role?.data?.name;
|
|
roleDesc = role?.data?.description;
|
|
}
|
|
|
|
return {
|
|
json: {
|
|
customerId,
|
|
roleId,
|
|
roleName,
|
|
roleDesc,
|
|
token,
|
|
effective,
|
|
desiredPaths,
|
|
currentScopes,
|
|
diff: { toAdd, toRemove },
|
|
newScopes: desiredPaths.map(p => ({ path: p, permission: effective[p].permission })),
|
|
},
|
|
};
|