Minor: clean up config handling.

This commit is contained in:
Sebastian Jeltsch
2026-02-06 11:58:10 +01:00
parent ce3e8883a6
commit 8528523eab
8 changed files with 107 additions and 80 deletions
@@ -1,4 +1,4 @@
import { splitProps, Show } from "solid-js";
import { splitProps, Match, Switch } from "solid-js";
import type { ValidComponent } from "solid-js";
import {
Tooltip,
@@ -23,14 +23,20 @@ export function IconButton<T extends ValidComponent = "button">(
const B = () => <Button variant="ghost" size="icon" {...others} />;
return (
<Show when={local.tooltip} fallback={<B />}>
<Tooltip>
<TooltipTrigger as="div">
<B />
</TooltipTrigger>
<Switch>
<Match when={local.tooltip}>
<Tooltip>
<TooltipTrigger as="div">
<B />
</TooltipTrigger>
<TooltipContent>{props.tooltip}</TooltipContent>
</Tooltip>
</Show>
<TooltipContent>{props.tooltip}</TooltipContent>
</Tooltip>
</Match>
<Match when={true}>
<B />
</Match>
</Switch>
);
}
@@ -295,7 +295,11 @@ function createAuthSettingsForm(opts: {
newConfig.auth = proxyToConfig(value);
console.debug("Submitting provider config:", value);
await setConfig(queryClient, newConfig);
await setConfig({
client: queryClient,
config: newConfig,
throw: true,
});
opts.postSubmit();
},
@@ -56,7 +56,11 @@ function DatabaseSettingsForm(props: {
const linkDb = async (name: string) => {
const newConfig = Config.fromPartial(props.config);
newConfig.databases = [...newConfig.databases, { name }];
await setConfig(queryClient, newConfig, { throw: false });
await setConfig({
client: queryClient,
config: newConfig,
throw: false,
});
};
const unlinkSelectedDbs = async () => {
@@ -67,7 +71,11 @@ function DatabaseSettingsForm(props: {
(d) => !markedForUnlink.has(d.name ?? ""),
);
await setConfig(queryClient, newConfig, { throw: false });
await setConfig({
client: queryClient,
config: newConfig,
throw: false,
});
};
const dbTable = createMemo(() => {
@@ -141,7 +141,11 @@ export function EmailSettings(props: {
const newConfig = Config.fromPartial(c);
newConfig.email = value;
await setConfig(queryClient, newConfig);
await setConfig({
client: queryClient,
config: newConfig,
throw: true,
});
props.postSubmit();
},
@@ -139,7 +139,12 @@ function JobSettingsImpl(props: {
jobs,
} satisfies Config;
await setConfig(queryClient, newConfig);
await setConfig({
client: queryClient,
config: newConfig,
throw: true,
});
props.refetchJobs();
props.postSubmit();
},
@@ -65,7 +65,7 @@ import { Version } from "@/components/Version";
import {
createConfigQuery,
setConfig,
invalidateAllAdminQueries,
invalidateConfig,
} from "@/lib/api/config";
import { createSystemInfoQuery } from "@/lib/api/info";
import { createIsMobile } from "@/lib/signals";
@@ -155,7 +155,11 @@ function ServerSettingsForm(
onSubmit: async ({ value }: { value: ServerConfig }) => {
const newConfig = Config.fromPartial(props.config);
newConfig.server = value;
await setConfig(queryClient, newConfig);
await setConfig({
client: queryClient,
config: newConfig,
throw: true,
});
props.postSubmit?.();
},
@@ -512,7 +516,7 @@ export function SettingsPage() {
titleSelect={activeSite().label}
leading={<SidebarTrigger />}
left={
<IconButton onClick={() => invalidateAllAdminQueries(queryClient)}>
<IconButton onClick={() => invalidateConfig(queryClient)}>
<TbRefresh />
</IconButton>
}
@@ -726,8 +726,11 @@ function IndividualRecordApiSettingsForm(props: {
const isCreate = props.mode === Mode.Create;
try {
const newConfig = updateRecordApiConfig(c, value);
await setConfig(queryClient, newConfig);
await setConfig({
client: queryClient,
config: updateRecordApiConfig(c, value),
throw: true,
});
showToast({
title: "Success",
@@ -1294,9 +1297,12 @@ function DeleteUpdateButtons(props: {
if (apiName !== undefined) {
try {
const newConfig = removeRecordApiConfig(c, tableName.name, apiName);
await setConfig({
client: queryClient,
config: removeRecordApiConfig(c, tableName.name, apiName),
throw: true,
});
await setConfig(queryClient, newConfig);
showToast({
title: "Success",
description: `API "${apiName}" deleted`,
+49 -59
View File
@@ -5,48 +5,15 @@ import { GetConfigResponse, UpdateConfigRequest } from "@proto/config_api";
import { adminFetch } from "@/lib/fetch";
import { showToast } from "@/components/ui/toast";
type UpdateOptions = {
throw?: boolean;
};
export async function setConfig(
queryClient: QueryClient,
config: Config,
opts?: UpdateOptions,
): Promise<void> {
const data = queryClient.getQueryData<GetConfigResponse>(key);
const hash = data?.hash;
if (!hash) {
console.error("Missing hash from:", data);
return;
export function createConfigQuery() {
async function getConfig(): Promise<GetConfigResponse> {
const response = await adminFetch("/config");
const array = new Uint8Array(await (await response.blob()).arrayBuffer());
return GetConfigResponse.decode(array);
}
const request: UpdateConfigRequest = {
config,
hash,
};
console.debug("Updating config:", request);
await updateConfig(request, opts);
// Trigger refetch after updating config.
invalidateConfig(queryClient);
}
export function invalidateAllAdminQueries(queryClient: QueryClient) {
queryClient.invalidateQueries({
queryKey: ["admin"],
});
}
export function invalidateConfig(queryClient: QueryClient) {
queryClient.invalidateQueries({
queryKey: key,
});
}
export function createConfigQuery() {
return useQuery(() => ({
queryKey: key,
queryKey: _configKey,
queryFn: async () => {
const config = await getConfig();
console.debug("Fetched config:", config);
@@ -57,17 +24,12 @@ export function createConfigQuery() {
}));
}
async function getConfig(): Promise<GetConfigResponse> {
const response = await adminFetch("/config");
const array = new Uint8Array(await (await response.blob()).arrayBuffer());
return GetConfigResponse.decode(array);
}
async function updateConfig(
request: UpdateConfigRequest,
opts?: UpdateOptions,
): Promise<void> {
try {
export async function setConfig(opts: {
client: QueryClient;
config: Config;
throw: boolean;
}): Promise<void> {
async function updateConfig(request: UpdateConfigRequest) {
await adminFetch("/config", {
method: "POST",
headers: {
@@ -76,17 +38,45 @@ async function updateConfig(
body: new Uint8Array(UpdateConfigRequest.encode(request).finish()),
throwOnError: true,
});
} catch (err) {
showToast({
title: "Config Error",
description: `${err}`,
variant: "error",
});
}
if (!(opts?.throw ?? true)) {
throw err;
// Get previous fetch.
const hash = opts.client.getQueryData<GetConfigResponse>(_configKey)?.hash;
if (!hash) {
console.error("Missing hash");
return;
}
const request: UpdateConfigRequest = {
config: opts.config,
hash,
};
console.debug("Updating config:", request);
if (opts.throw) {
await updateConfig(request);
} else {
try {
await updateConfig(request);
} catch (err) {
showToast({
title: "Config update failed",
description: `${err}`,
variant: "error",
});
return;
}
}
// Trigger re-fetch after updating config.
invalidateConfig(opts.client);
}
const key = ["admin", "proto_config"];
export function invalidateConfig(queryClient: QueryClient) {
queryClient.invalidateQueries({
queryKey: _configKey,
});
}
const _configKey = ["admin", "proto_config"];