Unverified Commit e7bc6250 authored by Wesley Liddick's avatar Wesley Liddick Committed by GitHub
Browse files

Merge pull request #333 from whoismonay/main

fix: 普通用户接口移除管理员敏感字段透传
parents eb5e6214 c8fb9ef3
...@@ -42,13 +42,13 @@ ...@@ -42,13 +42,13 @@
import { computed } from 'vue' import { computed } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import GroupBadge from './GroupBadge.vue' import GroupBadge from './GroupBadge.vue'
import type { Group, GroupPlatform } from '@/types' import type { AdminGroup, GroupPlatform } from '@/types'
const { t } = useI18n() const { t } = useI18n()
interface Props { interface Props {
modelValue: number[] modelValue: number[]
groups: Group[] groups: AdminGroup[]
platform?: GroupPlatform // Optional platform filter platform?: GroupPlatform // Optional platform filter
mixedScheduling?: boolean // For antigravity accounts: allow anthropic/gemini groups mixedScheduling?: boolean // For antigravity accounts: allow anthropic/gemini groups
} }
......
...@@ -27,7 +27,6 @@ export interface FetchOptions { ...@@ -27,7 +27,6 @@ export interface FetchOptions {
export interface User { export interface User {
id: number id: number
username: string username: string
notes: string
email: string email: string
role: 'admin' | 'user' // User role for authorization role: 'admin' | 'user' // User role for authorization
balance: number // User balance for API usage balance: number // User balance for API usage
...@@ -39,6 +38,11 @@ export interface User { ...@@ -39,6 +38,11 @@ export interface User {
updated_at: string updated_at: string
} }
export interface AdminUser extends User {
// 管理员备注(普通用户接口不返回)
notes: string
}
export interface LoginRequest { export interface LoginRequest {
email: string email: string
password: string password: string
...@@ -270,12 +274,17 @@ export interface Group { ...@@ -270,12 +274,17 @@ export interface Group {
// Claude Code 客户端限制 // Claude Code 客户端限制
claude_code_only: boolean claude_code_only: boolean
fallback_group_id: number | null fallback_group_id: number | null
// 模型路由配置(仅 anthropic 平台使用) created_at: string
updated_at: string
}
export interface AdminGroup extends Group {
// 模型路由配置(仅管理员可见,内部信息)
model_routing: Record<string, number[]> | null model_routing: Record<string, number[]> | null
model_routing_enabled: boolean model_routing_enabled: boolean
// 分组下账号数量(仅管理员可见)
account_count?: number account_count?: number
created_at: string
updated_at: string
} }
export interface ApiKey { export interface ApiKey {
...@@ -637,7 +646,6 @@ export interface UsageLog { ...@@ -637,7 +646,6 @@ export interface UsageLog {
total_cost: number total_cost: number
actual_cost: number actual_cost: number
rate_multiplier: number rate_multiplier: number
account_rate_multiplier?: number | null
billing_type: number billing_type: number
stream: boolean stream: boolean
...@@ -651,18 +659,30 @@ export interface UsageLog { ...@@ -651,18 +659,30 @@ export interface UsageLog {
// User-Agent // User-Agent
user_agent: string | null user_agent: string | null
// IP 地址(仅管理员可见)
ip_address: string | null
created_at: string created_at: string
user?: User user?: User
api_key?: ApiKey api_key?: ApiKey
account?: Account
group?: Group group?: Group
subscription?: UserSubscription subscription?: UserSubscription
} }
export interface UsageLogAccountSummary {
id: number
name: string
}
export interface AdminUsageLog extends UsageLog {
// 账号计费倍率(仅管理员可见)
account_rate_multiplier?: number | null
// 用户请求 IP(仅管理员可见)
ip_address?: string | null
// 最小账号信息(仅管理员接口返回)
account?: UsageLogAccountSummary
}
export interface UsageCleanupFilters { export interface UsageCleanupFilters {
start_time: string start_time: string
end_time: string end_time: string
......
...@@ -187,14 +187,14 @@ import AccountCapacityCell from '@/components/account/AccountCapacityCell.vue' ...@@ -187,14 +187,14 @@ import AccountCapacityCell from '@/components/account/AccountCapacityCell.vue'
import PlatformTypeBadge from '@/components/common/PlatformTypeBadge.vue' import PlatformTypeBadge from '@/components/common/PlatformTypeBadge.vue'
import Icon from '@/components/icons/Icon.vue' import Icon from '@/components/icons/Icon.vue'
import { formatDateTime, formatRelativeTime } from '@/utils/format' import { formatDateTime, formatRelativeTime } from '@/utils/format'
import type { Account, Proxy, Group } from '@/types' import type { Account, Proxy, AdminGroup } from '@/types'
const { t } = useI18n() const { t } = useI18n()
const appStore = useAppStore() const appStore = useAppStore()
const authStore = useAuthStore() const authStore = useAuthStore()
const proxies = ref<Proxy[]>([]) const proxies = ref<Proxy[]>([])
const groups = ref<Group[]>([]) const groups = ref<AdminGroup[]>([])
const selIds = ref<number[]>([]) const selIds = ref<number[]>([])
const showCreate = ref(false) const showCreate = ref(false)
const showEdit = ref(false) const showEdit = ref(false)
......
...@@ -1107,7 +1107,7 @@ import { useI18n } from 'vue-i18n' ...@@ -1107,7 +1107,7 @@ import { useI18n } from 'vue-i18n'
import { useAppStore } from '@/stores/app' import { useAppStore } from '@/stores/app'
import { useOnboardingStore } from '@/stores/onboarding' import { useOnboardingStore } from '@/stores/onboarding'
import { adminAPI } from '@/api/admin' import { adminAPI } from '@/api/admin'
import type { Group, GroupPlatform, SubscriptionType } from '@/types' import type { AdminGroup, GroupPlatform, SubscriptionType } from '@/types'
import type { Column } from '@/components/common/types' import type { Column } from '@/components/common/types'
import AppLayout from '@/components/layout/AppLayout.vue' import AppLayout from '@/components/layout/AppLayout.vue'
import TablePageLayout from '@/components/layout/TablePageLayout.vue' import TablePageLayout from '@/components/layout/TablePageLayout.vue'
...@@ -1202,7 +1202,7 @@ const fallbackGroupOptionsForEdit = computed(() => { ...@@ -1202,7 +1202,7 @@ const fallbackGroupOptionsForEdit = computed(() => {
return options return options
}) })
const groups = ref<Group[]>([]) const groups = ref<AdminGroup[]>([])
const loading = ref(false) const loading = ref(false)
const searchQuery = ref('') const searchQuery = ref('')
const filters = reactive({ const filters = reactive({
...@@ -1223,8 +1223,8 @@ const showCreateModal = ref(false) ...@@ -1223,8 +1223,8 @@ const showCreateModal = ref(false)
const showEditModal = ref(false) const showEditModal = ref(false)
const showDeleteDialog = ref(false) const showDeleteDialog = ref(false)
const submitting = ref(false) const submitting = ref(false)
const editingGroup = ref<Group | null>(null) const editingGroup = ref<AdminGroup | null>(null)
const deletingGroup = ref<Group | null>(null) const deletingGroup = ref<AdminGroup | null>(null)
const createForm = reactive({ const createForm = reactive({
name: '', name: '',
...@@ -1529,7 +1529,7 @@ const handleCreateGroup = async () => { ...@@ -1529,7 +1529,7 @@ const handleCreateGroup = async () => {
} }
} }
const handleEdit = async (group: Group) => { const handleEdit = async (group: AdminGroup) => {
editingGroup.value = group editingGroup.value = group
editForm.name = group.name editForm.name = group.name
editForm.description = group.description || '' editForm.description = group.description || ''
...@@ -1585,7 +1585,7 @@ const handleUpdateGroup = async () => { ...@@ -1585,7 +1585,7 @@ const handleUpdateGroup = async () => {
} }
} }
const handleDelete = (group: Group) => { const handleDelete = (group: AdminGroup) => {
deletingGroup.value = group deletingGroup.value = group
showDeleteDialog.value = true showDeleteDialog.value = true
} }
......
...@@ -42,11 +42,11 @@ import UsageStatsCards from '@/components/admin/usage/UsageStatsCards.vue'; impo ...@@ -42,11 +42,11 @@ import UsageStatsCards from '@/components/admin/usage/UsageStatsCards.vue'; impo
import UsageTable from '@/components/admin/usage/UsageTable.vue'; import UsageExportProgress from '@/components/admin/usage/UsageExportProgress.vue' import UsageTable from '@/components/admin/usage/UsageTable.vue'; import UsageExportProgress from '@/components/admin/usage/UsageExportProgress.vue'
import UsageCleanupDialog from '@/components/admin/usage/UsageCleanupDialog.vue' import UsageCleanupDialog from '@/components/admin/usage/UsageCleanupDialog.vue'
import ModelDistributionChart from '@/components/charts/ModelDistributionChart.vue'; import TokenUsageTrend from '@/components/charts/TokenUsageTrend.vue' import ModelDistributionChart from '@/components/charts/ModelDistributionChart.vue'; import TokenUsageTrend from '@/components/charts/TokenUsageTrend.vue'
import type { UsageLog, TrendDataPoint, ModelStat } from '@/types'; import type { AdminUsageStatsResponse, AdminUsageQueryParams } from '@/api/admin/usage' import type { AdminUsageLog, TrendDataPoint, ModelStat } from '@/types'; import type { AdminUsageStatsResponse, AdminUsageQueryParams } from '@/api/admin/usage'
const { t } = useI18n() const { t } = useI18n()
const appStore = useAppStore() const appStore = useAppStore()
const usageStats = ref<AdminUsageStatsResponse | null>(null); const usageLogs = ref<UsageLog[]>([]); const loading = ref(false); const exporting = ref(false) const usageStats = ref<AdminUsageStatsResponse | null>(null); const usageLogs = ref<AdminUsageLog[]>([]); const loading = ref(false); const exporting = ref(false)
const trendData = ref<TrendDataPoint[]>([]); const modelStats = ref<ModelStat[]>([]); const chartsLoading = ref(false); const granularity = ref<'day' | 'hour'>('day') const trendData = ref<TrendDataPoint[]>([]); const modelStats = ref<ModelStat[]>([]); const chartsLoading = ref(false); const granularity = ref<'day' | 'hour'>('day')
let abortController: AbortController | null = null; let exportAbortController: AbortController | null = null let abortController: AbortController | null = null; let exportAbortController: AbortController | null = null
const exportProgress = reactive({ show: false, progress: 0, current: 0, total: 0, estimatedTime: '' }) const exportProgress = reactive({ show: false, progress: 0, current: 0, total: 0, estimatedTime: '' })
...@@ -92,7 +92,7 @@ const exportToExcel = async () => { ...@@ -92,7 +92,7 @@ const exportToExcel = async () => {
if (exporting.value) return; exporting.value = true; exportProgress.show = true if (exporting.value) return; exporting.value = true; exportProgress.show = true
const c = new AbortController(); exportAbortController = c const c = new AbortController(); exportAbortController = c
try { try {
const all: UsageLog[] = []; let p = 1; let total = pagination.total const all: AdminUsageLog[] = []; let p = 1; let total = pagination.total
while (true) { while (true) {
const res = await adminUsageAPI.list({ page: p, page_size: 100, ...filters.value }, { signal: c.signal }) const res = await adminUsageAPI.list({ page: p, page_size: 100, ...filters.value }, { signal: c.signal })
if (c.signal.aborted) break; if (p === 1) { total = res.total; exportProgress.total = total } if (c.signal.aborted) break; if (p === 1) { total = res.total; exportProgress.total = total }
......
...@@ -492,7 +492,7 @@ import Icon from '@/components/icons/Icon.vue' ...@@ -492,7 +492,7 @@ import Icon from '@/components/icons/Icon.vue'
const { t } = useI18n() const { t } = useI18n()
import { adminAPI } from '@/api/admin' import { adminAPI } from '@/api/admin'
import type { User, UserAttributeDefinition } from '@/types' import type { AdminUser, UserAttributeDefinition } from '@/types'
import type { BatchUserUsageStats } from '@/api/admin/dashboard' import type { BatchUserUsageStats } from '@/api/admin/dashboard'
import type { Column } from '@/components/common/types' import type { Column } from '@/components/common/types'
import AppLayout from '@/components/layout/AppLayout.vue' import AppLayout from '@/components/layout/AppLayout.vue'
...@@ -637,7 +637,7 @@ const columns = computed<Column[]>(() => ...@@ -637,7 +637,7 @@ const columns = computed<Column[]>(() =>
) )
) )
const users = ref<User[]>([]) const users = ref<AdminUser[]>([])
const loading = ref(false) const loading = ref(false)
const searchQuery = ref('') const searchQuery = ref('')
...@@ -736,16 +736,16 @@ const showEditModal = ref(false) ...@@ -736,16 +736,16 @@ const showEditModal = ref(false)
const showDeleteDialog = ref(false) const showDeleteDialog = ref(false)
const showApiKeysModal = ref(false) const showApiKeysModal = ref(false)
const showAttributesModal = ref(false) const showAttributesModal = ref(false)
const editingUser = ref<User | null>(null) const editingUser = ref<AdminUser | null>(null)
const deletingUser = ref<User | null>(null) const deletingUser = ref<AdminUser | null>(null)
const viewingUser = ref<User | null>(null) const viewingUser = ref<AdminUser | null>(null)
let abortController: AbortController | null = null let abortController: AbortController | null = null
// Action Menu State // Action Menu State
const activeMenuId = ref<number | null>(null) const activeMenuId = ref<number | null>(null)
const menuPosition = ref<{ top: number; left: number } | null>(null) const menuPosition = ref<{ top: number; left: number } | null>(null)
const openActionMenu = (user: User, e: MouseEvent) => { const openActionMenu = (user: AdminUser, e: MouseEvent) => {
if (activeMenuId.value === user.id) { if (activeMenuId.value === user.id) {
closeActionMenu() closeActionMenu()
} else { } else {
...@@ -821,11 +821,11 @@ const handleClickOutside = (event: MouseEvent) => { ...@@ -821,11 +821,11 @@ const handleClickOutside = (event: MouseEvent) => {
// Allowed groups modal state // Allowed groups modal state
const showAllowedGroupsModal = ref(false) const showAllowedGroupsModal = ref(false)
const allowedGroupsUser = ref<User | null>(null) const allowedGroupsUser = ref<AdminUser | null>(null)
// Balance (Deposit/Withdraw) modal state // Balance (Deposit/Withdraw) modal state
const showBalanceModal = ref(false) const showBalanceModal = ref(false)
const balanceUser = ref<User | null>(null) const balanceUser = ref<AdminUser | null>(null)
const balanceOperation = ref<'add' | 'subtract'>('add') const balanceOperation = ref<'add' | 'subtract'>('add')
// 计算剩余天数 // 计算剩余天数
...@@ -998,7 +998,7 @@ const applyFilter = () => { ...@@ -998,7 +998,7 @@ const applyFilter = () => {
loadUsers() loadUsers()
} }
const handleEdit = (user: User) => { const handleEdit = (user: AdminUser) => {
editingUser.value = user editingUser.value = user
showEditModal.value = true showEditModal.value = true
} }
...@@ -1008,7 +1008,7 @@ const closeEditModal = () => { ...@@ -1008,7 +1008,7 @@ const closeEditModal = () => {
editingUser.value = null editingUser.value = null
} }
const handleToggleStatus = async (user: User) => { const handleToggleStatus = async (user: AdminUser) => {
const newStatus = user.status === 'active' ? 'disabled' : 'active' const newStatus = user.status === 'active' ? 'disabled' : 'active'
try { try {
await adminAPI.users.toggleStatus(user.id, newStatus) await adminAPI.users.toggleStatus(user.id, newStatus)
...@@ -1022,7 +1022,7 @@ const handleToggleStatus = async (user: User) => { ...@@ -1022,7 +1022,7 @@ const handleToggleStatus = async (user: User) => {
} }
} }
const handleViewApiKeys = (user: User) => { const handleViewApiKeys = (user: AdminUser) => {
viewingUser.value = user viewingUser.value = user
showApiKeysModal.value = true showApiKeysModal.value = true
} }
...@@ -1032,7 +1032,7 @@ const closeApiKeysModal = () => { ...@@ -1032,7 +1032,7 @@ const closeApiKeysModal = () => {
viewingUser.value = null viewingUser.value = null
} }
const handleAllowedGroups = (user: User) => { const handleAllowedGroups = (user: AdminUser) => {
allowedGroupsUser.value = user allowedGroupsUser.value = user
showAllowedGroupsModal.value = true showAllowedGroupsModal.value = true
} }
...@@ -1042,7 +1042,7 @@ const closeAllowedGroupsModal = () => { ...@@ -1042,7 +1042,7 @@ const closeAllowedGroupsModal = () => {
allowedGroupsUser.value = null allowedGroupsUser.value = null
} }
const handleDelete = (user: User) => { const handleDelete = (user: AdminUser) => {
deletingUser.value = user deletingUser.value = user
showDeleteDialog.value = true showDeleteDialog.value = true
} }
...@@ -1061,13 +1061,13 @@ const confirmDelete = async () => { ...@@ -1061,13 +1061,13 @@ const confirmDelete = async () => {
} }
} }
const handleDeposit = (user: User) => { const handleDeposit = (user: AdminUser) => {
balanceUser.value = user balanceUser.value = user
balanceOperation.value = 'add' balanceOperation.value = 'add'
showBalanceModal.value = true showBalanceModal.value = true
} }
const handleWithdraw = (user: User) => { const handleWithdraw = (user: AdminUser) => {
balanceUser.value = user balanceUser.value = user
balanceOperation.value = 'subtract' balanceOperation.value = 'subtract'
showBalanceModal.value = true showBalanceModal.value = true
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment