Files
webui4frpc/web/src/types.ts
jianf f29ec81e4a feat: session-cookie login/logout + three-tier roles (superadmin/admin/viewer) with read-only UI + remove pink theme
- 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
2026-08-20 09:08:15 +08:00

341 lines
8.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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[];
}