mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-29 14:06:36 +08:00
fix(openflare): improve SWOfflineDomains validation and snapshot diff logic
This commit is contained in:
@@ -26,6 +26,11 @@ sidebar: false
|
||||
|
||||
- 支持 Service Worker 离线兜底:为启用 HTTPS 的网站下发 Service Worker 并缓存离线联系页,域名无法访问时浏览器展示联系站长页面,减少用户流失。可指定生效域名范围(仅对选中的 HTTPS 域名生效),配置位于「响应页面」-「联系页」,可在版本发布中批量生效。
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复 `SWOfflineDomains` 校验与版本比对逻辑:校验器严格拒绝 JSON `null` 输入,配置版本 Diff 比对改用 JSON 序列化避免 `strings.Join` 逗号分隔符歧义。
|
||||
- 修复 HTML 编辑器预览标题:`HtmlEditorWorkspace` 支持传入 `previewTitle`,离线联系页预览使用「离线联系页实时预览」。
|
||||
|
||||
### 改进
|
||||
|
||||
- 响应页面统一:将源站错误页与 Service Worker 离线兜底联系页整合至统一的「响应页面」(`/responses`)标签页中,移除原 `/error-pages` 独立路由。
|
||||
|
||||
@@ -194,6 +194,7 @@ export function ContactPageTab({
|
||||
preview={(html) => html}
|
||||
footerHint={null}
|
||||
showPreviewLink={false}
|
||||
previewTitle='离线联系页实时预览'
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@@ -42,9 +42,7 @@ export function ErrorPageTab({
|
||||
optionMap: Record<string, string>;
|
||||
}) {
|
||||
const queryClient = useQueryClient();
|
||||
const [fields, setFields] = useState<ErrorPageFields>(
|
||||
defaultErrorPageFields,
|
||||
);
|
||||
const [fields, setFields] = useState<ErrorPageFields>(defaultErrorPageFields);
|
||||
const [tagError, setTagError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -21,6 +21,7 @@ type HtmlEditorWorkspaceProps = {
|
||||
preview?: (html: string) => string;
|
||||
footerHint?: React.ReactNode;
|
||||
showPreviewLink?: boolean;
|
||||
previewTitle?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -38,6 +39,7 @@ export function HtmlEditorWorkspace({
|
||||
</>
|
||||
),
|
||||
showPreviewLink = true,
|
||||
previewTitle = '源站错误页实时预览',
|
||||
}: HtmlEditorWorkspaceProps) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [editorWidthPercent, setEditorWidthPercent] = useState(48);
|
||||
@@ -145,7 +147,7 @@ export function HtmlEditorWorkspace({
|
||||
</div>
|
||||
<div className='flex-1 min-h-0 overflow-hidden bg-background'>
|
||||
<iframe
|
||||
title='源站错误页实时预览'
|
||||
title={previewTitle}
|
||||
sandbox=''
|
||||
srcDoc={previewSrcDoc}
|
||||
className='h-full w-full border-0 bg-background'
|
||||
|
||||
@@ -540,7 +540,7 @@ func diffOpenRestyOptionDetails(left openRestyConfigSnapshot, right openRestyCon
|
||||
appendIfChanged("OriginErrorPageGetOnly", fmt.Sprintf("%t", left.OriginErrorPageGetOnly), fmt.Sprintf("%t", right.OriginErrorPageGetOnly))
|
||||
appendIfChanged("SWOfflineEnabled", fmt.Sprintf("%t", left.SWOfflineEnabled), fmt.Sprintf("%t", right.SWOfflineEnabled))
|
||||
appendIfChanged("SWOfflineHTML", left.SWOfflineHTML, right.SWOfflineHTML)
|
||||
appendIfChanged("SWOfflineDomains", strings.Join(left.SWOfflineDomains, ","), strings.Join(right.SWOfflineDomains, ","))
|
||||
appendIfChanged("SWOfflineDomains", encodeSWOfflineDomains(left.SWOfflineDomains), encodeSWOfflineDomains(right.SWOfflineDomains))
|
||||
return changes
|
||||
}
|
||||
|
||||
@@ -555,6 +555,17 @@ func encodeOriginErrorPageStatusCodes(tags []string) string {
|
||||
return string(payload)
|
||||
}
|
||||
|
||||
func encodeSWOfflineDomains(domains []string) string {
|
||||
if len(domains) == 0 {
|
||||
return ""
|
||||
}
|
||||
payload, err := json.Marshal(domains)
|
||||
if err != nil {
|
||||
return strings.Join(domains, ",")
|
||||
}
|
||||
return string(payload)
|
||||
}
|
||||
|
||||
func extractOptionDiffKeys(details []ConfigOptionDiffItem) []string {
|
||||
keys := make([]string, 0, len(details))
|
||||
for _, item := range details {
|
||||
|
||||
@@ -101,3 +101,21 @@ func TestDiffOpenRestyOptionDetailsOriginErrorPage(t *testing.T) {
|
||||
assert.Equal(t, "", keys["OriginErrorPageHTML"].PreviousValue)
|
||||
assert.Equal(t, "<p>x</p>", keys["OriginErrorPageHTML"].CurrentValue)
|
||||
}
|
||||
|
||||
func TestDiffOpenRestyOptionDetailsSWOfflineDomains(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
left := openRestyConfigSnapshot{
|
||||
SWOfflineDomains: []string{"a.com,b.com"},
|
||||
}
|
||||
right := openRestyConfigSnapshot{
|
||||
SWOfflineDomains: []string{"a.com", "b.com"},
|
||||
}
|
||||
details := diffOpenRestyOptionDetails(left, right)
|
||||
keys := make(map[string]ConfigOptionDiffItem, len(details))
|
||||
for _, item := range details {
|
||||
keys[item.Key] = item
|
||||
}
|
||||
assert.Equal(t, `["a.com,b.com"]`, keys["SWOfflineDomains"].PreviousValue)
|
||||
assert.Equal(t, `["a.com","b.com"]`, keys["SWOfflineDomains"].CurrentValue)
|
||||
}
|
||||
|
||||
@@ -260,7 +260,7 @@ func validateSWOfflineHTML(key, value string) error {
|
||||
|
||||
func validateSWOfflineDomains(key, value string) error {
|
||||
var domains []string
|
||||
if err := json.Unmarshal([]byte(value), &domains); err != nil {
|
||||
if err := json.Unmarshal([]byte(value), &domains); err != nil || domains == nil {
|
||||
return fmt.Errorf("%s 必须为 JSON 字符串数组", key)
|
||||
}
|
||||
if len(domains) > maxSWOfflineDomains {
|
||||
|
||||
@@ -106,6 +106,7 @@ func TestValidateSWOfflineDomains(t *testing.T) {
|
||||
{"single", `["example.com"]`, true},
|
||||
{"multiple", `["example.com","api.example.com"]`, true},
|
||||
{"invalid json", `not-json`, false},
|
||||
{"null", "null", false},
|
||||
{"empty element", `[""]`, false},
|
||||
{"duplicate", `["example.com","example.com"]`, false},
|
||||
{"whitespace dedup", `[" Example.com ","example.com"]`, false},
|
||||
|
||||
Reference in New Issue
Block a user