mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-16 22:59:04 +02:00
The two prior MUL-3254 fixes preserved draft/description state across a modal close, but Desktop still could not RENDER the reopened image: in CloudFront signed-URL mode every URL the renderer holds after reopen is unloadable. The persisted record strips the expired signed download_url, the raw CDN url is unsigned (403 on a signed distribution), and the durable /api/attachments/<id>/download endpoint needs credentials that a cross-site file:// <img> fetch cannot carry (web works via the same-site session cookie, which is why the bug was desktop-only). Two changes close the last mile: - /api/config now reports cdn_signed when CloudFront signing is enabled, and pickInlineMediaURL stops picking the raw (unsigned) CDN url in that mode — it is a guaranteed 403. - The Attachment renderer upgrades an auth-gated media URL to a freshly signed one via authenticated GET /api/attachments/<id> (the same re-sign the click-time download path already does), but only on clients without a same-origin /api proxy (api.getBaseUrl() non-empty: Desktop, mobile webview). Cached via TanStack Query with a 20-minute staleTime, inside the server's 30-minute signed-URL TTL. Old servers omit cdn_signed; the schema defaults it to false so behavior is unchanged there. Non-CloudFront deployments return the API path again from the metadata fetch and the renderer keeps the original URL. Co-authored-by: J <j@multica.ai> Co-authored-by: multica-agent <github@multica.ai>
50 lines
1.8 KiB
TypeScript
50 lines
1.8 KiB
TypeScript
import { createStore } from "zustand/vanilla";
|
|
import { useStore } from "zustand";
|
|
|
|
interface ConfigState {
|
|
cdnDomain: string;
|
|
// True when cdnDomain serves private content via time-bounded signed URLs
|
|
// (CloudFront signing enabled server-side). Renderers must not treat a raw
|
|
// storage URL on that domain as a loadable media source (MUL-3254).
|
|
cdnSigned: boolean;
|
|
allowSignup: boolean;
|
|
googleClientId: string;
|
|
daemonServerUrl: string;
|
|
daemonAppUrl: string;
|
|
// Self-host gate (#3433): when true, every "Create workspace" affordance
|
|
// must be hidden. Defaults to false so unknown / older servers behave like
|
|
// the managed-cloud case.
|
|
workspaceCreationDisabled: boolean;
|
|
setCdnConfig: (config: { cdnDomain: string; cdnSigned?: boolean }) => void;
|
|
setAuthConfig: (config: {
|
|
allowSignup: boolean;
|
|
googleClientId?: string;
|
|
workspaceCreationDisabled?: boolean;
|
|
}) => void;
|
|
setDaemonConfig: (config: {
|
|
daemonServerUrl?: string;
|
|
daemonAppUrl?: string;
|
|
}) => void;
|
|
}
|
|
|
|
export const configStore = createStore<ConfigState>((set) => ({
|
|
cdnDomain: "",
|
|
cdnSigned: false,
|
|
allowSignup: true,
|
|
googleClientId: "",
|
|
daemonServerUrl: "",
|
|
daemonAppUrl: "",
|
|
workspaceCreationDisabled: false,
|
|
setCdnConfig: ({ cdnDomain, cdnSigned = false }) => set({ cdnDomain, cdnSigned }),
|
|
setAuthConfig: ({ allowSignup, googleClientId = "", workspaceCreationDisabled = false }) =>
|
|
set({ allowSignup, googleClientId, workspaceCreationDisabled }),
|
|
setDaemonConfig: ({ daemonServerUrl = "", daemonAppUrl = "" }) =>
|
|
set({ daemonServerUrl, daemonAppUrl }),
|
|
}));
|
|
|
|
export function useConfigStore(): ConfigState;
|
|
export function useConfigStore<T>(selector: (state: ConfigState) => T): T;
|
|
export function useConfigStore<T>(selector?: (state: ConfigState) => T) {
|
|
return useStore(configStore, selector as (state: ConfigState) => T);
|
|
}
|