chore: 初始提交 Fonu 1.0.2
- 公网反代:Nginx 状态卡片(版本 / 运行状态 / 配置校验 / 端口监听) - 部署设置第一、二梯队(HTTPS 开关、安全头、限流、连接数、仅中国大陆 IP) - 隧道检测兼容 frps v0.68 / v0.71(V2 与旧版端点) - 恢复被清空的 internal/frp/store.go、internal/api/ddns_handler.go、web/src/views/DdnsView.vue
This commit is contained in:
@@ -0,0 +1,372 @@
|
||||
import type {
|
||||
AccessLogEntry,
|
||||
ApiError,
|
||||
AppVersion,
|
||||
AuthStatus,
|
||||
CertificateCAOption,
|
||||
CertificateRecord,
|
||||
DashboardStatus,
|
||||
DDNSConfig,
|
||||
DDNSSavePayload,
|
||||
DDNSTestPayload,
|
||||
FRPResponse,
|
||||
FRPSavePayload,
|
||||
FRPSaveResponse,
|
||||
FRPStatus,
|
||||
FrpBinary,
|
||||
FrpDownloadTask,
|
||||
FrpProxy,
|
||||
FrpProxyPayload,
|
||||
FrpProxiesTraffic,
|
||||
FrpRelease,
|
||||
FrpRuntimeInfo,
|
||||
FrpServer,
|
||||
FrpServerDiagnostics,
|
||||
FrpServerDetail,
|
||||
FrpServerPayload,
|
||||
FrpStatus,
|
||||
DiscoveredService,
|
||||
ProxyClientConn,
|
||||
ChinaCIDRStatus,
|
||||
ProxyRule,
|
||||
ProxySavePayload,
|
||||
ProxyTraffic,
|
||||
GlobalNginxView,
|
||||
RuleNginxView,
|
||||
SettingsMap,
|
||||
SystemLogEntry,
|
||||
NotifyTestPayload,
|
||||
} from './types'
|
||||
|
||||
export function asList<T>(value: T[] | null | undefined): T[] {
|
||||
return Array.isArray(value) ? value : []
|
||||
}
|
||||
|
||||
const REQUEST_TIMEOUT_MS = 30_000
|
||||
const CERT_REQUEST_TIMEOUT_MS = 10 * 60_000
|
||||
|
||||
async function request<T>(path: string, init?: RequestInit, timeoutMs = REQUEST_TIMEOUT_MS): Promise<T> {
|
||||
const controller = new AbortController()
|
||||
const timeout = setTimeout(() => controller.abort(), timeoutMs)
|
||||
try {
|
||||
return await requestWithSignal<T>(path, init, controller.signal)
|
||||
} catch (error) {
|
||||
if (error instanceof DOMException && error.name === 'AbortError') {
|
||||
throw new Error(
|
||||
timeoutMs > REQUEST_TIMEOUT_MS
|
||||
? '证书操作超时,请稍后在证书列表查看状态,或在「日志 → 实时日志」查看 Nginx 输出'
|
||||
: '请求超时,请检查 Fonu 服务或 Nginx 状态',
|
||||
)
|
||||
}
|
||||
throw error
|
||||
} finally {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
}
|
||||
|
||||
async function requestWithSignal<T>(path: string, init?: RequestInit, signal?: AbortSignal): Promise<T> {
|
||||
const response = await fetch(path, {
|
||||
credentials: 'include',
|
||||
signal,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(init?.headers ?? {}),
|
||||
},
|
||||
...init,
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
let message = '请求失败'
|
||||
try {
|
||||
const body = (await response.json()) as ApiError
|
||||
if (body.error) {
|
||||
message = body.error
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
if (response.status === 204) {
|
||||
return undefined as T
|
||||
}
|
||||
return (await response.json()) as T
|
||||
}
|
||||
|
||||
export const api = {
|
||||
getVersion: () => request<AppVersion>('/api/version'),
|
||||
authStatus: () => request<AuthStatus>('/api/auth/status'),
|
||||
login: (username: string, password: string) =>
|
||||
request<{ message: string }>('/api/auth/login', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ username, password }),
|
||||
}),
|
||||
logout: () => request<{ message: string }>('/api/auth/logout', { method: 'POST' }),
|
||||
changePassword: (old_password: string, new_password: string) =>
|
||||
request<{ message: string }>('/api/auth/password', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ old_password, new_password }),
|
||||
}),
|
||||
|
||||
getStatus: () => request<DashboardStatus>('/api/status'),
|
||||
listProxies: () => request<ProxyRule[]>('/api/proxies'),
|
||||
createProxy: (payload: ProxySavePayload) =>
|
||||
request<ProxyRule>('/api/proxies', { method: 'POST', body: JSON.stringify(payload) }),
|
||||
updateProxy: (id: number, payload: Partial<ProxySavePayload>) =>
|
||||
request<ProxyRule>(`/api/proxies/${id}`, { method: 'PUT', body: JSON.stringify(payload) }),
|
||||
deleteProxy: (id: number) => request<void>(`/api/proxies/${id}`, { method: 'DELETE' }),
|
||||
reorderProxies: (ids: number[]) =>
|
||||
request<void>('/api/proxies/reorder', { method: 'PUT', body: JSON.stringify({ ids }) }),
|
||||
getProxyTraffic: () => request<ProxyTraffic[]>('/api/proxies/traffic'),
|
||||
getProxyClients: (id: number) => request<ProxyClientConn[]>(`/api/proxies/${id}/clients`),
|
||||
getProxyNginx: (id: number) => request<RuleNginxView>(`/api/proxies/${id}/nginx`),
|
||||
saveProxyNginx: (id: number, payload: { mode: 'auto' | 'custom'; content?: string }) =>
|
||||
request<RuleNginxView>(`/api/proxies/${id}/nginx`, { method: 'PUT', body: JSON.stringify(payload) }),
|
||||
rollbackProxyNginx: (id: number, backup?: string) =>
|
||||
request<RuleNginxView>(`/api/proxies/${id}/nginx/rollback`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(backup ? { backup } : {}),
|
||||
}),
|
||||
|
||||
listDDNS: () => request<DDNSConfig[]>('/api/ddns'),
|
||||
listDDNSLite: () => request<DDNSConfig[]>('/api/ddns?lite=1'),
|
||||
createDDNS: (payload: DDNSSavePayload) =>
|
||||
request<DDNSConfig>('/api/ddns', { method: 'POST', body: JSON.stringify(payload) }),
|
||||
updateDDNS: (id: number, payload: DDNSSavePayload) =>
|
||||
request<DDNSConfig>(`/api/ddns/${id}`, { method: 'PUT', body: JSON.stringify(payload) }),
|
||||
deleteDDNS: (id: number) => request<void>(`/api/ddns/${id}`, { method: 'DELETE' }),
|
||||
testDDNS: (payload: DDNSTestPayload) =>
|
||||
request<{ message: string }>('/api/ddns/test', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(payload),
|
||||
}),
|
||||
updateAllDDNS: () => request<DDNSConfig[]>('/api/ddns/update', { method: 'POST' }),
|
||||
updateDDNSOne: (id: number) => request<DDNSConfig>(`/api/ddns/${id}/update`, { method: 'POST' }),
|
||||
|
||||
listCertificates: () => request<CertificateRecord[]>('/api/certificates'),
|
||||
listCertificateCAOptions: () => request<CertificateCAOption[]>('/api/certificates/ca-options'),
|
||||
applyCertificate: (payload: {
|
||||
dns_zone?: string
|
||||
ddns_config_id?: number
|
||||
domains: string[]
|
||||
ca?: string
|
||||
email?: string
|
||||
}) =>
|
||||
request<{ job_id: string }>('/api/certificates/apply', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
dns_zone: payload.dns_zone ?? '',
|
||||
ddns_config_id: payload.ddns_config_id ?? 0,
|
||||
domains: payload.domains,
|
||||
ca: payload.ca ?? '',
|
||||
email: payload.email ?? '',
|
||||
}),
|
||||
}),
|
||||
certificateApplyStreamURL: (jobId: string) => `/api/certificates/jobs/${encodeURIComponent(jobId)}/stream`,
|
||||
importCertificate: (payload: {
|
||||
certificate?: string
|
||||
private_key?: string
|
||||
cert_path?: string
|
||||
key_path?: string
|
||||
}) =>
|
||||
request<CertificateRecord>('/api/certificates/import', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(payload),
|
||||
}),
|
||||
async downloadCertificate(domain: string, part: 'zip' | 'cert' | 'key' = 'zip') {
|
||||
const response = await fetch(
|
||||
`/api/certificates/${encodeURIComponent(domain)}/download?part=${part}`,
|
||||
{ credentials: 'include' },
|
||||
)
|
||||
if (!response.ok) {
|
||||
let message = '下载证书失败'
|
||||
try {
|
||||
const body = (await response.json()) as ApiError
|
||||
if (body.error) message = body.error
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
throw new Error(message)
|
||||
}
|
||||
const blob = await response.blob()
|
||||
const disposition = response.headers.get('Content-Disposition') ?? ''
|
||||
const match = disposition.match(/filename="?([^";\n]+)"?/)
|
||||
const filename = match?.[1] ?? `${domain}.zip`
|
||||
const url = URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = filename
|
||||
link.click()
|
||||
URL.revokeObjectURL(url)
|
||||
},
|
||||
deleteCertificate: (domain: string) =>
|
||||
request<{ message: string }>(`/api/certificates/${encodeURIComponent(domain)}`, { method: 'DELETE' }),
|
||||
renewCertificate: (domain?: string, ca?: string) =>
|
||||
request<CertificateRecord>(
|
||||
'/api/certificates/renew',
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ domain: domain ?? '', ca: ca ?? '' }),
|
||||
},
|
||||
CERT_REQUEST_TIMEOUT_MS,
|
||||
),
|
||||
|
||||
getSettings: () => request<SettingsMap>('/api/settings'),
|
||||
saveSettings: (payload: SettingsMap) =>
|
||||
request<SettingsMap>('/api/settings', { method: 'PUT', body: JSON.stringify(payload) }),
|
||||
getGlobalNginx: () => request<GlobalNginxView>('/api/settings/nginx/global'),
|
||||
saveGlobalNginx: (payload: { mode: 'auto' | 'custom'; content?: string }) =>
|
||||
request<GlobalNginxView>('/api/settings/nginx/global', { method: 'PUT', body: JSON.stringify(payload) }),
|
||||
rollbackGlobalNginx: (backup?: string) =>
|
||||
request<GlobalNginxView>('/api/settings/nginx/global/rollback', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(backup ? { backup } : {}),
|
||||
}),
|
||||
getChinaCIDRStatus: () => request<ChinaCIDRStatus>('/api/settings/china-cidr'),
|
||||
refreshChinaCIDR: () =>
|
||||
request<ChinaCIDRStatus>('/api/settings/china-cidr/refresh', { method: 'POST' }),
|
||||
testNotify: (payload: NotifyTestPayload) =>
|
||||
request<{ message: string }>('/api/settings/notify/test', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(payload),
|
||||
}),
|
||||
|
||||
getFRP: () => request<FRPResponse>('/api/frp'),
|
||||
saveFRP: (payload: FRPSavePayload) =>
|
||||
request<FRPSaveResponse>('/api/frp', {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(payload),
|
||||
}),
|
||||
getFRPStatus: () => request<FRPStatus>('/api/frp/status'),
|
||||
syncFRPDomains: () =>
|
||||
request<FRPSaveResponse & { domains: string[] }>('/api/frp/sync-domains', {
|
||||
method: 'POST',
|
||||
}),
|
||||
getFRPLogs: (limit = 200) => request<string[]>(`/api/frp/logs?limit=${limit}`),
|
||||
|
||||
// ===== FRP 多服务端模块(/api/frpmulti)=====
|
||||
getFrpStatus: () => request<FrpStatus>('/api/frpmulti/status'),
|
||||
getFrpRuntime: () => request<FrpRuntimeInfo>('/api/frpmulti/runtime'),
|
||||
setFrpDownloadProxy: (proxy: string) => request<FrpRuntimeInfo>('/api/frpmulti/runtime/download-proxy', { method: 'PUT', body: JSON.stringify({ proxy }) }),
|
||||
listFrpReleases: () => request<FrpRelease[]>('/api/frpmulti/releases'),
|
||||
listFrpBinaries: () => request<FrpBinary[]>('/api/frpmulti/binaries'),
|
||||
getFrpDownloadStatus: () => request<FrpDownloadTask>('/api/frpmulti/download'),
|
||||
downloadFrpRelease: (version: string) => request<FrpDownloadTask>(`/api/frpmulti/releases/${encodeURIComponent(version)}/download`, { method: 'POST' }),
|
||||
activateFrpBinary: (version: string) => request<FrpStatus>(`/api/frpmulti/binaries/${encodeURIComponent(version)}/activate`, { method: 'POST' }),
|
||||
deleteFrpBinary: (version: string) => request<void>(`/api/frpmulti/binaries/${encodeURIComponent(version)}`, { method: 'DELETE' }),
|
||||
listFrpServers: () => request<FrpServer[]>('/api/frpmulti/servers'),
|
||||
getFrpServerDiagnostics: (id: number) => request<FrpServerDiagnostics>(`/api/frpmulti/servers/${id}/diagnostics`),
|
||||
getFrpServerDetail: (id: number) => request<FrpServerDetail>(`/api/frpmulti/servers/${id}/detail`),
|
||||
startFrpServer: (id: number) => request<FrpServer>(`/api/frpmulti/servers/${id}/start`, { method: 'POST' }),
|
||||
stopFrpServer: (id: number) => request<FrpServer>(`/api/frpmulti/servers/${id}/stop`, { method: 'POST' }),
|
||||
createFrpServer: (payload: FrpServerPayload) =>
|
||||
request<FrpServer>('/api/frpmulti/servers', { method: 'POST', body: JSON.stringify(payload) }),
|
||||
updateFrpServer: (id: number, payload: FrpServerPayload) =>
|
||||
request<FrpServer>(`/api/frpmulti/servers/${id}`, { method: 'PUT', body: JSON.stringify(payload) }),
|
||||
deleteFrpServer: (id: number) => request<void>(`/api/frpmulti/servers/${id}`, { method: 'DELETE' }),
|
||||
testFrpServer: (id: number) => request<{ message: string }>(`/api/frpmulti/servers/${id}/test`, { method: 'POST' }),
|
||||
listFrpProxies: () => request<FrpProxy[]>('/api/frpmulti/proxies'),
|
||||
getFrpProxyLogs: (id: number, limit = 200) => request<string[]>(`/api/frpmulti/proxies/${id}/logs?limit=${limit}`),
|
||||
listFrpProxiesTraffic: () => request<FrpProxiesTraffic>('/api/frpmulti/proxies/traffic'),
|
||||
createFrpProxy: (payload: FrpProxyPayload) =>
|
||||
request<FrpProxy>('/api/frpmulti/proxies', { method: 'POST', body: JSON.stringify(payload) }),
|
||||
updateFrpProxy: (id: number, payload: FrpProxyPayload) =>
|
||||
request<FrpProxy>(`/api/frpmulti/proxies/${id}`, { method: 'PUT', body: JSON.stringify(payload) }),
|
||||
deleteFrpProxy: (id: number) => request<void>(`/api/frpmulti/proxies/${id}`, { method: 'DELETE' }),
|
||||
startFrp: () => request<FrpStatus>('/api/frpmulti/start', { method: 'POST' }),
|
||||
stopFrp: () => request<FrpStatus>('/api/frpmulti/stop', { method: 'POST' }),
|
||||
reloadFrp: () => request<FrpStatus>('/api/frpmulti/reload', { method: 'POST' }),
|
||||
getFrpLogs: (limit = 200, serverID?: number) => {
|
||||
const query = new URLSearchParams({ limit: String(limit) })
|
||||
if (serverID) query.set('server_id', String(serverID))
|
||||
return request<string[]>(`/api/frpmulti/logs?${query.toString()}`)
|
||||
},
|
||||
exportFrpConfig: () => request<{ content: string }>('/api/frpmulti/config/export'),
|
||||
getFrpFRPSConfig: (id: number) => request<{ content: string }>(`/api/frpmulti/servers/${id}/frps-config`),
|
||||
testFrpAgent: (id: number) => request<{ ok: boolean }>(`/api/frpmulti/servers/${id}/agent/test`, { method: 'POST' }),
|
||||
probeFrpAgent: (id: number) => request<Record<string, any>>(`/api/frpmulti/servers/${id}/agent/probe`, { method: 'POST' }),
|
||||
installFrpAgent: (id: number) => request<FrpServer>(`/api/frpmulti/servers/${id}/agent/install`, { method: 'POST' }),
|
||||
uninstallFrpAgent: (id: number, keepData: boolean) => request<{ ok: boolean; output: string }>(`/api/frpmulti/servers/${id}/agent/uninstall?keep_data=${keepData}`, { method: 'POST' }),
|
||||
sshDiagnoseFrpAgent: (id: number) => request<{ output: string }>(`/api/frpmulti/servers/${id}/agent/ssh-diagnose`, { method: 'POST' }),
|
||||
getFrpAgentStatus: (id: number) => request<import('./types').FrpAgentStatus>(`/api/frpmulti/servers/${id}/agent/status`),
|
||||
getFrpAgentRoutes: (id: number) => request<import('./types').FrpAgentRouteDetail[]>(`/api/frpmulti/servers/${id}/agent/routes`),
|
||||
getFrpAgentRouteCandidates: (id: number) => request<import('./types').FrpProxy[]>(`/api/frpmulti/servers/${id}/agent/route-candidates`),
|
||||
deployFrpAgentRoute: (serverId: number, proxyId: number) => request<{ ok: boolean }>(`/api/frpmulti/servers/${serverId}/agent/routes/${proxyId}`, { method: 'POST' }),
|
||||
getFrpAgentRouteConf: (serverId: number, proxyId: number) =>
|
||||
request<{ proxy_id: number; domain: string; content: string }>(`/api/frpmulti/servers/${serverId}/agent/routes/${proxyId}/conf`),
|
||||
installFrpAgentNginx: (serverId: number) =>
|
||||
request<{ ok: boolean; manager?: string; output?: string; path?: string; error?: string }>(`/api/frpmulti/servers/${serverId}/agent/nginx/install`, { method: 'POST' }),
|
||||
getFrpAgentRouteHealth: (serverId: number) =>
|
||||
request<import('./types').FrpAgentRouteHealth[]>(`/api/frpmulti/servers/${serverId}/agent/routes/health`),
|
||||
removeFrpAgentRoute: (serverId: number, proxyId: number) => request<{ ok: boolean }>(`/api/frpmulti/servers/${serverId}/agent/routes/${proxyId}`, { method: 'DELETE' }),
|
||||
pushFrpConfigToAgent: (id: number) => request<{ ok: boolean }>(`/api/frpmulti/servers/${id}/agent/frps-config`, { method: 'POST' }),
|
||||
frpsServerAction: (id: number, action: 'start' | 'stop' | 'restart') =>
|
||||
request<{ ok: boolean }>(`/api/frpmulti/servers/${id}/agent/frps-action`, { method: 'POST', body: JSON.stringify({ action }) }),
|
||||
frpsServerActionRaw: (id: number, action: 'start' | 'stop' | 'restart') =>
|
||||
request<{ ok: boolean; error?: string }>(`/api/frpmulti/servers/${id}/agent/frps-action`, { method: 'POST', body: JSON.stringify({ action }) }),
|
||||
installFrpServer: (id: number, version: string, proxy: string) =>
|
||||
request<Record<string, any>>(`/api/frpmulti/servers/${id}/agent/frps-install`, { method: 'POST', body: JSON.stringify({ version, proxy }) }),
|
||||
|
||||
getAccessLogs: (params?: { limit?: number; keyword?: string; status?: number }) => {
|
||||
const q = new URLSearchParams()
|
||||
if (params?.limit) q.set('limit', String(params.limit))
|
||||
if (params?.keyword) q.set('keyword', params.keyword)
|
||||
if (params?.status) q.set('status', String(params.status))
|
||||
return request<AccessLogEntry[]>(`/api/logs/access?${q}`)
|
||||
},
|
||||
getErrorLogs: (params?: { limit?: number; keyword?: string }) => {
|
||||
const q = new URLSearchParams()
|
||||
if (params?.limit) q.set('limit', String(params.limit))
|
||||
if (params?.keyword) q.set('keyword', params.keyword)
|
||||
return request<string[]>(`/api/logs/error?${q}`)
|
||||
},
|
||||
getSystemLogs: (params?: { limit?: number; level?: string; keyword?: string }) => {
|
||||
const q = new URLSearchParams()
|
||||
if (params?.limit) q.set('limit', String(params.limit))
|
||||
if (params?.level) q.set('level', params.level)
|
||||
if (params?.keyword) q.set('keyword', params.keyword)
|
||||
return request<SystemLogEntry[]>(`/api/logs/system?${q}`)
|
||||
},
|
||||
|
||||
scanDiscovery: (host?: string) => {
|
||||
const q = new URLSearchParams()
|
||||
if (host) q.set('host', host)
|
||||
return request<DiscoveredService[]>(`/api/discovery/scan?${q}`)
|
||||
},
|
||||
|
||||
async exportBackup() {
|
||||
const response = await fetch('/api/backup/export', { credentials: 'include' })
|
||||
if (!response.ok) {
|
||||
throw new Error('导出备份失败')
|
||||
}
|
||||
const blob = await response.blob()
|
||||
const url = URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = `fonu-backup-${new Date().toISOString().slice(0, 10)}.tar.gz`
|
||||
link.click()
|
||||
URL.revokeObjectURL(url)
|
||||
},
|
||||
|
||||
async restoreBackup(file: File) {
|
||||
const form = new FormData()
|
||||
form.append('file', file)
|
||||
const response = await fetch('/api/backup/restore', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
body: form,
|
||||
})
|
||||
if (!response.ok) {
|
||||
let message = '恢复备份失败'
|
||||
try {
|
||||
const body = (await response.json()) as ApiError
|
||||
if (body.error) message = body.error
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
throw new Error(message)
|
||||
}
|
||||
return (await response.json()) as { message: string }
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,794 @@
|
||||
export interface ProxyHost {
|
||||
id: number
|
||||
hostname: string
|
||||
listen_port?: number | null
|
||||
}
|
||||
|
||||
export interface BasicAuthConfig {
|
||||
enabled?: boolean
|
||||
username?: string
|
||||
has_password?: boolean
|
||||
}
|
||||
|
||||
export interface RateLimitConfig {
|
||||
enabled?: boolean
|
||||
rate?: number
|
||||
burst?: number
|
||||
}
|
||||
|
||||
export interface ConnLimitConfig {
|
||||
enabled?: boolean
|
||||
max?: number
|
||||
}
|
||||
|
||||
export interface ProxySecurityConfig {
|
||||
ip_blacklist?: string[]
|
||||
ip_whitelist?: string[]
|
||||
ip_whitelist_mode?: boolean
|
||||
china_only?: boolean
|
||||
basic_auth?: BasicAuthConfig
|
||||
rate_limit?: RateLimitConfig
|
||||
conn_limit?: ConnLimitConfig
|
||||
proxy_ssl_verify_off?: boolean
|
||||
proxy_host_upstream?: boolean
|
||||
tls_min_13_only?: boolean
|
||||
security_headers?: boolean
|
||||
}
|
||||
|
||||
export interface ProxyRule {
|
||||
id: number
|
||||
domain: string
|
||||
upstream: string
|
||||
listen_port: number
|
||||
listen_ipv4: boolean
|
||||
listen_ipv6: boolean
|
||||
hosts: ProxyHost[]
|
||||
https_enabled: boolean
|
||||
http_redirect: boolean
|
||||
enabled: boolean
|
||||
nginx_mode?: string
|
||||
name: string
|
||||
sort_order: number
|
||||
security: ProxySecurityConfig
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface ChinaCIDRStatus {
|
||||
updated_at?: string
|
||||
entry_count_v4: number
|
||||
entry_count_v6: number
|
||||
last_error?: string
|
||||
updating?: boolean
|
||||
ready?: boolean
|
||||
}
|
||||
|
||||
export interface ProxyTraffic {
|
||||
rule_id: number
|
||||
upload_total: number
|
||||
download_total: number
|
||||
upload_rate: number
|
||||
download_rate: number
|
||||
connections: number
|
||||
}
|
||||
|
||||
export interface ProxyClientConn {
|
||||
ip: string
|
||||
last_seen: string
|
||||
}
|
||||
|
||||
export interface ProxySecurityPayload {
|
||||
ip_blacklist?: string[]
|
||||
ip_whitelist?: string[]
|
||||
ip_whitelist_mode?: boolean
|
||||
ip_blacklist_text?: string
|
||||
ip_whitelist_text?: string
|
||||
china_only?: boolean
|
||||
basic_auth?: {
|
||||
enabled?: boolean
|
||||
username?: string
|
||||
password?: string
|
||||
}
|
||||
rate_limit?: RateLimitConfig
|
||||
conn_limit?: ConnLimitConfig
|
||||
proxy_ssl_verify_off?: boolean
|
||||
proxy_host_upstream?: boolean
|
||||
tls_min_13_only?: boolean
|
||||
security_headers?: boolean
|
||||
}
|
||||
|
||||
export interface ProxySavePayload {
|
||||
upstream: string
|
||||
listen_port?: number
|
||||
listen_ipv4?: boolean
|
||||
listen_ipv6?: boolean
|
||||
hosts: string[]
|
||||
https_enabled?: boolean
|
||||
http_redirect?: boolean
|
||||
enabled?: boolean
|
||||
name?: string
|
||||
security?: ProxySecurityPayload
|
||||
}
|
||||
|
||||
export interface AuthStatus {
|
||||
initialized: boolean
|
||||
authenticated: boolean
|
||||
}
|
||||
|
||||
export interface AppVersion {
|
||||
version: string
|
||||
nginx_http_port: number
|
||||
nginx_https_port: number
|
||||
}
|
||||
|
||||
export interface ApiError {
|
||||
error: string
|
||||
}
|
||||
|
||||
export interface DashboardStatus {
|
||||
public_ipv4: string
|
||||
public_ipv6: string
|
||||
public_ip_source?: 'ddns' | 'detect' | 'none'
|
||||
ddns_status: string
|
||||
ddns_count: number
|
||||
ddns_last_updated?: string
|
||||
certificate_status: string
|
||||
certificate_days: number
|
||||
certificate_count: number
|
||||
certificate_summary?: string
|
||||
proxy_count: number
|
||||
nginx_status: string
|
||||
request_today: number
|
||||
request_yesterday?: number
|
||||
request_trend?: number | null
|
||||
requests_hourly: number[]
|
||||
requests_hourly_labels?: string[]
|
||||
error_today: number
|
||||
avg_response_ms: number
|
||||
started_at: string
|
||||
uptime_seconds: number
|
||||
}
|
||||
|
||||
export interface DDNSDomainRecord {
|
||||
domain: string
|
||||
ipv4?: string
|
||||
ipv6?: string
|
||||
status: string
|
||||
message?: string
|
||||
}
|
||||
|
||||
export interface DDNSConfig {
|
||||
id: number
|
||||
provider: string
|
||||
root_domain: string
|
||||
record_name: string
|
||||
record_names?: string[]
|
||||
ipv4_enabled: boolean
|
||||
ipv6_enabled: boolean
|
||||
enabled: boolean
|
||||
has_token: boolean
|
||||
last_ipv4?: string
|
||||
last_ipv6?: string
|
||||
last_status?: string
|
||||
last_error?: string
|
||||
custom_ipv4?: string
|
||||
custom_ipv6?: string
|
||||
domain_records?: DDNSDomainRecord[]
|
||||
last_updated_at?: string
|
||||
}
|
||||
|
||||
export interface CertificateCAOption {
|
||||
value: string
|
||||
label: string
|
||||
}
|
||||
|
||||
export interface CertificateJobDone {
|
||||
ok: boolean
|
||||
error?: string
|
||||
domain?: string
|
||||
cert_path?: string
|
||||
key_path?: string
|
||||
cert_dir?: string
|
||||
expires_at?: string
|
||||
}
|
||||
|
||||
export interface CertificateJobEvent {
|
||||
type: 'log' | 'done'
|
||||
level?: string
|
||||
message?: string
|
||||
result?: CertificateJobDone
|
||||
}
|
||||
|
||||
export interface CertificateRecord {
|
||||
id: number
|
||||
domain: string
|
||||
domains?: string[]
|
||||
wildcard: boolean
|
||||
acme_ca?: string
|
||||
status: string
|
||||
days_left: number
|
||||
last_error?: string
|
||||
expires_at?: string
|
||||
last_renew_at?: string
|
||||
}
|
||||
|
||||
export interface AccessLogEntry {
|
||||
time: string
|
||||
domain: string
|
||||
server_port?: number
|
||||
method: string
|
||||
path: string
|
||||
status: number
|
||||
response_time: number
|
||||
client_ip: string
|
||||
upstream: string
|
||||
}
|
||||
|
||||
export interface SystemLogEntry {
|
||||
time: string
|
||||
level: string
|
||||
module: string
|
||||
message: string
|
||||
}
|
||||
|
||||
export type NotifyType = '' | 'email' | 'webhook' | 'telegram'
|
||||
export type WebhookProvider = 'bark' | 'ntfy' | 'gotify' | 'custom'
|
||||
|
||||
export interface NotifyEmailConfig {
|
||||
host: string
|
||||
port: number
|
||||
username: string
|
||||
from: string
|
||||
to: string[]
|
||||
tls: boolean
|
||||
has_password?: boolean
|
||||
}
|
||||
|
||||
export interface NotifyWebhookConfig {
|
||||
provider: WebhookProvider
|
||||
server: string
|
||||
key: string
|
||||
topic: string
|
||||
url: string
|
||||
has_secret?: boolean
|
||||
}
|
||||
|
||||
export interface NotifyTelegramConfig {
|
||||
chat_id: string
|
||||
proxy_url: string
|
||||
has_bot_token?: boolean
|
||||
}
|
||||
|
||||
export interface NotifyTestPayload {
|
||||
type: NotifyType
|
||||
email: NotifyEmailConfig
|
||||
webhook: NotifyWebhookConfig
|
||||
telegram: NotifyTelegramConfig
|
||||
on_ddns_ip_change: boolean
|
||||
on_ddns_failure: boolean
|
||||
on_cert_expiry: boolean
|
||||
on_cert_renew_success: boolean
|
||||
on_cert_renew_failure: boolean
|
||||
on_ip_frequent_access: boolean
|
||||
on_login_failure: boolean
|
||||
on_nginx_reload_failure: boolean
|
||||
ip_frequent_threshold: number
|
||||
ip_frequent_window_sec: number
|
||||
login_failure_threshold: number
|
||||
login_failure_window_sec: number
|
||||
smtp_password?: string
|
||||
webhook_secret?: string
|
||||
telegram_token?: string
|
||||
}
|
||||
|
||||
export type SettingsMap = Record<string, string>
|
||||
|
||||
export interface DiscoveredService {
|
||||
name: string
|
||||
port: number
|
||||
host: string
|
||||
upstream: string
|
||||
detected: boolean
|
||||
platform: string
|
||||
title?: string
|
||||
suggestion?: string
|
||||
}
|
||||
|
||||
export interface DDNSSavePayload {
|
||||
provider?: string
|
||||
root_domain?: string
|
||||
record_name?: string
|
||||
record_names?: string[]
|
||||
domains?: string[]
|
||||
ipv4_enabled?: boolean
|
||||
ipv6_enabled?: boolean
|
||||
enabled?: boolean
|
||||
custom_ipv4?: string
|
||||
custom_ipv6?: string
|
||||
api_token?: string
|
||||
api_token_id?: string
|
||||
api_secret?: string
|
||||
}
|
||||
|
||||
export interface DDNSTestPayload {
|
||||
config_id?: number
|
||||
provider?: string
|
||||
api_token?: string
|
||||
api_token_id?: string
|
||||
api_secret?: string
|
||||
}
|
||||
|
||||
export interface FRPStatus {
|
||||
enabled: boolean
|
||||
connected: boolean
|
||||
message: string
|
||||
last_error?: string
|
||||
server_endpoint?: string
|
||||
connected_at?: string
|
||||
uptime_seconds?: number
|
||||
client_version?: string
|
||||
http_gateway_enabled?: boolean
|
||||
https_gateway_enabled?: boolean
|
||||
synced_domain_count?: number
|
||||
}
|
||||
|
||||
export interface FRPConfig {
|
||||
enabled: boolean
|
||||
server_addr: string
|
||||
server_port: number
|
||||
auth_token: string
|
||||
has_auth_token: boolean
|
||||
tls_enabled: boolean
|
||||
custom_domains: string[]
|
||||
}
|
||||
|
||||
export interface FRPResponse {
|
||||
enabled: boolean
|
||||
server_addr: string
|
||||
server_port: number
|
||||
auth_token: string
|
||||
has_auth_token: boolean
|
||||
tls_enabled: boolean
|
||||
custom_domains: string[]
|
||||
status: FRPStatus
|
||||
frps_config: string
|
||||
nginx_http_port: number
|
||||
nginx_https_port: number
|
||||
}
|
||||
|
||||
export interface FRPSaveResponse {
|
||||
message: string
|
||||
config: FRPConfig
|
||||
status: FRPStatus
|
||||
frps_config: string
|
||||
nginx_http_port: number
|
||||
nginx_https_port: number
|
||||
}
|
||||
|
||||
export interface FRPSavePayload {
|
||||
enabled: boolean
|
||||
server_addr: string
|
||||
server_port: number
|
||||
auth_token?: string
|
||||
tls_enabled: boolean
|
||||
custom_domains: string[]
|
||||
}
|
||||
|
||||
// ===== FRP 多服务端模块(/api/frpmulti,0.1.25 核心迁移)=====
|
||||
|
||||
export interface FrpServer {
|
||||
agent_url?: string
|
||||
agent_configured?: boolean
|
||||
ssh_configured?: boolean
|
||||
mgmt_enabled?: boolean
|
||||
ssh_host?: string
|
||||
ssh_port?: number
|
||||
ssh_user?: string
|
||||
ssh_auth?: string
|
||||
id: number
|
||||
name: string
|
||||
server_addr: string
|
||||
server_port: number
|
||||
tls_enabled: boolean
|
||||
tls_server_name?: string
|
||||
enabled: boolean
|
||||
has_token: boolean
|
||||
has_oidc_client_secret: boolean
|
||||
dashboard_has_pwd?: boolean
|
||||
options: FrpServerOptions
|
||||
boot_status: 'starting' | 'running' | 'stopped' | 'error'
|
||||
desired_state: 'starting' | 'running' | 'stopped' | 'error' | string
|
||||
process_state: 'starting' | 'running' | 'stopped' | 'error' | 'unknown' | string
|
||||
connection_state: 'connected' | 'connecting' | 'disconnected' | 'error' | 'unknown' | string
|
||||
state_source: string
|
||||
process_pid?: number
|
||||
exit_code?: number
|
||||
last_connected_at?: string
|
||||
last_disconnected_at?: string
|
||||
last_error?: string
|
||||
last_started_at?: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface FrpServerOptions {
|
||||
user?: string
|
||||
location?: string
|
||||
/** frps 的 vhost 监听端口(http/https 反代回源端口),与 frps.toml 和 Nginx upstream 同源;留空用默认 8080/8443 */
|
||||
vhost_http_port?: number
|
||||
vhost_https_port?: number
|
||||
/** frps 进阶:子域名根(规则 subdomain 生效前提)*/
|
||||
sub_domain_host?: string
|
||||
/** HTTP vhost 404 页面路径(VPS 本地路径)*/
|
||||
custom_404_page?: string
|
||||
/** 强制 TLS:只接受 TLS 连接(frpc 必须开 transport.tls.enable)*/
|
||||
tls_force?: boolean
|
||||
/** 端口白名单:逗号分隔的单端口或范围,如 6000,6005-6010;留空不限制 */
|
||||
allow_ports?: string
|
||||
/** 每客户端可绑定端口上限;0/留空不限 */
|
||||
max_ports_per_client?: number
|
||||
/** tcpmux 规则的 HTTP CONNECT 监听端口 */
|
||||
tcpmux_http_connect_port?: number
|
||||
/** frps 写文件日志 /var/log/frps.log(journalctl 之外)*/
|
||||
frps_log_to_file?: boolean
|
||||
/** 文件日志保留天数,默认 3 */
|
||||
frps_log_max_days?: number
|
||||
/** frps 管理接口(webServer),仅用于 Fonu 拉取穿透流量等只读信息 */
|
||||
dashboard_addr?: string
|
||||
dashboard_port?: number
|
||||
dashboard_user?: string
|
||||
dashboard_has_pwd?: boolean
|
||||
auth_method: 'none' | 'token' | 'oidc'
|
||||
oidc_client_id?: string
|
||||
oidc_audience?: string
|
||||
oidc_scope?: string
|
||||
oidc_token_endpoint_url?: string
|
||||
oidc_trusted_ca_path?: string
|
||||
oidc_insecure_skip_verify?: boolean
|
||||
oidc_proxy_url?: string
|
||||
log_level: 'trace' | 'debug' | 'info' | 'warn' | 'error'
|
||||
log_max_days: number
|
||||
protocol: 'tcp' | 'kcp' | 'quic' | 'websocket' | 'wss'
|
||||
proxy_url?: string
|
||||
dial_server_timeout: number
|
||||
dial_server_keepalive: number
|
||||
connect_server_local_ip?: string
|
||||
dns_server?: string
|
||||
pool_count: number
|
||||
tcp_mux: boolean
|
||||
tcp_mux_keepalive_interval: number
|
||||
heartbeat_interval: number
|
||||
heartbeat_timeout: number
|
||||
login_fail_exit: boolean
|
||||
tls_disable_custom_first_byte: boolean
|
||||
auto_start: boolean
|
||||
remark?: string
|
||||
tls_certificate_configured?: boolean
|
||||
tls_key_configured?: boolean
|
||||
tls_trusted_ca_configured?: boolean
|
||||
}
|
||||
|
||||
export interface FrpProxy {
|
||||
id: number
|
||||
server_id: number
|
||||
name: string
|
||||
type: 'tcp' | 'udp' | 'http' | 'https' | 'tcpmux' | 'stcp' | 'sudp' | 'xtcp'
|
||||
local_ip: string
|
||||
local_port: number
|
||||
remote_port?: number | null
|
||||
custom_domains: string[]
|
||||
host_header_rewrite?: string
|
||||
options: FrpProxyOptions
|
||||
enabled: boolean
|
||||
remark?: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface FrpProxyOptions {
|
||||
subdomain?: string
|
||||
/** WebSocket 开关:仅作用于公网 Nginx 反代模板(加 Upgrade/Connection 头) */
|
||||
websocket?: boolean
|
||||
/** 公网反代:HTTP 80 强制 301 跳转 HTTPS(需公网证书已部署)*/
|
||||
redirect_https?: boolean
|
||||
/** 公网反代:IP 白名单(allow/deny),空则不限制 */
|
||||
allow_ips?: string[]
|
||||
/** 公网反代:Basic Auth 用户名;密码由 agent 首次部署自动生成(见配置注释)*/
|
||||
basic_auth_user?: string
|
||||
/** 公网反代:IP 黑名单(deny),与白名单可同时使用 */
|
||||
deny_ips?: string[]
|
||||
/** 公网反代:Basic Auth 自定义密码;留空则首次部署自动生成 */
|
||||
basic_auth_password?: string
|
||||
/** 公网反代:显式关闭 HTTPS(证书已推送也不监听 443);缺省 = 启用 */
|
||||
https_disabled?: boolean
|
||||
/** 公网反代:客户端上传大小限制,如 50m */
|
||||
client_max_body_size?: string
|
||||
/** 公网反代:代理读超时,如 3600s(长轮询 / 流媒体 / 大文件必需)*/
|
||||
proxy_read_timeout?: string
|
||||
/** 公网反代:安全响应头(HSTS / X-Frame-Options / X-Content-Type-Options / Referrer-Policy)*/
|
||||
security_headers?: boolean
|
||||
/** 公网反代:仅允许 TLS 1.3 */
|
||||
tls13_only?: boolean
|
||||
/** 公网反代:请求限流(次/秒);0 = 不限制 */
|
||||
rate_limit_rate?: number
|
||||
/** 公网反代:限流突发允许量 */
|
||||
rate_limit_burst?: number
|
||||
/** 公网反代:每 IP 并发连接数上限;0 = 不限制 */
|
||||
conn_limit_max?: number
|
||||
/** 公网反代:仅允许中国大陆 IP(部署时自动下发本地维护的 IP 段)*/
|
||||
china_only?: boolean
|
||||
locations?: string[]
|
||||
route_by_http_user?: string
|
||||
http_user?: string
|
||||
http_password?: string
|
||||
multiplexer?: string
|
||||
secret_key?: string
|
||||
allow_users?: string[]
|
||||
transport: {
|
||||
bandwidth_limit?: string
|
||||
bandwidth_limit_mode?: 'client' | 'server' | string
|
||||
use_encryption?: boolean
|
||||
use_compression?: boolean
|
||||
proxy_protocol_version?: 'v1' | 'v2' | string
|
||||
}
|
||||
health_check: {
|
||||
type?: 'tcp' | 'http' | string
|
||||
path?: string
|
||||
interval_seconds?: number
|
||||
max_failed?: number
|
||||
timeout_seconds?: number
|
||||
http_headers?: Array<{ name: string; value: string }>
|
||||
}
|
||||
load_balancer: { group?: string; group_key?: string }
|
||||
metadatas?: Record<string, string>
|
||||
request_headers?: Record<string, string>
|
||||
response_headers?: Record<string, string>
|
||||
plugin?: {
|
||||
type: string
|
||||
unix_path?: string
|
||||
local_path?: string
|
||||
strip_prefix?: string
|
||||
local_addr?: string
|
||||
http_user?: string
|
||||
http_password?: string
|
||||
username?: string
|
||||
password?: string
|
||||
crt_path?: string
|
||||
key_path?: string
|
||||
host_header_rewrite?: string
|
||||
destination_ip?: string
|
||||
}
|
||||
nat_traversal: { disable_assisted_addrs?: boolean }
|
||||
}
|
||||
|
||||
/** frps 侧单条穿透规则的流量快照;速率为两次采样间隔内的均值 */
|
||||
export interface FrpProxyTraffic {
|
||||
traffic_in: number
|
||||
traffic_out: number
|
||||
cur_conns: number
|
||||
traffic_in_rate: number
|
||||
traffic_out_rate: number
|
||||
}
|
||||
|
||||
/** 按 frp 规则 ID 聚合的流量数据;errors 记录采集失败的服务端/规则错误 */
|
||||
export interface FrpProxiesTraffic {
|
||||
items: Record<string, FrpProxyTraffic>
|
||||
errors?: Record<string, string>
|
||||
sampled_at: string
|
||||
}
|
||||
|
||||
export interface FrpStatus {
|
||||
status: 'running' | 'stopped' | 'unknown'
|
||||
pid?: number
|
||||
frpc_version?: string
|
||||
last_started_at?: string
|
||||
last_reloaded_at?: string
|
||||
last_error?: string
|
||||
configured: boolean
|
||||
enabled_proxies: number
|
||||
}
|
||||
|
||||
export interface FrpRuntimeInfo {
|
||||
platform: string
|
||||
configured_bin: string
|
||||
active_bin?: string
|
||||
active_version?: string
|
||||
detected_version?: string
|
||||
download_proxy: string
|
||||
config_path: string
|
||||
config_paths?: string[]
|
||||
config_exists: boolean
|
||||
}
|
||||
|
||||
export interface FrpRelease {
|
||||
version: string
|
||||
published_at?: string
|
||||
asset_name: string
|
||||
size: number
|
||||
}
|
||||
|
||||
export interface FrpBinary {
|
||||
version: string
|
||||
path: string
|
||||
size: number
|
||||
installed_at: string
|
||||
active: boolean
|
||||
}
|
||||
|
||||
/** 单项诊断结果;state 为 ok/failed/unknown,unknown 表示无法确认而非失败 */
|
||||
export interface FrpDiagnosticCheck {
|
||||
name: 'tcp_reachable' | 'frpc_login' | 'proxy_reachable' | 'dns_resolution' | 'location' | string
|
||||
state: 'ok' | 'failed' | 'unknown' | string
|
||||
detail?: string
|
||||
latency_ms?: number
|
||||
}
|
||||
|
||||
export interface FrpServerDiagnostics {
|
||||
server_id: number
|
||||
available: boolean
|
||||
latency_ms?: number
|
||||
location?: string
|
||||
checks?: FrpDiagnosticCheck[]
|
||||
error?: string
|
||||
}
|
||||
|
||||
export interface FrpServerSample {
|
||||
checked_at: string
|
||||
available: boolean
|
||||
latency_ms?: number
|
||||
error?: string
|
||||
}
|
||||
|
||||
export interface FrpServerStatusDetail {
|
||||
available: boolean
|
||||
availability: number
|
||||
average_latency_ms: number
|
||||
peak_latency_ms: number
|
||||
samples: FrpServerSample[]
|
||||
last_checked_at?: string
|
||||
last_error?: string
|
||||
}
|
||||
|
||||
export interface FrpServerDetail {
|
||||
server: FrpServer
|
||||
status: FrpServerStatusDetail
|
||||
proxies: FrpProxy[]
|
||||
}
|
||||
|
||||
export interface FrpDownloadTask {
|
||||
version?: string
|
||||
status: 'idle' | 'queued' | 'downloading' | 'verifying' | 'extracting' | 'completed' | 'failed'
|
||||
message: string
|
||||
downloaded: number
|
||||
total: number
|
||||
error?: string
|
||||
updated_at?: string
|
||||
}
|
||||
|
||||
export interface FrpServerPayload {
|
||||
agent_url?: string
|
||||
agent_token?: string
|
||||
clear_agent_token?: boolean
|
||||
ssh_host?: string
|
||||
ssh_port?: number
|
||||
ssh_user?: string
|
||||
ssh_auth?: string
|
||||
ssh_secret?: string
|
||||
clear_ssh_secret?: boolean
|
||||
mgmt_enabled?: boolean
|
||||
name: string
|
||||
server_addr: string
|
||||
server_port: number
|
||||
tls_enabled?: boolean
|
||||
tls_server_name?: string
|
||||
enabled?: boolean
|
||||
token?: string
|
||||
clear_token?: boolean
|
||||
options: FrpServerOptions
|
||||
oidc_client_secret?: string
|
||||
clear_oidc_client_secret?: boolean
|
||||
dashboard_password?: string
|
||||
clear_dashboard_password?: boolean
|
||||
tls_certificate?: string
|
||||
tls_key?: string
|
||||
tls_trusted_ca?: string
|
||||
clear_tls_certificate?: boolean
|
||||
clear_tls_key?: boolean
|
||||
clear_tls_trusted_ca?: boolean
|
||||
}
|
||||
|
||||
export interface FrpAgentRouteCandidate {
|
||||
id: number
|
||||
name: string
|
||||
type: 'http' | 'https'
|
||||
custom_domains: string[]
|
||||
local_ip: string
|
||||
local_port: number
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
export interface FrpAgentRouteHealth {
|
||||
proxy_id: number
|
||||
domain: string
|
||||
dns_ok: boolean
|
||||
dns_detail?: string
|
||||
cert_ok: boolean
|
||||
cert_detail?: string
|
||||
tunnel_ok: boolean
|
||||
tunnel_name?: string
|
||||
tunnel_detail?: string
|
||||
service_ok: boolean
|
||||
}
|
||||
|
||||
export interface FrpAgentRouteDetail {
|
||||
domain: string
|
||||
file_exists: boolean
|
||||
syntax_ok: boolean
|
||||
loaded: boolean
|
||||
}
|
||||
|
||||
export interface FrpAgentStatus {
|
||||
agent_version?: string
|
||||
ports?: Record<string, boolean>
|
||||
frps_active?: boolean
|
||||
frps_enabled?: boolean
|
||||
frps_version?: string
|
||||
frps_log_tail?: string
|
||||
frps_unit_exec?: string
|
||||
dashboard_addr?: string
|
||||
service: string
|
||||
nginx_binary: string
|
||||
nginx_conf_dir: string
|
||||
/** nginx -t 是否通过 */
|
||||
nginx_ok?: boolean
|
||||
/** 未找到 nginx 可执行文件 */
|
||||
nginx_missing?: boolean
|
||||
/** 解析到的 nginx 路径 */
|
||||
nginx_path?: string
|
||||
/** 有 nginx 进程在运行 */
|
||||
nginx_running?: boolean
|
||||
/** nginx 版本,如 nginx/1.24.0 */
|
||||
nginx_version?: string
|
||||
/** 探测失败原因(缺失或配置有误) */
|
||||
nginx_error?: string
|
||||
frps_binary: string
|
||||
frps_config_path: string
|
||||
routes: string[]
|
||||
checked_at: string
|
||||
}
|
||||
|
||||
export interface FrpProxyPayload {
|
||||
server_id: number
|
||||
name: string
|
||||
type: FrpProxy['type']
|
||||
local_ip: string
|
||||
local_port: number
|
||||
remote_port?: number | null
|
||||
custom_domains?: string[]
|
||||
host_header_rewrite?: string
|
||||
options?: FrpProxyOptions
|
||||
enabled?: boolean
|
||||
remark?: string
|
||||
}
|
||||
|
||||
export interface NginxBackupEntry {
|
||||
name: string
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export interface RuleNginxView {
|
||||
mode: 'auto' | 'custom'
|
||||
enabled: boolean
|
||||
active: boolean
|
||||
generated: string
|
||||
content: string
|
||||
backups: NginxBackupEntry[]
|
||||
}
|
||||
|
||||
export interface GlobalNginxView {
|
||||
mode: 'auto' | 'custom'
|
||||
generated_framework: string
|
||||
generated_snippet: string
|
||||
content: string
|
||||
backups: NginxBackupEntry[]
|
||||
}
|
||||
Reference in New Issue
Block a user