From 8a7e00886d19f2a2c4ff40960cd6ce2ece54f75b Mon Sep 17 00:00:00 2001 From: Steven Date: Thu, 11 Dec 2025 20:08:11 +0800 Subject: [PATCH] fix(web): convert enum values to string names in API resource paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Frontend was incorrectly using numeric enum values (e.g., 1, 2, 3) instead of string names (e.g., "GENERAL", "STORAGE") when constructing API resource paths. This caused the backend to fail with "unsupported instance setting key: INSTANCE_SETTING_KEY_UNSPECIFIED" errors during initialization. Changes: - Add helper functions in store/common.ts to convert enum values to names - getInstanceSettingKeyName() and buildInstanceSettingName() - getUserSettingKeyName() and buildUserSettingName() - Update instance store to use string enum names in API calls - Update user store to use string enum names in API calls - Update all components to use new helper functions for setting names Fixes enum string conversion for: - InstanceSetting_Key (6 locations) - UserSetting_Key (2 locations) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .../components/Settings/InstanceSection.tsx | 4 +-- .../Settings/MemoRelatedSettings.tsx | 4 +-- .../components/Settings/StorageSection.tsx | 4 +-- .../UpdateCustomizedProfileDialog.tsx | 4 +-- web/src/store/common.ts | 25 +++++++++++++++++++ web/src/store/instance.ts | 10 ++++---- web/src/store/user.ts | 5 ++-- 7 files changed, 41 insertions(+), 15 deletions(-) diff --git a/web/src/components/Settings/InstanceSection.tsx b/web/src/components/Settings/InstanceSection.tsx index c43abcb4c..a81b5656e 100644 --- a/web/src/components/Settings/InstanceSection.tsx +++ b/web/src/components/Settings/InstanceSection.tsx @@ -10,7 +10,7 @@ import { Textarea } from "@/components/ui/textarea"; import { identityProviderServiceClient } from "@/grpcweb"; import useDialog from "@/hooks/useDialog"; import { instanceStore } from "@/store"; -import { instanceSettingNamePrefix } from "@/store/common"; +import { buildInstanceSettingName } from "@/store/common"; import { IdentityProvider } from "@/types/proto/api/v1/idp_service_pb"; import { InstanceSetting_GeneralSetting, @@ -63,7 +63,7 @@ const InstanceSection = observer(() => { try { await instanceStore.upsertInstanceSetting( create(InstanceSettingSchema, { - name: `${instanceSettingNamePrefix}${InstanceSetting_Key.GENERAL}`, + name: buildInstanceSettingName(InstanceSetting_Key.GENERAL), value: { case: "generalSetting", value: instanceGeneralSetting, diff --git a/web/src/components/Settings/MemoRelatedSettings.tsx b/web/src/components/Settings/MemoRelatedSettings.tsx index ebf93d385..58203f772 100644 --- a/web/src/components/Settings/MemoRelatedSettings.tsx +++ b/web/src/components/Settings/MemoRelatedSettings.tsx @@ -9,7 +9,7 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Switch } from "@/components/ui/switch"; import { instanceStore } from "@/store"; -import { instanceSettingNamePrefix } from "@/store/common"; +import { buildInstanceSettingName } from "@/store/common"; import { InstanceSetting_Key, InstanceSetting_MemoRelatedSetting, @@ -63,7 +63,7 @@ const MemoRelatedSettings = observer(() => { try { await instanceStore.upsertInstanceSetting( create(InstanceSettingSchema, { - name: `${instanceSettingNamePrefix}${InstanceSetting_Key.MEMO_RELATED}`, + name: buildInstanceSettingName(InstanceSetting_Key.MEMO_RELATED), value: { case: "memoRelatedSetting", value: memoRelatedSetting, diff --git a/web/src/components/Settings/StorageSection.tsx b/web/src/components/Settings/StorageSection.tsx index c6f2d01cb..9b7c86fa2 100644 --- a/web/src/components/Settings/StorageSection.tsx +++ b/web/src/components/Settings/StorageSection.tsx @@ -9,7 +9,7 @@ import { Label } from "@/components/ui/label"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { Switch } from "@/components/ui/switch"; import { instanceStore } from "@/store"; -import { instanceSettingNamePrefix } from "@/store/common"; +import { buildInstanceSettingName } from "@/store/common"; import { InstanceSetting_Key, InstanceSetting_StorageSetting, @@ -154,7 +154,7 @@ const StorageSection = observer(() => { const saveInstanceStorageSetting = async () => { await instanceStore.upsertInstanceSetting( create(InstanceSettingSchema, { - name: `${instanceSettingNamePrefix}${InstanceSetting_Key.STORAGE}`, + name: buildInstanceSettingName(InstanceSetting_Key.STORAGE), value: { case: "storageSetting", value: instanceStorageSetting, diff --git a/web/src/components/UpdateCustomizedProfileDialog.tsx b/web/src/components/UpdateCustomizedProfileDialog.tsx index 1c1f5c4db..39b185f56 100644 --- a/web/src/components/UpdateCustomizedProfileDialog.tsx +++ b/web/src/components/UpdateCustomizedProfileDialog.tsx @@ -7,7 +7,7 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { instanceStore } from "@/store"; -import { instanceSettingNamePrefix } from "@/store/common"; +import { buildInstanceSettingName } from "@/store/common"; import { InstanceSetting_GeneralSetting_CustomProfile, InstanceSetting_GeneralSetting_CustomProfileSchema, @@ -78,7 +78,7 @@ function UpdateCustomizedProfileDialog({ open, onOpenChange, onSuccess }: Props) try { await instanceStore.upsertInstanceSetting( create(InstanceSettingSchema, { - name: `${instanceSettingNamePrefix}${InstanceSetting_Key.GENERAL}`, + name: buildInstanceSettingName(InstanceSetting_Key.GENERAL), value: { case: "generalSetting", value: { diff --git a/web/src/store/common.ts b/web/src/store/common.ts index d82ba3b39..d237f8464 100644 --- a/web/src/store/common.ts +++ b/web/src/store/common.ts @@ -1,3 +1,6 @@ +import { InstanceSetting_Key } from "@/types/proto/api/v1/instance_service_pb"; +import { UserSetting_Key } from "@/types/proto/api/v1/user_service_pb"; + export const instanceSettingNamePrefix = "instance/settings/"; export const userNamePrefix = "users/"; export const memoNamePrefix = "memos/"; @@ -15,3 +18,25 @@ export const extractMemoIdFromName = (name: string) => { export const extractIdentityProviderIdFromName = (name: string) => { return parseInt(name.split(identityProviderNamePrefix).pop() || "", 10); }; + +// Helper function to convert InstanceSetting_Key enum value to string name +export const getInstanceSettingKeyName = (key: InstanceSetting_Key): string => { + // TypeScript enum reverse mapping: converts numeric value to string name + return InstanceSetting_Key[key]; +}; + +// Helper function to build instance setting name from key +export const buildInstanceSettingName = (key: InstanceSetting_Key): string => { + return `${instanceSettingNamePrefix}${getInstanceSettingKeyName(key)}`; +}; + +// Helper function to convert UserSetting_Key enum value to string name +export const getUserSettingKeyName = (key: UserSetting_Key): string => { + // TypeScript enum reverse mapping: converts numeric value to string name + return UserSetting_Key[key]; +}; + +// Helper function to build user setting name from username and key +export const buildUserSettingName = (username: string, key: UserSetting_Key): string => { + return `${username}/settings/${getUserSettingKeyName(key)}`; +}; diff --git a/web/src/store/instance.ts b/web/src/store/instance.ts index b62202d78..6ade47d03 100644 --- a/web/src/store/instance.ts +++ b/web/src/store/instance.ts @@ -15,7 +15,7 @@ import { InstanceSettingSchema, } from "@/types/proto/api/v1/instance_service_pb"; import { createServerStore, StandardState } from "./base-store"; -import { instanceSettingNamePrefix } from "./common"; +import { buildInstanceSettingName, getInstanceSettingKeyName, instanceSettingNamePrefix } from "./common"; import { createRequestKey } from "./store-utils"; class InstanceState extends StandardState { @@ -25,7 +25,7 @@ class InstanceState extends StandardState { // Computed property for general settings (memoized) get generalSetting(): InstanceSetting_GeneralSetting { return computed(() => { - const setting = this.settings.find((s) => s.name === `${instanceSettingNamePrefix}${InstanceSetting_Key.GENERAL}`); + const setting = this.settings.find((s) => s.name === `${instanceSettingNamePrefix}GENERAL`); if (setting?.value.case === "generalSetting") { return setting.value.value; } @@ -36,7 +36,7 @@ class InstanceState extends StandardState { // Computed property for memo-related settings (memoized) get memoRelatedSetting(): InstanceSetting_MemoRelatedSetting { return computed(() => { - const setting = this.settings.find((s) => s.name === `${instanceSettingNamePrefix}${InstanceSetting_Key.MEMO_RELATED}`); + const setting = this.settings.find((s) => s.name === `${instanceSettingNamePrefix}MEMO_RELATED`); if (setting?.value.case === "memoRelatedSetting") { return setting.value.value; } @@ -60,7 +60,7 @@ const instanceStore = (() => { requestKey, async () => { const setting = await instanceServiceClient.getInstanceSetting({ - name: `${instanceSettingNamePrefix}${settingKey}`, + name: buildInstanceSettingName(settingKey), }); // Merge into settings array, avoiding duplicates @@ -88,7 +88,7 @@ const instanceStore = (() => { }; const getInstanceSettingByKey = (settingKey: InstanceSetting_Key): InstanceSetting => { - const setting = state.settings.find((s) => s.name === `${instanceSettingNamePrefix}${settingKey}`); + const setting = state.settings.find((s) => s.name === buildInstanceSettingName(settingKey)); return setting || create(InstanceSettingSchema, {}); }; diff --git a/web/src/store/user.ts b/web/src/store/user.ts index 94cb47e23..bbd77aa1a 100644 --- a/web/src/store/user.ts +++ b/web/src/store/user.ts @@ -16,6 +16,7 @@ import { UserSettingSchema, UserStats, } from "@/types/proto/api/v1/user_service_pb"; +import { buildUserSettingName } from "./common"; import { createRequestKey, RequestDeduplicator, StoreError } from "./store-utils"; // Helper to extract setting value from UserSetting oneof @@ -164,7 +165,7 @@ const userStore = (() => { throw new Error("No current user"); } - const settingName = `${state.currentUser}/settings/${UserSetting_Key.GENERAL}`; + const settingName = buildUserSettingName(state.currentUser, UserSetting_Key.GENERAL); const userSetting = create(UserSettingSchema, { name: settingName, value: { @@ -188,7 +189,7 @@ const userStore = (() => { throw new Error("No current user"); } - const settingName = `${state.currentUser}/settings/${UserSetting_Key.GENERAL}`; + const settingName = buildUserSettingName(state.currentUser, UserSetting_Key.GENERAL); const userSetting = await userServiceClient.getUserSetting({ name: settingName }); const generalSetting = getSettingValue(userSetting, "generalSetting");