diff --git a/src/lib/utils.ts b/src/lib/utils.ts index aec650e..1424654 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -160,18 +160,88 @@ export function ip16Str(base64str: string) { return ipv6BinaryToString(buf); } -function ipv6BinaryToString(binary: Uint8Array) { - let parts: string[] = []; - for (let i = 0; i < binary.length; i += 2) { - let hex = (binary[i] << 8 | binary[i + 1]).toString(16); - parts.push(hex); +const digits = '0123456789abcdef'; + +function appendHex(b: string[], x: number): void { + if (x >= 0x1000) { + b.push(digits[(x >> 12) & 0xf]); + } + if (x >= 0x100) { + b.push(digits[(x >> 8) & 0xf]); + } + if (x >= 0x10) { + b.push(digits[(x >> 4) & 0xf]); + } + b.push(digits[x & 0xf]); +} + +function ipv6BinaryToString(ip: Uint8Array): string { + let ipBytes: Uint8Array; + + if (ip.length !== 16) { + ipBytes = new Uint8Array(16); + const len = Math.min(ip.length, 16); + ipBytes.set(ip.subarray(0, len)); + } else { + ipBytes = ip; } - let ipv6 = parts.join(':'); + const hextets: number[] = []; + for (let i = 0; i < 16; i += 2) { + hextets.push((ipBytes[i] << 8) | ipBytes[i + 1]); + } - ipv6 = ipv6.replace(/(:0)+$/, ''); - if (ipv6.indexOf('::') === -1 && parts.filter(p => p === '0').length > 1) { - ipv6 = ipv6.replace(/(:0)+/, '::'); + let zeroStart = -1; + let zeroLength = 0; + + for (let i = 0; i <= hextets.length;) { + let j = i; + while (j < hextets.length && hextets[j] === 0) { + j++; + } + const length = j - i; + if (length >= 2 && length > zeroLength) { + zeroStart = i; + zeroLength = length; + } + if (j === i) { + i++; + } else { + i = j; + } + } + + const parts: string[] = []; + for (let i = 0; i < hextets.length; i++) { + if (zeroLength > 0 && i === zeroStart) { + parts.push(''); + i += zeroLength - 1; + continue; + } + + if (parts.length > 0) { + parts.push(':'); + } + + const b: string[] = []; + appendHex(b, hextets[i]); + parts.push(b.join('')); + } + + let ipv6 = parts.join(''); + + if (ipv6.startsWith('::')) { + + } else if (ipv6.startsWith(':')) { + ipv6 = ':' + ipv6; + } + if (ipv6.endsWith('::')) { + + } else if (ipv6.endsWith(':')) { + ipv6 = ipv6 + ':'; + } + if (ipv6 === '') { + ipv6 = '::'; } return ipv6; diff --git a/src/routes/settings.tsx b/src/routes/settings.tsx index ec0bb0c..7d0c93b 100644 --- a/src/routes/settings.tsx +++ b/src/routes/settings.tsx @@ -32,7 +32,7 @@ import { import { useTranslation } from "react-i18next"; const settingFormSchema = z.object({ - custom_nameservers: asOptionalField(z.string()), + dns_servers: asOptionalField(z.string()), ignored_ip_notification: asOptionalField(z.string()), ip_change_notification_group_id: z.coerce.number().int().min(0), cover: z.coerce.number().int().min(1), @@ -285,7 +285,7 @@ export default function SettingsPage() { /> ( diff --git a/src/types/api.ts b/src/types/api.ts index ff4106e..40c2369 100644 --- a/src/types/api.ts +++ b/src/types/api.ts @@ -10,235 +10,235 @@ */ export interface GithubComNezhahqNezhaModelCommonResponseAny { - data?: any; - error?: string; - success?: boolean; + data: any; + error: string; + success: boolean; } export interface GithubComNezhahqNezhaModelCommonResponseArrayModelAlertRule { - data?: ModelAlertRule[]; - error?: string; - success?: boolean; + data: ModelAlertRule[]; + error: string; + success: boolean; } export interface GithubComNezhahqNezhaModelCommonResponseArrayModelCron { - data?: ModelCron[]; - error?: string; - success?: boolean; + data: ModelCron[]; + error: string; + success: boolean; } export interface GithubComNezhahqNezhaModelCommonResponseArrayModelDDNSProfile { - data?: ModelDDNSProfile[]; - error?: string; - success?: boolean; + data: ModelDDNSProfile[]; + error: string; + success: boolean; } export interface GithubComNezhahqNezhaModelCommonResponseArrayModelNAT { - data?: ModelNAT[]; - error?: string; - success?: boolean; + data: ModelNAT[]; + error: string; + success: boolean; } export interface GithubComNezhahqNezhaModelCommonResponseArrayModelNotification { - data?: ModelNotification[]; - error?: string; - success?: boolean; + data: ModelNotification[]; + error: string; + success: boolean; } export interface GithubComNezhahqNezhaModelCommonResponseArrayModelNotificationGroupResponseItem { - data?: ModelNotificationGroupResponseItem[]; - error?: string; - success?: boolean; + data: ModelNotificationGroupResponseItem[]; + error: string; + success: boolean; } export interface GithubComNezhahqNezhaModelCommonResponseArrayModelServer { - data?: ModelServer[]; - error?: string; - success?: boolean; + data: ModelServer[]; + error: string; + success: boolean; } export interface GithubComNezhahqNezhaModelCommonResponseArrayModelServerGroupResponseItem { - data?: ModelServerGroupResponseItem[]; - error?: string; - success?: boolean; + data: ModelServerGroupResponseItem[]; + error: string; + success: boolean; } export interface GithubComNezhahqNezhaModelCommonResponseArrayModelService { - data?: ModelService[]; - error?: string; - success?: boolean; + data: ModelService[]; + error: string; + success: boolean; } export interface GithubComNezhahqNezhaModelCommonResponseArrayModelServiceInfos { - data?: ModelServiceInfos[]; - error?: string; - success?: boolean; + data: ModelServiceInfos[]; + error: string; + success: boolean; } export interface GithubComNezhahqNezhaModelCommonResponseArrayModelUser { - data?: ModelUser[]; - error?: string; - success?: boolean; + data: ModelUser[]; + error: string; + success: boolean; } export interface GithubComNezhahqNezhaModelCommonResponseArrayModelWAFApiMock { - data?: ModelWAFApiMock[]; - error?: string; - success?: boolean; + data: ModelWAFApiMock[]; + error: string; + success: boolean; } export interface GithubComNezhahqNezhaModelCommonResponseArrayString { - data?: string[]; - error?: string; - success?: boolean; + data: string[]; + error: string; + success: boolean; } export interface GithubComNezhahqNezhaModelCommonResponseArrayUint64 { - data?: number[]; - error?: string; - success?: boolean; + data: number[]; + error: string; + success: boolean; } export interface GithubComNezhahqNezhaModelCommonResponseModelForceUpdateResponse { - data?: ModelForceUpdateResponse; - error?: string; - success?: boolean; + data: ModelForceUpdateResponse; + error: string; + success: boolean; } export interface GithubComNezhahqNezhaModelCommonResponseModelLoginResponse { - data?: ModelLoginResponse; - error?: string; - success?: boolean; + data: ModelLoginResponse; + error: string; + success: boolean; } export interface GithubComNezhahqNezhaModelCommonResponseModelProfile { - data?: ModelProfile; - error?: string; - success?: boolean; + data: ModelProfile; + error: string; + success: boolean; } export interface GithubComNezhahqNezhaModelCommonResponseModelServiceResponse { - data?: ModelServiceResponse; - error?: string; - success?: boolean; + data: ModelServiceResponse; + error: string; + success: boolean; } export interface GithubComNezhahqNezhaModelCommonResponseModelSettingResponse { - data?: ModelSettingResponse; - error?: string; - success?: boolean; + data: ModelSettingResponse; + error: string; + success: boolean; } export interface GithubComNezhahqNezhaModelCommonResponseUint64 { - data?: number; - error?: string; - success?: boolean; + data: number; + error: string; + success: boolean; } export interface ModelAlertRule { - created_at?: string; - enable?: boolean; + created_at: string; + enable: boolean; /** 失败时执行的触发任务id */ - fail_trigger_tasks?: number[]; - id?: number; - name?: string; + fail_trigger_tasks: number[]; + id: number; + name: string; /** 该报警规则所在的通知组 */ - notification_group_id?: number; + notification_group_id: number; /** 恢复时执行的触发任务id */ - recover_trigger_tasks?: number[]; - rules?: ModelRule[]; + recover_trigger_tasks: number[]; + rules: ModelRule[]; /** 触发模式: 0-始终触发(默认) 1-单次触发 */ - trigger_mode?: number; - updated_at?: string; + trigger_mode: number; + updated_at: string; } export interface ModelAlertRuleForm { enable?: boolean; /** 失败时触发的任务id */ - fail_trigger_tasks?: number[]; + fail_trigger_tasks: number[]; /** @minLength 1 */ - name?: string; - notification_group_id?: number; + name: string; + notification_group_id: number; /** 恢复时触发的任务id */ - recover_trigger_tasks?: number[]; - rules?: ModelRule[]; + recover_trigger_tasks: number[]; + rules: ModelRule[]; /** @default 0 */ - trigger_mode?: number; + trigger_mode: number; } export interface ModelCreateFMResponse { - session_id?: string; + session_id: string; } export interface ModelCreateTerminalResponse { - server_id?: number; - server_name?: string; - session_id?: string; + server_id: number; + server_name: string; + session_id: string; } export interface ModelCron { - command?: string; + command: string; /** 计划任务覆盖范围 (0:仅覆盖特定服务器 1:仅忽略特定服务器 2:由触发该计划任务的服务器执行) */ - cover?: number; - created_at?: string; - cron_job_id?: number; - id?: number; + cover: number; + created_at: string; + cron_job_id: number; + id: number; /** 最后一次执行时间 */ - last_executed_at?: string; + last_executed_at: string; /** 最后一次执行结果 */ - last_result?: boolean; - name?: string; + last_result: boolean; + name: string; /** 指定通知方式的分组 */ - notification_group_id?: number; + notification_group_id: number; /** 推送成功的通知 */ - push_successful?: boolean; + push_successful: boolean; /** 分钟 小时 天 月 星期 */ - scheduler?: string; - servers?: number[]; + scheduler: string; + servers: number[]; /** 0:计划任务 1:触发任务 */ - task_type?: number; - updated_at?: string; + task_type: number; + updated_at: string; } export interface ModelCronForm { command?: string; /** @default 0 */ - cover?: number; + cover: number; /** @minLength 1 */ - name?: string; - notification_group_id?: number; + name: string; + notification_group_id: number; push_successful?: boolean; - scheduler?: string; - servers?: number[]; + scheduler: string; + servers: number[]; /** * 0:计划任务 1:触发任务 * @default 0 */ - task_type?: number; + task_type: number; } export interface ModelCycleTransferStats { - from?: string; - max?: number; - min?: number; - name?: string; - next_update?: Record; - server_name?: Record; - to?: string; - transfer?: Record; + from: string; + max: number; + min: number; + name: string; + next_update: Record; + server_name: Record; + to: string; + transfer: Record; } export interface ModelDDNSForm { access_id?: string; access_secret?: string; - domains?: string[]; + domains: string[]; enable_ipv4?: boolean; enable_ipv6?: boolean; /** @default 3 */ - max_retries?: number; + max_retries: number; /** @minLength 1 */ - name?: string; - provider?: string; + name: string; + provider: string; webhook_headers?: string; /** @default 1 */ webhook_method?: number; @@ -249,22 +249,22 @@ export interface ModelDDNSForm { } export interface ModelDDNSProfile { - access_id?: string; - access_secret?: string; - created_at?: string; - domains?: string[]; - enable_ipv4?: boolean; - enable_ipv6?: boolean; - id?: number; - max_retries?: number; - name?: string; - provider?: string; - updated_at?: string; - webhook_headers?: string; - webhook_method?: number; - webhook_request_body?: string; - webhook_request_type?: number; - webhook_url?: string; + access_id: string; + access_secret: string; + created_at: string; + domains: string[]; + enable_ipv4: boolean; + enable_ipv6: boolean; + id: number; + max_retries: number; + name: string; + provider: string; + updated_at: string; + webhook_headers: string; + webhook_method: number; + webhook_request_body: string; + webhook_request_type: number; + webhook_url: string; } export interface ModelForceUpdateResponse { @@ -274,148 +274,148 @@ export interface ModelForceUpdateResponse { } export interface ModelFrontendTemplate { - author?: string; - is_admin?: boolean; - is_official?: boolean; - name?: string; - path?: string; - repository?: string; - version?: string; + author: string; + is_admin: boolean; + is_official: boolean; + name: string; + path: string; + repository: string; + version: string; } export interface ModelGeoIP { - country_code?: string; - ip?: ModelIP; + country_code: string; + ip: ModelIP; } export interface ModelHost { - arch?: string; - boot_time?: number; - cpu?: string[]; - disk_total?: number; - gpu?: string[]; - mem_total?: number; - platform?: string; - platform_version?: string; - swap_total?: number; - version?: string; - virtualization?: string; + arch: string; + boot_time: number; + cpu: string[]; + disk_total: number; + gpu: string[]; + mem_total: number; + platform: string; + platform_version: string; + swap_total: number; + version: string; + virtualization: string; } export interface ModelHostState { - cpu?: number; - disk_used?: number; - gpu?: number[]; - load_1?: number; - load_15?: number; - load_5?: number; - mem_used?: number; - net_in_speed?: number; - net_in_transfer?: number; - net_out_speed?: number; - net_out_transfer?: number; - process_count?: number; - swap_used?: number; - tcp_conn_count?: number; - temperatures?: ModelSensorTemperature[]; - udp_conn_count?: number; - uptime?: number; + cpu: number; + disk_used: number; + gpu: number[]; + load_1: number; + load_15: number; + load_5: number; + mem_used: number; + net_in_speed: number; + net_in_transfer: number; + net_out_speed: number; + net_out_transfer: number; + process_count: number; + swap_used: number; + tcp_conn_count: number; + temperatures: ModelSensorTemperature[]; + udp_conn_count: number; + uptime: number; } export interface ModelIP { - ipv4_addr?: string; - ipv6_addr?: string; + ipv4_addr: string; + ipv6_addr: string; } export interface ModelLoginRequest { - password?: string; - username?: string; + password: string; + username: string; } export interface ModelLoginResponse { - expire?: string; - token?: string; + expire: string; + token: string; } export interface ModelNAT { - created_at?: string; - domain?: string; - host?: string; - id?: number; - name?: string; - server_id?: number; - updated_at?: string; + created_at: string; + domain: string; + host: string; + id: number; + name: string; + server_id: number; + updated_at: string; } export interface ModelNATForm { - domain?: string; - host?: string; + domain: string; + host: string; /** @minLength 1 */ - name?: string; - server_id?: number; + name: string; + server_id: number; } export interface ModelNotification { - created_at?: string; - id?: number; - name?: string; - request_body?: string; - request_header?: string; - request_method?: number; - request_type?: number; - updated_at?: string; - url?: string; - verify_tls?: boolean; + created_at: string; + id: number; + name: string; + request_body: string; + request_header: string; + request_method: number; + request_type: number; + updated_at: string; + url: string; + verify_tls: boolean; } export interface ModelNotificationForm { /** @minLength 1 */ - name?: string; - request_body?: string; - request_header?: string; - request_method?: number; - request_type?: number; + name: string; + request_body: string; + request_header: string; + request_method: number; + request_type: number; skip_check?: boolean; - url?: string; + url: string; verify_tls?: boolean; } export interface ModelNotificationGroup { - created_at?: string; - id?: number; - name?: string; - updated_at?: string; + created_at: string; + id: number; + name: string; + updated_at: string; } export interface ModelNotificationGroupForm { /** @minLength 1 */ - name?: string; - notifications?: number[]; + name: string; + notifications: number[]; } export interface ModelNotificationGroupResponseItem { - group?: ModelNotificationGroup; - notifications?: number[]; + group: ModelNotificationGroup; + notifications: number[]; } export interface ModelProfile { - created_at?: string; - id?: number; - login_ip?: string; - password?: string; - updated_at?: string; - username?: string; + created_at: string; + id: number; + login_ip: string; + password: string; + updated_at: string; + username: string; } export interface ModelProfileForm { - new_password?: string; - new_username?: string; - original_password?: string; + new_password: string; + new_username: string; + original_password: string; } export interface ModelRule { /** 覆盖范围 RuleCoverAll/IgnoreAll */ - cover?: number; + cover: number; /** 流量统计周期 */ cycle_interval?: number; /** 流量统计的开始时间 */ @@ -438,7 +438,7 @@ export interface ModelRule { * net_all_speed、transfer_in、transfer_out、transfer_all、offline * transfer_in_cycle、transfer_out_cycle、transfer_all_cycle */ - type?: string; + type: string; } export interface ModelSensorTemperature { @@ -447,27 +447,27 @@ export interface ModelSensorTemperature { } export interface ModelServer { - created_at?: string; + created_at: string; /** DDNS配置 */ ddns_profiles?: number[]; /** 展示排序,越大越靠前 */ - display_index?: number; + display_index: number; /** 启用DDNS */ - enable_ddns?: boolean; - geoip?: ModelGeoIP; + enable_ddns: boolean; + geoip: ModelGeoIP; /** 对游客隐藏 */ - hide_for_guest?: boolean; - host?: ModelHost; - id?: number; - last_active?: string; - name?: string; + hide_for_guest: boolean; + host: ModelHost; + id: number; + last_active: string; + name: string; /** 管理员可见备注 */ - note?: string; + note: string; /** 公开备注 */ - public_note?: string; - state?: ModelHostState; - updated_at?: string; - uuid?: string; + public_note: string; + state: ModelHostState; + updated_at: string; + uuid: string; } export interface ModelServerForm { @@ -477,12 +477,12 @@ export interface ModelServerForm { * 展示排序,越大越靠前 * @default 0 */ - display_index?: number; + display_index: number; /** 启用DDNS */ enable_ddns?: boolean; /** 对游客隐藏 */ hide_for_guest?: boolean; - name?: string; + name: string; /** 管理员可见备注 */ note?: string; /** 公开备注 */ @@ -490,190 +490,190 @@ export interface ModelServerForm { } export interface ModelServerGroup { - created_at?: string; - id?: number; - name?: string; - updated_at?: string; + created_at: string; + id: number; + name: string; + updated_at: string; } export interface ModelServerGroupForm { /** @minLength 1 */ - name?: string; - servers?: number[]; + name: string; + servers: number[]; } export interface ModelServerGroupResponseItem { - group?: ModelServerGroup; - servers?: number[]; + group: ModelServerGroup; + servers: number[]; } export interface ModelService { - cover?: number; - created_at?: string; - duration?: number; - enable_show_in_service?: boolean; - enable_trigger_task?: boolean; + cover: number; + created_at: string; + duration: number; + enable_show_in_service: boolean; + enable_trigger_task: boolean; /** 失败时执行的触发任务id */ - fail_trigger_tasks?: number[]; - id?: number; - latency_notify?: boolean; - max_latency?: number; - min_latency?: number; - name?: string; + fail_trigger_tasks: number[]; + id: number; + latency_notify: boolean; + max_latency: number; + min_latency: number; + name: string; /** 当前服务监控所属的通知组 ID */ - notification_group_id?: number; - notify?: boolean; + notification_group_id: number; + notify: boolean; /** 恢复时执行的触发任务id */ - recover_trigger_tasks?: number[]; - skip_servers?: Record; - target?: string; - type?: number; - updated_at?: string; + recover_trigger_tasks: number[]; + skip_servers: Record; + target: string; + type: number; + updated_at: string; } export interface ModelServiceForm { - cover?: number; - duration?: number; + cover: number; + duration: number; enable_show_in_service?: boolean; enable_trigger_task?: boolean; - fail_trigger_tasks?: number[]; + fail_trigger_tasks: number[]; latency_notify?: boolean; /** @default 0 */ - max_latency?: number; + max_latency: number; /** @default 0 */ - min_latency?: number; + min_latency: number; /** @minLength 1 */ - name?: string; - notification_group_id?: number; + name: string; + notification_group_id: number; notify?: boolean; - recover_trigger_tasks?: number[]; - skip_servers?: Record; - target?: string; - type?: number; + recover_trigger_tasks: number[]; + skip_servers: Record; + target: string; + type: number; } export interface ModelServiceInfos { - avg_delay?: number[]; - created_at?: number[]; - monitor_id?: number; - monitor_name?: string; - server_id?: number; - server_name?: string; + avg_delay: number[]; + created_at: number[]; + monitor_id: number; + monitor_name: string; + server_id: number; + server_name: string; } export interface ModelServiceResponse { - cycle_transfer_stats?: Record; - services?: Record; + cycle_transfer_stats: Record; + services: Record; } export interface ModelServiceResponseItem { - current_down?: number; - current_up?: number; - delay?: number[]; - down?: number[]; - service_name?: string; - total_down?: number; - total_up?: number; - up?: number[]; + current_down: number; + current_up: number; + delay: number[]; + down: number[]; + service_name: string; + total_down: number; + total_up: number; + up: number[]; } export interface ModelSettingForm { - cover?: number; + cover: number; custom_code?: string; custom_code_dashboard?: string; - custom_nameservers?: string; + dns_servers?: string; enable_ip_change_notification?: boolean; enable_plain_ip_in_notification?: boolean; ignored_ip_notification?: string; install_host?: string; /** IP变更提醒的通知组 */ - ip_change_notification_group_id?: number; + ip_change_notification_group_id: number; /** @minLength 2 */ - language?: string; + language: string; /** 真实IP */ real_ip_header?: string; /** @minLength 1 */ - site_name?: string; + site_name: string; tls?: boolean; user_template?: string; } export interface ModelSettingResponse { - admin_template?: string; - agent_secret_key?: string; - avg_ping_count?: number; + admin_template: string; + agent_secret_key: string; + avg_ping_count: number; /** 覆盖范围(0:提醒未被 IgnoredIPNotification 包含的所有服务器; 1:仅提醒被 IgnoredIPNotification 包含的服务器;) */ - cover?: number; - custom_code?: string; - custom_code_dashboard?: string; + cover: number; + custom_code: string; + custom_code_dashboard: string; /** debug模式开关 */ - debug?: boolean; - dns_servers?: string; + debug: boolean; + dns_servers: string; /** IP变更提醒 */ - enable_ip_change_notification?: boolean; + enable_ip_change_notification: boolean; /** 通知信息IP不打码 */ - enable_plain_ip_in_notification?: boolean; - frontend_templates?: ModelFrontendTemplate[]; + enable_plain_ip_in_notification: boolean; + frontend_templates: ModelFrontendTemplate[]; /** 特定服务器IP(多个服务器用逗号分隔) */ - ignored_ip_notification?: string; + ignored_ip_notification: string; /** [ServerID] -> bool(值为true代表当前ServerID在特定服务器列表内) */ - ignored_ip_notification_server_ids?: Record; - install_host?: string; - ip_change_notification_group_id?: number; - jwt_secret_key?: string; + ignored_ip_notification_server_ids: Record; + install_host: string; + ip_change_notification_group_id: number; + jwt_secret_key: string; /** 系统语言,默认 zh_CN */ - language?: string; - listen_host?: string; - listen_port?: number; + language: string; + listen_host: string; + listen_port: number; /** 时区,默认为 Asia/Shanghai */ - location?: string; + location: string; /** 真实IP */ - real_ip_header?: string; - site_name?: string; - tls?: boolean; - user_template?: string; - version?: string; + real_ip_header: string; + site_name: string; + tls: boolean; + user_template: string; + version: string; } export interface ModelStreamServer { - country_code?: string; + country_code: string; /** 展示排序,越大越靠前 */ - display_index?: number; - host?: ModelHost; - id?: number; - last_active?: string; - name?: string; + display_index: number; + host: ModelHost; + id: number; + last_active: string; + name: string; /** 公开备注,只第一个数据包有值 */ - public_note?: string; - state?: ModelHostState; + public_note: string; + state: ModelHostState; } export interface ModelStreamServerData { - now?: number; - servers?: ModelStreamServer[]; + now: number; + servers: ModelStreamServer[]; } export interface ModelTerminalForm { - protocol?: string; - server_id?: number; + protocol: string; + server_id: number; } export interface ModelUser { - created_at?: string; - id?: number; - password?: string; - updated_at?: string; - username?: string; + created_at: string; + id: number; + password: string; + updated_at: string; + username: string; } export interface ModelUserForm { - password?: string; - username?: string; + password: string; + username: string; } export interface ModelWAFApiMock { - count?: number; - ip?: string; - last_block_reason?: number; - last_block_timestamp?: number; + count: number; + ip: string; + last_block_reason: number; + last_block_timestamp: number; }