mirror of
https://gitcode.com/JianFeeeee/webui4frpc.git
synced 2026-09-19 16:38:31 +00:00
- cookie-based auth (/login /logout) replacing Basic Auth for UI, enabling logout - roles: superadmin (account management only), admin (full except accounts), viewer/audit (read-only status+cluster, export logs) - readonly accounts hide edit buttons (added remote node, group ops, canvas layout/save/import, cluster manage, install) instead of greying them - auditors see status+cluster only; ordinary admins lose the accounts nav; last-admin guard covers superadmin - remove pink theme entirely (switcher, [data-theme=pink], leftover localStorage), keep white/blue
341 lines
8.3 KiB
TypeScript
341 lines
8.3 KiB
TypeScript
// Data models shared with the Go backend.
|
||
|
||
// CanvasLocal / CanvasRemote are aliases kept for node components migrated
|
||
// from the original prototype; they equal Local / Remote.
|
||
export type CanvasLocal = Local;
|
||
export type CanvasRemote = Remote;
|
||
|
||
// CanvasLink mirrors the store.Link JSON shape used by the canvas editor.
|
||
export type CanvasLink = Link;
|
||
|
||
export interface Local {
|
||
name: string;
|
||
ip: string;
|
||
port: number;
|
||
protocol: string; // tcp | udp | http | https
|
||
// localOnly: forward stays on this node (loopback kept), no cluster.
|
||
localOnly?: boolean;
|
||
|
||
// M1 advanced transport knobs (optional; empty/zero = frpc defaults)
|
||
useEncryption?: boolean;
|
||
useCompression?: boolean;
|
||
bandwidthLimit?: string;
|
||
poolCount?: number;
|
||
metadatas?: Record<string, string>;
|
||
annotations?: Record<string, string>;
|
||
|
||
// M2 HTTP/HTTPS routing (http/https only)
|
||
customDomains?: string; // comma-separated
|
||
subdomain?: string;
|
||
locations?: string[];
|
||
hostHeaderRewrite?: string;
|
||
httpHeaders?: Record<string, string>;
|
||
basicAuthUser?: string;
|
||
basicAuthPassword?: string;
|
||
|
||
// M3 load balancing group + health check
|
||
lbGroup?: string;
|
||
lbGroupKey?: string;
|
||
healthCheckType?: string; // "" | tcp | http
|
||
healthCheckPath?: string;
|
||
healthCheckTimeout?: number;
|
||
healthCheckMaxFailed?: number;
|
||
healthCheckInterval?: number;
|
||
}
|
||
|
||
export interface Remote {
|
||
name: string;
|
||
ip: string;
|
||
port: number; // frps connect port
|
||
token?: string;
|
||
url?: string;
|
||
enabled: boolean;
|
||
|
||
// M1 transport section
|
||
transportProtocol?: string; // tcp | quic | kcp | websocket
|
||
transportTls?: boolean;
|
||
transportPool?: number;
|
||
transportTlsServerName?: string;
|
||
|
||
// M2: frps vhostHTTPPort
|
||
vhostHttpPort?: number;
|
||
|
||
// M3: local frpc admin API (webServer)
|
||
adminAddr?: string;
|
||
adminPort?: number;
|
||
adminUser?: string;
|
||
adminPassword?: string;
|
||
}
|
||
|
||
export interface Link {
|
||
id?: number;
|
||
local: string;
|
||
remote: string;
|
||
remotePort: number;
|
||
offsetX?: number;
|
||
offsetY?: number;
|
||
// group: management label for one-click group start/stop on the forwards
|
||
// page (unrelated to frps load-balancing lbGroup on Local).
|
||
group?: string;
|
||
// disabled: per-forward stop flag. A disabled forward is omitted from the
|
||
// frpc config (local) / left out of the ring topology (cluster), so stop is
|
||
// per-card and survives canvas re-saves.
|
||
disabled?: boolean;
|
||
}
|
||
|
||
export interface CanvasData {
|
||
locals: Local[];
|
||
remotes: Remote[];
|
||
links: Link[];
|
||
}
|
||
|
||
export interface Settings {
|
||
autoStartProfiles: boolean;
|
||
restartOnExit: boolean;
|
||
restartIntervalSeconds: number;
|
||
binaryPath?: string;
|
||
}
|
||
|
||
export interface ProcessStatus {
|
||
state: string;
|
||
pid?: number;
|
||
startTime?: number;
|
||
restartCount: number;
|
||
exitCode?: number;
|
||
err?: string;
|
||
}
|
||
|
||
export interface ProxyState {
|
||
name: string;
|
||
type: string;
|
||
status: string; // new | wait start | start error | running | check failed | closed
|
||
err?: string;
|
||
local_addr?: string;
|
||
remote_addr?: string;
|
||
}
|
||
|
||
export interface StatusProfile {
|
||
name: string;
|
||
enabled: boolean;
|
||
process: ProcessStatus;
|
||
hasProcess: boolean;
|
||
forwards: { service: string; remotePort: number; localPort?: number }[];
|
||
// connState: derived frps connection state (we do not control remote frps)
|
||
connState?: string; // connected | connecting | failed | not_started | disabled
|
||
// M3: admin API state
|
||
adminEnabled?: boolean;
|
||
proxyStates?: ProxyState[];
|
||
groupCount?: number;
|
||
}
|
||
|
||
export interface StatusResp {
|
||
version: string;
|
||
workDir: string;
|
||
settings: Settings;
|
||
services: Local[];
|
||
remotes: Remote[];
|
||
binaryPath: string;
|
||
profiles: StatusProfile[];
|
||
localStatus: LocalStatus[];
|
||
// forwards: the forward-centric list rendered as cards on the status page.
|
||
forwards: ForwardStatus[];
|
||
// selfId: this node's ring ID (== SelfAddr), to tell whether a cluster
|
||
// forward's owner is this node (worker runs here) or a peer.
|
||
selfId: string;
|
||
}
|
||
|
||
// ForwardStatus is one forward (link) in the forward-centric status view.
|
||
// kind distinguishes 本地转发 (localOnly) from 远程转发 (cluster-distributed).
|
||
export interface ForwardStatus {
|
||
local: string;
|
||
remote: string;
|
||
remotePort: number;
|
||
localOnly: boolean;
|
||
kind: "local" | "remote";
|
||
ownerId?: string; // remote = topology owner; local = selfId
|
||
active: boolean; // remote = in topology; local = local worker running
|
||
disabled: boolean; // per-forward stop flag (true = stopped)
|
||
group?: string;
|
||
localIp?: string;
|
||
localPort?: number;
|
||
localProto?: string;
|
||
}
|
||
|
||
export interface LocalTargetStatus {
|
||
remote: string;
|
||
remotePort: number;
|
||
workerState: string;
|
||
}
|
||
|
||
export interface LocalStatus {
|
||
local: Local;
|
||
targets: LocalTargetStatus[];
|
||
}
|
||
|
||
export interface BinaryStatus {
|
||
binaryPath: string;
|
||
version?: string;
|
||
}
|
||
|
||
export interface InstallResult {
|
||
path: string;
|
||
version: string;
|
||
}
|
||
|
||
// M6: cluster node registry + binary cache.
|
||
export interface ClusterNode {
|
||
addr: string;
|
||
cache?: string[];
|
||
version?: string;
|
||
}
|
||
|
||
export interface CacheEntry {
|
||
version: string;
|
||
path: string;
|
||
size: number;
|
||
modTime: number;
|
||
}
|
||
|
||
export interface ClusterNodesResp {
|
||
nodes: ClusterNode[];
|
||
}
|
||
|
||
export interface CacheResp {
|
||
cache: CacheEntry[];
|
||
removed?: string[];
|
||
}
|
||
|
||
// M6 token-ring snapshot (GET /cluster/ring).
|
||
export interface RingNode {
|
||
id: string;
|
||
addr: string;
|
||
isLeader?: boolean;
|
||
alive: boolean;
|
||
load: { memPct: number; netPct: number; forwards?: number };
|
||
version?: string;
|
||
cache?: string[];
|
||
lastSeen?: number;
|
||
}
|
||
|
||
export interface RingTaskInfo {
|
||
id: string;
|
||
revoke?: boolean;
|
||
removeNode?: string; // node-removal command (target rides the token to self-remove)
|
||
local: { name: string };
|
||
remote: { name: string };
|
||
link: { remotePort?: number };
|
||
}
|
||
|
||
export interface RingTopoEntry {
|
||
taskId: string;
|
||
ownerId: string;
|
||
active: boolean;
|
||
local: { name: string };
|
||
remote: { name: string };
|
||
link: { remotePort?: number };
|
||
}
|
||
|
||
export interface RingLogEntry {
|
||
seq: number;
|
||
node: string;
|
||
kind: string;
|
||
at?: number;
|
||
data?: Record<string, any> | null;
|
||
}
|
||
|
||
export interface RingSnapshot {
|
||
selfId: string;
|
||
leaderId: string; // 当前 leader(持首令牌 + 判丢失 + 被上家监控)
|
||
cycle: number; // 周期号(leader 每收令牌回 +1)
|
||
lastSync: number; // 上次同步时间 (unix sec)
|
||
roundDelayMs?: number;
|
||
nodes: RingNode[];
|
||
pending: RingTaskInfo[];
|
||
topology: RingTopoEntry[];
|
||
log: RingLogEntry[];
|
||
// nodeKey: this node's cluster admission key. Newcomers must present it to
|
||
// join via this node (handleClusterJoin verifies). Shown on the cluster page
|
||
// for the operator to copy; omitted for non-cluster (standalone) nodes.
|
||
nodeKey?: string;
|
||
}
|
||
|
||
// ---- Auth / accounts / API keys (M7) ----
|
||
|
||
export type AccessLevel = 'read' | 'write' | 'admin' | 'superadmin';
|
||
|
||
// MeResp is the authenticated principal (GET /me). The frontend uses level to
|
||
// gate the UI: viewer (= auditors) sees read + export controls only.
|
||
export interface MeResp {
|
||
type: 'user' | 'service' | 'key';
|
||
name: string;
|
||
level: AccessLevel;
|
||
userId?: number;
|
||
}
|
||
|
||
export interface User {
|
||
id: number;
|
||
username: string;
|
||
role: 'admin' | 'viewer' | 'superadmin';
|
||
enabled: boolean;
|
||
system?: boolean; // flag-synced built-in account (UI read-only)
|
||
createdAt: number;
|
||
lastLoginAt: number;
|
||
}
|
||
|
||
export interface ApiKey {
|
||
id: number;
|
||
userId: number;
|
||
prefix: string;
|
||
label: string;
|
||
scope: AccessLevel;
|
||
createdAt: number;
|
||
lastUsedAt: number;
|
||
expiresAt?: number;
|
||
}
|
||
|
||
// ApiKeyCreated is the one-time response from POST /apikeys: the plaintext key
|
||
// is shown exactly once and never retrievable again.
|
||
export interface ApiKeyCreated {
|
||
id: number;
|
||
userId: number;
|
||
label: string;
|
||
scope: AccessLevel;
|
||
prefix: string;
|
||
createdAt: number;
|
||
key: string;
|
||
}
|
||
|
||
export interface CanvasExportEnvelope {
|
||
_type: 'webui4frpc-canvas';
|
||
version: number;
|
||
exportedAt: number;
|
||
exporter: string;
|
||
canvas: CanvasData;
|
||
}
|
||
|
||
// Worker-log bundle (GET /cluster/logs/export): one entry per alive ring node.
|
||
export interface WorkerLogEntry {
|
||
name: string;
|
||
remote: string;
|
||
state: string;
|
||
lines: string;
|
||
}
|
||
export interface NodeLogsResp {
|
||
node: string;
|
||
workers: WorkerLogEntry[];
|
||
}
|
||
export interface ClusterWorkerLogNode {
|
||
id: string;
|
||
addr: string;
|
||
ok: boolean;
|
||
error?: string;
|
||
logs?: NodeLogsResp;
|
||
}
|
||
export interface WorkerLogBundle {
|
||
_type: 'webui4frpc-worker-logs';
|
||
version: number;
|
||
exportedAt: number;
|
||
requester: string;
|
||
nodes: ClusterWorkerLogNode[];
|
||
}
|