9501751792
三轮审查修复(60+ 项),相对远端 main(b536672)的关键变更:
- 安全: 数据面 SSRF 拨号防护(防 DNS rebinding)/上游凭据剥离/登录防枚举
与锁定态统一/可信代理(X-Forwarded-For)限流加固/会话版本失效机制/
撤销即时传播/弱密钥拒绝启动/脱敏字节级重写(保签名契约)
- 业务逻辑: 裸 body 上传 panic/bootstrap 审计管线卡死/定价通配符优先级/
全局工具可见性/调度器停机补跑/TOTP 挑战令牌消费顺序/熔断探针语义/
>4MB 响应 token 计量/管理员重置密码作废会话 等
- 前端: 新 logo(语枢 AI 网关主题)/Provider 凭据异常警示/删除入口/
后端错误消息透传/localStorage 敏感数据收敛
- 部署: CREDENTIAL_MASTER_KEY 持久化与弱值拒绝/Provider DELETE 接口/
nginx 安全头/worker 内存限制
- 新增迁移 000029(key_hash 索引)/000030(usage_daily 币种维度)
68 lines
5.6 KiB
TypeScript
68 lines
5.6 KiB
TypeScript
import axios from 'axios'
|
||
import request from '@/utils/http'
|
||
import { useUserStore } from '@/store/modules/user'
|
||
|
||
export interface Application { id:string;code:string;name:string;description:string;published_version?:number }
|
||
export interface Knowledge { id:string;name:string;description:string;document_count:number;chunk_count:number }
|
||
export interface Tool { id:string;code:string;name:string;description:string;input_schema:Record<string,unknown> }
|
||
export interface PromptVersion { version:number;content:string;variables:Array<{name:string;label?:string;required:boolean}> }
|
||
export interface Prompt { id:string;name:string;description:string;category_name:string;tags:string[];favorite:boolean;current?:PromptVersion }
|
||
export interface Catalog { applications:Application[];knowledge:Knowledge[];tools:Tool[];prompts:Prompt[] }
|
||
export interface Model { provider_code:string;id:string;owned_by:string }
|
||
export interface ModelRequest { id:string;provider_code:string;model:string;reason:string;requested_rpm:number;requested_monthly_tokens:number;status:string;decision_note:string;created_at:string }
|
||
export interface Stats { days:number;requests:number;failed_requests:number;prompt_tokens:number;completion_tokens:number;total_tokens:number;cost_microunits:number;avg_latency_ms?:number }
|
||
export interface AuditEvent { id:string;request_id:string;provider_code?:string;model?:string;protocol:string;status_code?:number;prompt_tokens?:number;completion_tokens?:number;cost_microunits?:number;latency_ms?:number;recorded_at:string }
|
||
|
||
export const fetchCatalog=()=>request.get<Catalog>({url:'/api/v1/portal/catalog'})
|
||
export const fetchPrompts=(q='')=>request.get<Prompt[]>({url:'/api/v1/portal/prompts',params:{q}})
|
||
export const setPromptFavorite=(id:string,value:boolean)=>value?request.post({url:`/api/v1/portal/prompts/${id}/favorite`}):request.del({url:`/api/v1/portal/prompts/${id}/favorite`})
|
||
export const fetchModels=()=>request.get<Model[]>({url:'/api/v1/portal/model-requests/available'})
|
||
export const fetchModelRequests=()=>request.get<ModelRequest[]>({url:'/api/v1/portal/model-requests'})
|
||
export const createModelRequest=(params:Pick<ModelRequest,'provider_code'|'model'|'reason'|'requested_rpm'|'requested_monthly_tokens'>)=>request.post<ModelRequest>({url:'/api/v1/portal/model-requests',params})
|
||
export const fetchStats=(days:number)=>request.get<Stats>({url:'/api/v1/portal/stats',params:{days}})
|
||
export const fetchLogs=(days:number,limit=50)=>request.get<{items:AuditEvent[]}>({url:'/api/v1/portal/logs',params:{days,limit}})
|
||
export const changePassword=(params:{old_password:string;new_password:string})=>request.post({url:'/api/v1/portal/password',params})
|
||
|
||
// --- 站内消息(M8 P4)---
|
||
export interface InboxMessage { id:string;recipient_kind:string;recipient_user_id:string;sender_type:string;category:string;title:string;body:string;link:string;payload?:Record<string,unknown>;read_at?:string;created_at:string }
|
||
export const fetchInbox=()=>request.get<InboxMessage[]>({url:'/api/v1/portal/inbox'})
|
||
export const fetchInboxUnread=()=>request.get<{unread:number}>({url:'/api/v1/portal/inbox/unread'})
|
||
export const markInboxRead=(id:string)=>request.post<{read:boolean}>({url:`/api/v1/portal/inbox/${id}/read`})
|
||
export const markInboxAllRead=()=>request.post<{read_all:number}>({url:'/api/v1/portal/inbox/read-all'})
|
||
|
||
// --- 个人文件仓库(M8 对象存储)---
|
||
export interface FileObject { id:string;original_name:string;content_type:string;size_bytes:number;content_sha256:string;scope:string;created_at:string;updated_at:string }
|
||
export const fetchMyFiles=()=>request.get<FileObject[]>({url:'/api/v1/portal/files'})
|
||
export const uploadMyFile=(file:File)=>{
|
||
const form=new FormData()
|
||
form.append('file',file)
|
||
return request.post<FileObject>({url:'/api/v1/portal/files',data:form,timeout:120000})
|
||
}
|
||
export const deleteMyFile=(id:string)=>request.del({url:`/api/v1/portal/files/${id}`})
|
||
export async function downloadMyFile(id:string,filename:string){
|
||
const { accessToken } = useUserStore()
|
||
const token = accessToken.startsWith('Bearer ')?accessToken:`Bearer ${accessToken}`
|
||
const res = await axios.get(`/api/v1/portal/files/${id}/download`,{
|
||
baseURL:import.meta.env.VITE_API_URL,
|
||
headers:{Authorization:token},
|
||
responseType:'blob'
|
||
})
|
||
const url = URL.createObjectURL(res.data)
|
||
const anchor = document.createElement('a')
|
||
anchor.href = url
|
||
anchor.download = filename
|
||
anchor.click()
|
||
URL.revokeObjectURL(url)
|
||
}
|
||
|
||
|
||
// --- 资源市场 ---
|
||
export interface MarketItem { type:string;code:string;name:string;description:string;category_id?:string;category_name:string;tags:string[];department_ids:string[];updated_at:string }
|
||
export interface MarketplaceCategory { id:string;name:string;description:string;resource_type:string;sort_order:number }
|
||
export const fetchMarketplace=(params?:{type?:string;category_id?:string;tag?:string;q?:string})=>request.get<MarketItem[]>({url:'/api/v1/portal/marketplace',params})
|
||
export const fetchMarketplaceCategories=()=>request.get<MarketplaceCategory[]>({url:'/api/v1/portal/marketplace/categories'})
|
||
export const fetchMarketplaceInstalled=()=>request.get<MarketItem[]>({url:'/api/v1/portal/marketplace/installed'})
|
||
export const fetchMarketplaceDetail=(type:string,code:string)=>request.get<{item:MarketItem;detail:Record<string,unknown>}>({url:`/api/v1/portal/marketplace/${type}/${code}`})
|
||
export const marketplaceInstall=(type:string,code:string)=>request.post<{installed:boolean;created:boolean}>({url:`/api/v1/portal/marketplace/${type}/${code}/install`})
|
||
export const marketplaceUninstall=(type:string,code:string)=>request.del<{installed:boolean}>({url:`/api/v1/portal/marketplace/${type}/${code}/install`})
|