refactor(frontend): API 子模块化、大组件拆分与 TanStack Query hooks
P1: kubernetes/system API 拆为子目录模块,导出工具下沉到 lib/export-utils,
http 统一 buildApiUrl/getWsBase;HostMonitor、DeploymentEdit* 拆至子组件。
P2: 新增 hooks/api(queryKeys、useTerminalSessions、useHostMonitor),
TerminalSessionsTab 与 HostMonitor 接入 TanStack Query。
P3: 保留 services/api 路径,未迁移至 api/ 目录。
This commit is contained in:
1 parent
7fb2236b4d
commit
0b404b4611
226 files changed
+3040
-2596
No files matched your search
@@ -0,0 +1,213 @@
|
||||
// container-utils.ts — K8s container spec ↔ form 转换工具
|
||||
// 作者: Logan <admin@attacker.club>
|
||||
import type {
|
||||
ContainerForm, ProbeDef, LifecycleHandler,
|
||||
} from './types';
|
||||
|
||||
// K8s container spec → form conversion // Logan.Li
|
||||
export function toContainerForm(c: any): ContainerForm {
|
||||
const probe = (p: any): ProbeDef | null => {
|
||||
if (!p) return null;
|
||||
let type: ProbeDef['type'] = 'none';
|
||||
if (p.httpGet) type = 'httpGet';
|
||||
else if (p.tcpSocket) type = 'tcpSocket';
|
||||
else if (p.exec) type = 'exec';
|
||||
return {
|
||||
type,
|
||||
httpPath: p.httpGet?.path || '',
|
||||
httpPort: p.httpGet?.port ?? '',
|
||||
httpHost: p.httpGet?.host || '',
|
||||
tcpPort: p.tcpSocket?.port ?? '',
|
||||
execCommand: Array.isArray(p.exec?.command) ? p.exec.command.join('\n') : (p.exec?.command || ''),
|
||||
initialDelaySeconds: p.initialDelaySeconds,
|
||||
periodSeconds: p.periodSeconds,
|
||||
timeoutSeconds: p.timeoutSeconds,
|
||||
successThreshold: p.successThreshold,
|
||||
failureThreshold: p.failureThreshold,
|
||||
};
|
||||
};
|
||||
const lh = (h: any): LifecycleHandler => {
|
||||
if (!h) return { type: 'none' };
|
||||
let type: LifecycleHandler['type'] = 'none';
|
||||
if (h.exec) type = 'exec';
|
||||
else if (h.httpGet) type = 'httpGet';
|
||||
else if (h.tcpSocket) type = 'tcpSocket';
|
||||
return {
|
||||
type,
|
||||
execCommand: Array.isArray(h.exec?.command) ? h.exec.command.join('\n') : '',
|
||||
httpPath: h.httpGet?.path || '',
|
||||
httpPort: h.httpGet?.port ?? '',
|
||||
tcpPort: h.tcpSocket?.port ?? '',
|
||||
};
|
||||
};
|
||||
const sc = c.securityContext;
|
||||
return {
|
||||
name: c.name || '',
|
||||
image: c.image || '',
|
||||
imagePullPolicy: c.imagePullPolicy || '',
|
||||
command: Array.isArray(c.command) ? c.command : [],
|
||||
args: Array.isArray(c.args) ? c.args : [],
|
||||
workingDir: c.workingDir || '',
|
||||
env: (Array.isArray(c.env) ? c.env : []).map((e: any) => ({ name: e.name || '', value: e.value ?? '' })),
|
||||
ports: (Array.isArray(c.ports) ? c.ports : []).map((p: any) => ({ name: p.name || '', containerPort: p.containerPort ?? '', protocol: p.protocol || 'TCP' })),
|
||||
resources: {
|
||||
requests: c.resources?.requests || {},
|
||||
limits: c.resources?.limits || {},
|
||||
},
|
||||
livenessProbe: probe(c.livenessProbe),
|
||||
readinessProbe: probe(c.readinessProbe),
|
||||
startupProbe: probe(c.startupProbe),
|
||||
lifecycle: c.lifecycle ? {
|
||||
postStart: c.lifecycle.postStart ? lh(c.lifecycle.postStart) : undefined,
|
||||
preStop: c.lifecycle.preStop ? lh(c.lifecycle.preStop) : undefined,
|
||||
} : null,
|
||||
securityContext: sc ? {
|
||||
runAsUser: sc.runAsUser,
|
||||
runAsGroup: sc.runAsGroup,
|
||||
runAsNonRoot: sc.runAsNonRoot,
|
||||
privileged: sc.privileged,
|
||||
readOnlyRootFilesystem: sc.readOnlyRootFilesystem,
|
||||
allowPrivilegeEscalation: sc.allowPrivilegeEscalation,
|
||||
capabilitiesAdd: sc.capabilities?.add || [],
|
||||
capabilitiesDrop: sc.capabilities?.drop || [],
|
||||
} : null,
|
||||
volumeMounts: (Array.isArray(c.volumeMounts) ? c.volumeMounts : []).map((m: any) => ({
|
||||
name: m.name || '', mountPath: m.mountPath || '', subPath: m.subPath || '', readOnly: m.readOnly || false,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
// form → K8s container spec // Logan.Li
|
||||
export function emptyContainer(name = ''): ContainerForm {
|
||||
return {
|
||||
name, image: '', imagePullPolicy: 'IfNotPresent',
|
||||
command: [], args: [], workingDir: '',
|
||||
env: [], ports: [], resources: {},
|
||||
livenessProbe: null, readinessProbe: null, startupProbe: null,
|
||||
lifecycle: null, securityContext: null, volumeMounts: [],
|
||||
};
|
||||
}
|
||||
|
||||
export function probeToK8s(p: ProbeDef | null | undefined): any {
|
||||
if (!p || p.type === 'none' || !p.type) return undefined;
|
||||
const out: Record<string, any> = {};
|
||||
if (p.type === 'httpGet') {
|
||||
out.httpGet = {};
|
||||
if (p.httpPath) out.httpGet.path = p.httpPath;
|
||||
if (p.httpPort !== '' && p.httpPort != null) out.httpGet.port = typeof p.httpPort === 'number' ? p.httpPort : Number(p.httpPort);
|
||||
if (p.httpHost) out.httpGet.host = p.httpHost;
|
||||
} else if (p.type === 'tcpSocket') {
|
||||
out.tcpSocket = {};
|
||||
if (p.tcpPort !== '' && p.tcpPort != null) out.tcpSocket.port = typeof p.tcpPort === 'number' ? p.tcpPort : Number(p.tcpPort);
|
||||
} else if (p.type === 'exec') {
|
||||
const cmds = (p.execCommand || '').split('\n').map(s => s.trim()).filter(Boolean);
|
||||
if (cmds.length) out.exec = { command: cmds };
|
||||
}
|
||||
if (p.initialDelaySeconds != null) out.initialDelaySeconds = p.initialDelaySeconds;
|
||||
if (p.periodSeconds != null) out.periodSeconds = p.periodSeconds;
|
||||
if (p.timeoutSeconds != null) out.timeoutSeconds = p.timeoutSeconds;
|
||||
if (p.successThreshold != null) out.successThreshold = p.successThreshold;
|
||||
if (p.failureThreshold != null) out.failureThreshold = p.failureThreshold;
|
||||
return Object.keys(out).length > 0 ? out : undefined;
|
||||
}
|
||||
|
||||
export function handlerToK8s(h: LifecycleHandler | undefined): any {
|
||||
if (!h || h.type === 'none' || !h.type) return undefined;
|
||||
if (h.type === 'exec') {
|
||||
const cmds = (h.execCommand || '').split('\n').map(s => s.trim()).filter(Boolean);
|
||||
return cmds.length ? { exec: { command: cmds } } : undefined;
|
||||
}
|
||||
if (h.type === 'httpGet') {
|
||||
const o: Record<string, any> = { httpGet: {} };
|
||||
if (h.httpPath) o.httpGet.path = h.httpPath;
|
||||
if (h.httpPort !== '' && h.httpPort != null) o.httpGet.port = Number(h.httpPort);
|
||||
return o;
|
||||
}
|
||||
if (h.type === 'tcpSocket') {
|
||||
const o: Record<string, any> = { tcpSocket: {} };
|
||||
if (h.tcpPort !== '' && h.tcpPort != null) o.tcpSocket.port = Number(h.tcpPort);
|
||||
return o;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function containerToK8s(c: ContainerForm): any {
|
||||
const out: Record<string, any> = { name: c.name };
|
||||
if (c.image) out.image = c.image;
|
||||
if (c.imagePullPolicy) out.imagePullPolicy = c.imagePullPolicy;
|
||||
if (c.workingDir) out.workingDir = c.workingDir;
|
||||
if (c.command.length > 0) out.command = c.command;
|
||||
if (c.args.length > 0) out.args = c.args;
|
||||
if (c.env.length > 0) {
|
||||
out.env = c.env.filter(e => e.name.trim()).map(e => {
|
||||
const o: Record<string, any> = { name: e.name };
|
||||
if (e.value != null) o.value = e.value;
|
||||
return o;
|
||||
});
|
||||
}
|
||||
if (c.ports.length > 0) {
|
||||
out.ports = c.ports.filter(p => p.containerPort !== '').map(p => {
|
||||
const o: Record<string, any> = { containerPort: typeof p.containerPort === 'number' ? p.containerPort : Number(p.containerPort) };
|
||||
if (p.name) o.name = p.name;
|
||||
if (p.protocol) o.protocol = p.protocol;
|
||||
return o;
|
||||
});
|
||||
}
|
||||
const req = c.resources.requests;
|
||||
const lim = c.resources.limits;
|
||||
const hasReq = req && Object.values(req).some(v => v && v.trim());
|
||||
const hasLim = lim && Object.values(lim).some(v => v && v.trim());
|
||||
if (hasReq || hasLim) {
|
||||
out.resources = {};
|
||||
if (hasReq) {
|
||||
out.resources.requests = {};
|
||||
if (req!.cpu?.trim()) out.resources.requests.cpu = req!.cpu;
|
||||
if (req!.memory?.trim()) out.resources.requests.memory = req!.memory;
|
||||
}
|
||||
if (hasLim) {
|
||||
out.resources.limits = {};
|
||||
if (lim!.cpu?.trim()) out.resources.limits.cpu = lim!.cpu;
|
||||
if (lim!.memory?.trim()) out.resources.limits.memory = lim!.memory;
|
||||
}
|
||||
}
|
||||
const lp = probeToK8s(c.livenessProbe); if (lp) out.livenessProbe = lp;
|
||||
const rp = probeToK8s(c.readinessProbe); if (rp) out.readinessProbe = rp;
|
||||
const sp = probeToK8s(c.startupProbe); if (sp) out.startupProbe = sp;
|
||||
if (c.lifecycle) {
|
||||
const lc: Record<string, any> = {};
|
||||
const ps = handlerToK8s(c.lifecycle.postStart); if (ps) lc.postStart = ps;
|
||||
const pst = handlerToK8s(c.lifecycle.preStop); if (pst) lc.preStop = pst;
|
||||
if (Object.keys(lc).length > 0) out.lifecycle = lc;
|
||||
}
|
||||
if (c.securityContext) {
|
||||
const s = c.securityContext;
|
||||
const sc: Record<string, any> = {};
|
||||
if (s.runAsUser !== '' && s.runAsUser != null) sc.runAsUser = Number(s.runAsUser);
|
||||
if (s.runAsGroup !== '' && s.runAsGroup != null) sc.runAsGroup = Number(s.runAsGroup);
|
||||
if (s.runAsNonRoot != null) sc.runAsNonRoot = s.runAsNonRoot;
|
||||
if (s.privileged != null) sc.privileged = s.privileged;
|
||||
if (s.readOnlyRootFilesystem != null) sc.readOnlyRootFilesystem = s.readOnlyRootFilesystem;
|
||||
if (s.allowPrivilegeEscalation != null) sc.allowPrivilegeEscalation = s.allowPrivilegeEscalation;
|
||||
const capAdd = (s.capabilitiesAdd || []).filter(Boolean);
|
||||
const capDrop = (s.capabilitiesDrop || []).filter(Boolean);
|
||||
if (capAdd.length || capDrop.length) {
|
||||
sc.capabilities = {};
|
||||
if (capAdd.length) sc.capabilities.add = capAdd;
|
||||
if (capDrop.length) sc.capabilities.drop = capDrop;
|
||||
}
|
||||
if (Object.keys(sc).length > 0) out.securityContext = sc;
|
||||
}
|
||||
if (c.volumeMounts.length > 0) {
|
||||
out.volumeMounts = c.volumeMounts.filter(m => m.name.trim() && m.mountPath.trim()).map(m => {
|
||||
const o: Record<string, any> = { name: m.name, mountPath: m.mountPath };
|
||||
if (m.subPath) o.subPath = m.subPath;
|
||||
if (m.readOnly) o.readOnly = true;
|
||||
return o;
|
||||
});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// String array helper (command/args editing) // Logan.Li
|
||||
export function arrToText(arr: string[]): string { return arr.join('\n'); }
|
||||
export function textToArr(text: string): string[] { return text.split('\n').map(s => s.trim()).filter(Boolean); }
|
||||
Reference in new issue
Block a user