mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-03 15:06:36 +08:00
ca21ff3a5b
fix(openresty): scope sw injection per cert partition fix(lint): satisfy revive and biome format for sw offline feature docs: sw offline scope changelog fix(frontend): use scoped query key for sw scope zones fix(frontend): hide preview link in sw contact page editor feat(frontend): add sw scope domain picker and contact page fields refactor(frontend): generalize html editor workspace for reuse feat(openresty): scope sw offline injection by route domains feat(openresty): add sw offline domains snapshot field feat(option): add sw offline domains scope option docs: fill html editor workspace generalization detail docs: sw offline scope implementation plan docs: sw offline scope design test(openresty): assert single merged access block in sw enabled servers fix(openresty): restrict sw intercept to https server blocks fix(openresty): version sw offline cache by html content fix(agent): escape redir in sw challenge page to prevent xss fix(agent): return sw.runtime module table and add lua spec docs: sw offline fallback changelog fix(frontend): memoize option map to preserve unsaved contact page edits feat(frontend): add response pages module with contact page tab feat(agent): ship sw offline lua assets and placeholder substitution feat(config): wire sw offline options into config snapshot feat(openresty): render sw offline assets and challenge intercept feat(openresty): add sw offline ConfigSnapshot fields and placeholder feat(db): seed sw offline options feat(option): add sw offline config keys and validation docs: add service worker offline fallback implementation plan docs: adopt global-option pattern for SW offline fallback (matches origin error page) docs: unify offline contact page with error pages as response pages docs: service worker offline fallback design (issue #23)
63 lines
1.6 KiB
TypeScript
63 lines
1.6 KiB
TypeScript
export const OPTIONS_QUERY_KEY = ['openflare', 'options'] as const;
|
|
|
|
export const KEY_SW_ENABLED = 'sw_offline_enabled';
|
|
export const KEY_SW_HTML = 'sw_offline_html';
|
|
export const KEY_SW_DOMAINS = 'sw_offline_domains';
|
|
|
|
export type ContactPageFields = {
|
|
enabled: boolean;
|
|
html: string;
|
|
domains: string[];
|
|
};
|
|
|
|
export const defaultContactPageFields: ContactPageFields = {
|
|
enabled: false,
|
|
html: '',
|
|
domains: [],
|
|
};
|
|
|
|
export function optionsToMap(options: Array<{ key: string; value: string }>) {
|
|
return options.reduce<Record<string, string>>((acc, option) => {
|
|
acc[option.key] = option.value;
|
|
return acc;
|
|
}, {});
|
|
}
|
|
|
|
function parseDomains(raw: string | undefined): string[] {
|
|
if (!raw) return [];
|
|
try {
|
|
const parsed: unknown = JSON.parse(raw);
|
|
return Array.isArray(parsed)
|
|
? parsed.filter((item): item is string => typeof item === 'string')
|
|
: [];
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
export function mapOptionsToContactFields(
|
|
optionMap: Record<string, string>,
|
|
): ContactPageFields {
|
|
return {
|
|
enabled: optionMap[KEY_SW_ENABLED] === 'true',
|
|
html: optionMap[KEY_SW_HTML] ?? '',
|
|
domains: parseDomains(optionMap[KEY_SW_DOMAINS]),
|
|
};
|
|
}
|
|
|
|
export async function invalidateResponseQueries(queryClient: {
|
|
invalidateQueries: (opts: {
|
|
queryKey: readonly unknown[];
|
|
}) => Promise<unknown>;
|
|
}) {
|
|
await Promise.all([
|
|
queryClient.invalidateQueries({ queryKey: OPTIONS_QUERY_KEY }),
|
|
queryClient.invalidateQueries({
|
|
queryKey: ['openflare', 'config-preview'],
|
|
}),
|
|
queryClient.invalidateQueries({
|
|
queryKey: ['openflare', 'config-versions'],
|
|
}),
|
|
]);
|
|
}
|