refactor: name sub2api manual binding sources in yaml
This commit is contained in:
@@ -137,16 +137,27 @@ profiles:
|
||||
configFile: config.toml.socap
|
||||
authFile: auth.json.socap
|
||||
manualAccounts:
|
||||
bindingSources:
|
||||
active-target-egress-proxy:
|
||||
enabled: true
|
||||
kind: proxy
|
||||
provider: target-egress-proxy
|
||||
description: Bind a protected manual account to the selected target's YAML-declared egress proxy.
|
||||
unified-pool-group:
|
||||
enabled: true
|
||||
kind: group
|
||||
provider: pool-group
|
||||
description: Attach a protected manual account to the unified Codex pool group.
|
||||
protected:
|
||||
- accountName: lucianepidgeon@gmail.com
|
||||
reason: Manually configured in Sub2API; keep outside UniDesk-managed Codex pool and sentinel control.
|
||||
proxyBinding:
|
||||
enabled: true
|
||||
source: target-egress-proxy
|
||||
source: active-target-egress-proxy
|
||||
proxyName: platform-infra-sub2api-egress-proxy
|
||||
groupBinding:
|
||||
enabled: true
|
||||
source: pool-group
|
||||
source: unified-pool-group
|
||||
publicExposure:
|
||||
enabled: false
|
||||
proxyName: platform-infra-sub2api
|
||||
|
||||
@@ -156,18 +156,35 @@ interface CodexPoolConfig {
|
||||
}
|
||||
|
||||
interface CodexPoolManualAccountsConfig {
|
||||
bindingSources: CodexPoolManualBindingSourcesConfig;
|
||||
protected: CodexPoolManualAccountProtection[];
|
||||
}
|
||||
|
||||
type CodexPoolManualBindingKind = "proxy" | "group";
|
||||
type CodexPoolManualBindingProvider = "target-egress-proxy" | "pool-group";
|
||||
|
||||
interface CodexPoolManualBindingSource {
|
||||
id: string;
|
||||
enabled: boolean;
|
||||
kind: CodexPoolManualBindingKind;
|
||||
provider: CodexPoolManualBindingProvider;
|
||||
description: string | null;
|
||||
}
|
||||
|
||||
interface CodexPoolManualBindingSourcesConfig {
|
||||
items: CodexPoolManualBindingSource[];
|
||||
byId: Record<string, CodexPoolManualBindingSource>;
|
||||
}
|
||||
|
||||
interface CodexPoolManualAccountProxyBinding {
|
||||
enabled: boolean;
|
||||
source: "target-egress-proxy";
|
||||
source: string;
|
||||
proxyName: string;
|
||||
}
|
||||
|
||||
interface CodexPoolManualAccountGroupBinding {
|
||||
enabled: boolean;
|
||||
source: "pool-group";
|
||||
source: string;
|
||||
}
|
||||
|
||||
interface CodexPoolManualAccountProtection {
|
||||
@@ -905,6 +922,10 @@ async function codexPoolSync(config: UniDeskConfig, options: SyncOptions): Promi
|
||||
defaultAccountLoadFactor: pool.defaultAccountLoadFactor,
|
||||
defaultSentinelProtect: pool.defaultSentinelProtect,
|
||||
},
|
||||
manualAccounts: {
|
||||
bindingSources: pool.manualAccounts.bindingSources.items.map(manualBindingSourcePlan),
|
||||
protected: resolvedManualAccountProtections(pool, runtimeTarget),
|
||||
},
|
||||
profiles: profiles.map((profile) => ({
|
||||
profile: profile.profile,
|
||||
accountName: profile.accountName,
|
||||
@@ -1514,6 +1535,7 @@ function desiredProfileCapacityTotal(profiles: CodexPoolProfileConfig[], default
|
||||
|
||||
function readManualAccountsConfig(value: unknown): CodexPoolManualAccountsConfig {
|
||||
if (!isRecord(value)) throw new Error(`${codexPoolConfigPath}.manualAccounts must be a YAML object`);
|
||||
const bindingSources = readManualBindingSources(value.bindingSources, "manualAccounts.bindingSources");
|
||||
const protectedRaw = value.protected;
|
||||
if (!Array.isArray(protectedRaw)) throw new Error(`${codexPoolConfigPath}.manualAccounts.protected must be a YAML array`);
|
||||
const seen = new Set<string>();
|
||||
@@ -1529,20 +1551,46 @@ function readManualAccountsConfig(value: unknown): CodexPoolManualAccountsConfig
|
||||
if (seen.has(normalized)) throw new Error(`${codexPoolConfigPath}.${key}.accountName is duplicated in manualAccounts.protected`);
|
||||
seen.add(normalized);
|
||||
const reason = isRecord(entry) ? readManualAccountReason(entry.reason, `${key}.reason`) : null;
|
||||
const proxyBinding = isRecord(entry) ? readManualAccountProxyBinding(entry.proxyBinding, `${key}.proxyBinding`) : null;
|
||||
const groupBinding = isRecord(entry) ? readManualAccountGroupBinding(entry.groupBinding, `${key}.groupBinding`) : null;
|
||||
const proxyBinding = isRecord(entry) ? readManualAccountProxyBinding(entry.proxyBinding, `${key}.proxyBinding`, bindingSources) : null;
|
||||
const groupBinding = isRecord(entry) ? readManualAccountGroupBinding(entry.groupBinding, `${key}.groupBinding`, bindingSources) : null;
|
||||
return { accountName, reason, proxyBinding, groupBinding };
|
||||
});
|
||||
return { protected: protectedAccounts };
|
||||
return { bindingSources, protected: protectedAccounts };
|
||||
}
|
||||
|
||||
function readManualAccountProxyBinding(value: unknown, key: string): CodexPoolManualAccountProxyBinding | null {
|
||||
function readManualBindingSources(value: unknown, key: string): CodexPoolManualBindingSourcesConfig {
|
||||
if (!isRecord(value)) throw new Error(`${codexPoolConfigPath}.${key} must be a YAML object`);
|
||||
const items: CodexPoolManualBindingSource[] = [];
|
||||
const byId: Record<string, CodexPoolManualBindingSource> = {};
|
||||
for (const [id, rawSource] of Object.entries(value)) {
|
||||
const path = `${key}.${id}`;
|
||||
validateManualBindingSourceId(id, path);
|
||||
if (!isRecord(rawSource)) throw new Error(`${codexPoolConfigPath}.${path} must be a YAML object`);
|
||||
const source: CodexPoolManualBindingSource = {
|
||||
id,
|
||||
enabled: readBooleanConfig(rawSource.enabled, `${path}.enabled`),
|
||||
kind: readManualBindingKind(rawSource.kind, `${path}.kind`),
|
||||
provider: readManualBindingProvider(rawSource.provider, `${path}.provider`),
|
||||
description: readManualAccountReason(rawSource.description, `${path}.description`),
|
||||
};
|
||||
if (source.kind === "proxy" && source.provider !== "target-egress-proxy") {
|
||||
throw new Error(`${codexPoolConfigPath}.${path}.provider must be target-egress-proxy for kind=proxy`);
|
||||
}
|
||||
if (source.kind === "group" && source.provider !== "pool-group") {
|
||||
throw new Error(`${codexPoolConfigPath}.${path}.provider must be pool-group for kind=group`);
|
||||
}
|
||||
byId[id] = source;
|
||||
items.push(source);
|
||||
}
|
||||
if (items.length === 0) throw new Error(`${codexPoolConfigPath}.${key} must declare at least one source`);
|
||||
return { items, byId };
|
||||
}
|
||||
|
||||
function readManualAccountProxyBinding(value: unknown, key: string, bindingSources: CodexPoolManualBindingSourcesConfig): CodexPoolManualAccountProxyBinding | null {
|
||||
if (value === undefined || value === null) return null;
|
||||
if (!isRecord(value)) throw new Error(`${codexPoolConfigPath}.${key} must be a YAML object`);
|
||||
const enabled = value.enabled === undefined ? true : value.enabled === true;
|
||||
const source = stringValue(value.source);
|
||||
if (source === null) throw new Error(`${codexPoolConfigPath}.${key}.source is required`);
|
||||
if (source !== "target-egress-proxy") throw new Error(`${codexPoolConfigPath}.${key}.source must be target-egress-proxy`);
|
||||
const enabled = readBooleanConfig(value.enabled, `${key}.enabled`);
|
||||
const source = readManualBindingSourceRef(value.source, `${key}.source`, bindingSources, "proxy", enabled);
|
||||
const proxyName = stringValue(value.proxyName);
|
||||
if (proxyName === null || proxyName.trim().length === 0) throw new Error(`${codexPoolConfigPath}.${key}.proxyName is required`);
|
||||
validateProxyName(proxyName, `${key}.proxyName`);
|
||||
@@ -1553,19 +1601,44 @@ function readManualAccountProxyBinding(value: unknown, key: string): CodexPoolMa
|
||||
};
|
||||
}
|
||||
|
||||
function readManualAccountGroupBinding(value: unknown, key: string): CodexPoolManualAccountGroupBinding | null {
|
||||
function readManualAccountGroupBinding(value: unknown, key: string, bindingSources: CodexPoolManualBindingSourcesConfig): CodexPoolManualAccountGroupBinding | null {
|
||||
if (value === undefined || value === null) return null;
|
||||
if (!isRecord(value)) throw new Error(`${codexPoolConfigPath}.${key} must be a YAML object`);
|
||||
const enabled = value.enabled === undefined ? true : value.enabled === true;
|
||||
const source = stringValue(value.source);
|
||||
if (source === null) throw new Error(`${codexPoolConfigPath}.${key}.source is required`);
|
||||
if (source !== "pool-group") throw new Error(`${codexPoolConfigPath}.${key}.source must be pool-group`);
|
||||
const enabled = readBooleanConfig(value.enabled, `${key}.enabled`);
|
||||
const source = readManualBindingSourceRef(value.source, `${key}.source`, bindingSources, "group", enabled);
|
||||
return {
|
||||
enabled,
|
||||
source,
|
||||
};
|
||||
}
|
||||
|
||||
function readManualBindingSourceRef(value: unknown, key: string, bindingSources: CodexPoolManualBindingSourcesConfig, expectedKind: CodexPoolManualBindingKind, bindingEnabled: boolean): string {
|
||||
const sourceId = stringValue(value);
|
||||
if (sourceId === null) throw new Error(`${codexPoolConfigPath}.${key} is required`);
|
||||
validateManualBindingSourceId(sourceId, key);
|
||||
const source = bindingSources.byId[sourceId];
|
||||
if (source === undefined) throw new Error(`${codexPoolConfigPath}.${key} references unknown manualAccounts.bindingSources id ${sourceId}`);
|
||||
if (source.kind !== expectedKind) throw new Error(`${codexPoolConfigPath}.${key} references ${sourceId} with kind=${source.kind}; expected ${expectedKind}`);
|
||||
if (bindingEnabled && !source.enabled) throw new Error(`${codexPoolConfigPath}.${key} references disabled manualAccounts.bindingSources.${sourceId}`);
|
||||
return sourceId;
|
||||
}
|
||||
|
||||
function readManualBindingKind(value: unknown, key: string): CodexPoolManualBindingKind {
|
||||
const text = stringValue(value);
|
||||
if (text !== "proxy" && text !== "group") throw new Error(`${codexPoolConfigPath}.${key} must be proxy or group`);
|
||||
return text;
|
||||
}
|
||||
|
||||
function readManualBindingProvider(value: unknown, key: string): CodexPoolManualBindingProvider {
|
||||
const text = stringValue(value);
|
||||
if (text !== "target-egress-proxy" && text !== "pool-group") throw new Error(`${codexPoolConfigPath}.${key} must be target-egress-proxy or pool-group`);
|
||||
return text;
|
||||
}
|
||||
|
||||
function validateManualBindingSourceId(value: string, key: string): void {
|
||||
if (!/^[A-Za-z0-9]([A-Za-z0-9._-]*[A-Za-z0-9])?$/u.test(value)) throw new Error(`${codexPoolConfigPath}.${key} has an unsupported source id format`);
|
||||
}
|
||||
|
||||
function readManualAccountName(value: unknown, key: string): string | null {
|
||||
const text = stringValue(value)?.trim() ?? null;
|
||||
if (text === null || text.length === 0) return null;
|
||||
@@ -2079,6 +2152,7 @@ function codexPoolConfigSummary(pool: CodexPoolConfig, target: CodexPoolRuntimeT
|
||||
defaultSentinelProtect: pool.defaultSentinelProtect,
|
||||
profileCount: pool.profiles.length,
|
||||
manualAccounts: {
|
||||
bindingSources: pool.manualAccounts.bindingSources.items.map(manualBindingSourcePlan),
|
||||
protectedCount: pool.manualAccounts.protected.length,
|
||||
protected: pool.manualAccounts.protected,
|
||||
controlPolicy: "manual accounts are not created, credential-updated, pruned, probed, or frozen by UniDesk codex-pool sync/sentinel; optional proxy_id and pool group membership bindings are narrow YAML-controlled exceptions",
|
||||
@@ -3078,6 +3152,72 @@ function sentinelProfileSecrets(profiles: CodexProfile[]): CodexPoolSentinelProf
|
||||
}));
|
||||
}
|
||||
|
||||
function resolvedManualAccountProtections(pool: CodexPoolConfig, target: CodexPoolRuntimeTarget): Record<string, unknown>[] {
|
||||
return pool.manualAccounts.protected.map((account) => ({
|
||||
accountName: account.accountName,
|
||||
reason: account.reason,
|
||||
proxyBinding: resolveManualProxyBinding(account.proxyBinding, pool, target),
|
||||
groupBinding: resolveManualGroupBinding(account.groupBinding, pool),
|
||||
}));
|
||||
}
|
||||
|
||||
function resolveManualProxyBinding(binding: CodexPoolManualAccountProxyBinding | null, pool: CodexPoolConfig, target: CodexPoolRuntimeTarget): Record<string, unknown> | null {
|
||||
if (binding === null) return null;
|
||||
const source = manualBindingSource(pool, binding.source);
|
||||
if (source.kind !== "proxy") throw new Error(`${codexPoolConfigPath}.manualAccounts binding source ${source.id} must have kind=proxy`);
|
||||
if (binding.enabled && source.provider === "target-egress-proxy" && (target.egressProxy === null || target.egressProxy.enabled !== true)) {
|
||||
throw new Error(`${codexPoolConfigPath}.manualAccounts.bindingSources.${source.id} requires ${sub2apiConfigPath}.targets[${target.id}].egressProxy.enabled=true`);
|
||||
}
|
||||
return {
|
||||
enabled: binding.enabled,
|
||||
source: source.id,
|
||||
proxyName: binding.proxyName,
|
||||
sourcePlan: {
|
||||
...manualBindingSourcePlan(source),
|
||||
targetEgressProxy: source.provider === "target-egress-proxy" && target.egressProxy !== null
|
||||
? {
|
||||
targetId: target.id,
|
||||
namespace: target.namespace,
|
||||
serviceName: target.egressProxy.serviceName,
|
||||
listenPort: target.egressProxy.listenPort,
|
||||
httpProxy: target.egressProxy.httpProxy,
|
||||
noProxy: target.egressProxy.noProxy,
|
||||
}
|
||||
: null,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function resolveManualGroupBinding(binding: CodexPoolManualAccountGroupBinding | null, pool: CodexPoolConfig): Record<string, unknown> | null {
|
||||
if (binding === null) return null;
|
||||
const source = manualBindingSource(pool, binding.source);
|
||||
if (source.kind !== "group") throw new Error(`${codexPoolConfigPath}.manualAccounts binding source ${source.id} must have kind=group`);
|
||||
return {
|
||||
enabled: binding.enabled,
|
||||
source: source.id,
|
||||
sourcePlan: {
|
||||
...manualBindingSourcePlan(source),
|
||||
poolGroupName: source.provider === "pool-group" ? pool.groupName : null,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function manualBindingSource(pool: CodexPoolConfig, sourceId: string): CodexPoolManualBindingSource {
|
||||
const source = pool.manualAccounts.bindingSources.byId[sourceId];
|
||||
if (source === undefined) throw new Error(`${codexPoolConfigPath}.manualAccounts binding source ${sourceId} is not declared`);
|
||||
return source;
|
||||
}
|
||||
|
||||
function manualBindingSourcePlan(source: CodexPoolManualBindingSource): Record<string, unknown> {
|
||||
return {
|
||||
id: source.id,
|
||||
enabled: source.enabled,
|
||||
kind: source.kind,
|
||||
provider: source.provider,
|
||||
description: source.description,
|
||||
};
|
||||
}
|
||||
|
||||
function targetPublicExposureSummary(target: CodexPoolRuntimeTarget): Record<string, unknown> | null {
|
||||
const exposure = target.publicExposure;
|
||||
if (exposure === null) return null;
|
||||
@@ -4160,7 +4300,7 @@ EXPECTED_ACCOUNT_CAPACITIES = ${JSON.stringify(desiredAccountCapacityMap(pool))}
|
||||
EXPECTED_ACCOUNT_LOAD_FACTORS = ${JSON.stringify(desiredAccountLoadFactorMap(pool))}
|
||||
EXPECTED_ACCOUNT_WS_MODES = json.loads(${JSON.stringify(JSON.stringify(desiredAccountWebSocketsV2ModeMap(pool)))})
|
||||
EXPECTED_ACCOUNT_TEMP_UNSCHEDULABLE = json.loads(${JSON.stringify(JSON.stringify(desiredAccountTempUnschedulableMap(pool)))})
|
||||
MANUAL_ACCOUNT_PROTECTIONS = json.loads(${JSON.stringify(JSON.stringify(pool.manualAccounts.protected))})
|
||||
MANUAL_ACCOUNT_PROTECTIONS = json.loads(${JSON.stringify(JSON.stringify(resolvedManualAccountProtections(pool, target)))})
|
||||
SENTINEL_CONFIG = json.loads(${JSON.stringify(JSON.stringify(pool.sentinel))})
|
||||
TARGET_EGRESS_PROXY = json.loads(${JSON.stringify(JSON.stringify(target.egressProxy))})
|
||||
MODE = "${mode}"
|
||||
@@ -4513,16 +4653,31 @@ def find_proxy_by_name(token, name):
|
||||
return item
|
||||
return None
|
||||
|
||||
def manual_binding_plan(binding, expected_kind):
|
||||
if not isinstance(binding, dict):
|
||||
return None
|
||||
plan = binding.get("sourcePlan")
|
||||
if not isinstance(plan, dict):
|
||||
raise RuntimeError("manual account binding is missing resolved sourcePlan")
|
||||
if plan.get("enabled") is not True:
|
||||
raise RuntimeError(f"manual account binding source {plan.get('id')} is disabled")
|
||||
if plan.get("kind") != expected_kind:
|
||||
raise RuntimeError(f"manual account binding source {plan.get('id')} kind must be {expected_kind}")
|
||||
return plan
|
||||
|
||||
def desired_manual_proxy_payload(protection):
|
||||
binding = protection.get("proxyBinding") if isinstance(protection, dict) else None
|
||||
if not isinstance(binding, dict) or binding.get("enabled") is not True:
|
||||
return None
|
||||
if binding.get("source") != "target-egress-proxy":
|
||||
raise RuntimeError("manual account proxyBinding source must be target-egress-proxy")
|
||||
if not isinstance(TARGET_EGRESS_PROXY, dict) or TARGET_EGRESS_PROXY.get("enabled") is not True:
|
||||
raise RuntimeError("manual account proxyBinding requires an enabled target egressProxy")
|
||||
service_name = TARGET_EGRESS_PROXY.get("serviceName")
|
||||
listen_port = TARGET_EGRESS_PROXY.get("listenPort")
|
||||
plan = manual_binding_plan(binding, "proxy")
|
||||
if plan.get("provider") != "target-egress-proxy":
|
||||
raise RuntimeError(f"manual account proxyBinding source {plan.get('id')} uses unsupported provider {plan.get('provider')}")
|
||||
target_proxy = plan.get("targetEgressProxy")
|
||||
if not isinstance(target_proxy, dict):
|
||||
raise RuntimeError(f"manual account proxyBinding source {plan.get('id')} requires an enabled target egressProxy")
|
||||
service_name = target_proxy.get("serviceName")
|
||||
listen_port = target_proxy.get("listenPort")
|
||||
namespace = target_proxy.get("namespace") if isinstance(target_proxy.get("namespace"), str) and target_proxy.get("namespace") else NAMESPACE
|
||||
proxy_name = binding.get("proxyName")
|
||||
if not isinstance(service_name, str) or not service_name:
|
||||
raise RuntimeError("target egressProxy serviceName is missing")
|
||||
@@ -4533,7 +4688,7 @@ def desired_manual_proxy_payload(protection):
|
||||
return {
|
||||
"name": proxy_name,
|
||||
"protocol": "http",
|
||||
"host": f"{service_name}.{NAMESPACE}.svc.cluster.local",
|
||||
"host": f"{service_name}.{namespace}.svc.cluster.local",
|
||||
"port": listen_port,
|
||||
"fallback_mode": "none",
|
||||
"expiry_warn_days": 0,
|
||||
@@ -4568,6 +4723,8 @@ def manual_proxy_status(token, account, protection):
|
||||
"action": "not-configured",
|
||||
"valuesPrinted": False,
|
||||
}
|
||||
binding = protection.get("proxyBinding") if isinstance(protection, dict) else None
|
||||
plan = manual_binding_plan(binding, "proxy")
|
||||
proxy = find_proxy_by_name(token, payload["name"])
|
||||
runtime_proxy = account.get("proxy") if isinstance(account, dict) and isinstance(account.get("proxy"), dict) else None
|
||||
binding_aligned = (
|
||||
@@ -4579,7 +4736,8 @@ def manual_proxy_status(token, account, protection):
|
||||
"enabled": True,
|
||||
"ok": binding_aligned,
|
||||
"action": "validate",
|
||||
"source": "target-egress-proxy",
|
||||
"source": plan.get("id"),
|
||||
"sourceProvider": plan.get("provider"),
|
||||
"expectedProxyName": payload["name"],
|
||||
"expectedProtocol": payload["protocol"],
|
||||
"expectedHost": payload["host"],
|
||||
@@ -4634,6 +4792,8 @@ def ensure_manual_account_proxy_bindings(token):
|
||||
})
|
||||
continue
|
||||
proxy, proxy_action = ensure_manual_proxy(token, payload)
|
||||
binding = protection.get("proxyBinding") if isinstance(protection, dict) else None
|
||||
plan = manual_binding_plan(binding, "proxy")
|
||||
proxy_id = proxy.get("id") if isinstance(proxy, dict) else None
|
||||
if proxy_id is None:
|
||||
raise RuntimeError(f"proxy {payload['name']} has no id")
|
||||
@@ -4649,6 +4809,8 @@ def ensure_manual_account_proxy_bindings(token):
|
||||
"enabled": True,
|
||||
"action": action,
|
||||
"proxyAction": proxy_action,
|
||||
"source": plan.get("id"),
|
||||
"sourceProvider": plan.get("provider"),
|
||||
"ok": account.get("proxy_id") == proxy_id,
|
||||
"expectedProxyName": payload["name"],
|
||||
"expectedProtocol": payload["protocol"],
|
||||
@@ -4668,17 +4830,21 @@ def ensure_manual_account_proxy_bindings(token):
|
||||
"valuesPrinted": False,
|
||||
}
|
||||
|
||||
def manual_group_binding_enabled(protection):
|
||||
def manual_group_binding_plan(protection):
|
||||
binding = protection.get("groupBinding") if isinstance(protection, dict) else None
|
||||
if not isinstance(binding, dict) or binding.get("enabled") is not True:
|
||||
return False
|
||||
if binding.get("source") != "pool-group":
|
||||
raise RuntimeError("manual account groupBinding source must be pool-group")
|
||||
return True
|
||||
return None
|
||||
plan = manual_binding_plan(binding, "group")
|
||||
if plan.get("provider") != "pool-group":
|
||||
raise RuntimeError(f"manual account groupBinding source {plan.get('id')} uses unsupported provider {plan.get('provider')}")
|
||||
return plan
|
||||
|
||||
def manual_group_binding_enabled(protection):
|
||||
return manual_group_binding_plan(protection) is not None
|
||||
|
||||
def manual_group_status(token, account, protection, group_id):
|
||||
enabled = manual_group_binding_enabled(protection)
|
||||
if not enabled:
|
||||
plan = manual_group_binding_plan(protection)
|
||||
if plan is None:
|
||||
return {
|
||||
"enabled": False,
|
||||
"ok": True,
|
||||
@@ -4697,7 +4863,8 @@ def manual_group_status(token, account, protection, group_id):
|
||||
"enabled": True,
|
||||
"ok": binding_aligned,
|
||||
"action": "validate",
|
||||
"source": "pool-group",
|
||||
"source": plan.get("id"),
|
||||
"sourceProvider": plan.get("provider"),
|
||||
"poolGroupName": POOL_GROUP_NAME,
|
||||
"poolGroupId": group_id,
|
||||
"bindingAligned": binding_aligned,
|
||||
@@ -4721,6 +4888,7 @@ def ensure_manual_account_group_bindings(token, group_id):
|
||||
"valuesPrinted": False,
|
||||
})
|
||||
continue
|
||||
plan = manual_group_binding_plan(protection)
|
||||
account = find_account_by_name(token, name)
|
||||
if not isinstance(account, dict):
|
||||
items.append({
|
||||
@@ -4764,7 +4932,8 @@ def ensure_manual_account_group_bindings(token, group_id):
|
||||
"enabled": True,
|
||||
"ok": binding_aligned,
|
||||
"action": action,
|
||||
"source": "pool-group",
|
||||
"source": plan.get("id") if isinstance(plan, dict) else None,
|
||||
"sourceProvider": plan.get("provider") if isinstance(plan, dict) else None,
|
||||
"poolGroupName": POOL_GROUP_NAME,
|
||||
"poolGroupId": group_id,
|
||||
"previousGroupIds": existing_group_ids,
|
||||
@@ -6408,7 +6577,13 @@ def api_key_preview(api_key):
|
||||
return api_key[:10] + "..." + api_key[-4:]
|
||||
|
||||
def run_sync():
|
||||
global MANUAL_ACCOUNT_PROTECTIONS
|
||||
payload = json.loads(base64.b64decode(PAYLOAD_B64).decode("utf-8"))
|
||||
manual_accounts_payload = payload.get("manualAccounts") if isinstance(payload.get("manualAccounts"), dict) else {}
|
||||
resolved_manual_protections = manual_accounts_payload.get("protected")
|
||||
if not isinstance(resolved_manual_protections, list):
|
||||
raise RuntimeError("sync payload has no manualAccounts.protected binding plan")
|
||||
MANUAL_ACCOUNT_PROTECTIONS = resolved_manual_protections
|
||||
profiles = payload.get("profiles") or []
|
||||
prune_removed = bool(payload.get("pruneRemoved"))
|
||||
sentinel_payload = payload.get("sentinel") if isinstance(payload.get("sentinel"), dict) else {}
|
||||
|
||||
Reference in New Issue
Block a user