1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-03-01 03:24:14 +08:00

feat: Add AI-enhanced configuration (#7870)

This commit is contained in:
zhengkunwang 2025-02-14 14:26:05 +08:00 committed by GitHub
parent c709e48ceb
commit 078d7fbe1e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 2138 additions and 156 deletions

View File

@ -134,3 +134,64 @@ func (b *BaseApi) LoadGpuInfo(c *gin.Context) {
}
helper.SuccessWithData(c, &common.GpuInfo{})
}
// @Tags AITools
// @Summary Bind domain
// @Accept json
// @Param request body dto.WebsiteConfig true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /aitool/domain/bind [post]
func (b *BaseApi) BindDomain(c *gin.Context) {
var req dto.OllamaBindDomain
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := AIToolService.BindDomain(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
// @Tags AITools
// @Summary Get bind domain
// @Accept json
// @Param request body dto.OllamaBindDomainReq true "request"
// @Success 200 {object} dto.OllamaBindDomainRes
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /aitool/domain/get [post]
func (b *BaseApi) GetBindDomain(c *gin.Context) {
var req dto.OllamaBindDomainReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
res, err := AIToolService.GetBindDomain(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, res)
}
// Tags AITools
// Summary Update bind domain
// Accept json
// Param request body dto.OllamaBindDomain true "request"
// Success 200
// Security ApiKeyAuth
// Security Timestamp
// Router /aitool/domain/update [post]
func (b *BaseApi) UpdateBindDomain(c *gin.Context) {
var req dto.OllamaBindDomain
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := AIToolService.UpdateBindDomain(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}

View File

@ -9,3 +9,22 @@ type OllamaModelInfo struct {
type OllamaModelName struct {
Name string `json:"name"`
}
type OllamaBindDomain struct {
Domain string `json:"domain" validate:"required"`
AppInstallID uint `json:"appInstallID" validate:"required"`
SSLID uint `json:"sslID"`
AllowIPs []string `json:"allowIPs"`
WebsiteID uint `json:"websiteID"`
}
type OllamaBindDomainReq struct {
AppInstallID uint `json:"appInstallID" validate:"required"`
}
type OllamaBindDomainRes struct {
Domain string `json:"domain"`
SSLID uint `json:"sslID"`
AllowIPs []string `json:"allowIPs"`
WebsiteID uint `json:"websiteID"`
}

View File

@ -1,7 +1,9 @@
package service
import (
"context"
"fmt"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"io"
"os"
"os/exec"
@ -22,6 +24,9 @@ type IAIToolService interface {
Create(name string) error
Delete(name string) error
LoadDetail(name string) (string, error)
BindDomain(req dto.OllamaBindDomain) error
GetBindDomain(req dto.OllamaBindDomainReq) (*dto.OllamaBindDomainRes, error)
UpdateBindDomain(req dto.OllamaBindDomain) error
}
func NewIAIToolService() IAIToolService {
@ -192,3 +197,98 @@ func (u *AIToolService) Delete(name string) error {
}
return nil
}
func (u *AIToolService) BindDomain(req dto.OllamaBindDomain) error {
nginxInstall, _ := getAppInstallByKey(constant.AppOpenresty)
if nginxInstall.ID == 0 {
return buserr.New("ErrOpenrestyInstall")
}
createWebsiteReq := request.WebsiteCreate{
PrimaryDomain: req.Domain,
Alias: strings.ToLower(req.Domain),
Type: constant.Deployment,
AppType: constant.InstalledApp,
AppInstallID: req.AppInstallID,
}
websiteService := NewIWebsiteService()
if err := websiteService.CreateWebsite(createWebsiteReq); err != nil {
return err
}
website, err := websiteRepo.GetFirst(websiteRepo.WithAlias(strings.ToLower(req.Domain)))
if err != nil {
return err
}
if err = ConfigAllowIPs(req.AllowIPs, website); err != nil {
return err
}
if req.SSLID > 0 {
sslReq := request.WebsiteHTTPSOp{
WebsiteID: website.ID,
Enable: true,
Type: "existed",
WebsiteSSLID: req.SSLID,
HttpConfig: "HTTPSOnly",
}
if _, err = websiteService.OpWebsiteHTTPS(context.Background(), sslReq); err != nil {
return err
}
}
return nil
}
func (u *AIToolService) GetBindDomain(req dto.OllamaBindDomainReq) (*dto.OllamaBindDomainRes, error) {
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(req.AppInstallID))
if err != nil {
return nil, err
}
res := &dto.OllamaBindDomainRes{}
website, _ := websiteRepo.GetFirst(websiteRepo.WithAppInstallId(install.ID))
if website.ID == 0 {
return res, nil
}
res.WebsiteID = website.ID
res.Domain = website.PrimaryDomain
if website.WebsiteSSLID > 0 {
res.SSLID = website.WebsiteSSLID
}
res.AllowIPs = GetAllowIps(website)
return res, nil
}
func (u *AIToolService) UpdateBindDomain(req dto.OllamaBindDomain) error {
nginxInstall, _ := getAppInstallByKey(constant.AppOpenresty)
if nginxInstall.ID == 0 {
return buserr.New("ErrOpenrestyInstall")
}
websiteService := NewIWebsiteService()
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
if err != nil {
return err
}
if err = ConfigAllowIPs(req.AllowIPs, website); err != nil {
return err
}
if req.SSLID > 0 {
sslReq := request.WebsiteHTTPSOp{
WebsiteID: website.ID,
Enable: true,
Type: "existed",
WebsiteSSLID: req.SSLID,
HttpConfig: "HTTPSOnly",
}
if _, err = websiteService.OpWebsiteHTTPS(context.Background(), sslReq); err != nil {
return err
}
return nil
}
if website.WebsiteSSLID > 0 && req.SSLID == 0 {
sslReq := request.WebsiteHTTPSOp{
WebsiteID: website.ID,
Enable: false,
}
if _, err = websiteService.OpWebsiteHTTPS(context.Background(), sslReq); err != nil {
return err
}
}
return nil
}

View File

@ -642,9 +642,15 @@ func applySSL(website model.Website, websiteSSL model.WebsiteSSL, req request.We
}
if param.Name == "ssl_protocols" {
nginxParams[i].Params = req.SSLProtocol
if len(req.SSLProtocol) == 0 {
nginxParams[i].Params = []string{"TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1"}
}
}
if param.Name == "ssl_ciphers" {
nginxParams[i].Params = []string{req.Algorithm}
if len(req.Algorithm) == 0 {
nginxParams[i].Params = []string{"ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED"}
}
}
}
if req.Hsts {
@ -1106,3 +1112,55 @@ func getResourceContent(fileOp files.FileOp, resourcePath string) (string, error
}
return "", nil
}
func ConfigAllowIPs(ips []string, website model.Website) error {
nginxFull, err := getNginxFull(&website)
if err != nil {
return err
}
nginxConfig := nginxFull.SiteConfig
config := nginxFull.SiteConfig.Config
server := config.FindServers()[0]
server.RemoveDirective("allow", nil)
server.RemoveDirective("deny", nil)
if len(ips) > 0 {
server.UpdateAllowIPs(ips)
}
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
return err
}
return nginxCheckAndReload(nginxConfig.OldContent, config.FilePath, nginxFull.Install.ContainerName)
}
func GetAllowIps(website model.Website) []string {
nginxFull, err := getNginxFull(&website)
if err != nil {
return nil
}
config := nginxFull.SiteConfig.Config
server := config.FindServers()[0]
dirs := server.GetDirectives()
var ips []string
for _, dir := range dirs {
if dir.GetName() == "allow" {
ips = append(ips, dir.GetParameters()...)
}
}
return ips
}
func ConfigAIProxy(website model.Website) error {
nginxFull, err := getNginxFull(&website)
if err != nil {
return nil
}
config := nginxFull.SiteConfig.Config
server := config.FindServers()[0]
dirs := server.GetDirectives()
for _, dir := range dirs {
if dir.GetName() == "location" && dir.GetParameters()[0] == "/" {
server.UpdateRootProxy()
}
}
return nil
}

View File

@ -284,3 +284,6 @@ UserInfoPassHelp: "Tip: To change the password, you can execute the command: "
DBConnErr: "Error: Failed to initialize database connection, {{ .err }}"
SystemVersion: "version: "
SystemMode: "mode: "
#ai-tool
ErrOpenrestyInstall: 'Please install Openresty first'

View File

@ -281,3 +281,6 @@ UserInfoPassHelp: "ヒント:パスワードを変更するには、コマン
DBConnErr: "エラー:データベース接続の初期化に失敗しました、{{.err}}"
SystemVersion: "バージョン:"
SystemMode: "モード:"
#ai-tool
ErrOpenrestyInstall: 'まず Openresty をインストールしてください'

View File

@ -284,3 +284,6 @@ UserInfoPassHelp: "팁: 비밀번호를 변경하려면 다음 명령어를 실
DBConnErr: "오류: 데이터베이스 연결 초기화 실패 {{ .err }}"
SystemVersion: "버전: "
SystemMode: "모드: "
#ai-tool
ErrOpenrestyInstall: '먼저 Openresty를 설치하세요'

View File

@ -283,3 +283,6 @@ UserInfoPassHelp: "Petua: Untuk menukar kata laluan, anda boleh menjalankan arah
DBConnErr: "Ralat: Gagal memulakan sambungan pangkalan data, {{ .err }}"
SystemVersion: "Versi: "
SystemMode: "Mod: "
#ai-tool
ErrOpenrestyInstall: 'Sila pasang Openresty terlebih dahulu'

View File

@ -281,3 +281,6 @@ UserInfoPassHelp: "Dica: Para alterar a senha, você pode executar o comando: "
DBConnErr: "Erro: Falha ao inicializar a conexão com o banco de dados, {{ .err }}"
SystemVersion: "versão: "
SystemMode: "modo: "
#ai-tool
ErrOpenrestyInstall: 'Por favor, instale o Openresty primeiro'

View File

@ -1,63 +0,0 @@
#cmd
AppVersion: "Versão do aplicativo"
AppCommands: "Comandos relacionados ao aplicativo"
AppInit: "Inicializar aplicativo"
AppKeyVal: "Chave do aplicativo (suporta apenas inglês)"
AppCreateFileErr: "Falha na criação do arquivo {{ .name }} {{ .err }}"
AppCreateDirErr: "Falha na criação da pasta {{ .name }} {{ .err }}"
AppMissKey: "Chave do aplicativo ausente, use -k para especificar"
AppMissVersion: "Versão do aplicativo ausente, use -v para especificar"
AppVersionExist: "Versão já existente!"
AppCreateSuccessful: "Criação bem-sucedida!"
AppWriteErr: "Falha na gravação do arquivo {{ .name }} {{ .err }}"
SudoHelper: "Por favor, use {{ .cmd }} ou troque para o usuário root"
ListenIPCommands: "Alterar IP de escuta"
ListenIPv4: "Escutar no IPv4"
ListenIPv6: "Escutar no IPv6"
ListenChangeSuccessful: "Alteração bem-sucedida! Agora escutando em {{ .value }}"
ResetCommands: "Redefinir informações do sistema"
ResetMFA: "Cancelar autenticação de dois fatores do 1Panel"
ResetHttps: "Cancelar login HTTPS do 1Panel"
ResetEntrance: "Cancelar entrada segura do 1Panel"
ResetIPs: "Cancelar restrições de IP autorizados do 1Panel"
ResetDomain: "Cancelar vinculação de domínio do 1Panel"
RestoreCommands: "Restaurar serviço e dados do 1Panel"
RestoreNoSuchFile: "Nenhum arquivo disponível para restauração"
RestoreStep1: "(1/5) Iniciando a restauração do serviço e dados do 1Panel a partir do diretório {{ .name }}..."
RestoreStep2: "(2/5) Restauração do binário do 1Panel bem-sucedida"
RestoreStep3: "(3/5) Restauração do script do 1Panel bem-sucedida"
RestoreStep4: "(4/5) Restauração do serviço do 1Panel bem-sucedida"
RestoreStep5: "(5/5) Restauração dos dados do 1Panel bem-sucedida"
RestoreSuccessful: "Restauração bem-sucedida! Reiniciando o serviço, aguarde..."
UpdateCommands: "Atualizar informações do painel"
UpdateUser: "Atualizar usuário do painel"
UpdatePassword: "Atualizar senha do painel"
UpdatePort: "Atualizar porta do painel"
UpdateUserNull: "Erro: usuário do painel está vazio!"
UpdateUserBlank: "Erro: usuário do painel contém espaços!"
UpdateUserFormat: "Erro: Formato de usuário inválido! Aceita apenas inglês, chinês, números e _, comprimento de 3-30"
UpdateUserErr: "Erro: Falha ao atualizar usuário do painel, {{ .err }}"
UpdateSuccessful: "Atualização bem-sucedida!"
UpdateUserResult: "Usuário do painel: {{ .name }}"
UpdatePasswordRead: "Erro: Falha ao ler informações da senha do painel, {{ .err }}"
UpdatePasswordNull: "Erro: Senha do painel está vazia!"
UpdateUPasswordBlank: "Erro: Senha do painel contém espaços!"
UpdatePasswordFormat: "Erro: A senha do painel aceita apenas letras, números, caracteres especiais !@#$%*_,.?, comprimento de 8-30!"
UpdatePasswordLen: "Erro: Por favor, insira uma senha com mais de 6 caracteres!"
UpdatePasswordRe: "Confirmar senha:"
UpdatePasswordErr: "Erro: Falha ao atualizar senha do painel, {{ .err }}"
UpdatePasswordSame: "Erro: As duas senhas não coincidem, por favor, verifique e tente novamente!"
UpdatePasswordResult: "Senha do painel: {{ .name }}"
UpdatePortFormat: "Erro: O número da porta deve estar entre 1 e 65535!"
UpdatePortUsed: "Erro: O número da porta já está em uso, por favor, verifique e tente novamente!"
UpdatePortErr: "Erro: Falha ao atualizar a porta do painel, {{ .err }}"
UpdatePortResult: "Porta do painel: {{ .name }}"
UpdatePortFirewallAdd: "Falha ao adicionar regra de porta no firewall, {{ .err }}, adicione manualmente a porta {{ .name }} às regras do firewall."
UpdatePortFirewallDel: "Erro: Falha ao excluir porta do firewall, {{ .err }}"
UpdatePortFirewallReload: "Falha ao recarregar o firewall, {{ .err }}, recarregue o firewall manualmente."
UserInfo: "Obter informações do painel"
UserInfoAddr: "Endereço do painel: "
UserInfoPassHelp: "Dica: Para alterar a senha, execute o comando: "
DBConnErr: "Erro: Falha ao inicializar conexão com o banco de dados, {{ .err }}"
SystemVersion: "versão: "
SystemMode: "modo: "

View File

@ -284,3 +284,6 @@ UserInfoPassHelp: "Подсказка: чтобы изменить пароль,
DBConnErr: "Ошибка: не удалось инициализировать подключение к базе данных, {{ .err }}"
SystemVersion: "версия: "
SystemMode: "режим: "
#ai-tool
"ErrOpenrestyInstall": "Пожалуйста, установите Openresty сначала"

View File

@ -284,3 +284,6 @@ UserInfoPassHelp: "提示:修改密碼可執行指令:"
DBConnErr: "錯誤:初始化資料庫連線失敗,{{ .err }}"
SystemVersion: "版本:"
SystemMode: "模式:"
#ai-tool
"ErrOpenrestyInstall": "請先安裝 Openresty"

View File

@ -284,3 +284,6 @@ UserInfoPassHelp: "提示:修改密码可执行命令:"
DBConnErr: "错误:初始化数据库连接失败,{{ .err }}"
SystemVersion: "版本:"
SystemMode: "模式:"
#ai-tool
ErrOpenrestyInstall: '请先安装 Openresty'

View File

@ -20,5 +20,8 @@ func (a *AIToolsRouter) InitRouter(Router *gin.RouterGroup) {
aiToolsRouter.POST("/ollama/model/load", baseApi.LoadOllamaModelDetail)
aiToolsRouter.POST("/ollama/model/del", baseApi.DeleteOllamaModel)
aiToolsRouter.GET("/gpu/load", baseApi.LoadGpuInfo)
aiToolsRouter.POST("/domain/bind", baseApi.BindDomain)
aiToolsRouter.POST("/domain/get", baseApi.GetBindDomain)
aiToolsRouter.POST("/domain/update", baseApi.UpdateBindDomain)
}
}

View File

@ -265,6 +265,41 @@ func (s *Server) UpdateRootProxy(proxy []string) {
s.UpdateDirectiveBySecondKey("location", "/", newDir)
}
func (s *Server) UpdateRootProxyForAi(proxy []string) {
newDir := Directive{
Name: "location",
Parameters: []string{"/"},
Block: &Block{},
}
block := &Block{}
block.Directives = []IDirective{
&Directive{
Name: "proxy_buffering",
Parameters: []string{
"off",
},
},
&Directive{
Name: "proxy_cache",
Parameters: []string{
"off",
},
},
&Directive{
Name: "proxy_http_version",
Parameters: []string{
"1.1",
},
},
}
block.Directives = append(block.Directives, &Directive{
Name: "proxy_pass",
Parameters: proxy,
})
newDir.Block = block
s.UpdateDirectiveBySecondKey("location", "/", newDir)
}
func (s *Server) UpdatePHPProxy(proxy []string, localPath string) {
newDir := Directive{
Name: "location",
@ -369,3 +404,30 @@ func (s *Server) AddHTTP2HTTPS() {
newDir.Block = block
s.UpdateDirectiveBySecondKey("if", "($scheme", newDir)
}
func (s *Server) UpdateAllowIPs(ips []string) {
index := -1
for i, directive := range s.Directives {
if directive.GetName() == "location" && directive.GetParameters()[0] == "/" {
index = i
break
}
}
ipDirectives := make([]IDirective, 0)
for _, ip := range ips {
ipDirectives = append(ipDirectives, &Directive{
Name: "allow",
Parameters: []string{ip},
})
}
ipDirectives = append(ipDirectives, &Directive{
Name: "deny",
Parameters: []string{"all"},
})
if index != -1 {
newDirectives := append(ipDirectives, s.Directives[index:]...)
s.Directives = append(s.Directives[:index], newDirectives...)
} else {
s.Directives = append(s.Directives, ipDirectives...)
}
}

View File

@ -1,4 +1,5 @@
// Package docs Code generated by swaggo/swag. DO NOT EDIT
// Package docs GENERATED BY SWAG; DO NOT EDIT
// This file was generated by swaggo/swag
package docs
import "github.com/swaggo/swag"
@ -20,6 +21,267 @@ const docTemplate = `{
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/aitool/domain/bind": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AITools"
],
"summary": "Bind domain",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.WebsiteConfig"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/aitool/domain/get": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AITools"
],
"summary": "Get bind domain",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OllamaBindDomainReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.OllamaBindDomainRes"
}
}
}
}
},
"/aitool/gpu/load": {
"get": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AITools"
],
"summary": "Load gpu / xpu info",
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/aitool/ollama/model/del": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AITools"
],
"summary": "Delete Ollama model",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OllamaModelName"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFunctions": [],
"bodyKeys": [
"name"
],
"formatEN": "remove Ollama model [name]",
"formatZH": "删除模型 [name]",
"paramKeys": []
}
}
},
"/aitools/ollama/model": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AITools"
],
"summary": "Create Ollama model",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OllamaModelName"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFunctions": [],
"bodyKeys": [
"name"
],
"formatEN": "add Ollama model [name]",
"formatZH": "添加模型 [name]",
"paramKeys": []
}
}
},
"/aitools/ollama/model/load": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AITools"
],
"summary": "Page Ollama models",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OllamaModelName"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
}
}
}
},
"/aitools/ollama/model/search": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AITools"
],
"summary": "Page Ollama models",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.SearchWithPage"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.PageResult"
}
}
}
}
},
"/apps/checkupdate": {
"get": {
"security": [
@ -6024,6 +6286,47 @@ const docTemplate = `{
}
}
},
"/files/batch/check": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"File"
],
"summary": "Batch check file exist",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.FilePathsCheck"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/response.ExistFileInfo"
}
}
}
}
}
},
"/files/batch/del": {
"post": {
"security": [
@ -16759,6 +17062,9 @@ const docTemplate = `{
"github": {
"type": "string"
},
"gpuSupport": {
"type": "boolean"
},
"key": {
"type": "string"
},
@ -16991,6 +17297,41 @@ const docTemplate = `{
}
}
},
"dto.CCConfig": {
"type": "object",
"properties": {
"action": {
"type": "string"
},
"code": {
"type": "integer"
},
"duration": {
"type": "integer"
},
"ipBlock": {
"type": "string"
},
"ipBlockTime": {
"type": "integer"
},
"mode": {
"type": "string"
},
"res": {
"type": "string"
},
"state": {
"type": "string"
},
"threshold": {
"type": "integer"
},
"type": {
"type": "string"
}
}
},
"dto.CaptchaResponse": {
"type": "object",
"properties": {
@ -17435,6 +17776,26 @@ const docTemplate = `{
}
}
},
"dto.CommonConfig": {
"type": "object",
"properties": {
"action": {
"type": "string"
},
"code": {
"type": "integer"
},
"res": {
"type": "string"
},
"state": {
"type": "string"
},
"type": {
"type": "string"
}
}
},
"dto.CommonRecover": {
"type": "object",
"required": [
@ -19131,6 +19492,29 @@ const docTemplate = `{
}
}
},
"dto.GeoRestrict": {
"type": "object",
"properties": {
"action": {
"type": "string"
},
"res": {
"type": "string"
},
"rules": {
"type": "array",
"items": {
"type": "string"
}
},
"state": {
"type": "string"
},
"type": {
"type": "string"
}
}
},
"dto.GroupCreate": {
"type": "object",
"required": [
@ -19564,6 +19948,9 @@ const docTemplate = `{
"ja": {
"type": "string"
},
"ko": {
"type": "string"
},
"ms": {
"type": "string"
},
@ -20217,6 +20604,45 @@ const docTemplate = `{
"ProxyCache"
]
},
"dto.OllamaBindDomainReq": {
"type": "object",
"required": [
"appInstallID"
],
"properties": {
"appInstallID": {
"type": "integer"
}
}
},
"dto.OllamaBindDomainRes": {
"type": "object",
"properties": {
"allowIPs": {
"type": "array",
"items": {
"type": "string"
}
},
"domain": {
"type": "string"
},
"sslID": {
"type": "integer"
},
"websiteID": {
"type": "integer"
}
}
},
"dto.OllamaModelName": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
},
"dto.OneDriveInfo": {
"type": "object",
"properties": {
@ -21683,6 +22109,14 @@ const docTemplate = `{
}
}
},
"dto.StateConfig": {
"type": "object",
"properties": {
"state": {
"type": "string"
}
}
},
"dto.SwapHelper": {
"type": "object",
"required": [
@ -21881,6 +22315,72 @@ const docTemplate = `{
}
}
},
"dto.WafConfig": {
"type": "object",
"required": [
"mode",
"secret",
"state"
],
"properties": {
"mode": {
"type": "string"
},
"secret": {
"type": "string"
},
"state": {
"type": "string"
},
"token": {
"type": "string"
}
}
},
"dto.WebsiteConfig": {
"type": "object",
"properties": {
"args": {
"$ref": "#/definitions/dto.CommonConfig"
},
"cc": {
"$ref": "#/definitions/dto.CCConfig"
},
"cdn": {
"$ref": "#/definitions/dto.StateConfig"
},
"cookie": {
"$ref": "#/definitions/dto.CommonConfig"
},
"defaultUaBlack": {
"$ref": "#/definitions/dto.CommonConfig"
},
"defaultUrlBlack": {
"$ref": "#/definitions/dto.CommonConfig"
},
"fileExt": {
"$ref": "#/definitions/dto.CommonConfig"
},
"geoRestrict": {
"$ref": "#/definitions/dto.GeoRestrict"
},
"header": {
"$ref": "#/definitions/dto.CommonConfig"
},
"methodWhite": {
"$ref": "#/definitions/dto.CommonConfig"
},
"sql": {
"$ref": "#/definitions/dto.CommonConfig"
},
"waf": {
"$ref": "#/definitions/dto.WafConfig"
},
"xss": {
"$ref": "#/definitions/dto.CommonConfig"
}
}
},
"dto.XPUInfo": {
"type": "object",
"properties": {
@ -22010,6 +22510,9 @@ const docTemplate = `{
"github": {
"type": "string"
},
"gpuSupport": {
"type": "boolean"
},
"icon": {
"type": "string"
},
@ -22210,32 +22713,6 @@ const docTemplate = `{
}
}
},
"model.Tag": {
"type": "object",
"properties": {
"createdAt": {
"type": "string"
},
"id": {
"type": "integer"
},
"key": {
"type": "string"
},
"name": {
"type": "string"
},
"sort": {
"type": "integer"
},
"translations": {
"type": "string"
},
"updatedAt": {
"type": "string"
}
}
},
"model.Website": {
"type": "object",
"properties": {
@ -22535,6 +23012,9 @@ const docTemplate = `{
"editCompose": {
"type": "boolean"
},
"gpuConfig": {
"type": "boolean"
},
"hostMode": {
"type": "boolean"
},
@ -22698,6 +23178,9 @@ const docTemplate = `{
"editCompose": {
"type": "boolean"
},
"gpuConfig": {
"type": "boolean"
},
"hostMode": {
"type": "boolean"
},
@ -23046,6 +23529,20 @@ const docTemplate = `{
}
}
},
"request.FilePathsCheck": {
"type": "object",
"required": [
"paths"
],
"properties": {
"paths": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"request.FileReadByLineReq": {
"type": "object",
"required": [
@ -23264,6 +23761,9 @@ const docTemplate = `{
"editCompose": {
"type": "boolean"
},
"gpuConfig": {
"type": "boolean"
},
"hostMode": {
"type": "boolean"
},
@ -24883,6 +25383,9 @@ const docTemplate = `{
"editCompose": {
"type": "boolean"
},
"gpuConfig": {
"type": "boolean"
},
"hostMode": {
"type": "boolean"
},
@ -24921,6 +25424,9 @@ const docTemplate = `{
"github": {
"type": "string"
},
"gpuSupport": {
"type": "boolean"
},
"icon": {
"type": "string"
},
@ -24966,7 +25472,7 @@ const docTemplate = `{
"tags": {
"type": "array",
"items": {
"$ref": "#/definitions/model.Tag"
"$ref": "#/definitions/response.TagDTO"
}
},
"type": {
@ -25007,6 +25513,9 @@ const docTemplate = `{
"enable": {
"type": "boolean"
},
"gpuSupport": {
"type": "boolean"
},
"hostMode": {
"type": "boolean"
},
@ -25087,6 +25596,9 @@ const docTemplate = `{
"description": {
"type": "string"
},
"gpuSupport": {
"type": "boolean"
},
"icon": {
"type": "string"
},
@ -25242,6 +25754,23 @@ const docTemplate = `{
}
}
},
"response.ExistFileInfo": {
"type": "object",
"properties": {
"modTime": {
"type": "string"
},
"name": {
"type": "string"
},
"path": {
"type": "string"
},
"size": {
"type": "number"
}
}
},
"response.FileInfo": {
"type": "object",
"properties": {
@ -26162,7 +26691,7 @@ const docTemplate = `{
},
"securityDefinitions": {
"ApiKeyAuth": {
"description": "Custom Token Format, Format: md5('1panel' + API-Key + UnixTimestamp).\n` + "`" + `` + "`" + `` + "`" + `\neg:\ncurl -X GET \"http://localhost:4004/api/v1/dashboard/current\" \\\n-H \"1Panel-Token: \u003c1panel_token\u003e\" \\\n-H \"1Panel-Timestamp: \u003ccurrent_unix_timestamp\u003e\"\n` + "`" + `` + "`" + `` + "`" + `\n- ` + "`" + `1Panel-Token` + "`" + ` is the key for the panel API Key.",
"description": "- ` + "`" + `1Panel-Token` + "`" + ` is the key for the panel API Key.",
"type": "apiKey",
"name": "1Panel-Token",
"in": "header"
@ -26186,8 +26715,6 @@ var SwaggerInfo = &swag.Spec{
Description: "Top-Rated Web-based Linux Server Management Tool",
InfoInstanceName: "swagger",
SwaggerTemplate: docTemplate,
LeftDelim: "{{",
RightDelim: "}}",
}
func init() {

View File

@ -17,6 +17,267 @@
},
"basePath": "/api/v1",
"paths": {
"/aitool/domain/bind": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AITools"
],
"summary": "Bind domain",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.WebsiteConfig"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/aitool/domain/get": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AITools"
],
"summary": "Get bind domain",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OllamaBindDomainReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.OllamaBindDomainRes"
}
}
}
}
},
"/aitool/gpu/load": {
"get": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AITools"
],
"summary": "Load gpu / xpu info",
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/aitool/ollama/model/del": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AITools"
],
"summary": "Delete Ollama model",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OllamaModelName"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFunctions": [],
"bodyKeys": [
"name"
],
"formatEN": "remove Ollama model [name]",
"formatZH": "删除模型 [name]",
"paramKeys": []
}
}
},
"/aitools/ollama/model": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AITools"
],
"summary": "Create Ollama model",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OllamaModelName"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFunctions": [],
"bodyKeys": [
"name"
],
"formatEN": "add Ollama model [name]",
"formatZH": "添加模型 [name]",
"paramKeys": []
}
}
},
"/aitools/ollama/model/load": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AITools"
],
"summary": "Page Ollama models",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OllamaModelName"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
}
}
}
},
"/aitools/ollama/model/search": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AITools"
],
"summary": "Page Ollama models",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.SearchWithPage"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.PageResult"
}
}
}
}
},
"/apps/checkupdate": {
"get": {
"security": [
@ -6021,6 +6282,47 @@
}
}
},
"/files/batch/check": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"File"
],
"summary": "Batch check file exist",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.FilePathsCheck"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/response.ExistFileInfo"
}
}
}
}
}
},
"/files/batch/del": {
"post": {
"security": [
@ -16756,6 +17058,9 @@
"github": {
"type": "string"
},
"gpuSupport": {
"type": "boolean"
},
"key": {
"type": "string"
},
@ -16988,6 +17293,41 @@
}
}
},
"dto.CCConfig": {
"type": "object",
"properties": {
"action": {
"type": "string"
},
"code": {
"type": "integer"
},
"duration": {
"type": "integer"
},
"ipBlock": {
"type": "string"
},
"ipBlockTime": {
"type": "integer"
},
"mode": {
"type": "string"
},
"res": {
"type": "string"
},
"state": {
"type": "string"
},
"threshold": {
"type": "integer"
},
"type": {
"type": "string"
}
}
},
"dto.CaptchaResponse": {
"type": "object",
"properties": {
@ -17432,6 +17772,26 @@
}
}
},
"dto.CommonConfig": {
"type": "object",
"properties": {
"action": {
"type": "string"
},
"code": {
"type": "integer"
},
"res": {
"type": "string"
},
"state": {
"type": "string"
},
"type": {
"type": "string"
}
}
},
"dto.CommonRecover": {
"type": "object",
"required": [
@ -19128,6 +19488,29 @@
}
}
},
"dto.GeoRestrict": {
"type": "object",
"properties": {
"action": {
"type": "string"
},
"res": {
"type": "string"
},
"rules": {
"type": "array",
"items": {
"type": "string"
}
},
"state": {
"type": "string"
},
"type": {
"type": "string"
}
}
},
"dto.GroupCreate": {
"type": "object",
"required": [
@ -19561,6 +19944,9 @@
"ja": {
"type": "string"
},
"ko": {
"type": "string"
},
"ms": {
"type": "string"
},
@ -20214,6 +20600,45 @@
"ProxyCache"
]
},
"dto.OllamaBindDomainReq": {
"type": "object",
"required": [
"appInstallID"
],
"properties": {
"appInstallID": {
"type": "integer"
}
}
},
"dto.OllamaBindDomainRes": {
"type": "object",
"properties": {
"allowIPs": {
"type": "array",
"items": {
"type": "string"
}
},
"domain": {
"type": "string"
},
"sslID": {
"type": "integer"
},
"websiteID": {
"type": "integer"
}
}
},
"dto.OllamaModelName": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
},
"dto.OneDriveInfo": {
"type": "object",
"properties": {
@ -21680,6 +22105,14 @@
}
}
},
"dto.StateConfig": {
"type": "object",
"properties": {
"state": {
"type": "string"
}
}
},
"dto.SwapHelper": {
"type": "object",
"required": [
@ -21878,6 +22311,72 @@
}
}
},
"dto.WafConfig": {
"type": "object",
"required": [
"mode",
"secret",
"state"
],
"properties": {
"mode": {
"type": "string"
},
"secret": {
"type": "string"
},
"state": {
"type": "string"
},
"token": {
"type": "string"
}
}
},
"dto.WebsiteConfig": {
"type": "object",
"properties": {
"args": {
"$ref": "#/definitions/dto.CommonConfig"
},
"cc": {
"$ref": "#/definitions/dto.CCConfig"
},
"cdn": {
"$ref": "#/definitions/dto.StateConfig"
},
"cookie": {
"$ref": "#/definitions/dto.CommonConfig"
},
"defaultUaBlack": {
"$ref": "#/definitions/dto.CommonConfig"
},
"defaultUrlBlack": {
"$ref": "#/definitions/dto.CommonConfig"
},
"fileExt": {
"$ref": "#/definitions/dto.CommonConfig"
},
"geoRestrict": {
"$ref": "#/definitions/dto.GeoRestrict"
},
"header": {
"$ref": "#/definitions/dto.CommonConfig"
},
"methodWhite": {
"$ref": "#/definitions/dto.CommonConfig"
},
"sql": {
"$ref": "#/definitions/dto.CommonConfig"
},
"waf": {
"$ref": "#/definitions/dto.WafConfig"
},
"xss": {
"$ref": "#/definitions/dto.CommonConfig"
}
}
},
"dto.XPUInfo": {
"type": "object",
"properties": {
@ -22007,6 +22506,9 @@
"github": {
"type": "string"
},
"gpuSupport": {
"type": "boolean"
},
"icon": {
"type": "string"
},
@ -22207,32 +22709,6 @@
}
}
},
"model.Tag": {
"type": "object",
"properties": {
"createdAt": {
"type": "string"
},
"id": {
"type": "integer"
},
"key": {
"type": "string"
},
"name": {
"type": "string"
},
"sort": {
"type": "integer"
},
"translations": {
"type": "string"
},
"updatedAt": {
"type": "string"
}
}
},
"model.Website": {
"type": "object",
"properties": {
@ -22532,6 +23008,9 @@
"editCompose": {
"type": "boolean"
},
"gpuConfig": {
"type": "boolean"
},
"hostMode": {
"type": "boolean"
},
@ -22695,6 +23174,9 @@
"editCompose": {
"type": "boolean"
},
"gpuConfig": {
"type": "boolean"
},
"hostMode": {
"type": "boolean"
},
@ -23043,6 +23525,20 @@
}
}
},
"request.FilePathsCheck": {
"type": "object",
"required": [
"paths"
],
"properties": {
"paths": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"request.FileReadByLineReq": {
"type": "object",
"required": [
@ -23261,6 +23757,9 @@
"editCompose": {
"type": "boolean"
},
"gpuConfig": {
"type": "boolean"
},
"hostMode": {
"type": "boolean"
},
@ -24880,6 +25379,9 @@
"editCompose": {
"type": "boolean"
},
"gpuConfig": {
"type": "boolean"
},
"hostMode": {
"type": "boolean"
},
@ -24918,6 +25420,9 @@
"github": {
"type": "string"
},
"gpuSupport": {
"type": "boolean"
},
"icon": {
"type": "string"
},
@ -24963,7 +25468,7 @@
"tags": {
"type": "array",
"items": {
"$ref": "#/definitions/model.Tag"
"$ref": "#/definitions/response.TagDTO"
}
},
"type": {
@ -25004,6 +25509,9 @@
"enable": {
"type": "boolean"
},
"gpuSupport": {
"type": "boolean"
},
"hostMode": {
"type": "boolean"
},
@ -25084,6 +25592,9 @@
"description": {
"type": "string"
},
"gpuSupport": {
"type": "boolean"
},
"icon": {
"type": "string"
},
@ -25239,6 +25750,23 @@
}
}
},
"response.ExistFileInfo": {
"type": "object",
"properties": {
"modTime": {
"type": "string"
},
"name": {
"type": "string"
},
"path": {
"type": "string"
},
"size": {
"type": "number"
}
}
},
"response.FileInfo": {
"type": "object",
"properties": {
@ -26159,7 +26687,7 @@
},
"securityDefinitions": {
"ApiKeyAuth": {
"description": "Custom Token Format, Format: md5('1panel' + API-Key + UnixTimestamp).\n```\neg:\ncurl -X GET \"http://localhost:4004/api/v1/dashboard/current\" \\\n-H \"1Panel-Token: \u003c1panel_token\u003e\" \\\n-H \"1Panel-Timestamp: \u003ccurrent_unix_timestamp\u003e\"\n```\n- `1Panel-Token` is the key for the panel API Key.",
"description": "- `1Panel-Token` is the key for the panel API Key.",
"type": "apiKey",
"name": "1Panel-Token",
"in": "header"

View File

@ -108,6 +108,8 @@ definitions:
type: string
github:
type: string
gpuSupport:
type: boolean
key:
type: string
limit:
@ -261,6 +263,29 @@ definitions:
- permission
- username
type: object
dto.CCConfig:
properties:
action:
type: string
code:
type: integer
duration:
type: integer
ipBlock:
type: string
ipBlockTime:
type: integer
mode:
type: string
res:
type: string
state:
type: string
threshold:
type: integer
type:
type: string
type: object
dto.CaptchaResponse:
properties:
captchaID:
@ -555,6 +580,19 @@ definitions:
required:
- type
type: object
dto.CommonConfig:
properties:
action:
type: string
code:
type: integer
res:
type: string
state:
type: string
type:
type: string
type: object
dto.CommonRecover:
properties:
detailName:
@ -1702,6 +1740,21 @@ definitions:
required:
- encryptionMode
type: object
dto.GeoRestrict:
properties:
action:
type: string
res:
type: string
rules:
items:
type: string
type: array
state:
type: string
type:
type: string
type: object
dto.GroupCreate:
properties:
id:
@ -1991,6 +2044,8 @@ definitions:
type: string
ja:
type: string
ko:
type: string
ms:
type: string
pt-br:
@ -2442,6 +2497,31 @@ definitions:
- CACHE
- HttpPer
- ProxyCache
dto.OllamaBindDomainReq:
properties:
appInstallID:
type: integer
required:
- appInstallID
type: object
dto.OllamaBindDomainRes:
properties:
allowIPs:
items:
type: string
type: array
domain:
type: string
sslID:
type: integer
websiteID:
type: integer
type: object
dto.OllamaModelName:
properties:
name:
type: string
type: object
dto.OneDriveInfo:
properties:
client_id:
@ -3432,6 +3512,11 @@ definitions:
upload:
type: string
type: object
dto.StateConfig:
properties:
state:
type: string
type: object
dto.SwapHelper:
properties:
isNew:
@ -3561,6 +3646,50 @@ definitions:
type:
type: string
type: object
dto.WafConfig:
properties:
mode:
type: string
secret:
type: string
state:
type: string
token:
type: string
required:
- mode
- secret
- state
type: object
dto.WebsiteConfig:
properties:
args:
$ref: '#/definitions/dto.CommonConfig'
cc:
$ref: '#/definitions/dto.CCConfig'
cdn:
$ref: '#/definitions/dto.StateConfig'
cookie:
$ref: '#/definitions/dto.CommonConfig'
defaultUaBlack:
$ref: '#/definitions/dto.CommonConfig'
defaultUrlBlack:
$ref: '#/definitions/dto.CommonConfig'
fileExt:
$ref: '#/definitions/dto.CommonConfig'
geoRestrict:
$ref: '#/definitions/dto.GeoRestrict'
header:
$ref: '#/definitions/dto.CommonConfig'
methodWhite:
$ref: '#/definitions/dto.CommonConfig'
sql:
$ref: '#/definitions/dto.CommonConfig'
waf:
$ref: '#/definitions/dto.WafConfig'
xss:
$ref: '#/definitions/dto.CommonConfig'
type: object
dto.XPUInfo:
properties:
deviceID:
@ -3646,6 +3775,8 @@ definitions:
type: string
github:
type: string
gpuSupport:
type: boolean
icon:
type: string
id:
@ -3778,23 +3909,6 @@ definitions:
workDir:
type: string
type: object
model.Tag:
properties:
createdAt:
type: string
id:
type: integer
key:
type: string
name:
type: string
sort:
type: integer
translations:
type: string
updatedAt:
type: string
type: object
model.Website:
properties:
IPV6:
@ -3990,6 +4104,8 @@ definitions:
type: string
editCompose:
type: boolean
gpuConfig:
type: boolean
hostMode:
type: boolean
memoryLimit:
@ -4099,6 +4215,8 @@ definitions:
type: string
editCompose:
type: boolean
gpuConfig:
type: boolean
hostMode:
type: boolean
installId:
@ -4332,6 +4450,15 @@ definitions:
required:
- path
type: object
request.FilePathsCheck:
properties:
paths:
items:
type: string
type: array
required:
- paths
type: object
request.FileReadByLineReq:
properties:
ID:
@ -4479,6 +4606,8 @@ definitions:
type: string
editCompose:
type: boolean
gpuConfig:
type: boolean
hostMode:
type: boolean
memoryLimit:
@ -5573,6 +5702,8 @@ definitions:
type: string
editCompose:
type: boolean
gpuConfig:
type: boolean
hostMode:
type: boolean
memoryLimit:
@ -5598,6 +5729,8 @@ definitions:
type: string
github:
type: string
gpuSupport:
type: boolean
icon:
type: string
id:
@ -5628,7 +5761,7 @@ definitions:
type: string
tags:
items:
$ref: '#/definitions/model.Tag'
$ref: '#/definitions/response.TagDTO'
type: array
type:
type: string
@ -5655,6 +5788,8 @@ definitions:
type: string
enable:
type: boolean
gpuSupport:
type: boolean
hostMode:
type: boolean
id:
@ -5708,6 +5843,8 @@ definitions:
properties:
description:
type: string
gpuSupport:
type: boolean
icon:
type: string
id:
@ -5810,6 +5947,17 @@ definitions:
required:
- size
type: object
response.ExistFileInfo:
properties:
modTime:
type: string
name:
type: string
path:
type: string
size:
type: number
type: object
response.FileInfo:
properties:
content:
@ -6421,6 +6569,159 @@ info:
title: 1Panel
version: "1.0"
paths:
/aitool/domain/bind:
post:
consumes:
- application/json
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.WebsiteConfig'
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
- Timestamp: []
summary: Bind domain
tags:
- AITools
/aitool/domain/get:
post:
consumes:
- application/json
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.OllamaBindDomainReq'
responses:
"200":
description: OK
schema:
$ref: '#/definitions/dto.OllamaBindDomainRes'
security:
- ApiKeyAuth: []
- Timestamp: []
summary: Get bind domain
tags:
- AITools
/aitool/gpu/load:
get:
consumes:
- application/json
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
- Timestamp: []
summary: Load gpu / xpu info
tags:
- AITools
/aitool/ollama/model/del:
post:
consumes:
- application/json
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.OllamaModelName'
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
- Timestamp: []
summary: Delete Ollama model
tags:
- AITools
x-panel-log:
BeforeFunctions: []
bodyKeys:
- name
formatEN: remove Ollama model [name]
formatZH: 删除模型 [name]
paramKeys: []
/aitools/ollama/model:
post:
consumes:
- application/json
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.OllamaModelName'
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
- Timestamp: []
summary: Create Ollama model
tags:
- AITools
x-panel-log:
BeforeFunctions: []
bodyKeys:
- name
formatEN: add Ollama model [name]
formatZH: 添加模型 [name]
paramKeys: []
/aitools/ollama/model/load:
post:
consumes:
- application/json
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.OllamaModelName'
responses:
"200":
description: OK
schema:
type: string
security:
- ApiKeyAuth: []
- Timestamp: []
summary: Page Ollama models
tags:
- AITools
/aitools/ollama/model/search:
post:
consumes:
- application/json
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.SearchWithPage'
responses:
"200":
description: OK
schema:
$ref: '#/definitions/dto.PageResult'
security:
- ApiKeyAuth: []
- Timestamp: []
summary: Page Ollama models
tags:
- AITools
/apps/{key}:
get:
consumes:
@ -10045,6 +10346,30 @@ paths:
formatEN: Create dir or file [path]
formatZH: 创建文件/文件夹 [path]
paramKeys: []
/files/batch/check:
post:
consumes:
- application/json
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/request.FilePathsCheck'
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/response.ExistFileInfo'
type: array
security:
- ApiKeyAuth: []
- Timestamp: []
summary: Batch check file exist
tags:
- File
/files/batch/del:
post:
consumes:
@ -16422,15 +16747,7 @@ schemes:
- https
securityDefinitions:
ApiKeyAuth:
description: |-
Custom Token Format, Format: md5('1panel' + API-Key + UnixTimestamp).
```
eg:
curl -X GET "http://localhost:4004/api/v1/dashboard/current" \
-H "1Panel-Token: <1panel_token>" \
-H "1Panel-Timestamp: <current_unix_timestamp>"
```
- `1Panel-Token` is the key for the panel API Key.
description: '- `1Panel-Token` is the key for the panel API Key.'
in: header
name: 1Panel-Token
type: apiKey

View File

@ -79,4 +79,23 @@ export namespace AITool {
shr: string;
memory: string;
}
export interface BindDomain {
domain: string;
sslID: number;
allowIPs: string[];
appInstallID: number;
websiteID?: number;
}
export interface BindDomainReq {
appInstallID: number;
}
export interface BindDomainRes {
domain: string;
sslID: number;
allowIPs: string[];
websiteID?: number;
}
}

View File

@ -18,3 +18,15 @@ export const loadOllamaModel = (name: string) => {
export const loadGPUInfo = () => {
return http.get<any>(`/aitools/gpu/load`);
};
export const bindDomain = (req: AITool.BindDomain) => {
return http.post(`/aitools/domain/bind`, req);
};
export const getBindDomain = (req: AITool.BindDomainReq) => {
return http.post<AITool.BindDomainRes>(`/aitools/domain/get`, req);
};
export const updateBindDomain = (req: AITool.BindDomain) => {
return http.post(`/aitools/domain/update`, req);
};

View File

@ -65,6 +65,11 @@
>
{{ $t('nginx.clearProxyCache') }}
</el-button>
<el-divider direction="vertical" v-if="slots.extra" />
<span v-if="slots.extra">
<slot name="extra"></slot>
</span>
</div>
<div class="ml-5" v-if="key === 'openresty' && (httpPort != 80 || httpsPort != 443)">
@ -109,12 +114,13 @@
<script lang="ts" setup>
import { CheckAppInstalled, InstalledOp } from '@/api/modules/app';
import router from '@/routers';
import { onMounted, reactive, ref } from 'vue';
import { onMounted, reactive, ref, useSlots } from 'vue';
import Status from '@/components/status/index.vue';
import { ElMessageBox } from 'element-plus';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import { ClearNginxCache } from '@/api/modules/nginx';
const slots = useSlots();
const props = defineProps({
appKey: {
@ -151,7 +157,15 @@ let refresh = ref(1);
const httpPort = ref(0);
const httpsPort = ref(0);
const em = defineEmits(['setting', 'isExist', 'before', 'after', 'update:loading', 'update:maskShow']);
const em = defineEmits([
'setting',
'isExist',
'before',
'after',
'update:loading',
'update:maskShow',
'update:appInstallID',
]);
const setting = () => {
em('setting', false);
};
@ -171,6 +185,7 @@ const onCheck = async (key: any, name: any) => {
em('isExist', res.data);
em('update:maskShow', res.data.status !== 'Running');
operateReq.installId = res.data.appInstallId;
em('update:appInstallID', res.data.appInstallId);
httpPort.value = res.data.httpPort;
httpsPort.value = res.data.httpsPort;
refresh.value++;

View File

@ -2579,6 +2579,13 @@ const message = {
licenseHelper: 'Professional version supports SMS alert',
alertCountHelper: 'Maximum daily alarm frequency',
},
aitool: {
proxy: 'AI Proxy Enhancement',
proxyHelper1: 'Bind domain and enable HTTPS for enhanced transmission security',
proxyHelper2: 'Limit IP access to prevent exposure on the public internet',
proxyHelper3: 'Enable streaming',
proxyHelper4: 'Once created, you can view and manage it in the website list',
},
};
export default {

View File

@ -2547,6 +2547,13 @@ const message = {
licenseHelper: 'プロのバージョンはSMSアラートをサポートします',
alertCountHelper: '最大毎日のアラーム周波数',
},
aitool: {
proxy: 'AI プロキシ強化',
proxyHelper1: 'ドメインをバインドしHTTPS を有効にして通信のセキュリティを強化',
proxyHelper2: 'IP アクセスを制限しパブリックインターネットでの露出を防止',
proxyHelper3: 'ストリーミングを有効にする',
proxyHelper4: '作成後ウェブサイトリストで確認および管理できます',
},
};
export default {
...fit2cloudJaLocale,

View File

@ -2509,6 +2509,13 @@ const message = {
licenseHelper: '전문 버전에서는 SMS 알림을 지원합니다.',
alertCountHelper: '최대 일일 알림 빈도',
},
aitool: {
proxy: 'AI 프록시 강화',
proxyHelper1: '도메인을 바인딩하고 HTTPS를 활성화하여 전송 보안을 강화',
proxyHelper2: 'IP 접근을 제한하여 공용 인터넷에서의 노출을 방지',
proxyHelper3: '스트리밍을 활성화',
proxyHelper4: '생성 , 웹사이트 목록에서 이를 보고 관리할 있습니다',
},
};
export default {

View File

@ -2607,6 +2607,13 @@ const message = {
licenseHelper: 'Versi profesional menyokong amaran SMS',
alertCountHelper: 'Kekerapan maksimum amaran harian',
},
aitool: {
proxy: 'Peningkatan Proksi AI',
proxyHelper1: 'Ikatkan domain dan aktifkan HTTPS untuk meningkatkan keselamatan penghantaran',
proxyHelper2: 'Hadkan akses IP untuk mengelakkan pendedahan di internet awam',
proxyHelper3: 'Aktifkan penstriman',
proxyHelper4: 'Setelah selesai, anda boleh melihat dan mengurusnya dalam senarai laman web',
},
};
export default {

View File

@ -2601,6 +2601,13 @@ const message = {
licenseHelper: 'A versão profissional suporta alertas via SMS',
alertCountHelper: 'Frequência máxima diária de alertas',
},
aitool: {
proxy: 'Melhoria de Proxy AI',
proxyHelper1: 'Vincule o domínio e habilite o HTTPS para aumentar a segurança na transmissão',
proxyHelper2: 'Limite o acesso por IP para evitar exposição na internet pública',
proxyHelper3: 'Habilite a transmissão em fluxo',
proxyHelper4: 'Após a criação, você pode visualizar e gerenciar no lista de sites',
},
};
export default {

View File

@ -2600,6 +2600,13 @@ const message = {
licenseHelper: 'Профессиональная версия поддерживает SMS-оповещения',
alertCountHelper: 'Максимальная дневная частота оповещений',
},
aitool: {
proxy: 'Усиление AI-прокси',
proxyHelper1: 'Привяжите домен и включите HTTPS для повышения безопасности передачи данных',
proxyHelper2: 'Ограничьте доступ по IP, чтобы предотвратить утечку данных в публичной сети',
proxyHelper3: 'Включите потоковую передачу',
proxyHelper4: 'После создания вы можете просматривать и управлять этим в списке сайтов',
},
};
export default {

View File

@ -2416,6 +2416,13 @@ const message = {
licenseHelper: '專業版支援簡訊告警功能',
alertCountHelper: '每日最大告警次數',
},
aitool: {
proxy: 'AI 代理增強',
proxyHelper1: '綁定域名並啟用 HTTPS提高傳輸安全性',
proxyHelper2: '限制 IP 訪問防止在公共網絡上暴露',
proxyHelper3: '啟用流式傳輸',
proxyHelper4: '創建後您可以在網站列表中查看並管理',
},
};
export default {
...fit2cloudTwLocale,

View File

@ -2418,6 +2418,13 @@ const message = {
licenseHelper: '专业版支持短信告警功能',
alertCountHelper: '每日最大告警次数',
},
aitool: {
proxy: 'AI 代理增强',
proxyHelper1: '绑定域名并开启 HTTPS增强传输安全性',
proxyHelper2: '限制 IP 访问防止在公网暴露',
proxyHelper3: '开启流式传输',
proxyHelper4: '创建完成之后可以在网站列表中查看并管理',
},
};
export default {
...fit2cloudZhLocale,

View File

@ -0,0 +1,234 @@
<template>
<el-drawer
v-model="open"
:destroy-on-close="true"
:close-on-click-modal="false"
:close-on-press-escape="false"
size="50%"
>
<template #header>
<DrawerHeader :header="$t('aitool.proxy')" :back="handleClose" />
</template>
<div v-loading="loading">
<el-form ref="formRef" label-position="top" @submit.prevent :model="req" :rules="rules">
<el-row type="flex" justify="center">
<el-col :span="22">
<el-alert class="common-prompt" :closable="false" type="warning">
<template #default>
<ul>
<li>{{ $t('aitool.proxyHelper1') }}</li>
<li>{{ $t('aitool.proxyHelper2') }}</li>
<li>{{ $t('aitool.proxyHelper3') }}</li>
</ul>
</template>
</el-alert>
<el-form-item :label="$t('website.domain')" prop="domain">
<el-input v-model.trim="req.domain" :disabled="operate === 'update'" />
</el-form-item>
<el-form-item :label="$t('firewall.address')" prop="ipList">
<el-input
:rows="3"
type="textarea"
clearable
v-model.trim="req.ipList"
:placeholder="$t('xpack.waf.ipGroupHelper')"
/>
</el-form-item>
<el-form-item>
<el-checkbox v-model="req.enableSSL" @change="changeSSL">
{{ $t('website.enable') + ' ' + 'HTTPS' }}
</el-checkbox>
</el-form-item>
<el-form-item
:label="$t('website.acmeAccountManage')"
prop="acmeAccountID"
v-if="req.enableSSL"
>
<el-select
v-model="req.acmeAccountID"
:placeholder="$t('website.selectAcme')"
@change="listSSL"
>
<el-option
v-for="(acme, index) in acmeAccounts"
:key="index"
:label="acme.email"
:value="acme.id"
>
<span>
{{ acme.email }}
<el-tag class="ml-5">{{ getAccountName(acme.type) }}</el-tag>
</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item
:label="$t('website.ssl')"
prop="sslID"
:hide-required-asterisk="true"
v-if="req.enableSSL"
>
<el-select
v-model="req.sslID"
:placeholder="$t('website.selectSSL')"
@change="changeSSl(req.sslID)"
>
<el-option
v-for="(ssl, index) in ssls"
:key="index"
:label="ssl.primaryDomain"
:value="ssl.id"
></el-option>
</el-select>
</el-form-item>
<el-alert :closable="false">
{{ $t('aitool.proxyHelper4') }}
</el-alert>
</el-col>
</el-row>
</el-form>
</div>
<template #footer>
<el-button @click="handleClose">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button type="primary" @click="onSubmit(formRef)">
{{ $t('commons.button.add') }}
</el-button>
</template>
</el-drawer>
</template>
<script lang="ts" setup>
import { Website } from '@/api/interface/website';
import { ListSSL, SearchAcmeAccount } from '@/api/modules/website';
import { Rules } from '@/global/form-rules';
import { FormInstance, FormRules } from 'element-plus';
import { reactive, ref } from 'vue';
import { getAccountName } from '@/utils/util';
import { bindDomain, getBindDomain, updateBindDomain } from '@/api/modules/ai-tool';
import { MsgSuccess } from '@/utils/message';
import i18n from '@/lang';
const open = ref(false);
const operate = ref('create');
const loading = ref(false);
const ssls = ref([]);
const websiteSSL = ref<Website.SSL>();
const acmeAccounts = ref();
const formRef = ref();
const req = ref({
domain: '',
sslID: 0,
ipList: '',
acmeAccountID: 0,
enableSSL: false,
allowIPs: [],
appInstallID: 0,
websiteID: 0,
});
const rules = reactive<FormRules>({
domain: [Rules.requiredInput],
});
const emit = defineEmits(['search']);
const handleClose = () => {
emit('search');
open.value = false;
};
const acceptParams = (installID: number) => {
req.value.appInstallID = installID;
search(installID);
open.value = true;
};
const changeSSl = (sslid: number) => {
const res = ssls.value.filter((element: Website.SSL) => {
return element.id == sslid;
});
websiteSSL.value = res[0];
};
const changeSSL = () => {
if (!req.value.enableSSL) {
req.value.sslID = 0;
} else {
listAcmeAccount();
}
};
const listSSL = () => {
const sslReq = {
acmeAccountID: String(req.value.acmeAccountID),
};
ListSSL(sslReq).then((res) => {
ssls.value = res.data || [];
if (ssls.value.length > 0) {
let exist = false;
for (const ssl of ssls.value) {
if (ssl.id === req.value.sslID) {
exist = true;
break;
}
}
if (!exist) {
req.value.sslID = ssls.value[0].id;
}
changeSSl(req.value.sslID);
} else {
req.value.sslID = 0;
}
});
};
const listAcmeAccount = () => {
SearchAcmeAccount({ page: 1, pageSize: 100 }).then((res) => {
acmeAccounts.value = res.data.items || [];
if (acmeAccounts.value.length > 0) {
req.value.acmeAccountID = acmeAccounts.value[0].id;
listSSL();
}
});
};
const onSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
req.value.allowIPs = req.value.ipList.split('\n');
if (operate.value === 'update') {
await updateBindDomain(req.value);
} else {
await bindDomain(req.value);
}
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
handleClose();
});
};
const search = async (appInstallID: number) => {
try {
const res = await getBindDomain({
appInstallID: appInstallID,
});
if (res.data.websiteID > 0) {
operate.value = 'update';
req.value.domain = res.data.domain;
req.value.websiteID = res.data.websiteID;
if (res.data.allowIPs.length > 0) {
req.value.ipList = res.data.allowIPs.join('\n');
}
if (res.data.sslID > 0) {
req.value.enableSSL = true;
req.value.sslID = res.data.sslID;
listSSL();
}
}
} catch (e) {}
};
defineExpose({
acceptParams,
});
</script>

View File

@ -15,9 +15,14 @@
v-model:loading="loading"
:hide-setting="true"
v-model:mask-show="maskShow"
v-model:appInstallID="appInstallID"
@is-exist="checkExist"
ref="appStatusRef"
></AppStatus>
>
<template #extra>
<el-button link type="primary" @click="bindDomain">{{ $t('aitool.proxy') }}</el-button>
</template>
</AppStatus>
</template>
<template #toolbar v-if="modelInfo.isExist">
<div class="flex justify-between gap-2 flex-wrap sm:flex-row">
@ -110,6 +115,7 @@
<Conn ref="connRef" />
<CodemirrorDialog ref="detailRef" />
<PortJumpDialog ref="dialogPortJumpRef" />
<BindDomain ref="bindDomainRef" />
</div>
</template>
@ -129,11 +135,11 @@ import { AITool } from '@/api/interface/ai-tool';
import { GetAppPort } from '@/api/modules/app';
import router from '@/routers';
import { MsgSuccess } from '@/utils/message';
import BindDomain from '@/views/ai-tools/model/domain/index.vue';
const globalStore = GlobalStore();
const loading = ref(false);
const maskShow = ref(true);
const addRef = ref();
const logRef = ref();
const detailRef = ref();
@ -141,9 +147,8 @@ const connRef = ref();
const openWebUIPort = ref();
const dashboardVisible = ref(false);
const dialogPortJumpRef = ref();
const appStatusRef = ref();
const bindDomainRef = ref();
const data = ref();
const paginationConfig = reactive({
cacheSizeKey: 'model-page-size',
@ -152,6 +157,7 @@ const paginationConfig = reactive({
total: 0,
});
const searchName = ref();
const appInstallID = ref(0);
const modelInfo = reactive({
status: '',
@ -209,6 +215,10 @@ const goDashboard = async () => {
dialogPortJumpRef.value.acceptParams({ port: openWebUIPort.value });
};
const bindDomain = () => {
bindDomainRef.value.acceptParams(appInstallID.value);
};
const goInstall = (name: string) => {
router.push({ name: 'AppAll', query: { install: name } });
};