初始提交:OCI 面板前端(含 GenAI 网关一期)

This commit is contained in:
Wang Defa
2026-07-09 15:31:29 +08:00
commit db45a669e3
135 changed files with 25220 additions and 0 deletions
+969
View File
@@ -0,0 +1,969 @@
/**
* 后端 DTO 类型,与《API接口文档.md》逐字段对应。
* 网络三资源(VCN/Subnet/SecurityList)字段与 internal/oci/network.go 对齐。
*/
// ---- 认证 ----
export interface LoginRequest {
username: string
password: string
/** 两步验证码;账号启用 TOTP 后必填,缺失时后端返回 428 */
totpCode?: string
}
export interface LoginResponse {
token: string
expiresAt: string
}
/** 两步验证状态 */
export interface TotpStatus {
enabled: boolean
}
/** 两步验证设置返回:手动输入码与 otpauth URI(渲染二维码) */
export interface TotpSetup {
secret: string
otpauthUri: string
}
/** 已绑定的外部登录身份 */
export interface UserIdentityInfo {
id: number
provider: string
display: string
createdAt: string
}
/** 登录凭据摘要 */
export interface CredentialsInfo {
username: string
/** 密码登录禁用态;开启需至少绑定一个外部身份 */
passwordLoginDisabled: boolean
}
/** 修改登录凭据;两项至少改一项,当前密码必填 */
export interface UpdateCredentialsRequest {
newUsername?: string
newPassword?: string
currentPassword: string
}
/** OAuth provider 配置视图;secret 只回设置态 */
export interface OAuthSettings {
oidcIssuer: string
oidcClientId: string
oidcSecretSet: boolean
oidcDisplayName: string
oidcDisabled: boolean
githubClientId: string
githubSecretSet: boolean
githubDisplayName: string
githubDisabled: boolean
}
/** 保存 provider 配置;secret 缺省沿用、空串清除 */
export interface UpdateOAuthRequest {
oidcIssuer: string
oidcClientId: string
oidcClientSecret?: string
oidcDisplayName: string
oidcDisabled: boolean
githubClientId: string
githubClientSecret?: string
githubDisplayName: string
githubDisabled: boolean
}
/** 登录页公开的 provider 信息;displayName 空配置时后端已回默认名 */
export interface OauthProviderInfo {
provider: string
displayName: string
}
// ---- API Key 配置(租户)----
export type AccountType = 'paid' | 'trial' | 'free' | 'unknown'
export type AliveStatus = 'alive' | 'dead' | 'unknown'
/** 列表接口(GET /oci-configs)的瘦身摘要:只含列表页与全局选择器消费的字段 */
export interface OciConfigSummary {
id: number
alias: string
/** 面板内自定义分组,空串表示未分组 */
group: string
tenancyOcid: string
tenancyName: string
region: string
accountType: AccountType
aliveStatus: AliveStatus
lastError: string
lastVerifiedAt: string | null
/** 多区域支持:开启后订阅区域入库缓存,筛选器可切换区域 */
multiRegion: boolean
/** 多区间支持:开启后 compartment 入库缓存,筛选器可切换区间 */
multiCompartment: boolean
/** 关联出站代理 id,null 直连 */
proxyId: number | null
/** 关联代理名称,列表接口按关联填充 */
proxyName: string
createdAt: string
}
/** 详情接口(GET /oci-configs/:id)的完整快照,订阅 / 促销 / 凭据元数据只在这里返回 */
export interface OciConfig extends OciConfigSummary {
userOcid: string
fingerprint: string
homeRegionKey: string
subscriptionId: string
paymentModel: string
subscriptionTier: string
promotionStatus: string
promotionAmount: number
promotionExpires: string | null
updatedAt: string
}
export interface ImportConfigRequest {
alias: string
group?: string
privateKey: string
configIni?: string
tenancyOcid?: string
userOcid?: string
region?: string
fingerprint?: string
multiRegion?: boolean
multiCompartment?: boolean
passphrase?: string
/** 关联出站代理,缺省直连 */
proxyId?: number
}
export interface VerifyResult {
config: OciConfig
changes: Record<string, [string, string]>
}
/** 修改配置:空字段不变更;替换私钥须同时给新指纹;group 一经提供整体覆盖(空串取消分组) */
export interface UpdateConfigRequest {
alias?: string
group?: string
region?: string
fingerprint?: string
privateKey?: string
passphrase?: string
multiRegion?: boolean
multiCompartment?: boolean
/** 非 null 切换代理关联:0 解除,>0 关联到该代理 */
proxyId?: number
}
// ---- 出站代理 ----
export interface ProxyInfo {
id: number
name: string
/** socks5 / http / https */
type: string
host: string
port: number
username: string
/** 密码已配置(不回明文) */
passwordSet: boolean
/** 出口实测国家 / 城市;探测失败为空串 */
country: string
city: string
/** 最近一次出口地理探测时间;空串表示尚未探测(显示「检测中」) */
geoAt: string
/** 关联此代理的租户数 */
usedBy: number
createdAt: string
}
/** 创建 / 更新代理;name 留空自动生成,password 缺省沿用已存值,空串清除 */
export interface ProxyInput {
name?: string
type: string
host: string
port: number
username?: string
password?: string
}
/** 批量导入结果;失败行已脱敏(凭据段为 ***) */
export interface ProxyImportResult {
created: ProxyInfo[]
failed: { lineNo: number; line: string; reason: string }[]
}
// ---- 区域 ----
export interface RegionInfo {
alias: string
key: string
name: string
}
export interface RegionSubscription {
key: string
name: string
status: string
isHomeRegion: boolean
alias: string
}
// ---- 配额 ----
export interface LimitItem {
name: string
scopeType: string
availabilityDomain?: string
value: number
used?: number
available?: number
}
export interface LimitService {
name: string
description: string
}
// ---- 订阅 ----
export interface SubscriptionSummary {
id: string
serviceName: string
timeCreated: string
timeUpdated: string
}
export interface Promotion {
duration: number
durationUnit: string
amount: number
status: string
isIntentToPay: boolean
currencyUnit: string
timeStarted: string | null
timeExpired: string | null
}
export interface SubscriptionDetail {
id: string
serviceName: string
classicSubscriptionId: string
paymentModel: string
subscriptionTier: string
lifecycleState: string
programType: string
customerCountryCode: string
cloudAmountCurrency: string
csiNumber: string
regionAssignment: string
isGovernmentSubscription: boolean
promotions: Promotion[]
startDate: string | null
endDate: string | null
timeCreated: string
timeUpdated: string
}
// ---- 镜像与可用域 ----
export interface ImageInfo {
id: string
displayName: string
operatingSystem: string
operatingSystemVersion: string
sizeInMBs: number
timeCreated: string
}
// ---- 网络 ----
export interface Vcn {
id: string
displayName: string
lifecycleState: string
compartmentId: string
cidrBlocks: string[]
ipv6CidrBlocks: string[]
dnsLabel: string
defaultRouteTableId: string
defaultSecurityListId: string
timeCreated: string | null
}
export interface CreateVcnRequest {
region?: string
/** 目标 compartment,空为租户根 */
compartmentId?: string
displayName: string
cidrBlock: string
dnsLabel?: string
enableIpv6?: boolean
}
export interface Subnet {
id: string
vcnId: string
displayName: string
lifecycleState: string
cidrBlock: string
ipv6CidrBlock: string
dnsLabel: string
prohibitPublicIp: boolean
availabilityDomain: string
routeTableId: string
timeCreated: string | null
}
export interface SecurityRule {
protocol: string
source?: string
destination?: string
isStateless: boolean
portMin?: number
portMax?: number
icmpType?: number
icmpCode?: number
description?: string
}
export interface SecurityList {
id: string
vcnId: string
displayName: string
lifecycleState: string
ingressRules: SecurityRule[]
egressRules: SecurityRule[]
timeCreated: string | null
}
export interface Ipv6Step {
step: string
status: 'done' | 'skipped' | 'failed'
detail: string
}
export interface Ipv6Report {
steps: Ipv6Step[]
}
// ---- 实例 ----
export interface Instance {
id: string
displayName: string
lifecycleState: string
shape: string
ocpus: number
memoryInGBs: number
availabilityDomain: string
region: string
compartmentId: string
imageId: string
subnetId: string
privateIp: string
publicIp: string
ipv6Addresses: string[]
freeformTags?: Record<string, string>
definedTags?: Record<string, Record<string, unknown>>
timeCreated: string
}
export interface CreateInstanceRequest {
region?: string
/** 目标 compartment,空为租户根 */
compartmentId?: string
availabilityDomain?: string
displayName: string
shape: string
count?: number
ocpus?: number
memoryInGBs?: number
imageId?: string
bootVolumeId?: string
bootVolumeSizeGBs?: number
bootVolumeVpusPerGB?: number
subnetId?: string
assignPublicIp?: boolean
assignIpv6?: boolean
sshPublicKey?: string
rootPassword?: string
/** 服务端生成随机 root 密码并写入 TAG RootPassword */
generateRootPassword?: boolean
userData?: string
}
export interface CreateInstancesResponse {
/** 旧版后端空值序列化为 null,读取时判空 */
instances: Instance[] | null
errors: string[] | null
}
export type PowerAction = 'START' | 'STOP' | 'RESET' | 'SOFTSTOP' | 'SOFTRESET'
export interface UpdateInstanceRequest {
region?: string
displayName?: string
shape?: string
ocpus?: number
memoryInGBs?: number
}
export interface ConsoleConnection {
id: string
instanceId: string
lifecycleState: string
connectionString: string
vncConnectionString: string
}
export interface BootVolumeAttachment {
id: string
instanceId: string
bootVolumeId: string
lifecycleState: string
displayName: string
availabilityDomain: string
}
export interface VolumeAttachment {
id: string
instanceId: string
volumeId: string
/** 卷本身的名称(displayName 是自动生成的挂载记录名) */
volumeName?: string
lifecycleState: string
displayName: string
device: string
isReadOnly: boolean
availabilityDomain: string
}
// ---- VNIC ----
export interface Vnic {
attachmentId: string
vnicId: string
displayName: string
isPrimary: boolean
privateIp: string
publicIp: string
/** 后端 nil slice 序列化为 null,使用处需兜底空数组 */
ipv6Addresses: string[] | null
subnetId: string
subnetName?: string
lifecycleState: string
timeCreated: string | null
}
export interface AttachVnicRequest {
region?: string
subnetId: string
displayName?: string
privateIp?: string
assignPublicIp?: boolean
/** 附加时同步自动分配一个 IPv6,要求子网已启用 IPv6 */
assignIpv6?: boolean
}
// ---- 块卷 ----
export interface Volume {
id: string
displayName: string
lifecycleState: string
availabilityDomain: string
sizeInGBs: number
vpusPerGB: number
timeCreated: string
}
// ---- 实例规格 ----
export interface ComputeShape {
name: string
billingType: string // ALWAYS_FREE / LIMITED_FREE / PAID
isFlexible: boolean
ocpus?: number
memoryInGBs?: number
ocpusMin?: number
ocpusMax?: number
memoryMinGBs?: number
memoryMaxGBs?: number
gpus?: number
processorDescription?: string
}
// ---- 引导卷 ----
export interface BootVolume {
id: string
displayName: string
lifecycleState: string
availabilityDomain: string
sizeInGBs: number
vpusPerGB: number
imageId: string
imageName?: string
attachedInstanceId: string
/** 挂载目标实例名(引导卷作数据卷挂到别的实例时,卷名与目标实例名不同) */
attachedInstanceName?: string
timeCreated: string
}
export interface UpdateBootVolumeRequest {
region?: string
displayName?: string
sizeInGBs?: number
vpusPerGB?: number
}
// ---- 流量与成本 ----
export interface TrafficPoint {
timestamp: string
bytes: number
}
export interface VnicTraffic {
vnicId: string
inbound: TrafficPoint[]
outbound: TrafficPoint[]
}
export interface InstanceTraffic {
instanceId: string
inboundBytes: number
outboundBytes: number
vnics: VnicTraffic[]
}
export interface CostItem {
timeStart: string
groupValue: string
computedAmount: number
computedQuantity: number
currency: string
unit: string
}
// ---- 租户用户管理 ----
export interface IamUser {
id: string
name: string
description: string
email: string
emailVerified: boolean
mfaActivated: boolean
lifecycleState: string
isCurrentUser: boolean
timeCreated: string
lastLoginTime: string | null
}
// 编辑表单用的域档案与管理员状态;inDomain 为 false 表示经典 IAM 用户
export interface IamUserDetail {
inDomain: boolean
givenName: string
familyName: string
isDomainAdmin: boolean
inAdminGroup: boolean
}
// ---- 租户审计日志 ----
// OCI Audit 事件的列表精简视图;来自实时透传查询,不落库;
// 原始 JSON 不随列表返回,详情弹窗按 eventId 懒取
export interface AuditEvent {
/** CloudEvents 全局唯一 id,详情反查与追加去重的键 */
eventId: string
eventTime: string | null
eventName: string
source: string
resourceName: string
compartmentName: string
principalName: string
ipAddress: string
status: string
requestAction: string
requestPath: string
}
// truncated 为 true 表示服务端翻页到限(10 页)被截断;nextPage 为续查游标,
// 配合 start/end 绝对时间窗可断点加载更早的事件
export interface AuditEventsResult {
items: AuditEvent[]
truncated: boolean
nextPage?: string
/** 本次实际查询的绝对窗(RFC3339),续查必须原样带回 */
start: string
end: string
}
// ---- 域通知与密码策略 ----
export interface NotificationRecipients {
recipients: string[]
testModeEnabled: boolean
}
export interface PasswordPolicy {
id: string
name: string
priority: number
passwordStrength: string
passwordExpiresAfter?: number
minLength?: number
numPasswordsInHistory?: number
}
// 身份设置:用户是否必须提供主电子邮件地址
export interface IdentitySetting {
id: string
primaryEmailRequired: boolean
}
// ---- Federation ----
export interface IdentityProvider {
id: string
name: string
type: string
enabled: boolean
partnerProviderId: string
jitEnabled: boolean
timeCreated: string
}
export interface SignOnRule {
id: string
name: string
sequence: number
authenticationFactor: string
conditionAttribute: string
conditionValue: string
builtIn: boolean
}
// ---- Compartment ----
export interface Compartment {
id: string
name: string
description: string
parentId: string
lifecycleState: string
timeCreated: string | null
}
// ---- 总览 ----
export interface OverviewTenants {
total: number
alive: number
dead: number
byType: Record<string, number>
}
export interface OverviewCheck {
hasActiveTask: boolean
coveredConfigs: number
totalConfigs: number
instanceCount: number
lastCheckedAt: string | null
}
export interface OverviewCostDay {
day: string
amount: number
}
export interface OverviewCost {
hasActiveTask: boolean
coveredConfigs: number
currency: string
total: number
days: OverviewCostDay[]
}
export interface OverviewTasks {
total: number
active: number
healthCheck: number
cost: number
snatch: number
}
export interface Overview {
tenants: OverviewTenants
check: OverviewCheck
cost: OverviewCost
tasks: OverviewTasks
}
// ---- 后台任务 ----
export type TaskType = 'health_check' | 'snatch' | 'cost' | 'ai_probe'
export type TaskStatus = 'active' | 'paused' | 'succeeded' | 'failed'
export interface Task {
id: number
name: string
type: TaskType
cronExpr: string
payload: string
status: TaskStatus
lastRunAt: string | null
lastError: string
runCount: number
createdAt: string
updatedAt: string
}
export interface TaskLog {
id: number
taskId: number
success: boolean
message: string
durationMs: number
createdAt: string
}
// ---- 系统设置 ----
export interface TelegramSetting {
enabled: boolean
chatId: string
/** 是否已保存过 bot token(接口绝不回明文) */
tokenSet: boolean
/** 已存 token 尾 4 位,供占位提示 */
tokenTail: string
}
/** 通知事件开关(通知管理);存量部署未保存过时后端按全开返回 */
export interface NotifyEventsSetting {
/** 任务由成功转失败时推送 */
taskFail: boolean
/** 任务由失败恢复正常时推送 */
taskRecover: boolean
/** 抢机任务达成目标台数时推送 */
snatchSuccess: boolean
/** 测活失联集合变化时推送 */
tenantDead: boolean
/** 抢机连续鉴权失败熔断停止时推送 */
taskStop: boolean
/** 登录锁定(连续失败触发) */
loginLock: boolean
/** AI 网关在池模型 30 天内弃用预警(随每日探测推送) */
modelDeprecated: boolean
/** 云端事件·实例生命周期(Launch/Terminate/InstanceAction) */
logEventInstance: boolean
/** 云端事件·用户与凭据(Create/Delete/UpdateUser、ApiKey、Capabilities) */
logEventIdentity: boolean
/** 云端事件·策略变更(Create/Update/DeletePolicy) */
logEventPolicy: boolean
/** 云端事件·区域订阅(CreateRegionSubscription) */
logEventRegion: boolean
/** 云端事件·控制台登录(InteractiveLogin) */
logEventLogin: boolean
}
/** 通知模板(设置页「通知管理 → 模板」);template 空串表示未自定义(按默认发送) */
export interface NotifyTemplateItem {
kind: string
label: string
/** 可用变量名(不含公共变量 time,发送时一并可用) */
vars: string[]
defaultTemplate: string
template: string
}
/** 安全设置(设置页「安全」tab):WAF 参数 / 真实IP请求头 / 面板地址 */
export interface SecuritySetting {
/** 登录失败阈值(1-20):锁定时长窗口内失败达到该次数即锁定 */
loginFailLimit: number
/** 登录锁定时长分钟(1-1440),同时是失败计数窗口 */
loginLockMinutes: number
/** 全局每 IP 限速 req/s(1-100) */
ipRateRps: number
/** 令牌桶突发额度(1-500) */
ipRateBurst: number
/** 真实IP请求头:空=直连(RemoteAddr);反代后必须选择匹配的头 */
realIpHeader: string
/** 面板公网基址,优先于 PUBLIC_URL 环境变量;回传链路与 OAuth 回调用 */
appUrl: string
}
/** 任务行为设置(设置页「任务」tab) */
export interface TaskSetting {
/** 抢机连续 NotAuthenticated 熔断阈值,1-10,未保存过时后端回默认 3 */
snatchAuthFailLimit: number
}
// ---- 系统日志 ----
/** 面板自身的操作留痕(写请求与登录成败),不含请求体 */
export interface SystemLog {
id: number
/** 登录失败时为请求提交的用户名 */
username: string
method: string
/** 路由模板(含 :id 占位符),登录为 /api/v1/auth/login */
path: string
status: number
durationMs: number
clientIp: string
userAgent: string
/** 仅失败请求(>=400)有值:响应 error 文案 */
errMsg: string
createdAt: string
}
export interface SystemLogPage {
items: SystemLog[]
total: number
}
// ---- 日志回传(方案A:OCI Connector Hub → Notifications → HTTPS 订阅) ----
/** OCI 回传的日志事件;payload 为 ONS 消息原文,eventType 等由后台异步解析回填 */
export interface LogEvent {
id: number
ociConfigId: number
/** ONS X-OCI-NS-MessageId,幂等键 */
messageId: string
/** 如 com.oraclecloud.ComputeApi.TerminateInstance,解析前为空 */
eventType: string
source: string
/** Audit 事件发起方 IP,解析回填,可能为空 */
sourceIp: string
eventTime: string | null
payload: string
/** 超过面板 256KB 防御上限被截断 */
truncated: boolean
processed: boolean
receivedAt: string
}
export interface LogEventPage {
items: LogEvent[]
total: number
}
/** 每租户回传回调地址;path 需以面板公网域名拼接完整 URL */
export interface LogWebhookInfo {
path: string
secret: string
createdAt: string
}
/** 回调地址查询结果:未生成时 exists 为 false 且无 webhook */
export interface LogWebhookState {
exists: boolean
webhook?: LogWebhookInfo
}
/** OCI 侧链路单资源状态;id 为空表示资源不存在 */
export interface RelayResource {
id: string
state: string
}
/** OCI 侧回传链路状态(P1 引导创建):Topic/订阅/Connector/Policy 一览 */
export interface LogRelayView {
webhook?: LogWebhookInfo
/** 完整回调 URL;后端 PUBLIC_URL 已配置且 secret 已生成时返回 */
endpoint?: string
topic: RelayResource
subscription: RelayResource
connector: RelayResource
policy: RelayResource
/** 全链路可用(四资源 ACTIVE 且 secret 有效) */
ready: boolean
/** 关键事件清单:Connector Log Filter 白名单,命中才回传 */
events: string[]
}
// ---- AI 网关 ----
/** 网关密钥;明文不回显,仅 tail 尾 4 位。group 非空 = 只在同分组渠道内路由 */
export interface AiKey {
id: number
name: string
tail: string
group: string
enabled: boolean
lastUsedAt: string | null
/** 内容日志开启截止时间;null = 永关(默认,红线),开启必须限时到期自动停写 */
contentLogUntil: string | null
createdAt: string
}
export type AiProbeStatus = '' | 'ok' | 'no_service' | 'no_quota' | 'error'
/** 号池渠道 = 租户配置 × 区域 */
export interface AiChannel {
id: number
name: string
ociConfigId: number
region: string
group: string
enabled: boolean
priority: number
weight: number
failCount: number
disabledUntil: string | null
lastProbeAt: string | null
probeStatus: AiProbeStatus
probeError: string
createdAt: string
updatedAt: string
}
/** 聚合可用模型(OpenAI /models 形态) */
export interface AiModel {
id: string
object: string
created: number
owned_by: string
}
/** 渠道模型缓存行 */
export interface AiModelCacheItem {
channelId: number
modelOcid: string
name: string
vendor: string
syncedAt: string
}
/** 调用日志:仅元数据与 token 用量 */
export interface AiCallLog {
id: number
keyId: number
keyName: string
endpoint: string
model: string
channelId: number
channelName: string
stream: boolean
status: number
/** 网关调用方来源 IP(尊重「真实 IP 头」安全设置) */
clientIp: string
promptTokens: number
completionTokens: number
totalTokens: number
/** 输入中缓存命中(读取)的 token 数;OCI 隐式缓存无写入计数 */
cachedTokens: number
latencyMs: number
retries: number
errMsg: string
createdAt: string
}
/** 内容日志:「不落正文」红线的显式例外,密钥限时开启期间的调用正文快照 */
export interface AiContentLog {
id: number
/** 关联同次调用的 AiCallLog id */
callLogId: number
keyId: number
keyName: string
endpoint: string
model: string
stream: boolean
/** 截断后的正文 JSON(上限 64KB);流式与向量结果不记录响应正文 */
requestBody: string
responseBody: string
createdAt: string
}
// ---- 关于 ----
/** GET /about:构建与运行环境信息 */
export interface AboutInfo {
version: string
buildTime: string
goVersion: string
platform: string
}
+15
View File
@@ -0,0 +1,15 @@
declare module '@novnc/novnc' {
/** noVNC RFB 客户端(官方包无类型声明,这里只声明用到的成员) */
export default class RFB extends EventTarget {
constructor(
target: Element,
url: string,
options?: { credentials?: { username?: string; password?: string; target?: string } },
)
scaleViewport: boolean
resizeSession: boolean
background: string
disconnect(): void
sendCtrlAltDel(): void
}
}