fix: crypto.randomUUID 在非安全上下文(非HTTPS)下崩溃

新增 src/lib/uuid.js 提供 genUUID(), 优先使用 crypto.randomUUID,
不可用时回退到 getRandomValues 手动拼装 v4 UUID, 最终回退到 Math.random。

原因: crypto.randomUUID() 仅在安全上下文(HTTPS 或 localhost)可用。
当前站点通过 http://f.zikai.wang:8888 访问, 属于非安全上下文,
导致 event-factory.js 的新增日程和 split.js 的拆分事件功能崩溃。

涉及文件:
- src/lib/uuid.js (新增): 带 fallback 的 UUID 生成
- src/lib/event-factory.js: crypto.randomUUID -> genUUID
- src/lib/split.js: crypto.randomUUID -> genUUID
This commit is contained in:
zikai
2026-07-22 02:43:29 +00:00
parent 6997f7a708
commit 4a2c53204f
3 changed files with 41 additions and 2 deletions

View File

@@ -6,6 +6,7 @@
* and cloning so that useCalendar.js stays focused on state management.
*/
import { todayISO, nowCompactTimestamp } from './date.js'
import { genUUID } from './uuid.js'
/**
* Create a new single-occurrence event with sensible defaults.
@@ -14,7 +15,7 @@ import { todayISO, nowCompactTimestamp } from './date.js'
*/
export function createEvent(overrides = {}) {
return {
uid: crypto.randomUUID(),
uid: genUUID(),
summary: '新日程',
location: '',
description: '',

View File

@@ -12,6 +12,7 @@
*/
import { intervalDays } from './recur.js'
import { cloneEvent } from './event-factory.js'
import { genUUID } from './uuid.js'
import { toDate, toISODate, addDays, daysBetween } from './date.js'
/** Snap a date forward to the next grid point on or after it. */
@@ -42,7 +43,7 @@ export function splitRecurringEvent(ev, splitDate) {
partA.exdates = ev.exdates.filter((d) => d < splitDate)
const partB = cloneEvent(ev)
partB.uid = crypto.randomUUID()
partB.uid = genUUID()
partB.dtstartDate = snapStr
partB.exdates = ev.exdates.filter((d) => d >= splitDate)

37
src/lib/uuid.js Normal file
View File

@@ -0,0 +1,37 @@
/**
* UUID 生成工具。
*
* 优先使用 crypto.randomUUID(), 但该方法仅在安全上下文 (HTTPS 或 localhost)
* 中可用。当前站点通过 http://f.zikai.wang:8888 访问, 属于非安全上下文,
* crypto.randomUUID 会是 undefined, 因此需要回退方案。
*
* 回退一: 用 crypto.getRandomValues (不受安全上下文限制) 手动拼装
* RFC 4122 version 4 UUID。
* 回退二: 若 crypto 对象完全不存在 (如旧版 Node 直接运行 .mjs), 用
* Math.random 拼装, 牺牲少量随机性但保证可用。
*/
/**
* 生成一个 RFC 4122 version 4 UUID 字符串。
* @returns {string} 形如 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
*/
export function genUUID() {
const c = typeof globalThis !== 'undefined' ? globalThis.crypto : undefined
if (c && typeof c.randomUUID === 'function') {
return c.randomUUID()
}
if (c && typeof c.getRandomValues === 'function') {
const bytes = new Uint8Array(16)
c.getRandomValues(bytes)
bytes[6] = (bytes[6] & 0x0f) | 0x40 // version 4
bytes[8] = (bytes[8] & 0x3f) | 0x80 // variant
const h = [...bytes].map((b) => b.toString(16).padStart(2, '0'))
return `${h.slice(0, 4).join('')}-${h.slice(4, 6).join('')}-${h.slice(6, 8).join('')}-${h.slice(8, 10).join('')}-${h.slice(10, 16).join('')}`
}
// 最终回退: Math.random
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (ch) => {
const r = (Math.random() * 16) | 0
const v = ch === 'x' ? r : (r & 0x3) | 0x8
return v.toString(16)
})
}