mirror of
https://github.com/APIParkLab/APIPark.git
synced 2026-06-14 20:41:15 +08:00
Compare commits
53 Commits
fix/152
...
v1.4.4-beta
| Author | SHA1 | Date | |
|---|---|---|---|
| 901bef1463 | |||
| 8d44d796b4 | |||
| 5a10ad478e | |||
| fd6680d615 | |||
| e03cdfc42b | |||
| 945d53fcfd | |||
| ac7045b724 | |||
| c907bdc4a5 | |||
| 733ed9ac2f | |||
| 1d8e579a10 | |||
| 567cac9c95 | |||
| 095c09c8c0 | |||
| e9c949822d | |||
| 3482d5416c | |||
| d8cb4a0c94 | |||
| 59acfa7a47 | |||
| 2eb2e690d1 | |||
| f7801261c3 | |||
| 7e7be7f040 | |||
| 0187fd16b2 | |||
| ba0bdb5e99 | |||
| 9d3e4f07bf | |||
| bd81d7584d | |||
| 9577339e14 | |||
| 5c292ef1cb | |||
| 4f3de85068 | |||
| 07a25c9643 | |||
| 8f60426b4c | |||
| 37f87615bd | |||
| 3f96de660b | |||
| e86999770f | |||
| a8bb0c24ec | |||
| 6ba2a08b62 | |||
| d232269416 | |||
| 9d2208e14d | |||
| 8d69d45d1d | |||
| b0c37918b5 | |||
| d5af1c8da3 | |||
| a6105cfc3c | |||
| 7c827804f4 | |||
| b0dacbda0d | |||
| d5abde2593 | |||
| bc3290de3b | |||
| 7f438bf776 | |||
| 13cfe24b2f | |||
| f5cfd77550 | |||
| 4a8f5152b3 | |||
| 83ac747cb1 | |||
| d5eedd1dd2 | |||
| 86758383c4 | |||
| 6ce3e0bfac | |||
| e4eadf863e | |||
| ca328e784c |
@@ -25,7 +25,7 @@ jobs:
|
||||
echo "Build frontend..."
|
||||
cd ./frontend && pnpm run build
|
||||
- name: upload frontend release
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: frontend-package
|
||||
path: frontend/dist
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
- name: Checkout #Checkout代码
|
||||
uses: actions/checkout@v3
|
||||
- name: download frontend release
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: frontend-package
|
||||
path: frontend/dist
|
||||
@@ -71,7 +71,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: download frontend release
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: frontend-package
|
||||
path: frontend/dist
|
||||
|
||||
@@ -210,7 +210,7 @@ APIPark uses the Apache 2.0 License. For more details, please refer to the LICEN
|
||||
For enterprise-level features and professional technical support, contact our pre-sales experts for personalized demos, customized solutions, and pricing.
|
||||
|
||||
- Website: https://apipark.com
|
||||
- Email: dev@apipark.com
|
||||
- Email: contact@apipark.com
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
+17
-11
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -27,8 +28,8 @@ func init() {
|
||||
}
|
||||
|
||||
type NSQConfig struct {
|
||||
Addr string `json:"addr"`
|
||||
TopicPrefix string `json:"topic_prefix"`
|
||||
Addr string `json:"addr" yaml:"addr"`
|
||||
TopicPrefix string `json:"topic_prefix" yaml:"topic_prefix"`
|
||||
}
|
||||
|
||||
// 定义 NSQ 消息结构
|
||||
@@ -78,6 +79,11 @@ func convertInt(value interface{}) int {
|
||||
}
|
||||
}
|
||||
|
||||
func genAIKey(key string, provider string) string {
|
||||
keys := strings.Split(key, "@")
|
||||
return strings.TrimSuffix(keys[0], fmt.Sprintf("-%s", provider))
|
||||
}
|
||||
|
||||
// HandleMessage 处理从 NSQ 读取的消息
|
||||
func (h *NSQHandler) HandleMessage(message *nsq.Message) error {
|
||||
log.Printf("Received message: %s", string(message.Body))
|
||||
@@ -87,14 +93,14 @@ func (h *NSQHandler) HandleMessage(message *nsq.Message) error {
|
||||
err := json.Unmarshal(message.Body, &data)
|
||||
if err != nil {
|
||||
log.Printf("Failed to unmarshal message: %v", err)
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
// 将时间字符串转换为 time.Time
|
||||
timestamp, err := time.Parse(time.RFC3339, data.TimeISO8601)
|
||||
if err != nil {
|
||||
log.Printf("Failed to parse timestamp: %v", err)
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
day := time.Date(timestamp.Year(), timestamp.Month(), timestamp.Day(), 0, 0, 0, 0, timestamp.Location())
|
||||
@@ -104,14 +110,13 @@ func (h *NSQHandler) HandleMessage(message *nsq.Message) error {
|
||||
finalStatus := &AIProviderStatus{}
|
||||
for _, s := range data.AI.ProviderStats {
|
||||
status := ToKeyStatus(s.Status).Int()
|
||||
keys := strings.Split(s.Key, "@")
|
||||
key := keys[0]
|
||||
key := genAIKey(s.Key, s.Provider)
|
||||
err = h.aiKeyService.Save(ctx, key, &ai_key.Edit{
|
||||
Status: &status,
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Failed to save AI key: %v", err)
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
if s.Provider != data.AI.Provider {
|
||||
|
||||
@@ -128,11 +133,12 @@ func (h *NSQHandler) HandleMessage(message *nsq.Message) error {
|
||||
finalStatus = &s
|
||||
}
|
||||
if finalStatus != nil {
|
||||
keys := strings.Split(finalStatus.Key, "@")
|
||||
err = h.aiKeyService.IncrUseToken(ctx, keys[0], convertInt(data.AI.TotalToken))
|
||||
//keys := strings.Split(finalStatus.Key, "@")
|
||||
key := genAIKey(finalStatus.Key, finalStatus.Provider)
|
||||
err = h.aiKeyService.IncrUseToken(ctx, key, convertInt(data.AI.TotalToken))
|
||||
if err != nil {
|
||||
log.Printf("Failed to increment AI key token: %v", err)
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,7 +157,7 @@ func (h *NSQHandler) HandleMessage(message *nsq.Message) error {
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Failed to call AI API: %v", err)
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Printf("Message processed and saved to MySQL: %+v", data)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { PERMISSION_DEFINITION } from '@common/const/permissions'
|
||||
import { $t } from '@common/locales'
|
||||
import { Button, Tooltip, Upload } from 'antd'
|
||||
import { ReactElement, cloneElement, useEffect, useMemo, useState } from 'react'
|
||||
import { useGlobalContext } from '../../contexts/GlobalStateContext'
|
||||
import { PERMISSION_DEFINITION } from '@common/const/permissions'
|
||||
import { $t } from '@common/locales'
|
||||
|
||||
type WithPermissionProps = {
|
||||
access?: string | string[]
|
||||
|
||||
@@ -18,7 +18,11 @@ export const checkAccess: (access: AccessDataType, accessData: Map<string, strin
|
||||
if (accLevel === 'team') {
|
||||
accessSet = new Set(Array.from(accessSet).concat(accessData?.get('team') || []))
|
||||
}
|
||||
return accessSet!.size > 0 ? hasIntersection(neededBackendAccessArr, accessSet) : false
|
||||
if (!accessSet!.size) {
|
||||
return false
|
||||
}
|
||||
const hasAccess = hasIntersection(neededBackendAccessArr, accessSet)
|
||||
return hasAccess
|
||||
}
|
||||
|
||||
const hasIntersection = (arr1: string[], set1: Set<string>) => {
|
||||
|
||||
@@ -151,15 +151,15 @@ function App() {
|
||||
form={{ validateMessages }}
|
||||
>
|
||||
<PluginEventHubProvider>
|
||||
<AppAntd className="h-full" message={{ maxCount: 1 }}>
|
||||
<PluginSlotHubProvider>
|
||||
<GlobalProvider>
|
||||
<GlobalProvider>
|
||||
<AppAntd className="h-full" message={{ maxCount: 1 }}>
|
||||
<PluginSlotHubProvider>
|
||||
<BreadcrumbProvider>
|
||||
<RenderRoutes />
|
||||
</BreadcrumbProvider>
|
||||
</GlobalProvider>
|
||||
</PluginSlotHubProvider>
|
||||
</AppAntd>
|
||||
</PluginSlotHubProvider>
|
||||
</AppAntd>
|
||||
</GlobalProvider>
|
||||
</PluginEventHubProvider>
|
||||
</ConfigProvider>
|
||||
</StyleProvider>
|
||||
|
||||
@@ -27,6 +27,7 @@ export const KeyStatusNode: React.FC<{ data: KeyStatusNodeData }> = ({ data }) =
|
||||
<div
|
||||
className="flex gap-1 w-full"
|
||||
style={{
|
||||
minWidth: keys.length > 5 ? '118px' : 'auto',
|
||||
maxWidth: `calc(${MAX_KEYS} * ${KEY_SIZE} + (${MAX_KEYS} - 1) * ${KEY_GAP})`,
|
||||
minHeight: KEY_SIZE
|
||||
}}
|
||||
|
||||
@@ -29,46 +29,48 @@ export const ModelCardNode: React.FC<{ data: ModelCardNodeData }> = ({ data }) =
|
||||
const statusConfig = getStatusIcon(status)
|
||||
|
||||
return (
|
||||
<div
|
||||
className="node-card bg-white rounded-lg shadow-sm p-4 min-w-[280px] group"
|
||||
style={{ border: '1px solid var(--border-color)' }}
|
||||
>
|
||||
<Handle type="target" position={Position.Left} />
|
||||
<Handle type="source" position={Position.Right} />
|
||||
<div>
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex gap-2 items-center">
|
||||
<div className="flex flex-1 overflow-hidden items-center gap-[4px]">
|
||||
<span
|
||||
className="flex items-center h-[22px] ai-setting-svg-container"
|
||||
dangerouslySetInnerHTML={{ __html: logo }}
|
||||
></span>
|
||||
<>
|
||||
<div
|
||||
className="node-card bg-white rounded-lg shadow-sm p-4 min-w-[280px] group"
|
||||
style={{ border: '1px solid var(--border-color)' }}
|
||||
>
|
||||
<Handle type="target" position={Position.Left} />
|
||||
<Handle type="source" position={Position.Right} />
|
||||
<div>
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex gap-2 items-center">
|
||||
<div className="flex flex-1 overflow-hidden items-center gap-[4px]">
|
||||
<span
|
||||
className="flex items-center h-[22px] ai-setting-svg-container"
|
||||
dangerouslySetInnerHTML={{ __html: logo }}
|
||||
></span>
|
||||
</div>
|
||||
<span className="text-base text-gray-900 max-w-[180px] truncate">{name}</span>
|
||||
<Icon icon={statusConfig?.icon} className={`text-xl ${statusConfig?.color}`} />
|
||||
</div>
|
||||
<span className="text-base text-gray-900 max-w-[180px] truncate">{name}</span>
|
||||
<Icon icon={statusConfig?.icon} className={`text-xl ${statusConfig?.color}`} />
|
||||
</div>
|
||||
|
||||
{/* Action buttons */}
|
||||
<div className="flex gap-2 transition-opacity duration-200">
|
||||
<Icon
|
||||
icon="mdi:cog"
|
||||
className="text-xl text-gray-400 cursor-pointer hover:text-[--primary-color]"
|
||||
onClick={() => {
|
||||
openConfigModal({ id: data.id, defaultLlm: defaultLlm } as AiSettingListItem)
|
||||
}}
|
||||
/>
|
||||
{/* Action buttons */}
|
||||
<div className="flex gap-2 transition-opacity duration-200">
|
||||
<Icon
|
||||
icon="mdi:cog"
|
||||
className="text-xl text-gray-400 cursor-pointer hover:text-[--primary-color]"
|
||||
onClick={() => {
|
||||
openConfigModal({ id: data.id, defaultLlm: defaultLlm } as AiSettingListItem)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-2 text-sm text-gray-500">
|
||||
{$t('默认:')}
|
||||
{defaultLlm}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-2 text-sm text-gray-500">
|
||||
{$t('默认:')}
|
||||
{defaultLlm}
|
||||
</div>
|
||||
{status !== 'enabled' && alternativeModel && (
|
||||
<div className="mt-1 text-sm text-gray-500">
|
||||
{$t('关联 API 已转用')} {alternativeModel.name}/{alternativeModel.defaultLlm}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{status !== 'enabled' && alternativeModel && (
|
||||
<div className="ml-4 mt-1 text-sm text-gray-500">
|
||||
{$t('关联 API 已转用')} {alternativeModel.name}/{alternativeModel.defaultLlm}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,11 +18,15 @@
|
||||
.react-flow__node {
|
||||
padding: 0;
|
||||
border-radius: 8px;
|
||||
min-width: 150px;
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.react-flow__node-modelCard,
|
||||
.react-flow__node-serviceCard {
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
/* Custom Node Styles */
|
||||
.custom-node {
|
||||
background: white;
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function ServiceCategory() {
|
||||
const { accessData } = useGlobalContext()
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
|
||||
const onDrop: TreeProps['onDrop'] = info => {
|
||||
const onDrop: TreeProps['onDrop'] = (info) => {
|
||||
const dropKey = info.node.key
|
||||
const dragKey = info.dragNode.key
|
||||
const dropPos = info.node.pos.split('-')
|
||||
@@ -59,7 +59,7 @@ export default function ServiceCategory() {
|
||||
|
||||
if (!info.dropToGap) {
|
||||
// Drop on the content
|
||||
loop(data, dropKey, item => {
|
||||
loop(data, dropKey, (item) => {
|
||||
item.children = item.children || []
|
||||
// where to insert. New item was inserted to the start of the array in this example, but can be anywhere
|
||||
item.children.unshift(dragObj)
|
||||
@@ -129,9 +129,9 @@ export default function ServiceCategory() {
|
||||
const treeData = useMemo(() => {
|
||||
setExpandedKeys([])
|
||||
const loop = (data: CategorizesType[]): DataNode[] =>
|
||||
data?.map(item => {
|
||||
data?.map((item) => {
|
||||
if (item.children) {
|
||||
setExpandedKeys(prev => [...prev, item.id])
|
||||
setExpandedKeys((prev) => [...prev, item.id])
|
||||
return {
|
||||
title: (
|
||||
<TreeWithMore stopClick={false} dropdownMenu={dropdownMenu(item as CategorizesType)}>
|
||||
@@ -169,40 +169,22 @@ export default function ServiceCategory() {
|
||||
return !checkAccess(permission, accessData)
|
||||
}
|
||||
|
||||
const openModal = (
|
||||
type: 'addCate' | 'addChildCate' | 'renameCate' | 'delete',
|
||||
entity?: CategorizesType
|
||||
) => {
|
||||
const openModal = (type: 'addCate' | 'addChildCate' | 'renameCate' | 'delete', entity?: CategorizesType) => {
|
||||
let title: string = ''
|
||||
let content: string | React.ReactNode = ''
|
||||
switch (type) {
|
||||
case 'addCate':
|
||||
case 'addCate': {
|
||||
title = $t('添加分类')
|
||||
content = (
|
||||
<ServiceHubCategoryConfig WithPermission={WithPermission} ref={addRef} type={type} />
|
||||
)
|
||||
content = <ServiceHubCategoryConfig ref={addRef} type={type} />
|
||||
break
|
||||
}
|
||||
case 'addChildCate':
|
||||
title = $t('添加子分类')
|
||||
content = (
|
||||
<ServiceHubCategoryConfig
|
||||
WithPermission={WithPermission}
|
||||
ref={addChildRef}
|
||||
type={type}
|
||||
entity={entity}
|
||||
/>
|
||||
)
|
||||
content = <ServiceHubCategoryConfig ref={addChildRef} type={type} entity={entity} />
|
||||
break
|
||||
case 'renameCate':
|
||||
title = $t('重命名分类')
|
||||
content = (
|
||||
<ServiceHubCategoryConfig
|
||||
WithPermission={WithPermission}
|
||||
ref={renameRef}
|
||||
type={type}
|
||||
entity={entity}
|
||||
/>
|
||||
)
|
||||
content = <ServiceHubCategoryConfig ref={renameRef} type={type} entity={entity} />
|
||||
break
|
||||
case 'delete':
|
||||
title = $t('删除')
|
||||
@@ -215,19 +197,19 @@ export default function ServiceCategory() {
|
||||
onOk: () => {
|
||||
switch (type) {
|
||||
case 'addCate':
|
||||
return addRef.current?.save().then(res => {
|
||||
return addRef.current?.save().then((res) => {
|
||||
if (res === true) getCategoryList()
|
||||
})
|
||||
case 'addChildCate':
|
||||
return addChildRef.current?.save().then(res => {
|
||||
return addChildRef.current?.save().then((res) => {
|
||||
if (res === true) getCategoryList()
|
||||
})
|
||||
case 'renameCate':
|
||||
return renameRef.current?.save().then(res => {
|
||||
return renameRef.current?.save().then((res) => {
|
||||
if (res === true) getCategoryList()
|
||||
})
|
||||
case 'delete':
|
||||
return deleteCate(entity!).then(res => {
|
||||
return deleteCate(entity!).then((res) => {
|
||||
if (res === true) getCategoryList()
|
||||
})
|
||||
}
|
||||
@@ -249,7 +231,7 @@ export default function ServiceCategory() {
|
||||
method: 'DELETE',
|
||||
eoParams: { catalogue: entity.id }
|
||||
})
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
const { code, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
message.success(msg || $t(RESPONSE_TIPS.success))
|
||||
@@ -259,14 +241,14 @@ export default function ServiceCategory() {
|
||||
reject(msg || $t(RESPONSE_TIPS.error))
|
||||
}
|
||||
})
|
||||
.catch(errorInfo => reject(errorInfo))
|
||||
.catch((errorInfo) => reject(errorInfo))
|
||||
})
|
||||
}
|
||||
|
||||
const sortCategories = (newData: CategorizesType[]) => {
|
||||
setLoading(true)
|
||||
fetchData<BasicResponse<null>>('catalogue/sort', { method: 'PUT', eoBody: newData })
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
const { code, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
getCategoryList()
|
||||
@@ -288,7 +270,7 @@ export default function ServiceCategory() {
|
||||
fetchData<BasicResponse<{ catalogues: CategorizesType[]; tags: EntityItem[] }>>('catalogues', {
|
||||
method: 'GET'
|
||||
})
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
const { code, data, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
setGData(data.catalogues)
|
||||
@@ -308,11 +290,7 @@ export default function ServiceCategory() {
|
||||
|
||||
return (
|
||||
<div className="border border-solid border-BORDER p-[20px] rounded-[10px] ">
|
||||
<Spin
|
||||
indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />}
|
||||
spinning={loading}
|
||||
className=""
|
||||
>
|
||||
<Spin indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />} spinning={loading} className="">
|
||||
<Tree
|
||||
showIcon
|
||||
draggable
|
||||
|
||||
@@ -10,132 +10,131 @@ import {
|
||||
import { App, Form, Input } from 'antd'
|
||||
import { forwardRef, useEffect, useImperativeHandle } from 'react'
|
||||
|
||||
export const ServiceHubCategoryConfig = forwardRef<
|
||||
ServiceHubCategoryConfigHandle,
|
||||
ServiceHubCategoryConfigProps
|
||||
>((props, ref) => {
|
||||
const { message } = App.useApp()
|
||||
const [form] = Form.useForm()
|
||||
const { type, entity } = props
|
||||
const { fetchData } = useFetch()
|
||||
export const ServiceHubCategoryConfig = forwardRef<ServiceHubCategoryConfigHandle, ServiceHubCategoryConfigProps>(
|
||||
(props, ref) => {
|
||||
const { message } = App.useApp()
|
||||
const [form] = Form.useForm()
|
||||
const { type, entity } = props
|
||||
const { fetchData } = useFetch()
|
||||
|
||||
const save: () => Promise<boolean | string> = () => {
|
||||
const url: string = 'catalogue'
|
||||
let method: string
|
||||
switch (type) {
|
||||
case 'addCate':
|
||||
case 'addChildCate':
|
||||
method = 'POST'
|
||||
break
|
||||
case 'renameCate':
|
||||
method = 'PUT'
|
||||
break
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!url || !method) {
|
||||
reject($t(RESPONSE_TIPS.error))
|
||||
return
|
||||
const save: () => Promise<boolean | string> = () => {
|
||||
const url: string = 'catalogue'
|
||||
let method: string
|
||||
switch (type) {
|
||||
case 'addCate':
|
||||
case 'addChildCate':
|
||||
method = 'POST'
|
||||
break
|
||||
case 'renameCate':
|
||||
method = 'PUT'
|
||||
break
|
||||
}
|
||||
form
|
||||
.validateFields()
|
||||
.then(value => {
|
||||
fetchData<BasicResponse<null>>(url, {
|
||||
method,
|
||||
eoBody: value,
|
||||
eoParams: { ...(type === 'renameCate' ? { catalogue: value.id } : undefined) }
|
||||
})
|
||||
.then(response => {
|
||||
const { code, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
message.success(msg || $t(RESPONSE_TIPS.success))
|
||||
resolve(true)
|
||||
} else {
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
reject(msg || $t(RESPONSE_TIPS.error))
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!url || !method) {
|
||||
reject($t(RESPONSE_TIPS.error))
|
||||
return
|
||||
}
|
||||
form
|
||||
.validateFields()
|
||||
.then((value) => {
|
||||
fetchData<BasicResponse<null>>(url, {
|
||||
method,
|
||||
eoBody: value,
|
||||
eoParams: { ...(type === 'renameCate' ? { catalogue: value.id } : undefined) }
|
||||
})
|
||||
.catch(errorInfo => reject(errorInfo))
|
||||
})
|
||||
.catch(errorInfo => reject(errorInfo))
|
||||
})
|
||||
}
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
save
|
||||
}))
|
||||
|
||||
useEffect(() => {
|
||||
switch (type) {
|
||||
case 'addCate':
|
||||
form.setFieldsValue({})
|
||||
break
|
||||
case 'addChildCate':
|
||||
form.setFieldsValue({ parent: entity!.id })
|
||||
break
|
||||
case 'renameCate':
|
||||
form.setFieldsValue(entity)
|
||||
break
|
||||
.then((response) => {
|
||||
const { code, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
message.success(msg || $t(RESPONSE_TIPS.success))
|
||||
resolve(true)
|
||||
} else {
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
reject(msg || $t(RESPONSE_TIPS.error))
|
||||
}
|
||||
})
|
||||
.catch((errorInfo) => reject(errorInfo))
|
||||
})
|
||||
.catch((errorInfo) => reject(errorInfo))
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<WithPermission
|
||||
access={
|
||||
type === 'addCate'
|
||||
? 'system.api_market.service_classification.add'
|
||||
: 'system.api_market.service_classification.edit'
|
||||
useImperativeHandle(ref, () => ({
|
||||
save
|
||||
}))
|
||||
|
||||
useEffect(() => {
|
||||
switch (type) {
|
||||
case 'addCate':
|
||||
form.setFieldsValue({})
|
||||
break
|
||||
case 'addChildCate':
|
||||
form.setFieldsValue({ parent: entity!.id })
|
||||
break
|
||||
case 'renameCate':
|
||||
form.setFieldsValue(entity)
|
||||
break
|
||||
}
|
||||
>
|
||||
<Form
|
||||
layout="vertical"
|
||||
scrollToFirstError
|
||||
labelAlign="left"
|
||||
form={form}
|
||||
className="mx-auto"
|
||||
name="serviceHubCategoryConfig"
|
||||
autoComplete="off"
|
||||
>
|
||||
{type === 'renameCate' && (
|
||||
<Form.Item<ServiceHubCategoryConfigFieldType>
|
||||
label={$t('ID')}
|
||||
name="id"
|
||||
hidden
|
||||
rules={[{ required: true, whitespace: true }]}
|
||||
>
|
||||
<Input className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.input)} />
|
||||
</Form.Item>
|
||||
)}
|
||||
{(type === 'addCate' || type === 'renameCate') && (
|
||||
<Form.Item<ServiceHubCategoryConfigFieldType>
|
||||
label={$t('分类名称')}
|
||||
name="name"
|
||||
rules={[{ required: true, whitespace: true }]}
|
||||
>
|
||||
<Input className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.input)} />
|
||||
</Form.Item>
|
||||
)}
|
||||
}, [])
|
||||
|
||||
{type === 'addChildCate' && (
|
||||
<>
|
||||
return (
|
||||
<WithPermission
|
||||
access={
|
||||
type === 'addCate'
|
||||
? 'system.api_market.service_classification.add'
|
||||
: 'system.api_market.service_classification.edit'
|
||||
}
|
||||
>
|
||||
<Form
|
||||
layout="vertical"
|
||||
scrollToFirstError
|
||||
labelAlign="left"
|
||||
form={form}
|
||||
className="mx-auto"
|
||||
name="serviceHubCategoryConfig"
|
||||
autoComplete="off"
|
||||
>
|
||||
{type === 'renameCate' && (
|
||||
<Form.Item<ServiceHubCategoryConfigFieldType>
|
||||
label={$t('父分类 ID')}
|
||||
name="parent"
|
||||
label={$t('ID')}
|
||||
name="id"
|
||||
hidden
|
||||
rules={[{ required: true, whitespace: true }]}
|
||||
>
|
||||
<Input className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.input)} />
|
||||
</Form.Item>
|
||||
|
||||
)}
|
||||
{(type === 'addCate' || type === 'renameCate') && (
|
||||
<Form.Item<ServiceHubCategoryConfigFieldType>
|
||||
label={$t('子分类名称')}
|
||||
label={$t('分类名称')}
|
||||
name="name"
|
||||
rules={[{ required: true, whitespace: true }]}
|
||||
>
|
||||
<Input className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.input)} />
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
</WithPermission>
|
||||
)
|
||||
})
|
||||
)}
|
||||
|
||||
{type === 'addChildCate' && (
|
||||
<>
|
||||
<Form.Item<ServiceHubCategoryConfigFieldType>
|
||||
label={$t('父分类 ID')}
|
||||
name="parent"
|
||||
hidden
|
||||
rules={[{ required: true, whitespace: true }]}
|
||||
>
|
||||
<Input className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.input)} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item<ServiceHubCategoryConfigFieldType>
|
||||
label={$t('子分类名称')}
|
||||
name="name"
|
||||
rules={[{ required: true, whitespace: true }]}
|
||||
>
|
||||
<Input className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.input)} />
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
</WithPermission>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { DefaultOptionType } from 'antd/es/select'
|
||||
import { EntityItem } from '@common/const/type'
|
||||
import { SubscribeEnum, SubscribeFromEnum } from '@core/const/system/const'
|
||||
import WithPermission from '@common/components/aoplatform/WithPermission'
|
||||
import { DefaultOptionType } from 'antd/es/select'
|
||||
|
||||
export type ServiceBasicInfoType = {
|
||||
app: EntityItem
|
||||
@@ -37,7 +36,6 @@ export type ServiceHubCategoryConfigFieldType = {
|
||||
export type ServiceHubCategoryConfigProps = {
|
||||
type: 'addCate' | 'addChildCate' | 'renameCate'
|
||||
entity?: { [k: string]: unknown }
|
||||
WithPermission: typeof WithPermission
|
||||
}
|
||||
|
||||
export type ServiceHubCategoryConfigHandle = {
|
||||
|
||||
@@ -5,7 +5,7 @@ import WithPermission from '@common/components/aoplatform/WithPermission'
|
||||
import { BasicResponse, DATA_SHOW_TYPE_OPTIONS, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const'
|
||||
import { SimpleTeamItem } from '@common/const/type'
|
||||
import { useBreadcrumb } from '@common/contexts/BreadcrumbContext'
|
||||
import { GlobalProvider, useGlobalContext } from '@common/contexts/GlobalStateContext'
|
||||
import { useGlobalContext } from '@common/contexts/GlobalStateContext'
|
||||
import { useFetch } from '@common/hooks/http'
|
||||
import { $t } from '@common/locales'
|
||||
import { RouterParams } from '@core/components/aoplatform/RenderRoutes'
|
||||
@@ -158,28 +158,8 @@ export default function ServiceHubManagement() {
|
||||
switch (type) {
|
||||
case 'add':
|
||||
title = $t('添加消费者')
|
||||
content = (
|
||||
<GlobalProvider>
|
||||
<ManagementConfig ref={addManagementRef} dataShowType={dataShowType} type={type} teamId={teamId!} />
|
||||
</GlobalProvider>
|
||||
)
|
||||
content = <ManagementConfig ref={addManagementRef} dataShowType={dataShowType} type={type} teamId={teamId!} />
|
||||
break
|
||||
// case 'edit':{
|
||||
// title='配置 Open Api'
|
||||
// message.loading('正在加载数据')
|
||||
// const {code,data,msg} = await fetchData<BasicResponse<{app:ManagementConfigFieldType}>>('external-app',{method:'GET',eoParams:{id:entity!.id}})
|
||||
// message.destroy()
|
||||
// if(code === STATUS_CODE.SUCCESS){
|
||||
// content=<ManagementConfig ref={editManagementRef} type={type} entity={data.app}/>
|
||||
// }else{
|
||||
// message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
// return
|
||||
// }
|
||||
// break;}
|
||||
// case 'delete':
|
||||
// title='删除'
|
||||
// content='该数据删除后将无法找回,请确认是否删除?'
|
||||
// break;
|
||||
}
|
||||
|
||||
modal.confirm({
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
b: "subscription_service:#{application}"
|
||||
response:
|
||||
status_code: 403
|
||||
content_typ: "text/plan"
|
||||
content_type: "text/plan"
|
||||
charset: "utf-8"
|
||||
body: "Forbidden"
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
_ "github.com/APIParkLab/APIPark/frontend"
|
||||
_ "github.com/APIParkLab/APIPark/gateway/apinto"
|
||||
_ "github.com/APIParkLab/APIPark/plugins/core"
|
||||
_ "github.com/APIParkLab/APIPark/plugins/openapi"
|
||||
_ "github.com/APIParkLab/APIPark/plugins/permit"
|
||||
_ "github.com/APIParkLab/APIPark/plugins/publish_flow"
|
||||
_ "github.com/APIParkLab/APIPark/resources/locale"
|
||||
|
||||
+13
-18
@@ -533,16 +533,17 @@ func (i *imlProviderModule) UpdateProviderConfig(ctx context.Context, id string,
|
||||
DefaultLLM: &input.DefaultLLM,
|
||||
Config: &input.Config,
|
||||
Priority: input.Priority,
|
||||
Status: &status,
|
||||
}
|
||||
_, err = i.aiKeyService.DefaultKey(ctx, id)
|
||||
_, err = i.aiKeyService.DefaultKey(txCtx, id)
|
||||
if err != nil {
|
||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return err
|
||||
}
|
||||
err = i.aiKeyService.Create(ctx, &ai_key.Create{
|
||||
err = i.aiKeyService.Create(txCtx, &ai_key.Create{
|
||||
ID: id,
|
||||
Name: info.Name,
|
||||
Config: info.Config,
|
||||
Config: input.Config,
|
||||
Provider: id,
|
||||
Status: 1,
|
||||
ExpireTime: 0,
|
||||
@@ -550,27 +551,21 @@ func (i *imlProviderModule) UpdateProviderConfig(ctx context.Context, id string,
|
||||
Priority: 1,
|
||||
})
|
||||
} else {
|
||||
err = i.aiKeyService.Save(ctx, id, &ai_key.Edit{
|
||||
Config: &info.Config,
|
||||
err = i.aiKeyService.Save(txCtx, id, &ai_key.Edit{
|
||||
Config: &input.Config,
|
||||
Status: &status,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if input.Enable != nil {
|
||||
status = 0
|
||||
if *input.Enable {
|
||||
status = 1
|
||||
}
|
||||
pInfo.Status = &status
|
||||
}
|
||||
err = i.providerService.Save(ctx, id, pInfo)
|
||||
err = i.providerService.Save(txCtx, id, pInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if *pInfo.Status == 0 {
|
||||
return i.syncGateway(ctx, cluster.DefaultClusterID, []*gateway.DynamicRelease{
|
||||
return i.syncGateway(txCtx, cluster.DefaultClusterID, []*gateway.DynamicRelease{
|
||||
{
|
||||
BasicItem: &gateway.BasicItem{
|
||||
ID: id,
|
||||
@@ -579,8 +574,8 @@ func (i *imlProviderModule) UpdateProviderConfig(ctx context.Context, id string,
|
||||
},
|
||||
}, false)
|
||||
}
|
||||
// 获取当前供应商所有Key信息
|
||||
defaultKey, err := i.aiKeyService.DefaultKey(ctx, id)
|
||||
// 获取当前供应商默认Key信息
|
||||
defaultKey, err := i.aiKeyService.DefaultKey(txCtx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -590,7 +585,7 @@ func (i *imlProviderModule) UpdateProviderConfig(ctx context.Context, id string,
|
||||
cfg["model_config"] = model.DefaultConfig()
|
||||
cfg["priority"] = info.Priority
|
||||
cfg["base"] = fmt.Sprintf("%s://%s", p.URI().Scheme(), p.URI().Host())
|
||||
return i.syncGateway(ctx, cluster.DefaultClusterID, []*gateway.DynamicRelease{
|
||||
return i.syncGateway(txCtx, cluster.DefaultClusterID, []*gateway.DynamicRelease{
|
||||
{
|
||||
BasicItem: &gateway.BasicItem{
|
||||
ID: id,
|
||||
|
||||
@@ -100,7 +100,8 @@ func (i *imlAuthorizationModule) getApplications(ctx context.Context, appIds []s
|
||||
Config: authCfg,
|
||||
HideCredential: a.HideCredential,
|
||||
Label: map[string]string{
|
||||
"authorization": a.UUID,
|
||||
"authorization": a.UUID,
|
||||
"authorization_name": a.Name,
|
||||
},
|
||||
}
|
||||
}),
|
||||
@@ -157,7 +158,8 @@ func (i *imlAuthorizationModule) online(ctx context.Context, s *service.Service)
|
||||
Config: authCfg,
|
||||
HideCredential: a.HideCredential,
|
||||
Label: map[string]string{
|
||||
"authorization": a.UUID,
|
||||
"authorization": a.UUID,
|
||||
"authorization_name": a.Name,
|
||||
},
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package openapi
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/eolinker/go-common/pm3"
|
||||
)
|
||||
|
||||
func (p *plugin) appAuthorizationApis() []pm3.Api {
|
||||
return []pm3.Api{
|
||||
pm3.CreateApiWidthDoc(http.MethodPost, "/openapi/v1/app/authorization", []string{"context", "query:app", "body"}, []string{"authorization"}, p.authorizationController.AddAuthorization),
|
||||
pm3.CreateApiWidthDoc(http.MethodPut, "/openapi/v1/app/authorization", []string{"context", "query:app", "query:authorization", "body"}, []string{"authorization"}, p.authorizationController.EditAuthorization),
|
||||
pm3.CreateApiWidthDoc(http.MethodDelete, "/openapi/v1/app/authorization", []string{"context", "query:app", "query:authorization"}, nil, p.authorizationController.DeleteAuthorization),
|
||||
pm3.CreateApiWidthDoc(http.MethodGet, "/openapi/v1/app/authorization", []string{"context", "query:app", "query:authorization"}, []string{"authorization"}, p.authorizationController.Info),
|
||||
pm3.CreateApiWidthDoc(http.MethodGet, "/openapi/v1/app/authorizations", []string{"context", "query:app"}, []string{"authorizations"}, p.authorizationController.Authorizations),
|
||||
pm3.CreateApiWidthDoc(http.MethodGet, "/openapi/v1/app/authorization/details", []string{"context", "query:app", "query:authorization"}, []string{"details"}, p.authorizationController.Detail),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package openapi
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/eolinker/eosc/env"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultAPIKey = "37eb0ebf"
|
||||
openCheck = newOpenapiCheck()
|
||||
)
|
||||
|
||||
type openapiCheck struct {
|
||||
apikey string
|
||||
}
|
||||
|
||||
func newOpenapiCheck() *openapiCheck {
|
||||
apikey, has := env.GetEnv("API_KEY")
|
||||
if !has {
|
||||
apikey = defaultAPIKey
|
||||
}
|
||||
return &openapiCheck{apikey: apikey}
|
||||
}
|
||||
|
||||
func (o *openapiCheck) Check(method string, path string) (bool, []gin.HandlerFunc) {
|
||||
if strings.HasPrefix(path, "/openapi/") {
|
||||
return true, []gin.HandlerFunc{o.Handler}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (o *openapiCheck) Sort() int {
|
||||
return -1
|
||||
}
|
||||
|
||||
func (o *openapiCheck) Handler(ginCtx *gin.Context) {
|
||||
authorization := ginCtx.GetHeader("Authorization")
|
||||
if authorization == "" {
|
||||
ginCtx.AbortWithStatusJSON(403, gin.H{"code": -8, "msg": "invalid token", "success": "fail"})
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package openapi
|
||||
|
||||
import (
|
||||
"github.com/eolinker/go-common/autowire"
|
||||
"github.com/eolinker/go-common/pm3"
|
||||
)
|
||||
|
||||
func init() {
|
||||
pm3.Register("openapi", new(Driver))
|
||||
}
|
||||
|
||||
type Driver struct {
|
||||
}
|
||||
|
||||
func (d *Driver) Create() (pm3.IPlugin, error) {
|
||||
p := new(plugin)
|
||||
autowire.Autowired(p)
|
||||
return p, nil
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package openapi
|
||||
|
||||
import (
|
||||
application_authorization "github.com/APIParkLab/APIPark/controller/application-authorization"
|
||||
"github.com/eolinker/go-common/pm3"
|
||||
)
|
||||
|
||||
var (
|
||||
_ pm3.IPlugin = (*plugin)(nil)
|
||||
_ pm3.IPluginMiddleware = (*plugin)(nil)
|
||||
)
|
||||
|
||||
type plugin struct {
|
||||
apis []pm3.Api
|
||||
authorizationController application_authorization.IAuthorizationController `autowired:""`
|
||||
}
|
||||
|
||||
func (p *plugin) Middlewares() []pm3.IMiddleware {
|
||||
return []pm3.IMiddleware{
|
||||
openCheck,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *plugin) APis() []pm3.Api {
|
||||
return p.apis
|
||||
}
|
||||
|
||||
func (p *plugin) Name() string {
|
||||
return "openapi"
|
||||
}
|
||||
func (p *plugin) OnComplete() {
|
||||
p.apis = p.appAuthorizationApis()
|
||||
}
|
||||
+1
-1
@@ -211,7 +211,7 @@ APIParkはApache 2.0ライセンスの下で提供されています。詳細に
|
||||
エンタープライズ機能や専門的な技術サポートについては、プリセールスの専門家に連絡し、個別デモ、カスタムソリューション、価格情報を入手してください。
|
||||
|
||||
- ウェブサイト: https://apipark.com
|
||||
- メール: dev@apipark.com
|
||||
- メール: contact@apipark.com
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
@@ -215,7 +215,7 @@ APIPark 使用 Apache 2.0 许可证。更多详情请查看 LICENSE 文件。
|
||||
对于企业级功能和专业技术支持,请联系售前专家进行个性化演示、定制方案和获取报价。
|
||||
|
||||
- 网站: https://apipark.com
|
||||
- 电子邮件: dev@apipark.com
|
||||
- 电子邮件: contact@apipark.com
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@ APIPark 使用 Apache 2.0 授權條款。更多詳情請參閱 LICENSE 文件。
|
||||
如需企業級功能與專業技術支援,請聯絡我們的售前專家,獲取個性化演示、定制方案和報價。
|
||||
|
||||
- 網站: https://apipark.com
|
||||
- 電子郵件: dev@apipark.com
|
||||
- 電子郵件: contact@apipark.com
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: v7
|
||||
version: v8
|
||||
sort:
|
||||
- "access_log"
|
||||
- "monitor"
|
||||
@@ -41,7 +41,7 @@ plugin:
|
||||
b: "subscription_service:#{application}"
|
||||
response:
|
||||
status_code: 403
|
||||
content_typ: "text/plan"
|
||||
content_type: "text/plan"
|
||||
charset: "utf-8"
|
||||
body: "Forbidden"
|
||||
|
||||
|
||||
+2
-2
@@ -8,8 +8,8 @@ RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
ARG APP
|
||||
|
||||
ENV NSQ_ADDR=nsq:4150
|
||||
ENV NSQ_TOPIC_PREFIX=apipark
|
||||
ENV NSQ_ADDR=${APP}-nsq:4150
|
||||
ENV NSQ_TOPIC_PREFIX=${APP}
|
||||
|
||||
RUN mkdir -p /${APP}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ source ./scripts/common.sh
|
||||
APP="apipark"
|
||||
|
||||
|
||||
mkdir -p scripts/cmd/ && cp cmd/${APP} scripts/cmd/
|
||||
mkdir -p scripts/cmd/ && cp cmd/${APP} scripts/cmd/ && cp cmd/apipark_ai_event_listen scripts/cmd/
|
||||
|
||||
VERSION=$(gen_version)
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ echo -e " - $s" >> config.yml
|
||||
done
|
||||
echo -e "nsq:" >> config.yml
|
||||
echo -e " addr: ${NSQ_ADDR}" >> config.yml
|
||||
echo -e " topic: ${NSQ_TOPIC}" >> config.yml
|
||||
echo -e " topic_prefix: ${NSQ_TOPIC_PREFIX}" >> config.yml
|
||||
echo -e "port: 8288" >> config.yml
|
||||
echo -e "error_log:" >> config.yml
|
||||
echo -e " dir: ${ERROR_DIR}" >> config.yml
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ type Provider struct {
|
||||
Name string `gorm:"type:varchar(100);not null;column:name;comment:name"`
|
||||
DefaultLLM string `gorm:"type:varchar(255);not null;column:default_llm;comment:默认模型ID"`
|
||||
Config string `gorm:"type:text;not null;column:config;comment:配置信息"`
|
||||
Status int `gorm:"type:tinyint(1);not null;column:status;comment:状态,0:停用;1:启用,2:异常"`
|
||||
Status int `gorm:"type:tinyint(1);not null;column:status;comment:状态,0:停用;1:启用,2:异常;default:1"`
|
||||
Priority int `gorm:"type:int;not null;column:priority;comment:优先级,值越小优先级越高"`
|
||||
Creator string `gorm:"size:36;not null;column:creator;comment:创建人;index:creator" aovalue:"creator"` // 创建人
|
||||
Updater string `gorm:"size:36;not null;column:updater;comment:更新人;index:updater" aovalue:"updater"` // 更新人
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ type Doc struct {
|
||||
Id int64 `gorm:"column:id;type:BIGINT(20);AUTO_INCREMENT;NOT NULL;comment:id;primary_key;comment:主键ID;"`
|
||||
UUID string `gorm:"type:varchar(36);not null;column:uuid;uniqueIndex:uuid;comment:UUID;"`
|
||||
Service string `gorm:"size:36;not null;column:service;comment:服务;index:service"`
|
||||
Content string `gorm:"type:text;null;column:content;comment:文档内容"`
|
||||
Content string `gorm:"type:longtext;null;column:content;comment:文档内容"`
|
||||
Updater string `gorm:"size:36;not null;column:updater;comment:更新人;index:updater" aovalue:"updater"`
|
||||
UpdateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:update_at;comment:更新时间"`
|
||||
APICount int64 `gorm:"type:int(11);not null;column:api_count;comment:接口数量"`
|
||||
|
||||
@@ -7,7 +7,7 @@ type Commit[H any] struct {
|
||||
UUID string `gorm:"size:36;not null;column:uuid;comment:uuid;uniqueIndex:uuid;"`
|
||||
Target string `gorm:"column:target;type:varchar(36);NOT NULL;comment:目标id;index:target;"`
|
||||
Key string `gorm:"size:50;not null;column:key;comment:类型;index:key;"`
|
||||
Data *H `gorm:"type:text;not null;column:data;comment:数据;charset=utf8mb4;serializer:json"`
|
||||
Data *H `gorm:"type:longtext;not null;column:data;comment:数据;charset=utf8mb4;serializer:json"`
|
||||
CreateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:create_at;comment:创建时间"`
|
||||
Operator string `gorm:"size:36;not null;column:operator;comment:操作人;index:operator;"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user