mirror of
https://github.com/APIParkLab/APIPark.git
synced 2026-06-14 20:41:15 +08:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 37f87615bd | |||
| e86999770f | |||
| 6ba2a08b62 | |||
| 9d2208e14d | |||
| 8d69d45d1d | |||
| 014a7e0362 |
+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)
|
||||
|
||||
@@ -4,48 +4,48 @@ import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { Button, Dropdown } from 'antd'
|
||||
import { memo, useEffect, useMemo } from 'react'
|
||||
|
||||
const LanguageItems = [
|
||||
{
|
||||
key: 'en-US',
|
||||
label: (
|
||||
<Button key="en" type="text" className="flex items-center p-0 bg-transparent border-none">
|
||||
English
|
||||
</Button>
|
||||
),
|
||||
title: 'English'
|
||||
},
|
||||
{
|
||||
key: 'ja-JP',
|
||||
label: (
|
||||
<Button key="jp" type="text" className="flex items-center p-0 bg-transparent border-none">
|
||||
日本語
|
||||
</Button>
|
||||
),
|
||||
title: '日本語'
|
||||
},
|
||||
{
|
||||
key: 'zh-TW',
|
||||
label: (
|
||||
<Button key="tw" type="text" className="flex items-center p-0 bg-transparent border-none">
|
||||
繁體中文
|
||||
</Button>
|
||||
),
|
||||
title: '繁體中文'
|
||||
},
|
||||
{
|
||||
key: 'zh-CN',
|
||||
label: (
|
||||
<Button key="cn" type="text" className="flex items-center p-0 bg-transparent border-none">
|
||||
简体中文
|
||||
</Button>
|
||||
),
|
||||
title: '简体中文'
|
||||
}
|
||||
]
|
||||
const LanguageSetting = ({ mode = 'light' }: { mode?: 'dark' | 'light' }) => {
|
||||
const { dispatch, state } = useGlobalContext()
|
||||
const items = [
|
||||
{
|
||||
key: 'en-US',
|
||||
label: (
|
||||
<Button key="en" type="text" className="flex items-center p-0 bg-transparent border-none">
|
||||
English
|
||||
</Button>
|
||||
),
|
||||
title: 'English'
|
||||
},
|
||||
{
|
||||
key: 'ja-JP',
|
||||
label: (
|
||||
<Button key="jp" type="text" className="flex items-center p-0 bg-transparent border-none">
|
||||
日本語
|
||||
</Button>
|
||||
),
|
||||
title: '日本語'
|
||||
},
|
||||
{
|
||||
key: 'zh-TW',
|
||||
label: (
|
||||
<Button key="tw" type="text" className="flex items-center p-0 bg-transparent border-none">
|
||||
繁體中文
|
||||
</Button>
|
||||
),
|
||||
title: '繁體中文'
|
||||
},
|
||||
{
|
||||
key: 'zh-CN',
|
||||
label: (
|
||||
<Button key="cn" type="text" className="flex items-center p-0 bg-transparent border-none">
|
||||
简体中文
|
||||
</Button>
|
||||
),
|
||||
title: '简体中文'
|
||||
}
|
||||
]
|
||||
|
||||
const langLabel = useMemo(() => LanguageItems.find((item) => item?.key === state.language)?.title, [state.language])
|
||||
const langLabel = useMemo(() => items.find((item) => item?.key === state.language)?.title, [state.language])
|
||||
|
||||
useEffect(() => {
|
||||
const savedLang = i18n.language || sessionStorage.getItem('i18nextLng')
|
||||
@@ -53,17 +53,17 @@ const LanguageSetting = ({ mode = 'light' }: { mode?: 'dark' | 'light' }) => {
|
||||
dispatch({ type: 'UPDATE_LANGUAGE', language: savedLang })
|
||||
} else if (!savedLang) {
|
||||
const browserLang = navigator.language
|
||||
const supportedLang = LanguageItems.find((item) => item.key === browserLang) ? browserLang : 'zh-CN'
|
||||
if (state.language === supportedLang) return
|
||||
const supportedLang = items.find((item) => item.key === browserLang) ? browserLang : 'zh-CN'
|
||||
dispatch({ type: 'UPDATE_LANGUAGE', language: supportedLang })
|
||||
i18n.changeLanguage(supportedLang)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
trigger={['hover']}
|
||||
menu={{
|
||||
items: LanguageItems,
|
||||
items,
|
||||
style: { minWidth: '80px' },
|
||||
onClick: (e) => {
|
||||
const { key } = e
|
||||
|
||||
@@ -342,7 +342,7 @@ export const GlobalProvider: FC<{ children: ReactNode }> = ({ children }) => {
|
||||
updateDate: '2024-07-01',
|
||||
powered: 'Powered by https://apipark.com',
|
||||
mainPage: '/guide/page',
|
||||
language: sessionStorage.getItem('i18nextLng') || 'en-US',
|
||||
language: 'en-US',
|
||||
pluginsLoaded: false
|
||||
})
|
||||
const [accessData, setAccessData] = useState<Map<string, string[]>>(new Map())
|
||||
@@ -510,7 +510,7 @@ export const useGlobalContext = () => {
|
||||
updateDate: '',
|
||||
powered: '',
|
||||
mainPage: '',
|
||||
language: sessionStorage.getItem('i18nextLng') || 'en-US',
|
||||
language: 'en-US',
|
||||
pluginsLoaded: false
|
||||
},
|
||||
dispatch: () => {},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import i18n from 'i18next'
|
||||
import { initReactI18next, useTranslation } from 'react-i18next'
|
||||
import { initReactI18next } from 'react-i18next'
|
||||
// i18next-browser-languagedetector插件 这是一个 i18next 语言检测插件,用于检测浏览器中的用户语言,
|
||||
import crc32 from 'crc/crc32'
|
||||
import LanguageDetector from 'i18next-browser-languagedetector'
|
||||
@@ -39,22 +39,23 @@ i18n
|
||||
.init({
|
||||
// 初始化
|
||||
resources, // 本地多语言数据
|
||||
supportedLngs: ['zh-CN', 'en-US', 'zh-TW', 'ja-JP'],
|
||||
// fallbackLng: config.lang, // 默认当前环境的语言
|
||||
detection: {
|
||||
caches: ['localStorage', 'sessionStorage', 'cookie']
|
||||
}
|
||||
})
|
||||
|
||||
// --------这里是i18next-scanner新增的配置-------------
|
||||
// 用于非 React 组件中的翻译
|
||||
export const $t = (key: string, params?: any[]): string => {
|
||||
// 将中文转换成crc32格式去匹配对应的json语言包
|
||||
const hashKey = `K${crc32(key).toString(16)}`
|
||||
const hashKey = `K${crc32(key).toString(16)}` // 将中文转换成crc32格式去匹配对应的json语言包
|
||||
let words = i18n.t(hashKey)
|
||||
// const { t } = useTranslation(); // 通过hooks
|
||||
// let words = t(hashKey);
|
||||
if (words === hashKey) {
|
||||
words = key
|
||||
}
|
||||
|
||||
// 配置传递参数的场景, 目前仅支持数组,可在此拓展
|
||||
if (Array.isArray(params)) {
|
||||
const reg = /\((\d)\)/g
|
||||
words = words.replace(reg, (a: string, b: number) => {
|
||||
@@ -64,25 +65,4 @@ export const $t = (key: string, params?: any[]): string => {
|
||||
return words
|
||||
}
|
||||
|
||||
// 用于 React 组件中的翻译
|
||||
export const useI18n = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (key: string, params?: any[]): string => {
|
||||
const hashKey = `K${crc32(key).toString(16)}`
|
||||
let words = t(hashKey)
|
||||
if (words === hashKey) {
|
||||
words = key
|
||||
}
|
||||
|
||||
if (Array.isArray(params)) {
|
||||
const reg = /\((\d)\)/g
|
||||
words = words.replace(reg, (a: string, b: number) => {
|
||||
return params[b]
|
||||
})
|
||||
}
|
||||
return words
|
||||
}
|
||||
}
|
||||
|
||||
export default i18n
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import InsidePage from '@common/components/aoplatform/InsidePage'
|
||||
import { useI18n } from '@common/locales'
|
||||
import { $t } from '@common/locales'
|
||||
import { Tabs } from 'antd'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useSearchParams } from 'react-router-dom'
|
||||
@@ -10,7 +10,6 @@ import { AiSettingProvider } from './contexts/AiSettingContext'
|
||||
const CONTENT_STYLE = { height: 'calc(-300px + 100vh)' } as const
|
||||
|
||||
const AiSettingContent = () => {
|
||||
const $t = useI18n()
|
||||
const [searchParams, setSearchParams] = useSearchParams()
|
||||
const [activeKey, setActiveKey] = useState(searchParams.get('status') === 'unconfigure' ? 'config' : 'flow')
|
||||
|
||||
|
||||
@@ -1,26 +1,33 @@
|
||||
import InsidePage from '@common/components/aoplatform/InsidePage'
|
||||
import { useI18n } from '@common/locales'
|
||||
import { Col, Row } from 'antd'
|
||||
import ApiRequestSetting from './ApiRequestSetting'
|
||||
import ServiceCategory from './ServiceCategory'
|
||||
import InsidePage from "@common/components/aoplatform/InsidePage";
|
||||
import { $t } from "@common/locales";
|
||||
import ServiceCategory from "./ServiceCategory";
|
||||
import ApiRequestSetting from "./ApiRequestSetting";
|
||||
import { Row, Col } from "antd";
|
||||
|
||||
export default function CommonPage() {
|
||||
const $t = useI18n()
|
||||
|
||||
return (
|
||||
<InsidePage pageTitle={$t('常规设置')} showBorder={false} contentClassName="pr-PAGE_INSIDE_X" scrollPage={false}>
|
||||
<Row className="mb-btnybase">
|
||||
<Col>
|
||||
<span className="font-bold mr-[13px]">{$t('API 请求设置')}</span>
|
||||
</Col>
|
||||
</Row>
|
||||
<ApiRequestSetting />
|
||||
<Row className="mb-btnybase mt-[40px]">
|
||||
<Col>
|
||||
<span className="font-bold mr-[13px]">{$t('服务分类')}</span>
|
||||
</Col>
|
||||
</Row>
|
||||
<ServiceCategory />
|
||||
</InsidePage>
|
||||
)
|
||||
}
|
||||
export default function CommonPage(){
|
||||
return (
|
||||
<InsidePage
|
||||
pageTitle={$t('常规设置')}
|
||||
showBorder={false}
|
||||
contentClassName="pr-PAGE_INSIDE_X"
|
||||
scrollPage={false}
|
||||
>
|
||||
<Row className="mb-btnybase" >
|
||||
<Col >
|
||||
<span className="font-bold mr-[13px]">
|
||||
{$t('API 请求设置')}
|
||||
</span>
|
||||
</Col>
|
||||
</Row>
|
||||
<ApiRequestSetting />
|
||||
<Row className="mb-btnybase mt-[40px]">
|
||||
<Col >
|
||||
<span className="font-bold mr-[13px]">
|
||||
{$t('服务分类')}
|
||||
</span>
|
||||
</Col>
|
||||
</Row>
|
||||
<ServiceCategory />
|
||||
</InsidePage>
|
||||
)
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -21,13 +21,13 @@ echo -e "redis:" >> config.yml
|
||||
echo -e " user_name: ${REDIS_USER_NAME}" >> config.yml
|
||||
echo -e " password: ${REDIS_PWD}" >> config.yml
|
||||
echo -e " addr: " >> config.yml
|
||||
echo -e "nsq:" >> config.yml
|
||||
echo -e " addr: ${NSQ_ADDR}" >> config.yml
|
||||
echo -e " topic: ${NSQ_TOPIC}" >> config.yml
|
||||
for s in ${arr[@]}
|
||||
do
|
||||
echo -e " - $s" >> config.yml
|
||||
done
|
||||
echo -e "nsq:" >> config.yml
|
||||
echo -e " addr: ${NSQ_ADDR}" >> 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"` // 更新人
|
||||
|
||||
Reference in New Issue
Block a user