Explorar el Código

refactor: update error messages and remove external volume in docker-compose

- Removed the external volume definition from docker-compose.dev.yaml.
- Added new error messages for key management in English, Japanese, Russian, and Chinese translations to enhance user feedback regarding group permissions and key status.
ding113 hace 1 mes
padre
commit
9baa59b65f

+ 0 - 2
docker-compose.dev.yaml

@@ -39,5 +39,3 @@ services:
 
 volumes:
   db_dev_data:
-    external: true
-    name: bdfda8f623068194e970cb2f8a15c83ea2cec18de02778a970474eb5c53ed146

+ 3 - 1
messages/en/errors.json

@@ -73,5 +73,7 @@
   "CREATE_KEY_FAILED": "Failed to create key, please try again later",
   "UPDATE_KEY_FAILED": "Failed to update key, please try again later",
   "DELETE_KEY_FAILED": "Failed to delete key, please try again later",
-  "CANNOT_DISABLE_LAST_KEY": "Cannot disable the last active key. Users must have at least one enabled key"
+  "CANNOT_DISABLE_LAST_KEY": "Cannot disable the last active key. Users must have at least one enabled key",
+  "NO_DEFAULT_GROUP_PERMISSION": "No permission to use default group. You don't have a Key with default group",
+  "NO_GROUP_PERMISSION": "No permission to use the following groups: {groups}"
 }

+ 13 - 1
messages/ja/errors.json

@@ -55,5 +55,17 @@
   "CREATE_USER_FAILED": "ユーザーの作成に失敗しました。後でもう一度お試しください",
   "UPDATE_USER_FAILED": "ユーザーの更新に失敗しました。後でもう一度お試しください",
   "DELETE_USER_FAILED": "ユーザーの削除に失敗しました。後でもう一度お試しください",
-  "GET_USER_QUOTA_FAILED": "ユーザークォータ情報の取得に失敗しました"
+  "GET_USER_QUOTA_FAILED": "ユーザークォータ情報の取得に失敗しました",
+
+  "KEY_NOT_FOUND": "キーが見つかりません",
+  "CREATE_KEY_FAILED": "キーの作成に失敗しました。後でもう一度お試しください",
+  "UPDATE_KEY_FAILED": "キーの更新に失敗しました。後でもう一度お試しください",
+  "DELETE_KEY_FAILED": "キーの削除に失敗しました。後でもう一度お試しください",
+  "CANNOT_DISABLE_LAST_KEY": "最後のアクティブなキーを無効にすることはできません。ユーザーには少なくとも1つの有効なキーが必要です",
+  "EXPIRES_AT_FIELD": "有効期限",
+  "EXPIRES_AT_MUST_BE_FUTURE": "有効期限は将来の日付である必要があります",
+  "EXPIRES_AT_TOO_FAR": "有効期限は10年を超えることはできません",
+  "CANNOT_DISABLE_SELF": "自分のアカウントを無効にすることはできません",
+  "NO_DEFAULT_GROUP_PERMISSION": "defaultグループを使用する権限がありません。defaultグループのキーを持っていません",
+  "NO_GROUP_PERMISSION": "以下のグループを使用する権限がありません: {groups}"
 }

+ 13 - 1
messages/ru/errors.json

@@ -55,5 +55,17 @@
   "CREATE_USER_FAILED": "Не удалось создать пользователя, попробуйте позже",
   "UPDATE_USER_FAILED": "Не удалось обновить пользователя, попробуйте позже",
   "DELETE_USER_FAILED": "Не удалось удалить пользователя, попробуйте позже",
-  "GET_USER_QUOTA_FAILED": "Не удалось получить информацию о квоте пользователя"
+  "GET_USER_QUOTA_FAILED": "Не удалось получить информацию о квоте пользователя",
+
+  "KEY_NOT_FOUND": "Ключ не найден",
+  "CREATE_KEY_FAILED": "Не удалось создать ключ, попробуйте позже",
+  "UPDATE_KEY_FAILED": "Не удалось обновить ключ, попробуйте позже",
+  "DELETE_KEY_FAILED": "Не удалось удалить ключ, попробуйте позже",
+  "CANNOT_DISABLE_LAST_KEY": "Невозможно отключить последний активный ключ. У пользователя должен быть хотя бы один включенный ключ",
+  "EXPIRES_AT_FIELD": "Дата истечения",
+  "EXPIRES_AT_MUST_BE_FUTURE": "Дата истечения должна быть в будущем",
+  "EXPIRES_AT_TOO_FAR": "Дата истечения не может превышать 10 лет",
+  "CANNOT_DISABLE_SELF": "Невозможно отключить свой собственный аккаунт",
+  "NO_DEFAULT_GROUP_PERMISSION": "Нет разрешения на использование группы default. У вас нет ключа с группой default",
+  "NO_GROUP_PERMISSION": "Нет разрешения на использование следующих групп: {groups}"
 }

+ 3 - 1
messages/zh-CN/errors.json

@@ -72,5 +72,7 @@
   "CREATE_KEY_FAILED": "创建密钥失败,请稍后重试",
   "UPDATE_KEY_FAILED": "更新密钥失败,请稍后重试",
   "DELETE_KEY_FAILED": "删除密钥失败,请稍后重试",
-  "CANNOT_DISABLE_LAST_KEY": "无法禁用最后一个可用密钥,用户至少需要保留一个启用的密钥"
+  "CANNOT_DISABLE_LAST_KEY": "无法禁用最后一个可用密钥,用户至少需要保留一个启用的密钥",
+  "NO_DEFAULT_GROUP_PERMISSION": "无权使用 default 分组,您当前没有 default 分组的 Key",
+  "NO_GROUP_PERMISSION": "无权使用以下分组: {groups}"
 }

+ 13 - 1
messages/zh-TW/errors.json

@@ -55,5 +55,17 @@
   "CREATE_USER_FAILED": "創建使用者失敗,請稍後重試",
   "UPDATE_USER_FAILED": "更新使用者失敗,請稍後重試",
   "DELETE_USER_FAILED": "刪除使用者失敗,請稍後重試",
-  "GET_USER_QUOTA_FAILED": "獲取使用者額度使用情況失敗"
+  "GET_USER_QUOTA_FAILED": "獲取使用者額度使用情況失敗",
+
+  "KEY_NOT_FOUND": "金鑰不存在",
+  "CREATE_KEY_FAILED": "創建金鑰失敗,請稍後重試",
+  "UPDATE_KEY_FAILED": "更新金鑰失敗,請稍後重試",
+  "DELETE_KEY_FAILED": "刪除金鑰失敗,請稍後重試",
+  "CANNOT_DISABLE_LAST_KEY": "無法禁用最後一個可用金鑰,使用者至少需要保留一個啟用的金鑰",
+  "EXPIRES_AT_FIELD": "過期時間",
+  "EXPIRES_AT_MUST_BE_FUTURE": "過期時間必須是未來時間",
+  "EXPIRES_AT_TOO_FAR": "過期時間不能超過10年",
+  "CANNOT_DISABLE_SELF": "不能禁用自己的帳戶",
+  "NO_DEFAULT_GROUP_PERMISSION": "無權使用 default 分組,您當前沒有 default 分組的 Key",
+  "NO_GROUP_PERMISSION": "無權使用以下分組: {groups}"
 }

+ 8 - 25
src/actions/keys.ts

@@ -10,6 +10,7 @@ import { getSession } from "@/lib/auth";
 import { PROVIDER_GROUP } from "@/lib/constants/provider.constants";
 import { logger } from "@/lib/logger";
 import { ERROR_CODES } from "@/lib/utils/error-messages";
+import { normalizeProviderGroup, parseProviderGroups } from "@/lib/utils/provider-group";
 import { KeyFormSchema } from "@/lib/validation/schemas";
 import type { KeyStatistics } from "@/repository/key";
 import {
@@ -26,32 +27,13 @@ import type { Key } from "@/types/key";
 import type { ActionResult } from "./types";
 import { type BatchUpdateResult, syncUserProviderGroupFromKeys } from "./users";
 
-function normalizeProviderGroup(value: unknown): string {
-  if (value === null || value === undefined) return PROVIDER_GROUP.DEFAULT;
-  if (typeof value !== "string") return PROVIDER_GROUP.DEFAULT;
-  const trimmed = value.trim();
-  if (trimmed === "") return PROVIDER_GROUP.DEFAULT;
-
-  const groups = trimmed
-    .split(",")
-    .map((g) => g.trim())
-    .filter(Boolean);
-  if (groups.length === 0) return PROVIDER_GROUP.DEFAULT;
-
-  return Array.from(new Set(groups)).sort().join(",");
-}
-
-function parseProviderGroups(value: string): string[] {
-  return value
-    .split(",")
-    .map((g) => g.trim())
-    .filter(Boolean);
-}
+type TranslationFunction = (key: string, values?: Record<string, string>) => string;
 
 function validateNonAdminProviderGroup(
   userProviderGroup: string,
   requestedProviderGroup: string,
-  options: { hasDefaultKey: boolean }
+  options: { hasDefaultKey: boolean },
+  tError: TranslationFunction
 ): string {
   const userGroups = parseProviderGroups(userProviderGroup);
   const requestedGroups = parseProviderGroups(requestedProviderGroup);
@@ -63,12 +45,12 @@ function validateNonAdminProviderGroup(
   const userGroupSet = new Set(userGroups);
 
   if (requestedGroups.includes(PROVIDER_GROUP.DEFAULT) && !options.hasDefaultKey) {
-    throw new Error("无权使用 default 分组,您当前没有 default 分组的 Key");
+    throw new Error(tError("NO_DEFAULT_GROUP_PERMISSION"));
   }
 
   const invalidGroups = requestedGroups.filter((g) => !userGroupSet.has(g));
   if (invalidGroups.length > 0) {
-    throw new Error(`无权使用以下分组: ${invalidGroups.join(", ")}`);
+    throw new Error(tError("NO_GROUP_PERMISSION", { groups: invalidGroups.join(", ") }));
   }
 
   return requestedProviderGroup;
@@ -168,7 +150,8 @@ export async function addKey(data: {
         requestedProviderGroup,
         {
           hasDefaultKey,
-        }
+        },
+        tError
       );
     }
 

+ 1 - 15
src/actions/users.ts

@@ -12,6 +12,7 @@ import { USER_DEFAULTS } from "@/lib/constants/user.constants";
 import { logger } from "@/lib/logger";
 import { getUnauthorizedFields } from "@/lib/permissions/user-field-permissions";
 import { ERROR_CODES } from "@/lib/utils/error-messages";
+import { normalizeProviderGroup } from "@/lib/utils/provider-group";
 import { maskKey } from "@/lib/utils/validation";
 import { formatZodError } from "@/lib/utils/zod-i18n";
 import { CreateUserSchema, UpdateUserSchema } from "@/lib/validation/schemas";
@@ -75,21 +76,6 @@ class BatchUpdateError extends Error {
   }
 }
 
-function normalizeProviderGroup(value: unknown): string {
-  if (value === null || value === undefined) return PROVIDER_GROUP.DEFAULT;
-  if (typeof value !== "string") return PROVIDER_GROUP.DEFAULT;
-  const trimmed = value.trim();
-  if (trimmed === "") return PROVIDER_GROUP.DEFAULT;
-
-  const groups = trimmed
-    .split(",")
-    .map((g) => g.trim())
-    .filter(Boolean);
-  if (groups.length === 0) return PROVIDER_GROUP.DEFAULT;
-
-  return Array.from(new Set(groups)).sort().join(",");
-}
-
 /**
  * 验证过期时间的公共函数
  * @param expiresAt - 过期时间

+ 32 - 0
src/lib/utils/provider-group.ts

@@ -0,0 +1,32 @@
+import { PROVIDER_GROUP } from "@/lib/constants/provider.constants";
+
+/**
+ * Normalize provider group value to a consistent format
+ * - Returns "default" for null/undefined/empty values
+ * - Trims whitespace and removes duplicates
+ * - Sorts groups alphabetically for consistency
+ */
+export function normalizeProviderGroup(value: unknown): string {
+  if (value === null || value === undefined) return PROVIDER_GROUP.DEFAULT;
+  if (typeof value !== "string") return PROVIDER_GROUP.DEFAULT;
+  const trimmed = value.trim();
+  if (trimmed === "") return PROVIDER_GROUP.DEFAULT;
+
+  const groups = trimmed
+    .split(",")
+    .map((g) => g.trim())
+    .filter(Boolean);
+  if (groups.length === 0) return PROVIDER_GROUP.DEFAULT;
+
+  return Array.from(new Set(groups)).sort().join(",");
+}
+
+/**
+ * Parse a comma-separated provider group string into an array
+ */
+export function parseProviderGroups(value: string): string[] {
+  return value
+    .split(",")
+    .map((g) => g.trim())
+    .filter(Boolean);
+}