Files
ai-gateway-go/web/apps/portal/src/api/portal.ts
T
superidou 9501751792 0.10.1: 安全与业务逻辑加固、新品牌与部署加固
三轮审查修复(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 币种维度)
2026-08-13 10:50:51 +08:00

68 lines
5.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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`})