Files
panel/web/src/utils/common/common.ts
T
耗子 65e2750102 fix(website): 修复 IP 作为域名的处理(尤其 IPv6)
- 抽取 IsIPv6/WrapIPv6/UnwrapIPv6 到 pkg/tools,复用于 acme panelSolver
- server_name 写入时为 IPv6 套方括号、读取时剥离,Domains 统一裸地址口径,证书 SAN 判定自然正确
- 前端打开网站链接时为 IPv6 套方括号,避免拼出非法 URL

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 01:21:04 +08:00

64 lines
2.3 KiB
Go

import { DateTime, Duration } from 'luxon'
/** 格式化时间,默认格式:yyyy-MM-dd HH:mm:ss */
export function formatDateTime(time: any, format = 'yyyy-MM-dd HH:mm:ss'): string {
const dateTime = time ? DateTime.fromJSDate(new Date(time)) : DateTime.now()
return dateTime.toFormat(format)
}
/** 格式化日期,默认格式:yyyy-MM-dd */
export function formatDate(date: any, format = 'yyyy-MM-dd') {
return formatDateTime(date, format)
}
/** 格式化持续时间,转为 x天x小时x分钟x秒 */
export function formatDuration(seconds: number) {
const duration = Duration.fromObject({ seconds }).shiftTo('days', 'hours', 'minutes', 'seconds')
const days = duration.days
const hours = duration.hours
const minutes = duration.minutes
const secs = duration.seconds
return `${days}天${hours}小时${minutes}分钟${secs}秒`
}
/** 转时间戳 */
export function toTimestamp(time: any) {
return DateTime.fromJSDate(new Date(time)).toSeconds()
}
/** 生成随机字符串 */
export function generateRandomString(length: number) {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
const charactersLength = characters.length
let result = ''
if (!window.crypto || !window.crypto.getRandomValues) {
// fallback to insecure Math.random if crypto is not available
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * charactersLength)
result += characters[randomIndex]
}
return result
}
// Use Uint8Array for random bytes
while (result.length < length) {
const randomBytes = new Uint8Array(length - result.length)
window.crypto.getRandomValues(randomBytes)
for (let i = 0; i < randomBytes.length && result.length < length; i++) {
// Only use values that map evenly to the character set to avoid bias
const maxValue = Math.floor(256 / charactersLength) * charactersLength
const byteValue = randomBytes[i] ?? 0
if (byteValue < maxValue) {
const randomIndex = byteValue % charactersLength
result += characters[randomIndex] ?? ''
}
}
}
return result
}
/** 为裸 IPv6 地址套上方括号(如 ::1 → [::1]),用于拼接 URL;非 IPv6 或已带方括号则原样返回 */
export function wrapIPv6(host: string): string {
return host.includes(':') && !host.startsWith('[') ? `[${host}]` : host
}