Update Healer Man server

This commit is contained in:
phenom committed 2026-09-19 21:01:51 -04:00
1 parent f8314e7de1
commit 1fedb9368d
76 files changed
+20556 -2523

No files matched your search

+96
View File
@@ -0,0 +1,96 @@
import { create } from "zustand";
import type { PlayerSession } from "../app/types";
import { requestOnlineJson } from "../app/onlineAccountClient";
import {
DEFAULT_COMPANION_COOLDOWN_POLICY,
installCompanionCooldownPolicy,
normalizeCompanionCooldownPolicy,
type CompanionCooldownPolicy,
} from "./companionCooldownPolicy";
interface PublishedCompanionCooldownPolicy {
readonly revision: number;
readonly updatedAt: number | null;
readonly updatedBy?: string | null;
readonly config: CompanionCooldownPolicy;
}
interface CompanionCooldownPolicyState extends PublishedCompanionCooldownPolicy {
readonly draft: CompanionCooldownPolicy;
readonly loading: boolean;
readonly saving: boolean;
readonly dirty: boolean;
readonly error: string | null;
refresh: (session?: PlayerSession | null) => Promise<void>;
replaceDraft: (draft: CompanionCooldownPolicy) => void;
resetDraft: () => void;
publish: (session: PlayerSession | null) => Promise<boolean>;
}
const CACHE_KEY = "healer-man.companion-cooldown-policy.v1";
function cached(): PublishedCompanionCooldownPolicy {
try {
const value = JSON.parse(window.localStorage.getItem(CACHE_KEY) ?? "null") as Partial<PublishedCompanionCooldownPolicy> | null;
if (!value || !Number.isInteger(value.revision)) throw new Error("missing");
return { revision: value.revision!, updatedAt: value.updatedAt ?? null, config: normalizeCompanionCooldownPolicy(value.config) };
} catch {
return { revision: 0, updatedAt: null, config: DEFAULT_COMPANION_COOLDOWN_POLICY };
}
}
function cache(payload: PublishedCompanionCooldownPolicy): void {
try { window.localStorage.setItem(CACHE_KEY, JSON.stringify(payload)); } catch { /* optional cache */ }
}
const initial = cached();
installCompanionCooldownPolicy(initial.config);
export const useCompanionCooldownPolicyStore = create<CompanionCooldownPolicyState>((set, get) => ({
...initial,
draft: initial.config,
loading: false,
saving: false,
dirty: false,
error: null,
refresh: async (session = null) => {
set({ loading: true, error: null });
try {
const gm = Boolean(session?.accessToken && session.roles?.includes("gm"));
const payload = await requestOnlineJson<PublishedCompanionCooldownPolicy>(
gm ? "/api/gm/companion-cooldown-policy" : "/api/companion-cooldown-policy",
{},
gm ? session?.accessToken : undefined,
);
const normalized = { ...payload, config: normalizeCompanionCooldownPolicy(payload.config) };
cache(normalized);
installCompanionCooldownPolicy(normalized.config);
set({ ...normalized, draft: normalized.config, loading: false, dirty: false });
} catch (error) {
set({ loading: false, error: error instanceof Error ? error.message : "Could not load companion cooldown policy." });
}
},
replaceDraft: (draft) => set({ draft: normalizeCompanionCooldownPolicy(draft), dirty: true, error: null }),
resetDraft: () => set((state) => ({ draft: state.config, dirty: false, error: null })),
publish: async (session) => {
if (!session?.accessToken || !session.roles?.includes("gm")) {
set({ error: "Game Master access is required." });
return false;
}
set({ saving: true, error: null });
try {
const payload = await requestOnlineJson<PublishedCompanionCooldownPolicy>("/api/gm/companion-cooldown-policy", {
method: "PUT",
body: JSON.stringify({ expectedRevision: get().revision, config: get().draft }),
}, session.accessToken);
const normalized = { ...payload, config: normalizeCompanionCooldownPolicy(payload.config) };
cache(normalized);
installCompanionCooldownPolicy(normalized.config);
set({ ...normalized, draft: normalized.config, saving: false, dirty: false });
return true;
} catch (error) {
set({ saving: false, error: error instanceof Error ? error.message : "Could not publish companion cooldown policy." });
return false;
}
},
}));