diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 6a1a20e..e26ab4d 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -7,28 +7,47 @@ jobs: deploy: runs-on: ubuntu-latest env: - # 从组织级Secrets读取,不用在仓库重复配置 - K3S_HOST: ${{ secrets.K3S_HOST }} + K3S_HOST: 121.37.117.181 APP_NAME: ${{ gitea.repo_name }} + REGISTRY: 你的镜像仓库地址 # 比如 docker.io/你的用户名 steps: - - name: 拉取代码 - uses: actions/checkout@v4 + - uses: gitea/actions/checkout@v4 + # 1. 初始化 Docker Buildx + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + # 2. 登录镜像仓库(按需) + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USER }} + password: ${{ secrets.DOCKER_PWD }} + + # 3. 构建+推送,启用缓存 + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: ${{ env.REGISTRY }}/${{ env.APP_NAME }}:${{ gitea.sha }} + # 缓存配置:推送到镜像仓库 + cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.APP_NAME }}:buildcache + cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.APP_NAME }}:buildcache,mode=max + + # 4. 核心修改:先上传deploy.yaml到K3s服务器,再执行kubectl - name: SSH部署K3s - run: | + run: mkdir -p ~/.ssh - # 写入组织配置的SSH私钥 - echo "${{ secrets.K3S_SSH_KEY }}" > k3s.pem + echo "${{ secrets.K3S_PEM_KEY }}" > k3s.pem chmod 600 k3s.pem - # 调试:验证私钥是否正确写入 - echo "私钥文件权限:" - ls -l k3s.pem - echo "私钥头部(仅前5行):" - head -5 k3s.pem - # 测试连接(会输出服务器主机名和kubectl版本) - ssh -i k3s.pem -o StrictHostKeyChecking=no -o ConnectTimeout=10 root@${K3S_HOST} "hostname && kubectl version --client" - # 正式执行部署命令 + # 关键1:把Gitea仓库里的deploy.yaml上传到K3s服务器临时目录(/tmp) + # 注意:如果你的deploy.yaml不在仓库根目录,要修改./deploy.yaml为实际路径 + scp -i k3s.pem -o StrictHostKeyChecking=no ./deploy.yaml root@${K3S_HOST}:/tmp/ + # 关键2:执行kubectl时指向临时目录的文件,而非不存在的/k8s/ ssh -i k3s.pem -o StrictHostKeyChecking=no root@${K3S_HOST} << CMD - kubectl apply -f /k8s/deploy.yaml - kubectl rollout restart deployment ${APP_NAME} + kubectl apply -f /tmp/deploy.yaml + kubectl rollout restart deployment ${APP_NAME} -n default + # 可选:部署完成后删除临时文件,清理服务器 + rm -f /tmp/deploy.yaml CMD \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index ba8ad82..4ddaeed 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,6 +26,6 @@ COPY ngnix.conf /etc/nginx/conf.d/default.conf # 复制SSL证书 COPY ssl/* /etc/nginx/ssl/ -EXPOSE 80 443 +EXPOSE 443 CMD ["nginx", "-g", "daemon off;"] diff --git a/src/api/settings/modelConfig/modelModule/index.ts b/src/api/settings/modelConfig/modelModule/index.ts index 89741eb..a706c79 100644 --- a/src/api/settings/modelConfig/modelModule/index.ts +++ b/src/api/settings/modelConfig/modelModule/index.ts @@ -112,6 +112,7 @@ export interface CreateModelParams { modelName: string; /** 与 listType 返回的类型 id 一致,可能为数字或字符串 */ modelType: number | string; + operatorName?: string; baseUrl: string; httpMethod?: string; headMsg?: string; @@ -122,6 +123,10 @@ export interface CreateModelParams { form: ModelFormEntry[]; requestMapping?: Record; responseMapping?: Record; + responseBody?: Record; + extendMapping?: Record; + responseTokenField?: string; + tokenConfig?: Record; maxConcurrency?: number; queueLimit?: number; timeoutSeconds: number; diff --git a/src/components/model/ModelSelector.vue b/src/components/model/ModelSelector.vue index 97206fc..7751e58 100644 --- a/src/components/model/ModelSelector.vue +++ b/src/components/model/ModelSelector.vue @@ -92,7 +92,14 @@ import { ref, reactive, watch, onMounted } from 'vue'; import { ElMessage, type FormInstance, type FormRules } from 'element-plus'; import { Search, CircleCheck } from '@element-plus/icons-vue'; -import { getModelModuleList, addModelModule, getModelTypeList, normalizeModelTypeOptions } from '/@/api/settings/modelConfig/modelModule'; +import { + getModelModuleList, + addModelModule, + getModelTypeList, + normalizeModelTypeOptions, + type CreateModelParams, + type ModelFormEntry, +} from '/@/api/settings/modelConfig/modelModule'; import { checkIsSuperAdmin } from '/@/api/system/user/index'; import { getApiErrorMessage } from '/@/utils/request'; import EditModule from '/@/views/settings/modelConfig/modelModule/component/editModule.vue'; @@ -101,7 +108,7 @@ interface ModelItem { id: string; tenantId?: number; modelName: string; - modelType: number; + modelType: number | string; baseUrl: string; route: string; httpMethod: string; @@ -113,9 +120,10 @@ interface ModelItem { operatorName?: string; responseBody?: Record; tokenConfig?: Record | string; - form?: any; - requestMapping?: any; - responseMapping?: any; + extendMapping?: Record | string; + form?: ModelFormEntry[] | Record; + requestMapping?: Record; + responseMapping?: Record; maxConcurrency?: number; queueLimit?: number; timeoutSeconds?: number; @@ -206,13 +214,30 @@ watch( } ); -const getModelTypeName = (type: number) => { +const getModelTypeName = (type: number | string) => { const typeMap: Record = { 1: '推理模型', 2: '图片模型', 3: '音频模型', }; - return typeMap[type] || '未知类型'; + return typeMap[Number(type)] || '未知类型'; +}; + +const parseJsonObjectField = (raw: unknown): Record => { + if (raw && typeof raw === 'object' && !Array.isArray(raw)) { + return raw as Record; + } + if (typeof raw === 'string') { + try { + const parsed = JSON.parse(raw || '{}'); + if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) { + return parsed as Record; + } + } catch { + return {}; + } + } + return {}; }; const fetchModelList = async () => { @@ -270,7 +295,13 @@ const handleCreatePrivateModel = async () => { creatingModel.value = true; const builtInModel = builtInModelToClone.value; - const createParams = { + const formList: ModelFormEntry[] = Array.isArray(builtInModel.form) + ? (builtInModel.form as ModelFormEntry[]) + : Object.entries((builtInModel.form as Record) || {}).map(([key, value]) => ({ + key: String(key), + value: String(value ?? ''), + })); + const createParams: CreateModelParams = { modelName: apiKeyForm.modelName, modelType: builtInModel.modelType, operatorName: builtInModel.operatorName || '', @@ -281,9 +312,9 @@ const handleCreatePrivateModel = async () => { enabled: builtInModel.enabled ?? 1, isChatModel: builtInModel.isChatModel || 0, apiKey: apiKeyForm.apiKey, - form: builtInModel.form || {}, - requestMapping: builtInModel.requestMapping || {}, - responseMapping: builtInModel.responseMapping || {}, + form: formList, + requestMapping: (builtInModel.requestMapping as Record) || {}, + responseMapping: (builtInModel.responseMapping as Record) || {}, responseBody: builtInModel.responseBody || {}, maxConcurrency: builtInModel.maxConcurrency || 10, queueLimit: builtInModel.queueLimit || 100, @@ -293,8 +324,9 @@ const handleCreatePrivateModel = async () => { retryQueueMaxSeconds: builtInModel.retryQueueMaxSeconds || 60, autoCleanSeconds: builtInModel.autoCleanSeconds || 300, remark: builtInModel.remark || '', - tokenMapping: builtInModel.tokenMapping || '', - tokenConfig: builtInModel.tokenConfig || {}, + + extendMapping: parseJsonObjectField(builtInModel.extendMapping), + tokenConfig: parseJsonObjectField(builtInModel.tokenConfig), }; const res: any = await addModelModule(createParams); diff --git a/src/views/settings/creation/component/SaveWorkflowDialog.vue b/src/views/settings/creation/component/SaveWorkflowDialog.vue new file mode 100644 index 0000000..6ce746b --- /dev/null +++ b/src/views/settings/creation/component/SaveWorkflowDialog.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/views/settings/creation/index.vue b/src/views/settings/creation/index.vue index f6e693b..afcf5e2 100644 --- a/src/views/settings/creation/index.vue +++ b/src/views/settings/creation/index.vue @@ -1,4 +1,4 @@ -