Conversation
WalkthroughIntroduces key notes functionality: adds Notes field to APIKey, a PUT /api/keys/:id/notes endpoint to update notes with validation, frontend API method to call it, UI in KeyTable.vue to edit/display notes, and localization strings in en-US, ja-JP, and zh-CN. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User
participant V as KeyTable.vue
participant A as keysApi
participant S as Server (UpdateKeyNotes)
participant D as DB
U->>V: Click "Notes" and Save (notes text)
V->>A: updateKeyNotes(keyId, notes)
A->>S: PUT /api/keys/:id/notes { notes }
S->>S: Validate id, bind JSON, trim, <=255 runes
S->>D: Verify key exists
D-->>S: Exists / not found
alt Key exists
S->>D: Update notes
D-->>S: OK
S-->>A: 200 No content
A-->>V: Resolve
V->>V: Update local key.notes, show success
else Not found / invalid
S-->>A: 4xx/5xx error
A-->>V: Reject
V->>V: Show error
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (5)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (8)
internal/models/types.go (1)
84-84: Notes column added — consider nullability consistency and DB defaultCurrent choice uses empty string as default. If you intend “optional,” consider either:
- Keep string but add
not nullexplicitly for clarity; or- Switch to
*stringto represent nulls consistently across DB and JSON (omitempty).Minimal GORM tag tweak (if staying non-null) for explicitness:
- Notes string `gorm:"type:varchar(255);default:''" json:"notes"` + Notes string `gorm:"type:varchar(255);not null;default:''" json:"notes"`web/src/types/models.ts (1)
16-16: Align TS type with backend payloadBackend always returns a string (often empty). Making
notesnon-optional avoidsundefinedchecks in the UI.- notes?: string; + notes: string;web/src/api/keys.ts (1)
109-112: Trim and bound notes client-side to reduce server rejectsMinor UX polish: trim and enforce 255 chars before sending.
// 更新密钥备注 async updateKeyNotes(keyId: number, notes: string): Promise<void> { - await http.put(`/keys/${keyId}/notes`, { notes }); + const payload = (notes ?? "").trim().slice(0, 255); + await http.put(`/keys/${keyId}/notes`, { notes: payload }); },internal/handler/key_handler.go (1)
452-485: Optional: audit trailConsider logging who changed notes and when (if you have user identity), to aid operational traceability.
web/src/components/keys/KeyTable.vue (4)
34-36: Type safety: make is_visible optional or initialize itData from API lacks
is_visible. Either mark it optional:-interface KeyRow extends APIKey { - is_visible: boolean; -} +interface KeyRow extends APIKey { + is_visible?: boolean; +}or initialize when loading (see next comment).
190-201: Initialize is_visible to avoid undefined at runtimeSet a default when mapping response items.
- keys.value = result.items as KeyRow[]; + keys.value = (result.items as APIKey[]).map(k => ({ ...k, is_visible: false })) as KeyRow[];
302-313: Trim notes before save for consistent UXAligns with API trimming suggestion.
async function saveKeyNotes() { if (!editingKey.value) return; try { - await keysApi.updateKeyNotes(editingKey.value.id, editingNotes.value); - editingKey.value.notes = editingNotes.value; + const payload = (editingNotes.value ?? "").trim().slice(0, 255); + await keysApi.updateKeyNotes(editingKey.value.id, payload); + editingKey.value.notes = payload; window.$message.success("备注已更新"); notesDialogShow.value = false; } catch (error) { window.$message.error("备注更新失败"); } }
1082-1091: Unused style block
.key-notesisn’t referenced in the template. Remove or apply it where intended to avoid dead CSS.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
internal/handler/key_handler.go(1 hunks)internal/models/types.go(1 hunks)internal/router/router.go(1 hunks)internal/services/key_service.go(1 hunks)web/src/api/keys.ts(1 hunks)web/src/components/keys/KeyTable.vue(7 hunks)web/src/types/models.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
internal/handler/key_handler.go (4)
internal/handler/handler.go (1)
Server(20-33)internal/response/response.go (2)
Error(34-39)Success(25-31)internal/errors/errors.go (5)
NewAPIError(44-50)ErrBadRequest(27-27)ErrInvalidJSON(28-28)ErrResourceNotFound(31-31)ParseDBError(62-91)internal/models/types.go (1)
APIKey(78-90)
🔇 Additional comments (5)
internal/router/router.go (2)
133-133: Route wiring LGTM
PUT /api/keys/:id/notescorrectly scoped under the protected keys group.
133-133: Confirm authorization model for cross-group accessHandler updates by key ID only. If you enforce group- or role-based access, verify the auth layer prevents modifying keys across unauthorized groups. If needed, add a group check.
Would you like a quick repo scan to locate existing group-ownership checks to mirror here?
internal/services/key_service.go (1)
133-134: Explicit initialization is fineSetting
Notes: ""on creation matches the DB default and frontend expectations.web/src/components/keys/KeyTable.vue (2)
287-293: Display logic LGTMNotes shown when hidden; full key when visible; masked otherwise. Matches spec.
830-843: Modal UX is solidTextarea with maxlength and counter matches backend constraints.
|
感谢你的贡献,之后会审查此PR。 Thank you for your contribution, this PR will be reviewed later. |
|
感谢你的贡献! 如果方便,希望能帮忙协助完成以下内容:
|
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (2)
internal/handler/key_handler.go (2)
443-446: Add validation tag to prevent DB constraint violations.The
Notesfield lacks server-side validation that could prevent values exceeding the database'svarchar(255)constraint, potentially causing 500 errors.This issue was previously flagged. As suggested in the earlier review, add
binding:"max=255"to the struct field.
448-481: Trim input and validate length explicitly before DB update.Without trimming and explicit length validation, oversized or untrimmed notes can hit the database and cause errors or inconsistent data.
This issue was previously flagged. As suggested in the earlier review, trim the input with
strings.TrimSpaceafter binding and validate the length before the DB operation.
🧹 Nitpick comments (2)
web/src/components/keys/KeyTable.vue (2)
309-313: Validate and trim notes before API call.The function should validate the input before making the API call to provide better UX and catch issues client-side.
Apply this diff:
async function saveKeyNotes() { if (!editingKey.value) return; + // Trim and validate + const trimmedNotes = editingNotes.value.trim(); + if (trimmedNotes.length > 255) { + window.$message.error(t("keys.notesTooLong")); + return; + } + try { - await keysApi.updateKeyNotes(editingKey.value.id, editingNotes.value); - editingKey.value.notes = editingNotes.value; + await keysApi.updateKeyNotes(editingKey.value.id, trimmedNotes); + editingKey.value.notes = trimmedNotes; window.$message.success(t("keys.notesUpdated")); notesDialogShow.value = false; } catch (error) { window.$message.error(t("keys.notesUpdateFailed")); } }
1096-1105: Remove unused CSS class.The
key-notesclass is defined but never used in the template. Consider removing it to reduce code clutter.Apply this diff to remove the unused class:
-/* 备注信息行 */ -.key-notes { - font-size: 12px; - color: var(--text-secondary); - margin-top: 8px; - padding: 4px 6px; - background: var(--bg-secondary); - border-radius: 4px; - line-height: 1.4; -} - /* 底部统计和按钮行 */
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
internal/handler/key_handler.go(1 hunks)internal/models/types.go(1 hunks)internal/router/router.go(1 hunks)web/src/api/keys.ts(1 hunks)web/src/components/keys/KeyTable.vue(7 hunks)web/src/types/models.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- internal/models/types.go
- web/src/types/models.ts
- internal/router/router.go
🧰 Additional context used
🧬 Code graph analysis (1)
internal/handler/key_handler.go (4)
internal/handler/handler.go (1)
Server(21-36)internal/response/response.go (2)
Error(36-41)Success(26-33)internal/errors/errors.go (5)
NewAPIError(44-50)ErrBadRequest(27-27)ErrInvalidJSON(28-28)ErrResourceNotFound(31-31)ParseDBError(62-91)internal/models/types.go (1)
APIKey(110-122)
🔇 Additional comments (3)
web/src/api/keys.ts (1)
119-122: LGTM! API method follows existing patterns.The implementation is clean and consistent with other methods in the file. The method signature correctly matches the backend endpoint structure.
web/src/components/keys/KeyTable.vue (2)
95-98: LGTM! State management setup is clean.The reactive variables for notes editing are well-organized and follow Vue 3 composition API conventions.
293-299: LGTM! Centralized display logic improves maintainability.The
getDisplayValuefunction properly implements the requirement: "有备注的密钥默认显示备注;无备注的密钥仍显示掩码" (keys with notes show notes by default; keys without notes show masked values).
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (3)
web/src/components/keys/KeyTable.vue (3)
293-299: Clarify the notes display priority logic.The function shows notes when they exist AND the key is hidden (
!key.is_visible). When the key is visible (is_visible=true), it always shows the actual key value instead of notes. This means users must toggle visibility to switch between viewing notes and the actual key.Consider documenting this behavior with a comment, or evaluate if notes should be displayed regardless of visibility state:
// 获取要显示的值(备注优先,否则显示密钥) function getDisplayValue(key: KeyRow): string { + // Notes are shown only when the key is hidden; when visible, always show actual key if (key.notes && !key.is_visible) { return key.notes; } return key.is_visible ? key.key_value : maskKey(key.key_value); }
309-320: Improve error handling with logging.The error handler in
saveKeyNotesshows a generic error message but doesn't log the error details. This makes debugging production issues difficult.Apply this diff:
try { await keysApi.updateKeyNotes(editingKey.value.id, editingNotes.value); editingKey.value.notes = editingNotes.value; window.$message.success(t("keys.notesUpdated")); notesDialogShow.value = false; } catch (error) { + console.error("Failed to update key notes:", error); window.$message.error(t("keys.notesUpdateFailed")); }
1096-1105: Remove unused CSS class.The
.key-notesCSS class is defined but never used in the template. Notes are displayed through thegetDisplayValue()function within the existing.key-textinput field, not as a separate element with the.key-notesclass.Apply this diff to remove the unused CSS:
-/* 备注信息行 */ -.key-notes { - font-size: 12px; - color: var(--text-secondary); - margin-top: 8px; - padding: 4px 6px; - background: var(--bg-secondary); - border-radius: 4px; - line-height: 1.4; -} - /* 底部统计和按钮行 */
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
internal/handler/key_handler.go(1 hunks)internal/models/types.go(1 hunks)internal/router/router.go(1 hunks)web/src/api/keys.ts(1 hunks)web/src/components/keys/KeyTable.vue(7 hunks)web/src/locales/en-US.ts(1 hunks)web/src/locales/ja-JP.ts(1 hunks)web/src/locales/zh-CN.ts(1 hunks)web/src/types/models.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- internal/router/router.go
- internal/handler/key_handler.go
- internal/models/types.go
- web/src/api/keys.ts
🔇 Additional comments (5)
web/src/types/models.ts (1)
22-22: LGTM! Clean addition of optional notes field.The optional
notesfield integrates cleanly with the existing APIKey interface and maintains backward compatibility.web/src/components/keys/KeyTable.vue (4)
95-98: LGTM! Notes editing state properly initialized.The reactive state for notes editing is well-structured and follows Vue 3 composition API best practices.
301-320: LGTM! I18n implementation is now correct.The previous issue with hard-coded Chinese strings has been properly resolved. All user-facing text now uses the i18n translation function
t().
745-754: LGTM! I18n implementation is now correct.The Notes button now properly uses translated strings via
t()for both the button text and title attribute.
843-857: LGTM! I18n implementation is now correct.The modal dialog now properly uses translated strings for the title, placeholder, and button labels.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
web/src/locales/ja-JP.ts (1)
401-406: Consider using "備考" instead of "注釈" for better UX alignment.The current translations use "注釈" (annotation), which is more formal/academic. Since the PR title references "备注" (remark/memo in Chinese), consider using "備考" (bikō) in Japanese, which is more commonly used in business and UI contexts for user notes/remarks.
Apply this diff:
- notesUpdated: "注釈が更新されました", - notesUpdateFailed: "注釈の更新に失敗しました", - editNotes: "注釈を編集", - notes: "注釈", - editKeyNotes: "キー注釈を編集", - enterNotes: "注釈を入力してください...", + notesUpdated: "備考が更新されました", + notesUpdateFailed: "備考の更新に失敗しました", + editNotes: "備考を編集", + notes: "備考", + editKeyNotes: "キー備考を編集", + enterNotes: "備考を入力してください...",
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
web/src/locales/en-US.ts(1 hunks)web/src/locales/ja-JP.ts(1 hunks)web/src/locales/zh-CN.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- web/src/locales/zh-CN.ts
- web/src/locales/en-US.ts
好的,本地测试已经正常,并且已添加国际化翻译 |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
web/src/components/keys/KeyTable.vue (1)
308-322: Add loading state and improve error handling.The save operation lacks a loading state, which could lead to duplicate submissions if users click the save button multiple times. Additionally, the error is not logged, making debugging difficult in production.
Consider this enhancement:
+const savingNotes = ref(false); + // 保存备注 async function saveKeyNotes() { - if (!editingKey.value) { + if (!editingKey.value || savingNotes.value) { return; } + savingNotes.value = true; try { await keysApi.updateKeyNotes(editingKey.value.id, editingNotes.value); editingKey.value.notes = editingNotes.value; window.$message.success(t("keys.notesUpdated")); notesDialogShow.value = false; - } catch (_error) { + } catch (error) { + console.error("Failed to update notes:", error); window.$message.error(t("keys.notesUpdateFailed")); + } finally { + savingNotes.value = false; } }Then update the save button to show loading state:
- <n-button type="primary" @click="saveKeyNotes">{{ t("common.save") }}</n-button> + <n-button type="primary" @click="saveKeyNotes" :loading="savingNotes">{{ t("common.save") }}</n-button>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
web/src/components/keys/KeyTable.vue(6 hunks)
🔇 Additional comments (2)
web/src/components/keys/KeyTable.vue (2)
293-299: LGTM! Smart display logic.The function correctly prioritizes showing notes when available and the key is hidden, providing a clean UX for the notes feature.
845-859: LGTM! Well-implemented modal.The modal implementation is solid:
- Properly internationalized throughout
maxlength="255"matches the backend constraintshow-countprovides helpful UX feedback
|
bug: 期望: |
…yia/gpt-load into feature-add-remark
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
web/src/components/keys/KeyTable.vue (2)
309-324: Consider adding a loading state to prevent double-submission.While the current implementation handles errors correctly, there's no loading state to prevent users from clicking the save button multiple times while the API call is in progress. This could lead to duplicate requests.
Consider adding a loading state:
+const isSavingNotes = ref(false); + async function saveKeyNotes() { - if (!editingKey.value) { + if (!editingKey.value || isSavingNotes.value) { return; } + isSavingNotes.value = true; try { const trimmed = editingNotes.value.trim(); await keysApi.updateKeyNotes(editingKey.value.id, trimmed); editingKey.value.notes = trimmed; window.$message.success(t("keys.notesUpdated")); notesDialogShow.value = false; } catch (_error) { window.$message.error(t("keys.notesUpdateFailed")); + } finally { + isSavingNotes.value = false; } }Then disable the save button while loading:
- <n-button type="primary" @click="saveKeyNotes">{{ t("common.save") }}</n-button> + <n-button type="primary" @click="saveKeyNotes" :loading="isSavingNotes">{{ t("common.save") }}</n-button>
743-790: Consider consolidating action buttons to improve layout for invalid keys.The maintainer reported that for invalid status keys, multiple action buttons in the bottom row cause text overlap with the statistics display. The maintainer suggested moving bottom buttons to the top row and using icon-only buttons to improve the layout and prevent text from being obscured.
Based on PR comments reporting UI layout issues with button overlap.
Current layout:
- Top row (lines 713-739): Icon-only buttons for Notes, Show/Hide, Copy
- Bottom row (lines 758-789): Text buttons for Test, Restore (conditional), Delete
Possible improvements:
- Move Restore and Delete buttons to the top row as icon-only buttons and keep only the Test button (or a combined "More actions" dropdown) at the bottom
- Use a dropdown menu for less-frequent actions (Restore, Delete) to reduce button count
- Reduce button padding or use smaller button sizes for the bottom row to prevent overlap
The maintainer referenced screenshots showing the overlap issue. Would you like me to generate a specific UI refactor proposal based on one of these approaches?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
image.pngis excluded by!**/*.png
📒 Files selected for processing (1)
web/src/components/keys/KeyTable.vue(6 hunks)
🔇 Additional comments (3)
web/src/components/keys/KeyTable.vue (3)
294-300: LGTM! Display logic correctly prioritizes notes.The function correctly prioritizes notes when the key is hidden, and falls back to showing the actual or masked key value. The truthy check on
key.notesproperly handles empty strings by falling through to display the masked key.
309-324: Trim implementation correctly addresses the maintainer's bug report.The function properly trims whitespace from notes before saving (line 316) and updates both the API and local state with the trimmed value. This ensures consistency between frontend and backend, resolving the issue where the frontend preserved whitespace but the backend removed it.
Based on PR comments requesting trim implementation to match backend behavior.
847-861: LGTM! Modal implementation is complete and user-friendly.The notes editing dialog properly uses i18n translations for all text elements, enforces the 255-character limit matching the database constraint, and provides a character counter for good UX.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
web/src/components/keys/KeyTable.vue (1)
847-861: LGTM! Modal implementation is correct.The notes editing modal properly uses:
v-model:showfor dialog visibility control- Internationalized strings for title, placeholder, and buttons
maxlength="255"matching the database field sizeshow-countfor user feedback- Proper action buttons (Cancel/Save) with correct handlers
Optional: Consider adding a loading state to the Save button during the API call to provide visual feedback and prevent double-clicks.
Apply this diff to add loading state:
+const isSavingNotes = ref(false); + // 保存备注 async function saveKeyNotes() { if (!editingKey.value) { return; } try { + isSavingNotes.value = true; const trimmed = editingNotes.value.trim(); await keysApi.updateKeyNotes(editingKey.value.id, trimmed); editingKey.value.notes = trimmed; window.$message.success(t("keys.notesUpdated")); notesDialogShow.value = false; } catch (_error) { window.$message.error(t("keys.notesUpdateFailed")); + } finally { + isSavingNotes.value = false; } }Then update the Save button:
- <n-button type="primary" @click="saveKeyNotes">{{ t("common.save") }}</n-button> + <n-button type="primary" :loading="isSavingNotes" @click="saveKeyNotes">{{ t("common.save") }}</n-button>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
web/src/components/keys/KeyTable.vue(6 hunks)
🔇 Additional comments (6)
web/src/components/keys/KeyTable.vue (6)
15-15: LGTM! Imports are appropriate.The
Pencilicon andNModalcomponent imports are correctly added to support the notes editing feature.Also applies to: 24-24
96-99: LGTM! State management is clean.The three refs (
notesDialogShow,editingKey,editingNotes) properly manage the modal visibility, the key being edited, and the notes content. The state is well-scoped and minimal.
294-300: LGTM! Display logic correctly prioritizes notes.The
getDisplayValuefunction implements the requirement: when notes exist and the key is hidden, display notes; otherwise display the key value (visible or masked). This allows users to toggle between notes and the actual key using the eye icon.
302-307: LGTM! Edit initialization is straightforward.The
editKeyNotesfunction correctly sets up the editing state, initializingeditingNoteswith the current notes value (or empty string) and opening the modal dialog.
309-324: LGTM! Save function correctly addresses trim requirement.The
saveKeyNotesfunction properly addresses the maintainer's concern by trimming the notes on line 316 before saving. Both the API call (line 317) and the local state update (line 318) use the trimmed value, ensuring consistency between frontend and backend. Error handling keeps the dialog open on failure, allowing users to retry.Note: Empty notes after trimming will be saved, which appears intentional to allow clearing notes.
Based on PR comments requesting trim on frontend save to match backend behavior.
714-723: LGTM! Icon-only edit button improves space efficiency.The notes edit button is implemented as an icon-only button with a tooltip (
:title="t('keys.editNotes')"), which saves space in the quick-actions area while remaining accessible. This design choice aligns with the other quick-action buttons (toggle visibility, copy).
|
感谢你的贡献!!! This PR got merged and will be out in version v1.3.1. Big thanks for your contribution! |




关联 Issue / Related Issue
Closes #
变更内容 / Change Content
在 web\src\types\models.ts 模型中新增 notes字段 (varchar(255), 可选)
新增 PUT /api/keys/:id/notes API 端点用于更新密钥备注
优化密钥显示逻辑:有备注的密钥默认显示备注,无备注的密钥显示掩码
新增"备注"按钮,支持实时编辑密钥备注
现有密钥正常工作,备注字段为可选
不影响任何现有功能
有备注:默认显示备注,点击眼睛图标显示完整密钥
无备注:维持原有行为(显示掩码密钥)
每个密钥旁边都有备注按钮
备注存储在数据库中,与密钥一同管理
自查清单 / Checklist
Summary by CodeRabbit
New Features
Chores