diff --git a/public/locales/de.json b/public/locales/de.json index 7314b677..07cf45bd 100644 --- a/public/locales/de.json +++ b/public/locales/de.json @@ -139,12 +139,41 @@ "no_key_message": "Sie haben noch keinen API-Schlüssel generiert.", "generate_button": "API-Schlüssel generieren", "notifications": { - "fetch_error": "API-Schlüssel konnte nicht abgerufen werden - ${error}", - "generate_success": "API-Schlüssel erfolgreich generiert", - "generate_error": "API-Schlüssel konnte nicht generiert werden - ${error}", - "delete_success": "API-Schlüssel erfolgreich gelöscht", - "delete_error": "API-Schlüssel konnte nicht gelöscht werden - ${error}", - "copy_success": "API-Schlüssel erfolgreich kopiert" + "errors": { + "fetch": { + "network": "Netzwerkfehler beim Abrufen des API-Schlüssels: ${error}", + "unauthorized": "Sie müssen angemeldet sein, um auf den API-Schlüssel zuzugreifen", + "not_found": "API-Schlüssel für Ihr Konto wurde nicht gefunden", + "server": "Serverfehler beim Abrufen des API-Schlüssels. Bitte versuchen Sie es später erneut", + "unknown": "Unbekannter Fehler beim Abrufen des API-Schlüssels: ${error}" + }, + "generate": { + "network": "Netzwerkfehler bei der Generierung des API-Schlüssels: ${error}", + "unauthorized": "Sie müssen angemeldet sein, um einen API-Schlüssel zu generieren", + "key_exists": "Sie haben bereits einen API-Schlüssel. Bitte löschen Sie zuerst den vorhandenen", + "not_found": "Benutzerkonto nicht gefunden", + "server": "Serverfehler bei der Generierung des API-Schlüssels. Bitte versuchen Sie es später erneut", + "unknown": "Unbekannter Fehler bei der Generierung des API-Schlüssels: ${error}" + }, + "delete": { + "network": "Netzwerkfehler beim Löschen des API-Schlüssels: ${error}", + "unauthorized": "Sie müssen angemeldet sein, um den API-Schlüssel zu löschen", + "not_found": "Benutzerkonto nicht gefunden", + "key_not_found": "Kein API-Schlüssel zum Löschen gefunden", + "server": "Serverfehler beim Löschen des API-Schlüssels. Bitte versuchen Sie es später erneut", + "unknown": "Unbekannter Fehler beim Löschen des API-Schlüssels: ${error}" + }, + "copy": { + "failed": "Fehler beim Kopieren des API-Schlüssels in die Zwischenablage", + "no_key": "Kein API-Schlüssel zum Kopieren verfügbar" + } + }, + "success": { + "fetch": "API-Schlüssel erfolgreich abgerufen", + "generate": "Neuer API-Schlüssel erfolgreich generiert", + "delete": "API-Schlüssel erfolgreich gelöscht", + "copy": "API-Schlüssel in die Zwischenablage kopiert" + } } }, "action_description": { diff --git a/public/locales/en.json b/public/locales/en.json index 703ec9c0..e847d832 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -140,12 +140,41 @@ "no_key_message": "You haven't generated an API key yet.", "generate_button": "Generate API Key", "notifications": { - "fetch_error": "Failed to fetch API Key - ${error}", - "generate_success": "Generated API Key successfully", - "generate_error": "Failed to generate API Key - ${error}", - "delete_success": "API Key deleted successfully", - "delete_error": "Failed to delete API Key - ${error}", - "copy_success": "Copied API Key successfully" + "errors": { + "fetch": { + "network": "Network error while fetching API key: ${error}", + "unauthorized": "You must be logged in to access API key", + "not_found": "Unable to find API key for your account", + "server": "Server error while fetching API key. Please try again later", + "unknown": "Unknown error occurred while fetching API key: ${error}" + }, + "generate": { + "network": "Network error while generating API key: ${error}", + "unauthorized": "You must be logged in to generate an API key", + "key_exists": "You already have an API key. Please delete the existing one first", + "not_found": "User account not found", + "server": "Server error while generating API key. Please try again later", + "unknown": "Unknown error occurred while generating API key: ${error}" + }, + "delete": { + "network": "Network error while deleting API key: ${error}", + "unauthorized": "You must be logged in to delete API key", + "not_found": "User account not found", + "key_not_found": "No API key found to delete", + "server": "Server error while deleting API key. Please try again later", + "unknown": "Unknown error occurred while deleting API key: ${error}" + }, + "copy": { + "failed": "Failed to copy API key to clipboard", + "no_key": "No API key available to copy" + } + }, + "success": { + "fetch": "API key retrieved successfully", + "generate": "New API key generated successfully", + "delete": "API key deleted successfully", + "copy": "API key copied to clipboard" + } } }, "action_description": { diff --git a/public/locales/es.json b/public/locales/es.json index ca4edc5d..895ab9bd 100644 --- a/public/locales/es.json +++ b/public/locales/es.json @@ -140,12 +140,41 @@ "no_key_message": "Aún no has generado una clave API.", "generate_button": "Generar Clave API", "notifications": { - "fetch_error": "Error al obtener la clave API - ${error}", - "generate_success": "Clave API generada con éxito", - "generate_error": "Error al generar la clave API - ${error}", - "delete_success": "Clave API eliminada con éxito", - "delete_error": "Error al eliminar la clave API - ${error}", - "copy_success": "Clave API copiada con éxito" + "errors": { + "fetch": { + "network": "Error de red al obtener la clave API: ${error}", + "unauthorized": "Debes iniciar sesión para acceder a la clave API", + "not_found": "No se pudo encontrar la clave API para tu cuenta", + "server": "Error del servidor al obtener la clave API. Por favor, inténtalo más tarde", + "unknown": "Error desconocido al obtener la clave API: ${error}" + }, + "generate": { + "network": "Error de red al generar la clave API: ${error}", + "unauthorized": "Debes iniciar sesión para generar una clave API", + "key_exists": "Ya tienes una clave API. Por favor, elimina la existente primero", + "not_found": "Cuenta de usuario no encontrada", + "server": "Error del servidor al generar la clave API. Por favor, inténtalo más tarde", + "unknown": "Error desconocido al generar la clave API: ${error}" + }, + "delete": { + "network": "Error de red al eliminar la clave API: ${error}", + "unauthorized": "Debes iniciar sesión para eliminar la clave API", + "not_found": "Cuenta de usuario no encontrada", + "key_not_found": "No se encontró ninguna clave API para eliminar", + "server": "Error del servidor al eliminar la clave API. Por favor, inténtalo más tarde", + "unknown": "Error desconocido al eliminar la clave API: ${error}" + }, + "copy": { + "failed": "Error al copiar la clave API al portapapeles", + "no_key": "No hay clave API disponible para copiar" + } + }, + "success": { + "fetch": "Clave API obtenida exitosamente", + "generate": "Nueva clave API generada exitosamente", + "delete": "Clave API eliminada exitosamente", + "copy": "Clave API copiada al portapapeles" + } } }, "action_description": { diff --git a/public/locales/ja.json b/public/locales/ja.json index a4a66143..ee18c96c 100644 --- a/public/locales/ja.json +++ b/public/locales/ja.json @@ -140,12 +140,41 @@ "no_key_message": "APIキーはまだ生成されていません。", "generate_button": "APIキーを生成", "notifications": { - "fetch_error": "APIキーの取得に失敗しました - ${error}", - "generate_success": "APIキーが正常に生成されました", - "generate_error": "APIキーの生成に失敗しました - ${error}", - "delete_success": "APIキーが正常に削除されました", - "delete_error": "APIキーの削除に失敗しました - ${error}", - "copy_success": "APIキーがコピーされました" + "errors": { + "fetch": { + "network": "APIキーの取得中にネットワークエラーが発生しました:${error}", + "unauthorized": "APIキーにアクセスするにはログインが必要です", + "not_found": "アカウントのAPIキーが見つかりません", + "server": "APIキーの取得中にサーバーエラーが発生しました。後でもう一度お試しください", + "unknown": "APIキーの取得中に不明なエラーが発生しました:${error}" + }, + "generate": { + "network": "APIキーの生成中にネットワークエラーが発生しました:${error}", + "unauthorized": "APIキーを生成するにはログインが必要です", + "key_exists": "APIキーが既に存在します。既存のキーを先に削除してください", + "not_found": "ユーザーアカウントが見つかりません", + "server": "APIキーの生成中にサーバーエラーが発生しました。後でもう一度お試しください", + "unknown": "APIキーの生成中に不明なエラーが発生しました:${error}" + }, + "delete": { + "network": "APIキーの削除中にネットワークエラーが発生しました:${error}", + "unauthorized": "APIキーを削除するにはログインが必要です", + "not_found": "ユーザーアカウントが見つかりません", + "key_not_found": "削除するAPIキーが見つかりません", + "server": "APIキーの削除中にサーバーエラーが発生しました。後でもう一度お試しください", + "unknown": "APIキーの削除中に不明なエラーが発生しました:${error}" + }, + "copy": { + "failed": "APIキーのクリップボードへのコピーに失敗しました", + "no_key": "コピーできるAPIキーがありません" + } + }, + "success": { + "fetch": "APIキーの取得に成功しました", + "generate": "新しいAPIキーの生成に成功しました", + "delete": "APIキーの削除に成功しました", + "copy": "APIキーをクリップボードにコピーしました" + } } }, "action_description": { diff --git a/public/locales/zh.json b/public/locales/zh.json index c92ed186..5cec8268 100644 --- a/public/locales/zh.json +++ b/public/locales/zh.json @@ -140,12 +140,41 @@ "no_key_message": "您还未生成API密钥。", "generate_button": "生成API密钥", "notifications": { - "fetch_error": "获取API密钥失败 - ${error}", - "generate_success": "API密钥生成成功", - "generate_error": "生成API密钥失败 - ${error}", - "delete_success": "API密钥删除成功", - "delete_error": "删除API密钥失败 - ${error}", - "copy_success": "API密钥复制成功" + "errors": { + "fetch": { + "network": "获取API密钥时发生网络错误:${error}", + "unauthorized": "您必须登录才能访问API密钥", + "not_found": "找不到您账户的API密钥", + "server": "获取API密钥时发生服务器错误。请稍后重试", + "unknown": "获取API密钥时发生未知错误:${error}" + }, + "generate": { + "network": "生成API密钥时发生网络错误:${error}", + "unauthorized": "您必须登录才能生成API密钥", + "key_exists": "您已经有一个API密钥。请先删除现有的密钥", + "not_found": "找不到用户账户", + "server": "生成API密钥时发生服务器错误。请稍后重试", + "unknown": "生成API密钥时发生未知错误:${error}" + }, + "delete": { + "network": "删除API密钥时发生网络错误:${error}", + "unauthorized": "您必须登录才能删除API密钥", + "not_found": "找不到用户账户", + "key_not_found": "找不到要删除的API密钥", + "server": "删除API密钥时发生服务器错误。请稍后重试", + "unknown": "删除API密钥时发生未知错误:${error}" + }, + "copy": { + "failed": "复制API密钥到剪贴板失败", + "no_key": "没有可复制的API密钥" + } + }, + "success": { + "fetch": "成功获取API密钥", + "generate": "成功生成新的API密钥", + "delete": "成功删除API密钥", + "copy": "已将API密钥复制到剪贴板" + } } }, "action_description": { diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index f18ee8c1..f77205b4 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -238,36 +238,56 @@ router.post( const authenticatedReq = req as AuthenticatedRequest; try { if (!authenticatedReq.user) { - return res.status(401).json({ ok: false, error: "Unauthorized" }); + return res.status(401).json({ + ok: false, + message: "Unauthorized", + code: "unauthorized" + }); } + const user = await User.findByPk(authenticatedReq.user.id, { attributes: { exclude: ["password"] }, }); if (!user) { - return res.status(404).json({ message: "User not found" }); + return res.status(404).json({ + ok: false, + message: "User not found", + code: "not_found" + }); } if (user.api_key) { - return res.status(400).json({ message: "API key already exists" }); + return res.status(400).json({ + ok: false, + message: "API key already exists", + code: "key_exists" + }); } - const apiKey = genAPIKey(); + const apiKey = genAPIKey(); await user.update({ api_key: apiKey }); + // Capture analytics event capture("maxun-oss-api-key-created", { user_id: user.id, created_at: new Date().toISOString(), }); return res.status(200).json({ + ok: true, message: "API key generated successfully", - api_key: apiKey, + api_key: apiKey }); + } catch (error) { - return res - .status(500) - .json({ message: "Error generating API key", error }); + console.error('API Key generation error:', error); + return res.status(500).json({ + ok: false, + message: "Error generating API key", + code: "server", + error: process.env.NODE_ENV === 'development' ? error : undefined + }); } } ); @@ -279,7 +299,11 @@ router.get( const authenticatedReq = req as AuthenticatedRequest; try { if (!authenticatedReq.user) { - return res.status(401).json({ ok: false, error: "Unauthorized" }); + return res.status(401).json({ + ok: false, + message: "Unauthorized", + code: "unauthorized" + }); } const user = await User.findByPk(authenticatedReq.user.id, { @@ -288,15 +312,27 @@ router.get( }); if (!user) { - return res.status(404).json({ message: "User not found" }); + return res.status(404).json({ + ok: false, + message: "User not found", + code: "not_found" + }); } return res.status(200).json({ + ok: true, message: "API key fetched successfully", - api_key: user.api_key || null, + api_key: user.api_key || null }); + } catch (error) { - return res.status(500).json({ message: "Error fetching API key", error }); + console.error('API Key fetch error:', error); + return res.status(500).json({ + ok: false, + message: "Error fetching API key", + code: "server", + error: process.env.NODE_ENV === 'development' ? error : undefined + }); } } ); @@ -306,33 +342,59 @@ router.delete( requireSignIn, async (req: Request, res) => { const authenticatedReq = req as AuthenticatedRequest; - if (!authenticatedReq.user) { - return res.status(401).send({ error: "Unauthorized" }); - } - try { - const user = await User.findByPk(authenticatedReq.user.id, { raw: true }); + if (!authenticatedReq.user) { + return res.status(401).json({ + ok: false, + message: "Unauthorized", + code: "unauthorized" + }); + } + + const user = await User.findByPk(authenticatedReq.user.id, { + raw: true, + attributes: ["id", "api_key"] + }); if (!user) { - return res.status(404).json({ message: "User not found" }); + return res.status(404).json({ + ok: false, + message: "User not found", + code: "not_found" + }); } if (!user.api_key) { - return res.status(404).json({ message: "API Key not found" }); + return res.status(404).json({ + ok: false, + message: "API Key not found", + code: "key_not_found" + }); } - await User.update({ api_key: null }, { where: { id: authenticatedReq.user.id } }); + await User.update( + { api_key: null }, + { where: { id: authenticatedReq.user.id } } + ); capture("maxun-oss-api-key-deleted", { user_id: user.id, deleted_at: new Date().toISOString(), }); - return res.status(200).json({ message: "API Key deleted successfully" }); - } catch (error: any) { - return res - .status(500) - .json({ message: "Error deleting API key", error: error.message }); + return res.status(200).json({ + ok: true, + message: "API Key deleted successfully" + }); + + } catch (error) { + console.error('API Key deletion error:', error); + return res.status(500).json({ + ok: false, + message: "Error deleting API key", + code: "server", + error: process.env.NODE_ENV === 'development' ? error : undefined + }); } } ); diff --git a/src/components/api/ApiKey.tsx b/src/components/api/ApiKey.tsx index f84f7a55..f4904574 100644 --- a/src/components/api/ApiKey.tsx +++ b/src/components/api/ApiKey.tsx @@ -43,8 +43,33 @@ const ApiKeyManager = () => { try { const { data } = await axios.get(`${apiUrl}/auth/api-key`); setApiKey(data.api_key); + notify('success', t('apikey.notifications.success.fetch')); } catch (error: any) { - notify('error', t('apikey.notifications.fetch_error', { error: error.message })); + const status = error.response?.status; + let errorKey = 'unknown'; + + switch (status) { + case 401: + errorKey = 'unauthorized'; + break; + case 404: + errorKey = 'not_found'; + break; + case 500: + errorKey = 'server'; + break; + default: + if (error.message?.includes('Network Error')) { + errorKey = 'network'; + } + } + + notify( + 'error', + t(`apikey.notifications.errors.fetch.${errorKey}`, { + error: error.response?.data?.message || error.message + }) + ); } finally { setLoading(false); } @@ -58,11 +83,36 @@ const ApiKeyManager = () => { setLoading(true); try { const { data } = await axios.post(`${apiUrl}/auth/generate-api-key`); - setApiKey(data.api_key); - - notify('success', t('apikey.notifications.generate_success')); + if (data.ok && data.api_key) { + setApiKey(data.api_key); + notify('success', t('apikey.notifications.success.generate')); + } } catch (error: any) { - notify('error', t('apikey.notifications.generate_error', { error: error.message })); + const status = error.response?.status; + let errorKey = 'unknown'; + + switch (status) { + case 401: + errorKey = 'unauthorized'; + break; + case 403: + errorKey = 'limit_reached'; + break; + case 500: + errorKey = 'server'; + break; + default: + if (error.message?.includes('Network Error')) { + errorKey = 'network'; + } + } + + notify( + 'error', + t(`apikey.notifications.errors.generate.${errorKey}`, { + error: error.response?.data?.message || error.message + }) + ); } finally { setLoading(false); } @@ -71,22 +121,54 @@ const ApiKeyManager = () => { const deleteApiKey = async () => { setLoading(true); try { - await axios.delete(`${apiUrl}/auth/delete-api-key`); - setApiKey(null); - notify('success', t('apikey.notifications.delete_success')); + const response = await axios.delete(`${apiUrl}/auth/delete-api-key`); + if (response.data.ok) { + setApiKey(null); + notify('success', t('apikey.notifications.success.delete')); + } } catch (error: any) { - notify('error', t('apikey.notifications.delete_error', { error: error.message })); + const status = error.response?.status; + let errorKey = 'unknown'; + + switch (status) { + case 401: + errorKey = 'unauthorized'; + break; + case 404: + errorKey = 'not_found'; + break; + case 500: + errorKey = 'server'; + break; + default: + if (error.message?.includes('Network Error')) { + errorKey = 'network'; + } + } + + notify( + 'error', + t(`apikey.notifications.errors.delete.${errorKey}`, { + error: error.response?.data?.message || error.message + }) + ); } finally { setLoading(false); } }; - const copyToClipboard = () => { - if (apiKey) { - navigator.clipboard.writeText(apiKey); + const copyToClipboard = async () => { + if (!apiKey) return; + + try { + await navigator.clipboard.writeText(apiKey); setCopySuccess(true); + notify('success', t('apikey.notifications.success.copy')); + + // Reset copy success state after 2 seconds setTimeout(() => setCopySuccess(false), 2000); - notify('info', t('apikey.notifications.copy_success')); + } catch (error) { + notify('error', t('apikey.notifications.errors.copy.failed')); } };