Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,15 @@
## Commit & Pull Request Guidelines
- Use Conventional Commits (e.g., `feat: add user settings`).
- Always work on a feature branch; do not commit directly to `main`.
- If no feature branch exists for the task, create one before making changes.
- Create or switch to a feature branch before any changes and always confirm the current branch before committing (e.g., `git checkout -b feature/my-change`).
- New features target the `feature` branch; other changes target `main`.
- PRs should include a clear description, link related issues, and note any UI changes with screenshots.
- Commit with signed-off and signed commits: `git commit -s -S -m "feat: ..."`.

## Git Permissions & Operations
- Direct modification of the `.git` directory is not permitted; use git commands for all branch switches, staging, and commits.

## Configuration & Environment
- Backend secrets live in `service/.env` (copy from `service/.env.example`).
- Frontend API base URL is in root `.env` as `VITE_GLOB_API_URL`.
Expand Down
1 change: 1 addition & 0 deletions auto-imports.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ declare global {
const IconMdiReorderHorizontal: typeof import('~icons/mdi/reorder-horizontal').default
const IconRiContrastLine: typeof import('~icons/ri/contrast-line').default
const IconRiDeleteBinLine: typeof import('~icons/ri/delete-bin-line').default
const IconRiEdit2Line: typeof import('~icons/ri/edit2-line').default
const IconRiExternalLinkLine: typeof import('~icons/ri/external-link-line').default
const IconRiFileCopy2Line: typeof import('~icons/ri/file-copy2-line').default
const IconRiMoonFoggyLine: typeof import('~icons/ri/moon-foggy-line').default
Expand Down
3 changes: 2 additions & 1 deletion components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ declare module 'vue' {
IconMdiLightbulbOutline: typeof import('~icons/mdi/lightbulb-outline')['default']
IconMdiReorderHorizontal: typeof import('~icons/mdi/reorder-horizontal')['default']
IconMdiWeb: typeof import('~icons/mdi/web')['default']
IconRiAddLine: typeof import('~icons/ri/add-line')['default']
IconRiAlignJustify: typeof import('~icons/ri/align-justify')['default']
IconRiAlignRight: typeof import('~icons/ri/align-right')['default']
IconRiArrowDownSLine: typeof import('~icons/ri/arrow-down-s-line')['default']
Expand All @@ -36,7 +37,6 @@ declare module 'vue' {
IconRiDownload2Line: typeof import('~icons/ri/download2-line')['default']
IconRiDownloadLine: typeof import('~icons/ri/download-line')['default']
IconRiEditLine: typeof import('~icons/ri/edit-line')['default']
IconRiEqualizerLine: typeof import('~icons/ri/equalizer-line')['default']
IconRiFileUserLine: typeof import('~icons/ri/file-user-line')['default']
IconRiImageEditLine: typeof import('~icons/ri/image-edit-line')['default']
IconRiInboxLine: typeof import('~icons/ri/inbox-line')['default']
Expand All @@ -48,6 +48,7 @@ declare module 'vue' {
IconRiQuestionAnswerLine: typeof import('~icons/ri/question-answer-line')['default']
IconRiRestartLine: typeof import('~icons/ri/restart-line')['default']
IconRiSaveLine: typeof import('~icons/ri/save-line')['default']
IconRiSearchLine: typeof import('~icons/ri/search-line')['default']
IconRiSendPlaneFill: typeof import('~icons/ri/send-plane-fill')['default']
IconRiSettings4Line: typeof import('~icons/ri/settings4-line')['default']
IconRiSettingsLine: typeof import('~icons/ri/settings-line')['default']
Expand Down
1 change: 0 additions & 1 deletion service/src/storage/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ export async function getApiKeys() {
if (result.keys.length <= 0) {
const defaultModel = config.siteConfig.chatModels.split(',')[0] || ''
result.keys.push(await upsertKey(new KeyConfig(config.apiKey, 'ChatGPTAPI', defaultModel, [], '')))
result.total++
}
result.keys.forEach((key) => {
if (key.userRoles == null || key.userRoles.length <= 0) {
Expand Down
5 changes: 2 additions & 3 deletions service/src/storage/mongo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1162,14 +1162,13 @@ export async function getUserStatisticsByModel(start?: number, end?: number): Pr
return result
}

export async function getKeys(): Promise<{ keys: KeyConfig[], total: number }> {
export async function getKeys(): Promise<{ keys: KeyConfig[] }> {
const query = { status: { $ne: Status.Disabled } }
const cursor = keyCol.find(query)
const total = await keyCol.countDocuments(query)
const keys = []
for await (const doc of cursor)
keys.push(doc)
return { keys, total }
return { keys }
}

export async function upsertKey(key: KeyConfig): Promise<KeyConfig> {
Expand Down
3 changes: 1 addition & 2 deletions src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -516,10 +516,9 @@ export function fetchUserStatisticsByModel<T = any>(start?: number, end?: number
})
}

export function fetchGetKeys<T = any>(page: number, size: number) {
export function fetchGetKeys<T = any>() {
return get<T>({
url: '/setting-keys',
data: { page, size },
})
}

Expand Down
40 changes: 20 additions & 20 deletions src/components/common/Setting/BuiltInPrompt.vue
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ function createColumns(): DataTableColumns<BuiltInPrompt> {
type: 'info',
onClick: () => openModal('edit', row),
},
{ default: () => t('common.edit') },
{ default: () => [h(IconRiEdit2Line, { class: 'mr-1 text-base' }), t('common.edit')] },
), h(
NButton,
{
Expand All @@ -302,7 +302,7 @@ function createColumns(): DataTableColumns<BuiltInPrompt> {
type: 'error',
onClick: () => handleDeletePrompt(row),
},
{ default: () => t('common.delete') },
{ default: () => [h(IconRiDeleteBinLine, { class: 'mr-1 text-base' }), t('common.delete')] },
)],
})
},
Expand Down Expand Up @@ -377,24 +377,24 @@ onMounted(async () => {
</script>

<template>
<div class="p-4 space-y-5 min-h-[300px]">
<div class="space-y-6">
<NSpace vertical :size="12">
<NSpace>
<NButton @click="openModal('add')">
{{ t('common.add') }}
</NButton>
</NSpace>
<NDataTable
:loading="loading"
:row-key="(rowData) => rowData._id"
:columns="columns"
:data="promptList"
:row-props="rowProps"
:max-height="444"
striped
/>
</NSpace>
<div class="box-border h-full flex-1 min-h-0 overflow-hidden px-4 pb-4 pt-2" style="height: 100%;">
<div class="flex h-full min-h-0 flex-col gap-3">
<div class="flex items-center justify-end shrink-0">
<NButton size="small" type="primary" @click="openModal('add')">
<IconRiAddLine class="mr-1 text-base" />
{{ t('common.add') }}
</NButton>
</div>
<NDataTable
class="flex-1 min-h-0"
:loading="loading"
:row-key="(rowData) => rowData._id"
:columns="columns"
:data="promptList"
:row-props="rowProps"
flex-height
striped
/>
</div>
</div>

Expand Down
129 changes: 51 additions & 78 deletions src/components/common/Setting/Keys.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,10 @@ const keyConfig = ref(new KeyConfig('', 'ChatGPTAPI', '', [], ''))
const keys = ref([])
function createColumns(): DataTableColumns {
return [
{
title: () => t('setting.model.table.key'),
key: 'key',
resizable: true,
width: 120,
minWidth: 50,
maxWidth: 120,
ellipsis: true,
},
{
title: () => t('setting.model.table.apiModel'),
key: 'keyModel',
width: 150,
width: 90,
},
{
title: () => t('setting.model.table.baseUrl'),
Expand All @@ -43,23 +34,32 @@ function createColumns(): DataTableColumns {
{
title: () => t('setting.model.table.chatModel'),
key: 'chatModel',
width: 200,
width: 140,
render(row: any) {
return row.chatModel || '-'
},
},
{
title: () => t('setting.model.table.alias'),
key: 'modelAlias',
width: 150,
width: 110,
render(row: any) {
return row.modelAlias || '-'
},
},
{
title: () => t('setting.model.table.key'),
key: 'key',
resizable: true,
width: 90,
minWidth: 50,
maxWidth: 90,
ellipsis: true,
},
{
title: () => t('setting.model.table.userRoles'),
key: 'userRoles',
width: 180,
width: 140,
render(row: any) {
const tags = row.userRoles.map((userRole: UserRole) => {
return h(
Expand All @@ -70,6 +70,7 @@ function createColumns(): DataTableColumns {
},
type: 'info',
bordered: false,
size: 'small',
},
{
default: () => UserRole[userRole],
Expand All @@ -82,41 +83,40 @@ function createColumns(): DataTableColumns {
{
title: () => t('setting.model.table.remark'),
key: 'remark',
width: 150,
width: 120,
},
{
title: () => t('setting.model.table.action'),
key: '_id',
width: 220,
width: 140,
fixed: 'right',
render(row: any) {
const actions: any[] = []
actions.push(h(
NButton,
{
size: 'small',
style: {
marginRight: '6px',
},
type: 'error',
onClick: () => handleUpdateApiKeyStatus(row._id as string, Status.Disabled),
},
{ default: () => t('common.delete') },
))
if (row.status === Status.Normal) {
actions.push(h(
NButton,
{
tertiary: true,
size: 'small',
style: {
marginRight: '6px',
},
type: 'info',
onClick: () => handleEditKey(row as KeyConfig),
},
{ default: () => t('common.edit') },
{ default: () => [h(IconRiEdit2Line, { class: 'mr-1 text-base' }), t('common.edit')] },
))
}
actions.push(h(
NButton,
{
tertiary: true,
size: 'small',
type: 'error',
onClick: () => handleUpdateApiKeyStatus(row._id as string, Status.Disabled),
},
{ default: () => [h(IconRiDeleteBinLine, { class: 'mr-1 text-base' }), t('common.delete')] },
))
return actions
},
},
Expand All @@ -125,41 +125,16 @@ function createColumns(): DataTableColumns {

const columns = createColumns()

const pagination = reactive({
page: 1,
pageSize: 100,
pageCount: 1,
itemCount: 1,
prefix({ itemCount }: { itemCount: number | undefined }) {
return t('setting.model.total', { count: itemCount ?? 0 })
},
showSizePicker: true,
pageSizes: [100],
onChange: (page: number) => {
pagination.page = page
handleGetKeys(pagination.page)
},
onUpdatePageSize: (pageSize: number) => {
pagination.pageSize = pageSize
pagination.page = 1
handleGetKeys(pagination.page)
},
})

async function handleGetKeys(page: number) {
async function handleGetKeys() {
if (loading.value)
return
keys.value.length = 0
loading.value = true
const size = pagination.pageSize
const data = (await fetchGetKeys(page, size)).data
const data = (await fetchGetKeys()).data
data.keys.forEach((key: never) => {
keys.value.push(key)
})
keyConfig.value = keys.value[0]
pagination.page = page
pagination.pageCount = data.total / size + (data.total % size === 0 ? 0 : 1)
pagination.itemCount = data.total
loading.value = false
}

Expand All @@ -172,7 +147,7 @@ async function handleUpdateApiKeyStatus(id: string, status: Status) {
onPositiveClick: async () => {
await fetchUpdateApiKeyStatus(id, status)
ms.info(t('common.success'))
await handleGetKeys(pagination.page)
await handleGetKeys()
},
})
}
Expand All @@ -185,7 +160,7 @@ async function handleUpdateKeyConfig() {
handleSaving.value = true
try {
await fetchUpsertApiKey(keyConfig.value)
await handleGetKeys(pagination.page)
await handleGetKeys()
show.value = false
}
catch (error: any) {
Expand All @@ -205,31 +180,29 @@ function handleEditKey(key: KeyConfig) {
}

onMounted(async () => {
await handleGetKeys(pagination.page)
await handleGetKeys()
})
</script>

<template>
<div class="p-4 space-y-5 min-h-[300px]">
<div class="space-y-6">
<NSpace vertical :size="12">
<NSpace>
<NButton @click="handleNewKey()">
{{ t('setting.model.new') }}
</NButton>
</NSpace>
<NDataTable
remote
:loading="loading"
:row-key="(rowData) => rowData._id"
:columns="columns"
:data="keys"
:pagination="pagination"
:max-height="444"
:scroll-x="1500"
striped @update:page="handleGetKeys"
/>
</NSpace>
<div class="box-border h-full flex-1 min-h-0 overflow-hidden px-4 pb-4 pt-2" style="height: 100%;">
<div class="flex h-full min-h-0 flex-col gap-3">
<div class="shrink-0 flex items-center justify-end">
<NButton size="small" type="primary" @click="handleNewKey()">
<IconRiAddLine class="mr-1 text-base" />
{{ t('common.add') }}
</NButton>
</div>
<NDataTable
class="flex-1 min-h-0"
:loading="loading"
:row-key="(rowData) => rowData._id"
:columns="columns"
:data="keys"
flex-height
:scroll-x="1150"
striped
/>
</div>
</div>

Expand Down
Loading