mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-10 00:00:30 +00:00
Compare commits
92
Commits
release-1.1
...
v1.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c5c2440fe | ||
|
|
f22caed20c | ||
|
|
ed3e7e7c26 | ||
|
|
54bc33a1a2 | ||
|
|
900e141297 | ||
|
|
ca1eca476c | ||
|
|
c4d2593a42 | ||
|
|
932811c167 | ||
|
|
95ad101a08 | ||
|
|
a0c329019f | ||
|
|
8a92913230 | ||
|
|
5a8deddc63 | ||
|
|
2a1e60da44 | ||
|
|
6ca3deeac1 | ||
|
|
1b2edbb79a | ||
|
|
49c9929a53 | ||
|
|
8ce73a38dd | ||
|
|
28ebf7a0cc | ||
|
|
4f168656fc | ||
|
|
7f1f758e60 | ||
|
|
f8ab71953d | ||
|
|
eb50ee1c03 | ||
|
|
a184cb9bc4 | ||
|
|
0b50a10fb8 | ||
|
|
9bafdc1b0b | ||
|
|
63af54353b | ||
|
|
b67739af13 | ||
|
|
40633619ca | ||
|
|
e672d1d896 | ||
|
|
c69d162f5a | ||
|
|
2fd97b1753 | ||
|
|
5f893929da | ||
|
|
1c06b7b608 | ||
|
|
3ac64d65b6 | ||
|
|
f4f83283d3 | ||
|
|
aed4a55655 | ||
|
|
5f55a56a9e | ||
|
|
e95e79b572 | ||
|
|
15c0d0074b | ||
|
|
f4716cb62f | ||
|
|
1f2dfce7d1 | ||
|
|
c679631440 | ||
|
|
c62fd4841a | ||
|
|
a073113817 | ||
|
|
09d462b829 | ||
|
|
379b171f0a | ||
|
|
c424e22924 | ||
|
|
18e171446e | ||
|
|
c0a1555c73 | ||
|
|
3b0c844f33 | ||
|
|
566a4f3568 | ||
|
|
2c8b19bff2 | ||
|
|
292dca6419 | ||
|
|
ebe0f98209 | ||
|
|
eba1e5495f | ||
|
|
0ebd04f012 | ||
|
|
c5e8a3fa04 | ||
|
|
42b76fd82d | ||
|
|
bbf610569d | ||
|
|
eeb5fa81a1 | ||
|
|
f65ca6678f | ||
|
|
680f48dcba | ||
|
|
8947e00302 | ||
|
|
b42cf32326 | ||
|
|
5b68332b9a | ||
|
|
d7f53862e9 | ||
|
|
7887bf96de | ||
|
|
db2aa35b2f | ||
|
|
936b0e59ab | ||
|
|
6e3923d0da | ||
|
|
4377575206 | ||
|
|
0c09b12680 | ||
|
|
8a32d8032f | ||
|
|
9f12a91f4a | ||
|
|
dd0ca4bcaf | ||
|
|
14cc97eb44 | ||
|
|
2d31c5b005 | ||
|
|
05e7506f61 | ||
|
|
37806f1113 | ||
|
|
79622f324b | ||
|
|
34e84081e3 | ||
|
|
edf07be281 | ||
|
|
1208514f37 | ||
|
|
bfd857ec4b | ||
|
|
9435290bb6 | ||
|
|
acf64dcf25 | ||
|
|
2dd88364f8 | ||
|
|
d900b52a50 | ||
|
|
bd8d96be4d | ||
|
|
17dd07fe32 | ||
|
|
a06e5f28b3 | ||
|
|
d5f400670c |
@@ -41,12 +41,9 @@ curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_
|
||||
|
||||
- [在线文档](https://1panel.cn/docs/)
|
||||
- [教学视频](https://space.bilibili.com/510493147/channel/collectiondetail?sid=1199760)
|
||||
- [社区论坛](https://bbs.fit2cloud.com/c/1p/7)
|
||||
|
||||
## 社区
|
||||
|
||||
如果您在使用过程中有任何疑问或对建议,欢迎提交 GitHub Issue 或加入到我们微信交流群进行交流沟通。
|
||||
|
||||
**微信交流群**
|
||||
**加入微信交流群**
|
||||
|
||||
<img src="https://1panel.cn/img/wechat-group.jpg" width="156" height="156"/>
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
如果您发现安全问题,请直接联系我们:
|
||||
|
||||
- wanghe@fit2cloud.com
|
||||
- zhengkun@fit2cloud.com
|
||||
- support@fit2cloud.com
|
||||
- 400-052-0755
|
||||
|
||||
@@ -13,6 +14,7 @@
|
||||
All security bugs should be reported to the contact as below:
|
||||
|
||||
- wanghe@fit2cloud.com
|
||||
- zhengkun@fit2cloud.com
|
||||
- support@fit2cloud.com
|
||||
- 400-052-0755
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
@@ -28,7 +26,7 @@ func (b *BaseApi) Login(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if req.AuthMethod != "jwt" {
|
||||
if req.AuthMethod != "jwt" && !req.IgnoreCaptcha {
|
||||
if err := captcha.VerifyCode(req.CaptchaID, req.Captcha); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
@@ -102,71 +100,10 @@ func (b *BaseApi) Captcha(c *gin.Context) {
|
||||
// @Summary Load safety status
|
||||
// @Description 获取系统安全登录状态
|
||||
// @Success 200
|
||||
// @Failure 402
|
||||
// @Router /auth/status [get]
|
||||
func (b *BaseApi) GetSafetyStatus(c *gin.Context) {
|
||||
if err := authService.SafetyStatus(c); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
func (b *BaseApi) SafeEntrance(c *gin.Context) {
|
||||
code, exist := c.Params.Get("code")
|
||||
if !exist {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, errors.New("missing code"))
|
||||
return
|
||||
}
|
||||
ok, err := authService.VerifyCode(code)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, errors.New("missing code"))
|
||||
return
|
||||
}
|
||||
if !ok {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, errors.New("missing code"))
|
||||
return
|
||||
}
|
||||
if err := authService.SafeEntrance(c, code); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, errors.New("missing code"))
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Auth
|
||||
// @Summary Check is First login
|
||||
// @Description 判断是否为首次登录
|
||||
// @Success 200
|
||||
// @Router /auth/status [get]
|
||||
func (b *BaseApi) CheckIsFirstLogin(c *gin.Context) {
|
||||
helper.SuccessWithData(c, authService.CheckIsFirst())
|
||||
}
|
||||
|
||||
// @Tags Auth
|
||||
// @Summary Init user
|
||||
// @Description 初始化用户
|
||||
// @Accept json
|
||||
// @Param request body dto.InitUser true "request"
|
||||
// @Success 200
|
||||
// @Router /auth/init [post]
|
||||
func (b *BaseApi) InitUserInfo(c *gin.Context) {
|
||||
var req dto.InitUser
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := authService.InitUser(c, req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
// @Router /auth/issafety [get]
|
||||
func (b *BaseApi) CheckIsSafety(c *gin.Context) {
|
||||
code := c.DefaultQuery("code", "")
|
||||
helper.SuccessWithData(c, authService.CheckIsSafety(code))
|
||||
}
|
||||
|
||||
// @Tags Auth
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -77,8 +78,9 @@ func (b *BaseApi) SearchJobRecords(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.StartTime = req.StartTime.Add(8 * time.Hour)
|
||||
req.EndTime = req.EndTime.Add(8 * time.Hour)
|
||||
loc, _ := time.LoadLocation(common.LoadTimeZone())
|
||||
req.StartTime = req.StartTime.In(loc)
|
||||
req.EndTime = req.EndTime.In(loc)
|
||||
|
||||
total, list, err := cronjobService.SearchRecords(req)
|
||||
if err != nil {
|
||||
|
||||
@@ -186,7 +186,29 @@ func (b *BaseApi) ChangeFileMode(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags File
|
||||
// @Summary Change file owner
|
||||
// @Description 修改文件用户/组
|
||||
// @Accept json
|
||||
// @Param request body request.FileRoleUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /files/owner [post]
|
||||
// @x-panel-log {"bodyKeys":["path","user","group"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"修改用户/组 [paths] => [user]/[group]","formatEN":"Change owner [paths] => [user]/[group]"}
|
||||
func (b *BaseApi) ChangeFileOwner(c *gin.Context) {
|
||||
var req request.FileRoleUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := fileService.ChangeOwner(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags File
|
||||
@@ -498,7 +520,6 @@ func (b *BaseApi) Size(c *gin.Context) {
|
||||
// @Success 200 {string} content
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /files/loadfile [post]
|
||||
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"读取文件 [path]","formatEN":"Read file [path]"}
|
||||
func (b *BaseApi) LoadFromFile(c *gin.Context) {
|
||||
var req dto.FilePath
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/shirou/gopsutil/v3/disk"
|
||||
"github.com/shirou/gopsutil/v3/net"
|
||||
@@ -24,8 +25,9 @@ func (b *BaseApi) LoadMonitor(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.StartTime = req.StartTime.Add(8 * time.Hour)
|
||||
req.EndTime = req.EndTime.Add(8 * time.Hour)
|
||||
loc, _ := time.LoadLocation(common.LoadTimeZone())
|
||||
req.StartTime = req.StartTime.In(loc)
|
||||
req.EndTime = req.EndTime.In(loc)
|
||||
|
||||
var backdatas []dto.MonitorData
|
||||
if req.Param == "all" || req.Param == "cpu" || req.Param == "memory" || req.Param == "load" {
|
||||
|
||||
@@ -92,6 +92,48 @@ func (b *BaseApi) UpdatePassword(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Update system ssl
|
||||
// @Description 修改系统 ssl 登录
|
||||
// @Accept json
|
||||
// @Param request body dto.SSLUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/ssl/update [post]
|
||||
// @x-panel-log {"bodyKeys":["ssl"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"修改系统 ssl => [ssl]","formatEN":"update system ssl => [ssl]"}
|
||||
func (b *BaseApi) UpdateSSL(c *gin.Context) {
|
||||
var req dto.SSLUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := settingService.UpdateSSL(c, req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Load system cert info
|
||||
// @Description 获取证书信息
|
||||
// @Success 200 {object} dto.SettingInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/ssl/info [get]
|
||||
func (b *BaseApi) LoadFromCert(c *gin.Context) {
|
||||
info, err := settingService.LoadFromCert()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, info)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Update system port
|
||||
// @Description 更新系统端口
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@@ -107,14 +108,15 @@ func (b *BaseApi) RedisWsSsh(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
defer wsConn.Close()
|
||||
commands := fmt.Sprintf("docker exec -it %s redis-cli", redisConf.ContainerName)
|
||||
commands := "redis-cli"
|
||||
if len(redisConf.Requirepass) != 0 {
|
||||
commands = fmt.Sprintf("docker exec -it %s redis-cli -a %s --no-auth-warning", redisConf.ContainerName, redisConf.Requirepass)
|
||||
commands = fmt.Sprintf("redis-cli -a %s --no-auth-warning", redisConf.Requirepass)
|
||||
}
|
||||
slave, err := terminal.NewCommand(commands)
|
||||
slave, err := terminal.NewCommand(fmt.Sprintf("docker exec -it %s %s", redisConf.ContainerName, commands))
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer killBash(redisConf.ContainerName, commands)
|
||||
defer slave.Close()
|
||||
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave)
|
||||
@@ -177,6 +179,7 @@ func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer killBash(containerID, command)
|
||||
defer slave.Close()
|
||||
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave)
|
||||
@@ -216,6 +219,15 @@ func wshandleError(ws *websocket.Conn, err error) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func killBash(containerID, comm string) {
|
||||
sudo := ""
|
||||
if cmd.HasNoPasswordSudo() {
|
||||
sudo = "sudo"
|
||||
}
|
||||
command := exec.Command("sh", "-c", fmt.Sprintf("%s kill -9 $(docker top %s -eo pid,command | grep '%s' | awk '{print $1}')", sudo, containerID, comm))
|
||||
_, _ = command.CombinedOutput()
|
||||
}
|
||||
|
||||
var upGrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024 * 1024 * 10,
|
||||
|
||||
@@ -648,3 +648,113 @@ func (b *BaseApi) UpdateSiteDirPermission(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Get proxy conf
|
||||
// @Description 获取反向代理配置
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteProxyReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/proxies [post]
|
||||
func (b *BaseApi) GetProxyConfig(c *gin.Context) {
|
||||
var req request.WebsiteProxyReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
res, err := websiteService.GetProxies(req.ID)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Update proxy conf
|
||||
// @Description 修改反向代理配置
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteProxyConfig true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/proxies/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"修改网站 [domain] 反向代理配置 ","formatEN":"Update domain [domain] proxy config"}
|
||||
func (b *BaseApi) UpdateProxyConfig(c *gin.Context) {
|
||||
var req request.WebsiteProxyConfig
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
err := websiteService.OperateProxy(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Update proxy file
|
||||
// @Description 更新反向代理文件
|
||||
// @Accept json
|
||||
// @Param request body request.NginxProxyUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/proxy/file [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteID"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteID","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新反向代理文件 [domain]","formatEN":"Nginx conf proxy file update [domain]"}
|
||||
func (b *BaseApi) UpdateProxyConfigFile(c *gin.Context) {
|
||||
var req request.NginxProxyUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdateProxyFile(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Get AuthBasic conf
|
||||
// @Description 获取密码访问配置
|
||||
// @Accept json
|
||||
// @Param request body request.NginxAuthReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/auths [post]
|
||||
func (b *BaseApi) GetAuthConfig(c *gin.Context) {
|
||||
var req request.NginxAuthReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
res, err := websiteService.GetAuthBasics(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Get AuthBasic conf
|
||||
// @Description 更新密码访问配置
|
||||
// @Accept json
|
||||
// @Param request body request.NginxAuthUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/auths/update [post]
|
||||
func (b *BaseApi) UpdateAuthConfig(c *gin.Context) {
|
||||
var req request.NginxAuthUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdateAuthBasic(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
+6
-10
@@ -17,11 +17,12 @@ type MfaCredential struct {
|
||||
}
|
||||
|
||||
type Login struct {
|
||||
Name string `json:"name"`
|
||||
Password string `json:"password"`
|
||||
Captcha string `json:"captcha"`
|
||||
CaptchaID string `json:"captchaID"`
|
||||
AuthMethod string `json:"authMethod"`
|
||||
Name string `json:"name"`
|
||||
Password string `json:"password"`
|
||||
IgnoreCaptcha bool `json:"ignoreCaptcha"`
|
||||
Captcha string `json:"captcha"`
|
||||
CaptchaID string `json:"captchaID"`
|
||||
AuthMethod string `json:"authMethod"`
|
||||
}
|
||||
|
||||
type MFALogin struct {
|
||||
@@ -30,8 +31,3 @@ type MFALogin struct {
|
||||
Code string `json:"code"`
|
||||
AuthMethod string `json:"authMethod"`
|
||||
}
|
||||
|
||||
type InitUser struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ type PageInfo struct {
|
||||
|
||||
type UpdateDescription struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Description string `json:"description"`
|
||||
Description string `json:"description" validate:"max=256"`
|
||||
}
|
||||
|
||||
type OperationWithName struct {
|
||||
|
||||
@@ -22,6 +22,10 @@ type ContainerInfo struct {
|
||||
State string `json:"state"`
|
||||
RunTime string `json:"runTime"`
|
||||
|
||||
CPUPercent float64 `json:"cpuPercent"`
|
||||
MemoryPercent float64 `json:"memoryPercent"`
|
||||
Ports []string `json:"ports"`
|
||||
|
||||
IsFromApp bool `json:"isFromApp"`
|
||||
IsFromCompose bool `json:"isFromCompose"`
|
||||
}
|
||||
@@ -59,8 +63,10 @@ type VolumeHelper struct {
|
||||
Mode string `json:"mode"`
|
||||
}
|
||||
type PortHelper struct {
|
||||
ContainerPort int `json:"containerPort"`
|
||||
HostPort int `json:"hostPort"`
|
||||
HostIP string `json:"hostIP"`
|
||||
HostPort string `json:"hostPort"`
|
||||
ContainerPort string `json:"containerPort"`
|
||||
Protocol string `json:"protocol"`
|
||||
}
|
||||
|
||||
type ContainerLog struct {
|
||||
@@ -140,6 +146,7 @@ type ComposeOperation struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Path string `json:"path" validate:"required"`
|
||||
Operation string `json:"operation" validate:"required,oneof=start stop down"`
|
||||
WithFile bool `json:"withFile"`
|
||||
}
|
||||
type ComposeUpdate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
|
||||
@@ -28,13 +28,20 @@ type NginxParam struct {
|
||||
Params []string
|
||||
}
|
||||
|
||||
type NginxAuth struct {
|
||||
Username string `json:"username"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type NginxKey string
|
||||
|
||||
const (
|
||||
Index NginxKey = "index"
|
||||
LimitConn NginxKey = "limit-conn"
|
||||
SSL NginxKey = "ssl"
|
||||
HttpPer NginxKey = "http-per"
|
||||
Index NginxKey = "index"
|
||||
LimitConn NginxKey = "limit-conn"
|
||||
SSL NginxKey = "ssl"
|
||||
CACHE NginxKey = "cache"
|
||||
HttpPer NginxKey = "http-per"
|
||||
ProxyCache NginxKey = "proxy-cache"
|
||||
)
|
||||
|
||||
var ScopeKeyMap = map[NginxKey][]string{
|
||||
@@ -46,5 +53,7 @@ var ScopeKeyMap = map[NginxKey][]string{
|
||||
|
||||
var StaticFileKeyMap = map[NginxKey]struct {
|
||||
}{
|
||||
SSL: {},
|
||||
SSL: {},
|
||||
CACHE: {},
|
||||
ProxyCache: {},
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ type FileCreate struct {
|
||||
IsLink bool `json:"isLink"`
|
||||
IsSymlink bool `json:"isSymlink"`
|
||||
LinkPath string `json:"linkPath"`
|
||||
Sub bool `json:"sub"`
|
||||
}
|
||||
|
||||
type FileDelete struct {
|
||||
@@ -88,3 +89,10 @@ type DirSizeReq struct {
|
||||
type FileProcessReq struct {
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
type FileRoleUpdate struct {
|
||||
Path string `json:"path" validate:"required"`
|
||||
User string `json:"user" validate:"required"`
|
||||
Group string `json:"group" validate:"required"`
|
||||
Sub bool `json:"sub" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -30,3 +30,21 @@ type NginxRewriteUpdate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Content string `json:"content" validate:"required"`
|
||||
}
|
||||
|
||||
type NginxProxyUpdate struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
Content string `json:"content" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
}
|
||||
|
||||
type NginxAuthUpdate struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
Operate string `json:"operate" validate:"required"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type NginxAuthReq struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -156,3 +156,24 @@ type WebsiteUpdateDirPermission struct {
|
||||
User string `json:"user" validate:"required"`
|
||||
Group string `json:"group" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsiteProxyConfig struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Operate string `json:"operate" validate:"required"`
|
||||
Enable bool `json:"enable" validate:"required"`
|
||||
Cache bool `json:"cache" validate:"required"`
|
||||
CacheTime int `json:"cacheTime" validate:"required"`
|
||||
CacheUnit string `json:"cacheUnit" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Modifier string `json:"modifier" validate:"required"`
|
||||
Match string `json:"match" validate:"required"`
|
||||
ProxyPass string `json:"proxyPass" validate:"required"`
|
||||
ProxyHost string `json:"proxyHost" validate:"required"`
|
||||
Content string `json:"content"`
|
||||
FilePath string `json:"filePath"`
|
||||
Replaces map[string]string `json:"replaces"`
|
||||
}
|
||||
|
||||
type WebsiteProxyReq struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package response
|
||||
|
||||
import "github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
|
||||
type NginxStatus struct {
|
||||
Active string `json:"active"`
|
||||
Accepts string `json:"accepts"`
|
||||
@@ -14,3 +16,8 @@ type NginxParam struct {
|
||||
Name string `json:"name"`
|
||||
Params []string `json:"params"`
|
||||
}
|
||||
|
||||
type NginxAuthRes struct {
|
||||
Enable bool `json:"enable"`
|
||||
Items []dto.NginxAuth `json:"items"`
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ type SettingInfo struct {
|
||||
Language string `json:"language"`
|
||||
|
||||
ServerPort string `json:"serverPort"`
|
||||
SSL string `json:"ssl"`
|
||||
SSLType string `json:"sslType"`
|
||||
SecurityEntrance string `json:"securityEntrance"`
|
||||
ExpirationDays string `json:"expirationDays"`
|
||||
ExpirationTime string `json:"expirationTime"`
|
||||
@@ -39,6 +41,23 @@ type SettingUpdate struct {
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type SSLUpdate struct {
|
||||
SSLType string `json:"sslType"`
|
||||
Domain string `json:"domain"`
|
||||
SSL string `json:"ssl" validate:"required,oneof=enable disable"`
|
||||
Cert string `json:"cert"`
|
||||
Key string `json:"key"`
|
||||
SSLID uint `json:"sslID"`
|
||||
}
|
||||
type SSLInfo struct {
|
||||
Domain string `json:"domain"`
|
||||
Timeout string `json:"timeout"`
|
||||
RootPath string `json:"rootPath"`
|
||||
Cert string `json:"cert"`
|
||||
Key string `json:"key"`
|
||||
SSLID uint `json:"sslID"`
|
||||
}
|
||||
|
||||
type PasswordUpdate struct {
|
||||
OldPassword string `json:"oldPassword" validate:"required"`
|
||||
NewPassword string `json:"newPassword" validate:"required"`
|
||||
@@ -50,7 +69,7 @@ type PortUpdate struct {
|
||||
|
||||
type SnapshotCreate struct {
|
||||
From string `json:"from" validate:"required,oneof=OSS S3 SFTP MINIO COS KODO"`
|
||||
Description string `json:"description"`
|
||||
Description string `json:"description" validate:"max=256"`
|
||||
}
|
||||
type SnapshotRecover struct {
|
||||
IsNew bool `json:"isNew"`
|
||||
@@ -60,12 +79,12 @@ type SnapshotRecover struct {
|
||||
type SnapshotImport struct {
|
||||
From string `json:"from"`
|
||||
Names []string `json:"names"`
|
||||
Description string `json:"description"`
|
||||
Description string `json:"description" validate:"max=256"`
|
||||
}
|
||||
type SnapshotInfo struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Description string `json:"description" validate:"max=256"`
|
||||
From string `json:"from"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
|
||||
@@ -343,13 +343,6 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
|
||||
}
|
||||
go upApp(appInstall)
|
||||
go updateToolApp(appInstall)
|
||||
ports := []int{appInstall.HttpPort}
|
||||
if appInstall.HttpsPort > 0 {
|
||||
ports = append(ports, appInstall.HttpsPort)
|
||||
}
|
||||
go func() {
|
||||
_ = OperateFirewallPort(nil, ports)
|
||||
}()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -226,6 +226,10 @@ func upgradeInstall(installId uint, detailId uint) error {
|
||||
}
|
||||
|
||||
detailDir := path.Join(constant.ResourceDir, "apps", install.App.Key, "versions", detail.Version)
|
||||
if install.App.Resource == constant.AppResourceLocal {
|
||||
detailDir = path.Join(constant.ResourceDir, "localApps", strings.TrimPrefix(install.App.Key, "local"), "versions", detail.Version)
|
||||
}
|
||||
|
||||
cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rf %s/* %s", detailDir, install.GetPath()))
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
@@ -19,11 +17,8 @@ import (
|
||||
type AuthService struct{}
|
||||
|
||||
type IAuthService interface {
|
||||
SafetyStatus(c *gin.Context) error
|
||||
CheckIsFirst() bool
|
||||
InitUser(c *gin.Context, req dto.InitUser) error
|
||||
CheckIsSafety(code string) bool
|
||||
VerifyCode(code string) (bool, error)
|
||||
SafeEntrance(c *gin.Context, code string) error
|
||||
Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error)
|
||||
LogOut(c *gin.Context) error
|
||||
MFALogin(c *gin.Context, info dto.MFALogin) (*dto.UserLoginInfo, error)
|
||||
@@ -33,22 +28,6 @@ func NewIAuthService() IAuthService {
|
||||
return &AuthService{}
|
||||
}
|
||||
|
||||
func (u *AuthService) SafeEntrance(c *gin.Context, code string) error {
|
||||
codeWithMD5 := encrypt.Md5(code)
|
||||
cookieValue, _ := encrypt.StringEncrypt(codeWithMD5)
|
||||
c.SetCookie(codeWithMD5, cookieValue, 604800, "", "", false, false)
|
||||
|
||||
expiredSetting, err := settingRepo.Get(settingRepo.WithByKey("ExpirationDays"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
timeout, _ := strconv.Atoi(expiredSetting.Value)
|
||||
if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, timeout).Format("2006-01-02 15:04:05")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *AuthService) Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error) {
|
||||
nameSetting, err := settingRepo.Get(settingRepo.WithByKey("UserName"))
|
||||
if err != nil {
|
||||
@@ -164,57 +143,13 @@ func (u *AuthService) VerifyCode(code string) (bool, error) {
|
||||
return setting.Value == code, nil
|
||||
}
|
||||
|
||||
func (u *AuthService) SafetyStatus(c *gin.Context) error {
|
||||
setting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance"))
|
||||
func (u *AuthService) CheckIsSafety(code string) bool {
|
||||
status, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance"))
|
||||
if err != nil {
|
||||
return err
|
||||
return false
|
||||
}
|
||||
codeWithEcrypt, err := c.Cookie(encrypt.Md5(setting.Value))
|
||||
if err != nil {
|
||||
return err
|
||||
if len(status.Value) == 0 {
|
||||
return true
|
||||
}
|
||||
code, err := encrypt.StringDecrypt(codeWithEcrypt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if code != encrypt.Md5(setting.Value) {
|
||||
return errors.New("code not match")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *AuthService) CheckIsFirst() bool {
|
||||
user, _ := settingRepo.Get(settingRepo.WithByKey("UserName"))
|
||||
pass, _ := settingRepo.Get(settingRepo.WithByKey("Password"))
|
||||
return len(user.Value) == 0 || len(pass.Value) == 0
|
||||
}
|
||||
|
||||
func (u *AuthService) InitUser(c *gin.Context, req dto.InitUser) error {
|
||||
user, _ := settingRepo.Get(settingRepo.WithByKey("UserName"))
|
||||
pass, _ := settingRepo.Get(settingRepo.WithByKey("Password"))
|
||||
if len(user.Value) == 0 || len(pass.Value) == 0 {
|
||||
newPass, err := encrypt.StringEncrypt(req.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.Update("UserName", req.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.Update("Password", newPass); err != nil {
|
||||
return err
|
||||
}
|
||||
expiredSetting, err := settingRepo.Get(settingRepo.WithByKey("ExpirationDays"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
timeout, _ := strconv.Atoi(expiredSetting.Value)
|
||||
if timeout != 0 {
|
||||
if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, timeout).Format("2006-01-02 15:04:05")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("can't init user because user %s is in system", user.Value)
|
||||
return status.Value == code
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
@@ -97,27 +98,45 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
|
||||
records = list[start:end]
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(records))
|
||||
for _, container := range records {
|
||||
IsFromCompose := false
|
||||
if _, ok := container.Labels[composeProjectLabel]; ok {
|
||||
IsFromCompose = true
|
||||
}
|
||||
IsFromApp := false
|
||||
if created, ok := container.Labels[composeCreatedBy]; ok && created == "Apps" {
|
||||
IsFromApp = true
|
||||
}
|
||||
backDatas = append(backDatas, dto.ContainerInfo{
|
||||
ContainerID: container.ID,
|
||||
CreateTime: time.Unix(container.Created, 0).Format("2006-01-02 15:04:05"),
|
||||
Name: container.Names[0][1:],
|
||||
ImageId: strings.Split(container.ImageID, ":")[1],
|
||||
ImageName: container.Image,
|
||||
State: container.State,
|
||||
RunTime: container.Status,
|
||||
IsFromApp: IsFromApp,
|
||||
IsFromCompose: IsFromCompose,
|
||||
})
|
||||
go func(item types.Container) {
|
||||
IsFromCompose := false
|
||||
if _, ok := item.Labels[composeProjectLabel]; ok {
|
||||
IsFromCompose = true
|
||||
}
|
||||
IsFromApp := false
|
||||
if created, ok := item.Labels[composeCreatedBy]; ok && created == "Apps" {
|
||||
IsFromApp = true
|
||||
}
|
||||
|
||||
var ports []string
|
||||
for _, port := range item.Ports {
|
||||
if port.IP == "::" || port.PublicPort == 0 {
|
||||
continue
|
||||
}
|
||||
ports = append(ports, fmt.Sprintf("%v:%v/%s", port.PublicPort, port.PrivatePort, port.Type))
|
||||
}
|
||||
cpu, mem := loadCpuAndMem(client, item.ID)
|
||||
backDatas = append(backDatas, dto.ContainerInfo{
|
||||
ContainerID: item.ID,
|
||||
CreateTime: time.Unix(item.Created, 0).Format("2006-01-02 15:04:05"),
|
||||
Name: item.Names[0][1:],
|
||||
ImageId: strings.Split(item.ImageID, ":")[1],
|
||||
ImageName: item.Image,
|
||||
State: item.State,
|
||||
RunTime: item.Status,
|
||||
CPUPercent: cpu,
|
||||
MemoryPercent: mem,
|
||||
Ports: ports,
|
||||
IsFromApp: IsFromApp,
|
||||
IsFromCompose: IsFromCompose,
|
||||
})
|
||||
wg.Done()
|
||||
}(container)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
return int64(total), backDatas, nil
|
||||
}
|
||||
@@ -147,24 +166,27 @@ func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) {
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerCreate(req dto.ContainerCreate) error {
|
||||
if len(req.ExposedPorts) != 0 {
|
||||
for _, port := range req.ExposedPorts {
|
||||
if common.ScanPort(port.HostPort) {
|
||||
return buserr.WithDetail(constant.ErrPortInUsed, port.HostPort, nil)
|
||||
}
|
||||
}
|
||||
portMap, err := checkPortStats(req.ExposedPorts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
exposeds := make(nat.PortSet)
|
||||
for port := range portMap {
|
||||
exposeds[port] = struct{}{}
|
||||
}
|
||||
config := &container.Config{
|
||||
Image: req.Image,
|
||||
Cmd: req.Cmd,
|
||||
Env: req.Env,
|
||||
Labels: stringsToMap(req.Labels),
|
||||
Tty: true,
|
||||
OpenStdin: true,
|
||||
Image: req.Image,
|
||||
Cmd: req.Cmd,
|
||||
Env: req.Env,
|
||||
Labels: stringsToMap(req.Labels),
|
||||
Tty: true,
|
||||
OpenStdin: true,
|
||||
ExposedPorts: exposeds,
|
||||
}
|
||||
hostConf := &container.HostConfig{
|
||||
AutoRemove: req.AutoRemove,
|
||||
@@ -181,11 +203,7 @@ func (u *ContainerService) ContainerCreate(req dto.ContainerCreate) error {
|
||||
hostConf.Memory = req.Memory
|
||||
}
|
||||
if len(req.ExposedPorts) != 0 {
|
||||
hostConf.PortBindings = make(nat.PortMap)
|
||||
for _, port := range req.ExposedPorts {
|
||||
bindItem := nat.PortBinding{HostPort: strconv.Itoa(port.HostPort)}
|
||||
hostConf.PortBindings[nat.Port(fmt.Sprintf("%d/tcp", port.ContainerPort))] = []nat.PortBinding{bindItem}
|
||||
}
|
||||
hostConf.PortBindings = portMap
|
||||
}
|
||||
if len(req.Volumes) != 0 {
|
||||
config.Volumes = make(map[string]struct{})
|
||||
@@ -270,16 +288,16 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainterStats, erro
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
res.Body.Close()
|
||||
return nil, err
|
||||
}
|
||||
res.Body.Close()
|
||||
var stats *types.StatsJSON
|
||||
if err := json.Unmarshal(body, &stats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var data dto.ContainterStats
|
||||
previousCPU := stats.PreCPUStats.CPUUsage.TotalUsage
|
||||
previousSystem := stats.PreCPUStats.SystemUsage
|
||||
data.CPUPercent = calculateCPUPercentUnix(previousCPU, previousSystem, stats)
|
||||
data.CPUPercent = calculateCPUPercentUnix(stats)
|
||||
data.IORead, data.IOWrite = calculateBlockIO(stats.BlkioStats)
|
||||
data.Memory = float64(stats.MemoryStats.Usage) / 1024 / 1024
|
||||
if cache, ok := stats.MemoryStats.Stats["cache"]; ok {
|
||||
@@ -294,25 +312,33 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainterStats, erro
|
||||
func stringsToMap(list []string) map[string]string {
|
||||
var lableMap = make(map[string]string)
|
||||
for _, label := range list {
|
||||
sps := strings.Split(label, "=")
|
||||
if len(sps) > 1 {
|
||||
if strings.Contains(label, "=") {
|
||||
sps := strings.SplitN(label, "=", 2)
|
||||
lableMap[sps[0]] = sps[1]
|
||||
}
|
||||
}
|
||||
return lableMap
|
||||
}
|
||||
func calculateCPUPercentUnix(previousCPU, previousSystem uint64, v *types.StatsJSON) float64 {
|
||||
var (
|
||||
cpuPercent = 0.0
|
||||
cpuDelta = float64(v.CPUStats.CPUUsage.TotalUsage) - float64(previousCPU)
|
||||
systemDelta = float64(v.CPUStats.SystemUsage) - float64(previousSystem)
|
||||
)
|
||||
|
||||
func calculateCPUPercentUnix(stats *types.StatsJSON) float64 {
|
||||
cpuPercent := 0.0
|
||||
cpuDelta := float64(stats.CPUStats.CPUUsage.TotalUsage) - float64(stats.PreCPUStats.CPUUsage.TotalUsage)
|
||||
systemDelta := float64(stats.CPUStats.SystemUsage) - float64(stats.PreCPUStats.SystemUsage)
|
||||
|
||||
if systemDelta > 0.0 && cpuDelta > 0.0 {
|
||||
cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CPUStats.CPUUsage.PercpuUsage)) * 100.0
|
||||
cpuPercent = (cpuDelta / systemDelta) * float64(len(stats.CPUStats.CPUUsage.PercpuUsage)) * 100.0
|
||||
}
|
||||
return cpuPercent
|
||||
}
|
||||
func calculateMemPercentUnix(memStats types.MemoryStats) float64 {
|
||||
memPercent := 0.0
|
||||
memUsage := float64(memStats.Usage - memStats.Stats["cache"])
|
||||
memLimit := float64(memStats.Limit)
|
||||
if memUsage > 0.0 && memLimit > 0.0 {
|
||||
memPercent = (memUsage / memLimit) * 100.0
|
||||
}
|
||||
return memPercent
|
||||
}
|
||||
func calculateBlockIO(blkio types.BlkioStats) (blkRead float64, blkWrite float64) {
|
||||
for _, bioEntry := range blkio.IoServiceBytesRecursive {
|
||||
switch strings.ToLower(bioEntry.Op) {
|
||||
@@ -363,3 +389,71 @@ func pullImages(ctx context.Context, client *client.Client, image string) error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadCpuAndMem(client *client.Client, container string) (float64, float64) {
|
||||
res, err := client.ContainerStats(context.Background(), container, false)
|
||||
if err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
res.Body.Close()
|
||||
return 0, 0
|
||||
}
|
||||
res.Body.Close()
|
||||
var stats *types.StatsJSON
|
||||
if err := json.Unmarshal(body, &stats); err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
CPUPercent := calculateCPUPercentUnix(stats)
|
||||
MemPercent := calculateMemPercentUnix(stats.MemoryStats)
|
||||
return CPUPercent, MemPercent
|
||||
}
|
||||
|
||||
func checkPortStats(ports []dto.PortHelper) (nat.PortMap, error) {
|
||||
portMap := make(nat.PortMap)
|
||||
if len(ports) == 0 {
|
||||
return portMap, nil
|
||||
}
|
||||
for _, port := range ports {
|
||||
if strings.Contains(port.ContainerPort, "-") {
|
||||
if !strings.Contains(port.HostPort, "-") {
|
||||
return portMap, buserr.New(constant.ErrPortRules)
|
||||
}
|
||||
hostStart, _ := strconv.Atoi(strings.Split(port.HostPort, "-")[0])
|
||||
hostEnd, _ := strconv.Atoi(strings.Split(port.HostPort, "-")[1])
|
||||
containerStart, _ := strconv.Atoi(strings.Split(port.ContainerPort, "-")[0])
|
||||
containerEnd, _ := strconv.Atoi(strings.Split(port.ContainerPort, "-")[1])
|
||||
if (hostEnd-hostStart) <= 0 || (containerEnd-containerStart) <= 0 {
|
||||
return portMap, buserr.New(constant.ErrPortRules)
|
||||
}
|
||||
if (containerEnd - containerStart) != (hostEnd - hostStart) {
|
||||
return portMap, buserr.New(constant.ErrPortRules)
|
||||
}
|
||||
for i := 0; i <= hostEnd-hostStart; i++ {
|
||||
bindItem := nat.PortBinding{HostPort: strconv.Itoa(hostStart + i), HostIP: port.HostIP}
|
||||
portMap[nat.Port(fmt.Sprintf("%d/%s", containerStart+i, port.Protocol))] = []nat.PortBinding{bindItem}
|
||||
}
|
||||
for i := hostStart; i <= hostEnd; i++ {
|
||||
if common.ScanPort(i) {
|
||||
return portMap, buserr.WithDetail(constant.ErrPortInUsed, i, nil)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
portItem := 0
|
||||
if strings.Contains(port.HostPort, "-") {
|
||||
portItem, _ = strconv.Atoi(strings.Split(port.HostPort, "-")[0])
|
||||
} else {
|
||||
portItem, _ = strconv.Atoi(port.HostPort)
|
||||
}
|
||||
if common.ScanPort(portItem) {
|
||||
return portMap, buserr.WithDetail(constant.ErrPortInUsed, portItem, nil)
|
||||
}
|
||||
bindItem := nat.PortBinding{HostPort: strconv.Itoa(portItem), HostIP: port.HostIP}
|
||||
portMap[nat.Port(fmt.Sprintf("%s/%s", port.ContainerPort, port.Protocol))] = []nat.PortBinding{bindItem}
|
||||
}
|
||||
}
|
||||
return portMap, nil
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
@@ -154,9 +155,10 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error)
|
||||
go func() {
|
||||
defer file.Close()
|
||||
cmd := exec.Command("docker-compose", "-f", req.Path, "up", "-d")
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
_, _ = file.Write(stdout)
|
||||
if err != nil {
|
||||
multiWriter := io.MultiWriter(os.Stdout, file)
|
||||
cmd.Stdout = multiWriter
|
||||
cmd.Stderr = multiWriter
|
||||
if err := cmd.Run(); err != nil {
|
||||
global.LOG.Errorf("docker-compose up %s failed, err: %v", req.Name, err)
|
||||
_, _ = compose.Down(req.Path)
|
||||
_, _ = file.WriteString("docker-compose up failed!")
|
||||
@@ -180,7 +182,9 @@ func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
|
||||
global.LOG.Infof("docker-compose %s %s successful", req.Operation, req.Name)
|
||||
if req.Operation == "down" {
|
||||
_ = composeRepo.DeleteRecord(commonRepo.WithByName(req.Name))
|
||||
_ = os.RemoveAll(strings.ReplaceAll(req.Path, "/docker-compose.yml", ""))
|
||||
if req.WithFile {
|
||||
_ = os.RemoveAll(path.Dir(req.Path))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -211,15 +215,7 @@ func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error {
|
||||
}
|
||||
|
||||
func (u *ContainerService) loadPath(req *dto.ComposeCreate) error {
|
||||
if req.From == "template" {
|
||||
template, err := composeRepo.Get(commonRepo.WithByID(req.Template))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.From = "edit"
|
||||
req.File = template.Content
|
||||
}
|
||||
if req.From == "edit" {
|
||||
if req.From == "template" || req.From == "edit" {
|
||||
dir := fmt.Sprintf("%s/docker/compose/%s", constant.DataDir, req.Name)
|
||||
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
|
||||
@@ -29,7 +29,7 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
if len(cronjob.Script) == 0 {
|
||||
return
|
||||
}
|
||||
stdout, errExec := cmd.ExecWithTimeOut(cronjob.Script, 5*time.Minute)
|
||||
stdout, errExec := cmd.ExecCronjobWithTimeOut(cronjob.Script, 5*time.Minute)
|
||||
if errExec != nil {
|
||||
err = errExec
|
||||
}
|
||||
|
||||
@@ -518,12 +518,12 @@ func excSQL(containerName, password, command string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
cmd := exec.CommandContext(ctx, "docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command)
|
||||
err := cmd.Run()
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
if ctx.Err() == context.DeadlineExceeded {
|
||||
return buserr.WithDetail(constant.ErrExecTimeOut, containerName, nil)
|
||||
}
|
||||
if err != nil {
|
||||
stdStr := strings.ReplaceAll(err.Error(), "mysql: [Warning] Using a password on the command line interface can be insecure.\n", "")
|
||||
stdStr := strings.ReplaceAll(string(stdout), "mysql: [Warning] Using a password on the command line interface can be insecure.\n", "")
|
||||
if err != nil || strings.HasPrefix(string(stdStr), "ERROR ") {
|
||||
return errors.New(stdStr)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -39,6 +39,7 @@ type IFileService interface {
|
||||
ChangeName(req request.FileRename) error
|
||||
Wget(w request.FileWget) (string, error)
|
||||
MvFile(m request.FileMove) error
|
||||
ChangeOwner(req request.FileRoleUpdate) error
|
||||
}
|
||||
|
||||
func NewIFileService() IFileService {
|
||||
@@ -159,7 +160,16 @@ func (f *FileService) BatchDelete(op request.FileBatchDelete) error {
|
||||
|
||||
func (f *FileService) ChangeMode(op request.FileCreate) error {
|
||||
fo := files.NewFileOp()
|
||||
return fo.Chmod(op.Path, fs.FileMode(op.Mode))
|
||||
if op.Sub {
|
||||
return fo.ChmodR(op.Path, op.Mode)
|
||||
} else {
|
||||
return fo.Chmod(op.Path, fs.FileMode(op.Mode))
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FileService) ChangeOwner(req request.FileRoleUpdate) error {
|
||||
fo := files.NewFileOp()
|
||||
return fo.ChownR(req.Path, req.User, req.Group, req.Sub)
|
||||
}
|
||||
|
||||
func (f *FileService) Compress(c request.FileCompress) error {
|
||||
|
||||
@@ -161,6 +161,10 @@ func getNginxParamsFromStaticFile(scope dto.NginxKey, newParams []dto.NginxParam
|
||||
switch scope {
|
||||
case dto.SSL:
|
||||
newConfig = parser.NewStringParser(string(nginx_conf.SSL)).Parse()
|
||||
case dto.CACHE:
|
||||
newConfig = parser.NewStringParser(string(nginx_conf.Cache)).Parse()
|
||||
case dto.ProxyCache:
|
||||
newConfig = parser.NewStringParser(string(nginx_conf.ProxyCache)).Parse()
|
||||
}
|
||||
for _, dir := range newConfig.GetDirectives() {
|
||||
addParam := dto.NginxParam{
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
@@ -12,6 +18,8 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ssl"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -22,6 +30,8 @@ type ISettingService interface {
|
||||
Update(key, value string) error
|
||||
UpdatePassword(c *gin.Context, old, new string) error
|
||||
UpdatePort(port uint) error
|
||||
UpdateSSL(c *gin.Context, req dto.SSLUpdate) error
|
||||
LoadFromCert() (*dto.SSLInfo, error)
|
||||
HandlePasswordExpired(c *gin.Context, old, new string) error
|
||||
}
|
||||
|
||||
@@ -90,6 +100,135 @@ func (u *SettingService) UpdatePort(port uint) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {
|
||||
secretDir := global.CONF.System.BaseDir + "/1panel/secret/"
|
||||
if req.SSL == "disable" {
|
||||
if err := settingRepo.Update("SSL", "disable"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.Update("SSLType", "self"); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = os.Remove(secretDir + "server.crt")
|
||||
_ = os.Remove(secretDir + "server.key")
|
||||
go func() {
|
||||
_, err := cmd.Exec("systemctl restart 1panel.service")
|
||||
if err != nil {
|
||||
global.LOG.Errorf("restart system failed, err: %v", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err := os.Stat(secretDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(secretDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := settingRepo.Update("SSLType", req.SSLType); err != nil {
|
||||
return err
|
||||
}
|
||||
if req.SSLType == "self" {
|
||||
if len(req.Domain) == 0 {
|
||||
return fmt.Errorf("load domain failed")
|
||||
}
|
||||
if err := ssl.GenerateSSL(req.Domain); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if req.SSLType == "select" {
|
||||
sslInfo, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(req.SSLID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Cert = sslInfo.Pem
|
||||
req.Key = sslInfo.PrivateKey
|
||||
req.SSLType = "import"
|
||||
if err := settingRepo.Update("SSLID", strconv.Itoa(int(req.SSLID))); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if req.SSLType == "import" {
|
||||
cert, err := os.OpenFile(secretDir+"server.crt.tmp", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cert.Close()
|
||||
if _, err := cert.WriteString(req.Cert); err != nil {
|
||||
return err
|
||||
}
|
||||
key, err := os.OpenFile(secretDir+"server.key.tmp", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := key.WriteString(req.Key); err != nil {
|
||||
return err
|
||||
}
|
||||
defer key.Close()
|
||||
}
|
||||
if err := checkCertValid(req.Domain); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
if err := fileOp.Rename(secretDir+"server.crt.tmp", secretDir+"server.crt"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fileOp.Rename(secretDir+"server.key.tmp", secretDir+"server.key"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.Update("SSL", req.SSL); err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
_, err := cmd.Exec("systemctl restart 1panel.service")
|
||||
if err != nil {
|
||||
global.LOG.Errorf("restart system failed, err: %v", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *SettingService) LoadFromCert() (*dto.SSLInfo, error) {
|
||||
ssl, err := settingRepo.Get(settingRepo.WithByKey("SSL"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ssl.Value == "disable" {
|
||||
return &dto.SSLInfo{}, nil
|
||||
}
|
||||
sslType, err := settingRepo.Get(settingRepo.WithByKey("SSLType"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := loadInfoFromCert()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch sslType.Value {
|
||||
case "import":
|
||||
if _, err := os.Stat(global.CONF.System.BaseDir + "/1panel/secret/server.crt"); err != nil {
|
||||
return nil, fmt.Errorf("load server.crt file failed, err: %v", err)
|
||||
}
|
||||
certFile, _ := os.ReadFile(global.CONF.System.BaseDir + "/1panel/secret/server.crt")
|
||||
data.Cert = string(certFile)
|
||||
|
||||
if _, err := os.Stat(global.CONF.System.BaseDir + "/1panel/secret/server.key"); err != nil {
|
||||
return nil, fmt.Errorf("load server.key file failed, err: %v", err)
|
||||
}
|
||||
keyFile, _ := os.ReadFile(global.CONF.System.BaseDir + "/1panel/secret/server.key")
|
||||
data.Key = string(keyFile)
|
||||
case "select":
|
||||
sslID, err := settingRepo.Get(settingRepo.WithByKey("SSLID"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
id, _ := strconv.Atoi(sslID.Value)
|
||||
data.SSLID = uint(id)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (u *SettingService) HandlePasswordExpired(c *gin.Context, old, new string) error {
|
||||
setting, err := settingRepo.Get(settingRepo.WithByKey("Password"))
|
||||
if err != nil {
|
||||
@@ -128,3 +267,60 @@ func (u *SettingService) UpdatePassword(c *gin.Context, old, new string) error {
|
||||
_ = global.SESSION.Clean()
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadInfoFromCert() (*dto.SSLInfo, error) {
|
||||
var info dto.SSLInfo
|
||||
certFile := global.CONF.System.BaseDir + "/1panel/secret/server.crt"
|
||||
if _, err := os.Stat(certFile); err != nil {
|
||||
return &info, err
|
||||
}
|
||||
certData, err := os.ReadFile(certFile)
|
||||
if err != nil {
|
||||
return &info, err
|
||||
}
|
||||
certBlock, _ := pem.Decode(certData)
|
||||
if certBlock == nil {
|
||||
return &info, err
|
||||
}
|
||||
certObj, err := x509.ParseCertificate(certBlock.Bytes)
|
||||
if err != nil {
|
||||
return &info, err
|
||||
}
|
||||
var domains []string
|
||||
if len(certObj.IPAddresses) != 0 {
|
||||
for _, ip := range certObj.IPAddresses {
|
||||
domains = append(domains, ip.String())
|
||||
}
|
||||
}
|
||||
if len(certObj.DNSNames) != 0 {
|
||||
domains = append(domains, certObj.DNSNames...)
|
||||
}
|
||||
return &dto.SSLInfo{
|
||||
Domain: strings.Join(domains, ","),
|
||||
Timeout: certObj.NotAfter.Format("2006-01-02 15:04:05"),
|
||||
RootPath: global.CONF.System.BaseDir + "/1panel/secret/server.crt",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func checkCertValid(domain string) error {
|
||||
certificate, err := os.ReadFile(global.CONF.System.BaseDir + "/1panel/secret/server.crt.tmp")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key, err := os.ReadFile(global.CONF.System.BaseDir + "/1panel/secret/server.key.tmp")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = tls.X509KeyPair(certificate, key); err != nil {
|
||||
return err
|
||||
}
|
||||
certBlock, _ := pem.Decode(certificate)
|
||||
if certBlock == nil {
|
||||
return err
|
||||
}
|
||||
if _, err := x509.ParseCertificate(certBlock.Bytes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+443
-10
@@ -10,12 +10,18 @@ import (
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser"
|
||||
"github.com/1Panel-dev/1Panel/cmd/server/nginx_conf"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gorm.io/gorm"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -64,6 +70,11 @@ type IWebsiteService interface {
|
||||
UpdateRewriteConfig(req request.NginxRewriteUpdate) error
|
||||
UpdateSiteDir(req request.WebsiteUpdateDir) error
|
||||
UpdateSitePermission(req request.WebsiteUpdateDirPermission) error
|
||||
OperateProxy(req request.WebsiteProxyConfig) (err error)
|
||||
GetProxies(id uint) (res []request.WebsiteProxyConfig, err error)
|
||||
UpdateProxyFile(req request.NginxProxyUpdate) (err error)
|
||||
GetAuthBasics(req request.NginxAuthReq) (res response.NginxAuthRes, err error)
|
||||
UpdateAuthBasic(req request.NginxAuthUpdate) (err error)
|
||||
}
|
||||
|
||||
func NewIWebsiteService() IWebsiteService {
|
||||
@@ -75,6 +86,13 @@ func (w WebsiteService) PageWebsite(req request.WebsiteSearch) (int64, []respons
|
||||
websiteDTOs []response.WebsiteDTO
|
||||
opts []repo.DBOption
|
||||
)
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return 0, nil, nil
|
||||
}
|
||||
return 0, nil, err
|
||||
}
|
||||
opts = append(opts, commonRepo.WithOrderBy("created_at desc"))
|
||||
if req.Name != "" {
|
||||
opts = append(opts, websiteRepo.WithDomainLike(req.Name))
|
||||
@@ -105,10 +123,12 @@ func (w WebsiteService) PageWebsite(req request.WebsiteSearch) (int64, []respons
|
||||
}
|
||||
runtimeName = runtime.Name
|
||||
}
|
||||
sitePath := path.Join(constant.AppInstallDir, constant.AppOpenresty, nginxInstall.Name, "www", "sites", web.Alias)
|
||||
websiteDTOs = append(websiteDTOs, response.WebsiteDTO{
|
||||
Website: web,
|
||||
AppName: appName,
|
||||
RuntimeName: runtimeName,
|
||||
SitePath: sitePath,
|
||||
})
|
||||
}
|
||||
return total, websiteDTOs, nil
|
||||
@@ -393,10 +413,16 @@ func (w WebsiteService) DeleteWebsite(req request.WebsiteDelete) error {
|
||||
}
|
||||
|
||||
func (w WebsiteService) CreateWebsiteDomain(create request.WebsiteDomainCreate) (model.WebsiteDomain, error) {
|
||||
var domainModel model.WebsiteDomain
|
||||
var ports []int
|
||||
var domains []string
|
||||
|
||||
var (
|
||||
domainModel model.WebsiteDomain
|
||||
ports []int
|
||||
domains []string
|
||||
)
|
||||
if create.Port != 80 {
|
||||
if common.ScanPort(create.Port) {
|
||||
return domainModel, buserr.WithDetail(constant.ErrPortInUsed, create.Port, nil)
|
||||
}
|
||||
}
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(create.WebsiteID))
|
||||
if err != nil {
|
||||
return domainModel, err
|
||||
@@ -413,6 +439,11 @@ func (w WebsiteService) CreateWebsiteDomain(create request.WebsiteDomainCreate)
|
||||
Port: create.Port,
|
||||
WebsiteID: create.WebsiteID,
|
||||
}
|
||||
if create.Port != 80 {
|
||||
go func() {
|
||||
_ = OperateFirewallPort(nil, []int{create.Port})
|
||||
}()
|
||||
}
|
||||
return domainModel, websiteDomainRepo.Create(context.TODO(), &domainModel)
|
||||
}
|
||||
|
||||
@@ -443,7 +474,11 @@ func (w WebsiteService) DeleteWebsiteDomain(domainId uint) error {
|
||||
domains = append(domains, webSiteDomain.Domain)
|
||||
}
|
||||
if len(ports) > 0 || len(domains) > 0 {
|
||||
if err := deleteListenAndServerName(website, ports, domains); err != nil {
|
||||
stringBinds := make([]string, len(ports))
|
||||
for i := 0; i < len(ports); i++ {
|
||||
stringBinds[i] = strconv.Itoa(ports[i])
|
||||
}
|
||||
if err := deleteListenAndServerName(website, stringBinds, domains); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -590,14 +625,28 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
|
||||
if !req.Enable {
|
||||
website.Protocol = constant.ProtocolHTTP
|
||||
website.WebsiteSSLID = 0
|
||||
if err := deleteListenAndServerName(website, []int{443}, []string{}); err != nil {
|
||||
if err := deleteListenAndServerName(website, []string{"443", "[::]:443"}, []string{}); err != nil {
|
||||
return response.WebsiteHTTPS{}, err
|
||||
}
|
||||
nginxParams := getNginxParamsFromStaticFile(dto.SSL, nil)
|
||||
nginxParams = append(nginxParams, dto.NginxParam{
|
||||
Name: "if",
|
||||
Params: []string{"($scheme", "=", "http)"},
|
||||
})
|
||||
nginxParams = append(nginxParams,
|
||||
dto.NginxParam{
|
||||
Name: "if",
|
||||
Params: []string{"($scheme", "=", "http)"},
|
||||
},
|
||||
dto.NginxParam{
|
||||
Name: "ssl_certificate",
|
||||
},
|
||||
dto.NginxParam{
|
||||
Name: "ssl_certificate_key",
|
||||
},
|
||||
dto.NginxParam{
|
||||
Name: "ssl_protocols",
|
||||
},
|
||||
dto.NginxParam{
|
||||
Name: "ssl_ciphers",
|
||||
},
|
||||
)
|
||||
if err := deleteNginxConfig(constant.NginxScopeServer, nginxParams, &website); err != nil {
|
||||
return response.WebsiteHTTPS{}, err
|
||||
}
|
||||
@@ -1105,3 +1154,387 @@ func (w WebsiteService) UpdateSitePermission(req request.WebsiteUpdateDirPermiss
|
||||
website.Group = req.Group
|
||||
return websiteRepo.Save(context.Background(), &website)
|
||||
}
|
||||
|
||||
func (w WebsiteService) OperateProxy(req request.WebsiteProxyConfig) (err error) {
|
||||
var (
|
||||
website model.Website
|
||||
params []response.NginxParam
|
||||
nginxInstall model.AppInstall
|
||||
par *parser.Parser
|
||||
oldContent []byte
|
||||
)
|
||||
|
||||
website, err = websiteRepo.GetFirst(commonRepo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
params, err = getNginxParamsByKeys(constant.NginxScopeHttp, []string{"proxy_cache"}, &website)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
nginxInstall, err = getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
if len(params) == 0 || len(params[0].Params) == 0 {
|
||||
commonDir := path.Join(nginxInstall.GetPath(), "www", "common", "proxy")
|
||||
proxyTempPath := path.Join(commonDir, "proxy_temp_dir")
|
||||
if !fileOp.Stat(proxyTempPath) {
|
||||
_ = fileOp.CreateDir(proxyTempPath, 0755)
|
||||
}
|
||||
proxyCacheDir := path.Join(commonDir, "proxy_temp_dir")
|
||||
if !fileOp.Stat(proxyCacheDir) {
|
||||
_ = fileOp.CreateDir(proxyCacheDir, 0755)
|
||||
}
|
||||
nginxParams := getNginxParamsFromStaticFile(dto.CACHE, nil)
|
||||
if err = updateNginxConfig(constant.NginxScopeHttp, nginxParams, &website); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
includeDir := path.Join(nginxInstall.GetPath(), "www", "sites", website.Alias, "proxy")
|
||||
if !fileOp.Stat(includeDir) {
|
||||
_ = fileOp.CreateDir(includeDir, 0755)
|
||||
}
|
||||
fileName := fmt.Sprintf("%s.conf", req.Name)
|
||||
includePath := path.Join(includeDir, fileName)
|
||||
backName := fmt.Sprintf("%s.bak", req.Name)
|
||||
backPath := path.Join(includeDir, backName)
|
||||
|
||||
if req.Operate == "create" && (fileOp.Stat(includePath) || fileOp.Stat(backPath)) {
|
||||
err = buserr.New(constant.ErrNameIsExist)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
switch req.Operate {
|
||||
case "create":
|
||||
_ = fileOp.DeleteFile(includePath)
|
||||
case "edit":
|
||||
_ = fileOp.WriteFile(includePath, bytes.NewReader(oldContent), 0755)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
var config *components.Config
|
||||
|
||||
switch req.Operate {
|
||||
case "create":
|
||||
config = parser.NewStringParser(string(nginx_conf.Proxy)).Parse()
|
||||
case "edit":
|
||||
par, err = parser.NewParser(includePath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
config = par.Parse()
|
||||
oldContent, err = fileOp.GetContent(includePath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case "delete":
|
||||
_ = fileOp.DeleteFile(includePath)
|
||||
_ = fileOp.DeleteFile(backPath)
|
||||
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
|
||||
case "disable":
|
||||
_ = fileOp.Rename(includePath, backPath)
|
||||
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
|
||||
case "enable":
|
||||
_ = fileOp.Rename(backPath, includePath)
|
||||
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
|
||||
}
|
||||
config.FilePath = includePath
|
||||
directives := config.Directives
|
||||
location, ok := directives[0].(*components.Location)
|
||||
if !ok {
|
||||
err = errors.New("error")
|
||||
return
|
||||
}
|
||||
location.UpdateDirective("proxy_pass", []string{req.ProxyPass})
|
||||
location.UpdateDirective("proxy_set_header", []string{"Host", req.ProxyHost})
|
||||
location.ChangePath(req.Modifier, req.Match)
|
||||
if req.Cache {
|
||||
location.AddCache(req.CacheTime, req.CacheUnit)
|
||||
} else {
|
||||
location.RemoveCache()
|
||||
}
|
||||
if len(req.Replaces) > 0 {
|
||||
location.AddSubFilter(req.Replaces)
|
||||
} else {
|
||||
location.RemoveSubFilter()
|
||||
}
|
||||
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, err)
|
||||
}
|
||||
nginxInclude := fmt.Sprintf("/www/sites/%s/proxy/*.conf", website.Alias)
|
||||
if err = updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: "include", Params: []string{nginxInclude}}}, &website); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (w WebsiteService) GetProxies(id uint) (res []request.WebsiteProxyConfig, err error) {
|
||||
var (
|
||||
website model.Website
|
||||
nginxInstall model.AppInstall
|
||||
fileList response.FileInfo
|
||||
)
|
||||
website, err = websiteRepo.GetFirst(commonRepo.WithByID(id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
nginxInstall, err = getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
includeDir := path.Join(nginxInstall.GetPath(), "www", "sites", website.Alias, "proxy")
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(includeDir) {
|
||||
return
|
||||
}
|
||||
fileList, err = NewIFileService().GetFileList(request.FileOption{FileOption: files.FileOption{Path: includeDir, Expand: true, Page: 1, PageSize: 100}})
|
||||
if len(fileList.Items) == 0 {
|
||||
return
|
||||
}
|
||||
var (
|
||||
content []byte
|
||||
config *components.Config
|
||||
)
|
||||
for _, configFile := range fileList.Items {
|
||||
proxyConfig := request.WebsiteProxyConfig{
|
||||
ID: website.ID,
|
||||
}
|
||||
parts := strings.Split(configFile.Name, ".")
|
||||
proxyConfig.Name = parts[0]
|
||||
if parts[1] == "conf" {
|
||||
proxyConfig.Enable = true
|
||||
} else {
|
||||
proxyConfig.Enable = false
|
||||
}
|
||||
proxyConfig.FilePath = configFile.Path
|
||||
content, err = fileOp.GetContent(configFile.Path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
proxyConfig.Content = string(content)
|
||||
config = parser.NewStringParser(string(content)).Parse()
|
||||
directives := config.GetDirectives()
|
||||
|
||||
location, ok := directives[0].(*components.Location)
|
||||
if !ok {
|
||||
err = errors.New("error")
|
||||
return
|
||||
}
|
||||
proxyConfig.ProxyPass = location.ProxyPass
|
||||
proxyConfig.Cache = location.Cache
|
||||
if location.CacheTime > 0 {
|
||||
proxyConfig.CacheTime = location.CacheTime
|
||||
proxyConfig.CacheUnit = location.CacheUint
|
||||
}
|
||||
proxyConfig.Match = location.Match
|
||||
proxyConfig.Modifier = location.Modifier
|
||||
proxyConfig.ProxyHost = location.Host
|
||||
proxyConfig.Replaces = location.Replaces
|
||||
res = append(res, proxyConfig)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (w WebsiteService) UpdateProxyFile(req request.NginxProxyUpdate) (err error) {
|
||||
var (
|
||||
website model.Website
|
||||
nginxFull dto.NginxFull
|
||||
oldRewriteContent []byte
|
||||
)
|
||||
website, err = websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nginxFull, err = getNginxFull(&website)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
includePath := fmt.Sprintf("/www/sites/%s/proxy/%s.conf", website.Alias, req.Name)
|
||||
absolutePath := path.Join(nginxFull.Install.GetPath(), includePath)
|
||||
fileOp := files.NewFileOp()
|
||||
oldRewriteContent, err = fileOp.GetContent(absolutePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = fileOp.WriteFile(absolutePath, strings.NewReader(req.Content), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
_ = fileOp.WriteFile(absolutePath, bytes.NewReader(oldRewriteContent), 0755)
|
||||
}
|
||||
}()
|
||||
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
|
||||
}
|
||||
|
||||
func (w WebsiteService) UpdateAuthBasic(req request.NginxAuthUpdate) (err error) {
|
||||
var (
|
||||
website model.Website
|
||||
nginxInstall model.AppInstall
|
||||
params []dto.NginxParam
|
||||
authContent []byte
|
||||
authArray []string
|
||||
)
|
||||
website, err = websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nginxInstall, err = getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
authPath := fmt.Sprintf("/www/sites/%s/auth_basic/auth.pass", website.Alias)
|
||||
absoluteAuthPath := path.Join(nginxInstall.GetPath(), authPath)
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(path.Dir(absoluteAuthPath)) {
|
||||
_ = fileOp.CreateDir(path.Dir(absoluteAuthPath), 0755)
|
||||
}
|
||||
if !fileOp.Stat(absoluteAuthPath) {
|
||||
_ = fileOp.CreateFile(absoluteAuthPath)
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
switch req.Operate {
|
||||
case "create":
|
||||
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
params = append(params, dto.NginxParam{Name: "auth_basic", Params: []string{`"Authentication"`}})
|
||||
params = append(params, dto.NginxParam{Name: "auth_basic_user_file", Params: []string{authPath}})
|
||||
authContent, err = fileOp.GetContent(absoluteAuthPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
authArray = strings.Split(string(authContent), "\n")
|
||||
switch req.Operate {
|
||||
case "disable":
|
||||
return deleteNginxConfig(constant.NginxScopeServer, params, &website)
|
||||
case "enable":
|
||||
return updateNginxConfig(constant.NginxScopeServer, params, &website)
|
||||
case "create":
|
||||
for _, line := range authArray {
|
||||
authParams := strings.Split(line, ":")
|
||||
username := authParams[0]
|
||||
if username == req.Username {
|
||||
err = buserr.New(constant.ErrUsernameIsExist)
|
||||
return
|
||||
}
|
||||
}
|
||||
var passwdHash []byte
|
||||
passwdHash, err = bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
line := fmt.Sprintf("%s:%s\n", req.Username, passwdHash)
|
||||
if req.Remark != "" {
|
||||
line = fmt.Sprintf("%s:%s:%s\n", req.Username, passwdHash, req.Remark)
|
||||
}
|
||||
authArray = append(authArray, line)
|
||||
case "edit":
|
||||
userExist := false
|
||||
for index, line := range authArray {
|
||||
authParams := strings.Split(line, ":")
|
||||
username := authParams[0]
|
||||
if username == req.Username {
|
||||
userExist = true
|
||||
var passwdHash []byte
|
||||
passwdHash, err = bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
userPasswd := fmt.Sprintf("%s:%s\n", req.Username, passwdHash)
|
||||
if req.Remark != "" {
|
||||
userPasswd = fmt.Sprintf("%s:%s:%s\n", req.Username, passwdHash, req.Remark)
|
||||
}
|
||||
authArray[index] = userPasswd
|
||||
}
|
||||
}
|
||||
if !userExist {
|
||||
err = buserr.New(constant.ErrUsernameIsNotExist)
|
||||
return
|
||||
}
|
||||
case "delete":
|
||||
deleteIndex := -1
|
||||
for index, line := range authArray {
|
||||
authParams := strings.Split(line, ":")
|
||||
username := authParams[0]
|
||||
if username == req.Username {
|
||||
deleteIndex = index
|
||||
}
|
||||
}
|
||||
if deleteIndex < 0 {
|
||||
return
|
||||
}
|
||||
authArray = append(authArray[:deleteIndex], authArray[deleteIndex+1:]...)
|
||||
}
|
||||
|
||||
var passFile *os.File
|
||||
passFile, err = os.Create(absoluteAuthPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer passFile.Close()
|
||||
writer := bufio.NewWriter(passFile)
|
||||
for _, line := range authArray {
|
||||
_, err = writer.WriteString(line + "\n")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
err = writer.Flush()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (w WebsiteService) GetAuthBasics(req request.NginxAuthReq) (res response.NginxAuthRes, err error) {
|
||||
var (
|
||||
website model.Website
|
||||
nginxInstall model.AppInstall
|
||||
authContent []byte
|
||||
nginxParams []response.NginxParam
|
||||
)
|
||||
website, err = websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
nginxInstall, err = getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
authPath := fmt.Sprintf("/www/sites/%s/auth_basic/auth.pass", website.Alias)
|
||||
absoluteAuthPath := path.Join(nginxInstall.GetPath(), authPath)
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(absoluteAuthPath) {
|
||||
return
|
||||
}
|
||||
nginxParams, err = getNginxParamsByKeys(constant.NginxScopeServer, []string{"auth_basic"}, &website)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
res.Enable = len(nginxParams[0].Params) > 0
|
||||
authContent, err = fileOp.GetContent(absoluteAuthPath)
|
||||
authArray := strings.Split(string(authContent), "\n")
|
||||
for _, line := range authArray {
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
params := strings.Split(line, ":")
|
||||
auth := dto.NginxAuth{
|
||||
Username: params[0],
|
||||
}
|
||||
if len(params) == 3 {
|
||||
auth.Remark = params[2]
|
||||
}
|
||||
res.Items = append(res.Items, auth)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -84,6 +86,40 @@ func createIndexFile(website *model.Website, runtime *model.Runtime) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func createProxyFile(website *model.Website, runtime *model.Runtime) error {
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
proxyFolder := path.Join(constant.AppInstallDir, constant.AppOpenresty, nginxInstall.Name, "www", "sites", website.Alias, "proxy")
|
||||
filePath := path.Join(proxyFolder, "root.conf")
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(proxyFolder) {
|
||||
if err := fileOp.CreateDir(proxyFolder, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !fileOp.Stat(filePath) {
|
||||
if err := fileOp.CreateFile(filePath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
config := parser.NewStringParser(string(nginx_conf.Proxy)).Parse()
|
||||
config.FilePath = filePath
|
||||
directives := config.Directives
|
||||
location, ok := directives[0].(*components.Location)
|
||||
if !ok {
|
||||
return errors.New("error")
|
||||
}
|
||||
location.ChangePath("^~", "/")
|
||||
location.UpdateDirective("proxy_pass", []string{website.Proxy})
|
||||
location.UpdateDirective("proxy_set_header", []string{"Host", "$host"})
|
||||
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createWebsiteFolder(nginxInstall model.AppInstall, website *model.Website, runtime *model.Runtime) error {
|
||||
nginxFolder := path.Join(constant.AppInstallDir, constant.AppOpenresty, nginxInstall.Name)
|
||||
siteFolder := path.Join(nginxFolder, "www", "sites", website.Alias)
|
||||
@@ -123,6 +159,11 @@ func createWebsiteFolder(nginxInstall model.AppInstall, website *model.Website,
|
||||
return err
|
||||
}
|
||||
}
|
||||
if website.Type == constant.Proxy {
|
||||
if err := createProxyFile(website, runtime); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return fileOp.CopyDir(path.Join(nginxFolder, "www", "common", "waf", "rules"), path.Join(siteFolder, "waf"))
|
||||
}
|
||||
@@ -167,9 +208,9 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
|
||||
server.UpdateRootProxy([]string{proxy})
|
||||
case constant.Static:
|
||||
server.UpdateRoot(rootIndex)
|
||||
//server.UpdateRootLocation()
|
||||
case constant.Proxy:
|
||||
server.UpdateRootProxy([]string{website.Proxy})
|
||||
nginxInclude := fmt.Sprintf("/www/sites/%s/proxy/*.conf", website.Alias)
|
||||
server.UpdateDirective("include", []string{nginxInclude})
|
||||
case constant.Runtime:
|
||||
if runtime.Resource == constant.ResourceLocal {
|
||||
switch runtime.Type {
|
||||
@@ -192,7 +233,6 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
|
||||
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := opNginx(nginxInstall.ContainerName, constant.NginxCheck); err != nil {
|
||||
_ = deleteWebsiteFolder(nginxInstall, website)
|
||||
return err
|
||||
@@ -261,7 +301,7 @@ func addListenAndServerName(website model.Website, ports []int, domains []string
|
||||
return nginxCheckAndReload(nginxConfig.OldContent, nginxConfig.FilePath, nginxFull.Install.ContainerName)
|
||||
}
|
||||
|
||||
func deleteListenAndServerName(website model.Website, ports []int, domains []string) error {
|
||||
func deleteListenAndServerName(website model.Website, binds []string, domains []string) error {
|
||||
nginxFull, err := getNginxFull(&website)
|
||||
if err != nil {
|
||||
return nil
|
||||
@@ -269,8 +309,8 @@ func deleteListenAndServerName(website model.Website, ports []int, domains []str
|
||||
nginxConfig := nginxFull.SiteConfig
|
||||
config := nginxFull.SiteConfig.Config
|
||||
server := config.FindServers()[0]
|
||||
for _, port := range ports {
|
||||
server.DeleteListen(strconv.Itoa(port))
|
||||
for _, bind := range binds {
|
||||
server.DeleteListen(bind)
|
||||
}
|
||||
for _, domain := range domains {
|
||||
server.DeleteServerName(domain)
|
||||
@@ -332,16 +372,20 @@ func applySSL(website model.Website, websiteSSL model.WebsiteSSL, req request.We
|
||||
config := nginxFull.SiteConfig.Config
|
||||
server := config.FindServers()[0]
|
||||
server.UpdateListen("443", false, "ssl")
|
||||
server.UpdateListen("[::]:443", false, "ssl")
|
||||
|
||||
switch req.HttpConfig {
|
||||
case constant.HTTPSOnly:
|
||||
server.RemoveListenByBind("80")
|
||||
server.RemoveListenByBind("[::]:80")
|
||||
server.RemoveDirective("if", []string{"($scheme"})
|
||||
case constant.HTTPToHTTPS:
|
||||
server.UpdateListen("80", website.DefaultServer)
|
||||
server.UpdateListen("[::]:80", false)
|
||||
server.AddHTTP2HTTPS()
|
||||
case constant.HTTPAlso:
|
||||
server.UpdateListen("80", website.DefaultServer)
|
||||
server.UpdateListen("[::]:80", false)
|
||||
server.RemoveDirective("if", []string{"($scheme"})
|
||||
}
|
||||
|
||||
@@ -453,16 +497,32 @@ func opWebsite(website *model.Website, operate string) error {
|
||||
}
|
||||
server := servers[0]
|
||||
if operate == constant.StopWeb {
|
||||
if website.Type == constant.Deployment || website.Type == constant.Static || website.Type == constant.Proxy {
|
||||
server.RemoveDirective("location", []string{"", "/"})
|
||||
}
|
||||
if website.Type == constant.Runtime {
|
||||
proxyInclude := fmt.Sprintf("/www/sites/%s/proxy/*.conf", website.Alias)
|
||||
server.RemoveDirective("include", []string{proxyInclude})
|
||||
rewriteInclude := fmt.Sprintf("/www/sites/%s/rewrite/%s.conf", website.Alias, website.Alias)
|
||||
server.RemoveDirective("include", []string{rewriteInclude})
|
||||
|
||||
switch website.Type {
|
||||
case constant.Deployment:
|
||||
server.RemoveDirective("location", []string{"/"})
|
||||
case constant.Runtime:
|
||||
server.RemoveDirective("location", []string{"~", "[^/]\\.php(/|$)"})
|
||||
}
|
||||
server.UpdateRoot("/usr/share/nginx/html/stop")
|
||||
website.Status = constant.WebStopped
|
||||
}
|
||||
if operate == constant.StartWeb {
|
||||
proxyInclude := fmt.Sprintf("/www/sites/%s/proxy/*.conf", website.Alias)
|
||||
absoluteIncludeDir := path.Join(nginxInstall.Install.GetPath(), fmt.Sprintf("/www/sites/%s/proxy", website.Alias))
|
||||
if files.NewFileOp().Stat(absoluteIncludeDir) {
|
||||
server.UpdateDirective("include", []string{proxyInclude})
|
||||
}
|
||||
server.UpdateDirective("include", []string{proxyInclude})
|
||||
rewriteInclude := fmt.Sprintf("/www/sites/%s/rewrite/%s.conf", website.Alias, website.Alias)
|
||||
absoluteRewritePath := path.Join(nginxInstall.Install.GetPath(), rewriteInclude)
|
||||
if files.NewFileOp().Stat(absoluteRewritePath) {
|
||||
server.UpdateDirective("include", []string{rewriteInclude})
|
||||
}
|
||||
switch website.Type {
|
||||
case constant.Deployment:
|
||||
server.RemoveDirective("root", nil)
|
||||
@@ -477,7 +537,6 @@ func opWebsite(website *model.Website, operate string) error {
|
||||
server.UpdateRootLocation()
|
||||
case constant.Proxy:
|
||||
server.RemoveDirective("root", nil)
|
||||
server.UpdateRootProxy([]string{website.Proxy})
|
||||
case constant.Runtime:
|
||||
rootIndex := path.Join("/www/sites", website.Alias, "index")
|
||||
server.UpdateRoot(rootIndex)
|
||||
|
||||
@@ -2,6 +2,7 @@ package configs
|
||||
|
||||
type System struct {
|
||||
Port string `mapstructure:"port"`
|
||||
SSL string `mapstructure:"ssl"`
|
||||
DbFile string `mapstructure:"db_file"`
|
||||
DbPath string `mapstructure:"db_path"`
|
||||
LogPath string `mapstructure:"log_path"`
|
||||
@@ -14,5 +15,8 @@ type System struct {
|
||||
Mode string `mapstructure:"mode"`
|
||||
RepoUrl string `mapstructure:"repo_url"`
|
||||
Version string `mapstructure:"version"`
|
||||
Username string `mapstructure:"username"`
|
||||
Password string `mapstructure:"password"`
|
||||
Entrance string `mapstructure:"entrance"`
|
||||
IsDemo bool `mapstructure:"is_demo"`
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ var (
|
||||
ErrTypeNotSafety = "ErrNotSafety"
|
||||
ErrNameIsExist = "ErrNameIsExist"
|
||||
ErrDemoEnvironment = "ErrDemoEnvironment"
|
||||
ErrInitUser = "ErrInitUser"
|
||||
)
|
||||
|
||||
// app
|
||||
@@ -65,10 +66,12 @@ var (
|
||||
|
||||
// website
|
||||
var (
|
||||
ErrDomainIsExist = "ErrDomainIsExist"
|
||||
ErrAliasIsExist = "ErrAliasIsExist"
|
||||
ErrAppDelete = "ErrAppDelete"
|
||||
ErrGroupIsUsed = "ErrGroupIsUsed"
|
||||
ErrDomainIsExist = "ErrDomainIsExist"
|
||||
ErrAliasIsExist = "ErrAliasIsExist"
|
||||
ErrAppDelete = "ErrAppDelete"
|
||||
ErrGroupIsUsed = "ErrGroupIsUsed"
|
||||
ErrUsernameIsExist = "ErrUsernameIsExist"
|
||||
ErrUsernameIsNotExist = "ErrUsernameIsNotExist"
|
||||
)
|
||||
|
||||
// ssl
|
||||
@@ -104,6 +107,7 @@ var (
|
||||
var (
|
||||
ErrInUsed = "ErrInUsed"
|
||||
ErrObjectInUsed = "ErrObjectInUsed"
|
||||
ErrPortRules = "ErrPortRules"
|
||||
)
|
||||
|
||||
// runtime
|
||||
|
||||
@@ -5,8 +5,9 @@ const (
|
||||
NginxScopeHttp = "http"
|
||||
NginxScopeOut = "out"
|
||||
|
||||
NginxReload = "reload"
|
||||
NginxCheck = "check"
|
||||
NginxReload = "reload"
|
||||
NginxCheck = "check"
|
||||
NginxRestart = "restart"
|
||||
|
||||
ConfigNew = "add"
|
||||
ConfigUpdate = "update"
|
||||
|
||||
@@ -9,22 +9,20 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/cron/job"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
|
||||
func Run() {
|
||||
nyc, _ := time.LoadLocation("Asia/Shanghai")
|
||||
nyc, _ := time.LoadLocation(common.LoadTimeZone())
|
||||
Cron := cron.New(cron.WithLocation(nyc), cron.WithChain(cron.Recover(cron.DefaultLogger)), cron.WithChain(cron.DelayIfStillRunning(cron.DefaultLogger)))
|
||||
_, err := Cron.AddJob("@every 5m", job.NewMonitorJob())
|
||||
if err != nil {
|
||||
if _, err := Cron.AddJob("@every 5m", job.NewMonitorJob()); err != nil {
|
||||
global.LOG.Errorf("can not add monitor corn job: %s", err.Error())
|
||||
}
|
||||
_, err = Cron.AddJob("@daily", job.NewWebsiteJob())
|
||||
if err != nil {
|
||||
if _, err := Cron.AddJob("@daily", job.NewWebsiteJob()); err != nil {
|
||||
global.LOG.Errorf("can not add website corn job: %s", err.Error())
|
||||
}
|
||||
_, err = Cron.AddJob("@daily", job.NewSSLJob())
|
||||
if err != nil {
|
||||
if _, err := Cron.AddJob("@daily", job.NewSSLJob()); err != nil {
|
||||
global.LOG.Errorf("can not add ssl corn job: %s", err.Error())
|
||||
}
|
||||
Cron.Start()
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/service"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -18,13 +19,14 @@ func (ssl *ssl) Run() {
|
||||
sslRepo := repo.NewISSLRepo()
|
||||
sslService := service.NewIWebsiteSSLService()
|
||||
sslList, _ := sslRepo.List()
|
||||
nyc, _ := time.LoadLocation(common.LoadTimeZone())
|
||||
global.LOG.Info("The scheduled certificate update task is currently in progress ...")
|
||||
now := time.Now().Add(10 * time.Second)
|
||||
for _, s := range sslList {
|
||||
if !s.AutoRenew || s.Provider == "manual" || s.Provider == "dnsManual" {
|
||||
continue
|
||||
}
|
||||
expireDate := s.ExpireDate.In(time.Now().Location())
|
||||
expireDate := s.ExpireDate.In(nyc)
|
||||
sub := expireDate.Sub(now)
|
||||
if sub.Hours() < 720 {
|
||||
global.LOG.Errorf("Update the SSL certificate for the [%s] domain", s.PrimaryDomain)
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/service"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -18,9 +19,10 @@ func NewWebsiteJob() *website {
|
||||
}
|
||||
|
||||
func (w *website) Run() {
|
||||
nyc, _ := time.LoadLocation(common.LoadTimeZone())
|
||||
websites, _ := repo.NewIWebsiteRepo().List()
|
||||
global.LOG.Info("website cron job start...")
|
||||
now := time.Now().Add(10 * time.Second)
|
||||
global.LOG.Info("Website scheduled task in progress ...")
|
||||
now := time.Now().Add(10 * time.Minute)
|
||||
if len(websites) > 0 {
|
||||
neverExpireDate, _ := time.Parse(constant.DateLayout, constant.DefaultDate)
|
||||
var wg sync.WaitGroup
|
||||
@@ -28,27 +30,33 @@ func (w *website) Run() {
|
||||
if site.Status != constant.WebRunning || neverExpireDate.Equal(site.ExpireDate) {
|
||||
continue
|
||||
}
|
||||
expireDate, _ := time.ParseInLocation(constant.DateTimeLayout, site.ExpireDate.String(), time.Now().Location())
|
||||
expireDate, err := time.ParseInLocation(constant.DateLayout, site.ExpireDate.Format(constant.DateLayout), nyc)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("time parse err %v", err)
|
||||
continue
|
||||
}
|
||||
if expireDate.Before(now) {
|
||||
wg.Add(1)
|
||||
go func(ws model.Website) {
|
||||
stopWebsite(ws.ID, &wg)
|
||||
stopWebsite(ws.ID, ws.PrimaryDomain, &wg)
|
||||
}(site)
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
global.LOG.Info("website cron job end...")
|
||||
global.LOG.Info("Website scheduled task has completed")
|
||||
}
|
||||
|
||||
func stopWebsite(websiteId uint, wg *sync.WaitGroup) {
|
||||
func stopWebsite(websiteId uint, websiteName string, wg *sync.WaitGroup) {
|
||||
websiteService := service.NewIWebsiteService()
|
||||
req := request.WebsiteOp{
|
||||
ID: websiteId,
|
||||
Operate: constant.StopWeb,
|
||||
}
|
||||
if err := websiteService.OpWebsite(req); err != nil {
|
||||
global.LOG.Errorf("stop website err: %s", err.Error())
|
||||
global.LOG.Errorf("Website [%s] seop failed err %v", websiteName, err)
|
||||
} else {
|
||||
global.LOG.Infof("Website [%s] stopped successfully", websiteName)
|
||||
}
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ ErrRepoNotValid: "Remote repository verification failed!"
|
||||
ErrNameIsExist: "Name is already exist"
|
||||
ErrDemoEnvironment: "Demo server, prohibit this operation!"
|
||||
ErrCmdTimeout: "Command execution timed out!"
|
||||
ErrInitUser: "The user initialization has been completed by the system. Please do not repeat the operation!"
|
||||
|
||||
#app
|
||||
ErrPortInUsed: "{{ .detail }} port already in use"
|
||||
@@ -43,6 +44,8 @@ ErrDomainIsExist: "Domain is already exist"
|
||||
ErrAliasIsExist: "Alias is already exist"
|
||||
ErrAppDelete: 'Other Website use this App'
|
||||
ErrGroupIsUsed: 'The group is in use and cannot be deleted'
|
||||
ErrUsernameIsExist: 'Username is already exist'
|
||||
ErrUsernameIsNotExist: 'Username is not exist'
|
||||
|
||||
#ssl
|
||||
ErrSSLCannotDelete: "The certificate is being used by the website and cannot be removed"
|
||||
@@ -61,6 +64,7 @@ ErrTypeOfRedis: "The recovery file type does not match the current persistence m
|
||||
#container
|
||||
ErrInUsed: "{{ .detail }} is in use and cannot be deleted"
|
||||
ErrObjectInUsed: "This object is in use and cannot be deleted"
|
||||
ErrPortRules: "Invalid port specification"
|
||||
|
||||
#runtime
|
||||
ErrDirNotFound: "The build folder does not exist! Please check file integrity!"
|
||||
|
||||
@@ -17,6 +17,7 @@ ErrRepoNotValid: "远程仓库校验失败!"
|
||||
ErrNameIsExist: "名称已存在"
|
||||
ErrDemoEnvironment: "演示服务器,禁止此操作!"
|
||||
ErrCmdTimeout: "命令执行超时!"
|
||||
ErrInitUser: "系统已完成用户初始化,请勿重复操作!"
|
||||
|
||||
#app
|
||||
ErrPortInUsed: "{{ .detail }} 端口已被占用!"
|
||||
@@ -43,6 +44,8 @@ ErrDomainIsExist: "域名已存在"
|
||||
ErrAliasIsExist: "代号已存在"
|
||||
ErrAppDelete: '其他网站使用此应用,无法删除'
|
||||
ErrGroupIsUsed: '分组正在使用中,无法删除'
|
||||
ErrUsernameIsExist: '用户名已存在'
|
||||
ErrUsernameIsNotExist: '用户不存在'
|
||||
|
||||
#ssl
|
||||
ErrSSLCannotDelete: "证书正在被网站使用,无法删除"
|
||||
@@ -61,6 +64,7 @@ ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后
|
||||
#container
|
||||
ErrInUsed: "{{ .detail }} 正被使用,无法删除"
|
||||
ErrObjectInUsed: "该对象正被使用,无法删除"
|
||||
ErrPortRules: "无效的端口规格"
|
||||
|
||||
#runtime
|
||||
ErrDirNotFound: "build 文件夹不存在!请检查文件完整性!"
|
||||
|
||||
@@ -17,6 +17,11 @@ func Init() {
|
||||
global.LOG.Errorf("load service encrypt key from setting failed, err: %v", err)
|
||||
}
|
||||
global.CONF.System.EncryptKey = enptrySetting.Value
|
||||
sslSetting, err := settingRepo.Get(settingRepo.WithByKey("SSL"))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load service ssl from setting failed, err: %v", err)
|
||||
}
|
||||
global.CONF.System.SSL = sslSetting.Value
|
||||
|
||||
if _, err := settingRepo.Get(settingRepo.WithByKey("SystemStatus")); err != nil {
|
||||
_ = settingRepo.Create("SystemStatus", "Free")
|
||||
|
||||
@@ -25,6 +25,7 @@ func Init() {
|
||||
migrations.UpdateTableApp,
|
||||
migrations.UpdateTableHost,
|
||||
migrations.UpdateTableWebsite,
|
||||
migrations.AddEntranceAndSSL,
|
||||
})
|
||||
if err := m.Migrate(); err != nil {
|
||||
global.LOG.Error(err)
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
|
||||
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/gorm"
|
||||
@@ -61,10 +62,13 @@ var AddTableSetting = &gormigrate.Migration{
|
||||
if err := tx.AutoMigrate(&model.Setting{}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "UserName", Value: ""}).Error; err != nil {
|
||||
encryptKey := common.RandStr(16)
|
||||
if err := tx.Create(&model.Setting{Key: "UserName", Value: global.CONF.System.Username}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "Password", Value: ""}).Error; err != nil {
|
||||
global.CONF.System.EncryptKey = encryptKey
|
||||
pass, _ := encrypt.StringEncrypt(global.CONF.System.Password)
|
||||
if err := tx.Create(&model.Setting{Key: "Password", Value: pass}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "Email", Value: ""}).Error; err != nil {
|
||||
@@ -91,13 +95,13 @@ var AddTableSetting = &gormigrate.Migration{
|
||||
if err := tx.Create(&model.Setting{Key: "ServerPort", Value: global.CONF.System.Port}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "SecurityEntrance", Value: "onepanel"}).Error; err != nil {
|
||||
if err := tx.Create(&model.Setting{Key: "SecurityEntrance", Value: global.CONF.System.Entrance}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "JWTSigningKey", Value: common.RandStr(16)}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "EncryptKey", Value: common.RandStr(16)}).Error; err != nil {
|
||||
if err := tx.Create(&model.Setting{Key: "EncryptKey", Value: encryptKey}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -287,3 +291,24 @@ var UpdateTableWebsite = &gormigrate.Migration{
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var AddEntranceAndSSL = &gormigrate.Migration{
|
||||
ID: "20230414-add-entrance-and-ssl",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.Model(&model.Setting{}).
|
||||
Where("key = ? AND value = ?", "SecurityEntrance", "onepanel").
|
||||
Updates(map[string]interface{}{"value": ""}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "SSLType", Value: "self"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "SSLID", Value: "0"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "SSL", Value: "disable"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return tx.AutoMigrate(&model.Website{})
|
||||
},
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/gin-contrib/gzip"
|
||||
|
||||
v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/i18n"
|
||||
"github.com/1Panel-dev/1Panel/backend/middleware"
|
||||
@@ -40,43 +39,31 @@ func setWebStatic(rootRouter *gin.Engine) {
|
||||
|
||||
func Routers() *gin.Engine {
|
||||
Router := gin.Default()
|
||||
|
||||
Router.Use(middleware.OperationLog())
|
||||
// Router.Use(middleware.CSRF())
|
||||
// Router.Use(middleware.LoadCsrfToken())
|
||||
|
||||
if global.CONF.System.IsDemo {
|
||||
Router.Use(middleware.DemoHandle())
|
||||
}
|
||||
|
||||
Router.Use(gzip.Gzip(gzip.DefaultCompression))
|
||||
setWebStatic(Router)
|
||||
|
||||
Router.Use(i18n.GinI18nLocalize())
|
||||
Router.GET("/api/v1/info", v1.ApiGroupApp.BaseApi.GetSafetyStatus)
|
||||
Router.GET("/api/v1/:code", v1.ApiGroupApp.BaseApi.SafeEntrance)
|
||||
|
||||
Router.SetFuncMap(template.FuncMap{
|
||||
"Localize": ginI18n.GetMessage,
|
||||
})
|
||||
Router.Use(middleware.JwtAuth())
|
||||
|
||||
systemRouter := rou.RouterGroupApp
|
||||
|
||||
swaggerRouter := Router.Group("1panel")
|
||||
docs.SwaggerInfo.BasePath = "/api/v1"
|
||||
swaggerRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth()).GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))
|
||||
|
||||
PublicGroup := Router.Group("")
|
||||
{
|
||||
PublicGroup.GET("/health", func(c *gin.Context) {
|
||||
c.JSON(200, "ok")
|
||||
})
|
||||
}
|
||||
|
||||
PrivateGroup := Router.Group("/api/v1")
|
||||
PrivateGroup.Use(middleware.GlobalLoading())
|
||||
//PrivateGroup.Use(middleware.SafetyAuth())
|
||||
{
|
||||
systemRouter.InitBaseRouter(PrivateGroup)
|
||||
systemRouter.InitDashboardRouter(PrivateGroup)
|
||||
|
||||
+31
-13
@@ -21,6 +21,7 @@ func Init() {
|
||||
port := "9999"
|
||||
mode := ""
|
||||
version := "v1.0.0"
|
||||
username, password, entrance := "", "", ""
|
||||
fileOp := files.NewFileOp()
|
||||
v := viper.NewWithOptions()
|
||||
v.SetConfigType("yaml")
|
||||
@@ -40,8 +41,11 @@ func Init() {
|
||||
}
|
||||
} else {
|
||||
baseDir = loadParams("BASE_DIR")
|
||||
port = loadParams("PANEL_PORT")
|
||||
version = loadParams("ORIGINAL_INSTALLED_VERSION")
|
||||
port = loadParams("ORIGINAL_PORT")
|
||||
version = loadParams("ORIGINAL_VERSION")
|
||||
username = loadParams("ORIGINAL_USERNAME")
|
||||
password = loadParams("ORIGINAL_PASSWORD")
|
||||
entrance = loadParams("ORIGINAL_ENTRANCE")
|
||||
|
||||
if strings.HasSuffix(baseDir, "/") {
|
||||
baseDir = baseDir[:strings.LastIndex(baseDir, "/")]
|
||||
@@ -60,14 +64,25 @@ func Init() {
|
||||
if err := v.Unmarshal(&serverConfig); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if mode == "dev" && fileOp.Stat("/opt/1panel/conf/app.yaml") && serverConfig.System.BaseDir != "" {
|
||||
baseDir = serverConfig.System.BaseDir
|
||||
}
|
||||
if mode == "dev" && fileOp.Stat("/opt/1panel/conf/app.yaml") && serverConfig.System.Port != "" {
|
||||
port = serverConfig.System.Port
|
||||
}
|
||||
if mode == "dev" && fileOp.Stat("/opt/1panel/conf/app.yaml") && serverConfig.System.Version != "" {
|
||||
version = serverConfig.System.Version
|
||||
if mode == "dev" && fileOp.Stat("/opt/1panel/conf/app.yaml") {
|
||||
if serverConfig.System.BaseDir != "" {
|
||||
baseDir = serverConfig.System.BaseDir
|
||||
}
|
||||
if serverConfig.System.Port != "" {
|
||||
port = serverConfig.System.Port
|
||||
}
|
||||
if serverConfig.System.Version != "" {
|
||||
version = serverConfig.System.Version
|
||||
}
|
||||
if serverConfig.System.Username != "" {
|
||||
version = serverConfig.System.Username
|
||||
}
|
||||
if serverConfig.System.Password != "" {
|
||||
version = serverConfig.System.Password
|
||||
}
|
||||
if serverConfig.System.Entrance != "" {
|
||||
version = serverConfig.System.Entrance
|
||||
}
|
||||
}
|
||||
|
||||
global.CONF = serverConfig
|
||||
@@ -81,6 +96,9 @@ func Init() {
|
||||
global.CONF.System.TmpDir = global.CONF.System.DataDir + "/tmp"
|
||||
global.CONF.System.Port = port
|
||||
global.CONF.System.Version = version
|
||||
global.CONF.System.Username = username
|
||||
global.CONF.System.Password = password
|
||||
global.CONF.System.Entrance = entrance
|
||||
global.Viper = v
|
||||
}
|
||||
|
||||
@@ -89,9 +107,9 @@ func loadParams(param string) string {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
baseDir := strings.ReplaceAll(stdout, "\n", "")
|
||||
if len(baseDir) == 0 {
|
||||
info := strings.ReplaceAll(stdout, "\n", "")
|
||||
if len(info) == 0 || info == `""` {
|
||||
panic(fmt.Sprintf("error `%s` find in /usr/bin/1pctl", param))
|
||||
}
|
||||
return baseDir
|
||||
return info
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -29,7 +30,8 @@ func PasswordExpired() gin.HandlerFunc {
|
||||
helper.ErrorWithDetail(c, constant.CodePasswordExpired, constant.ErrTypePasswordExpired, err)
|
||||
return
|
||||
}
|
||||
expiredTime, err := time.Parse("2006-01-02 15:04:05", extime.Value)
|
||||
loc, _ := time.LoadLocation(common.LoadTimeZone())
|
||||
expiredTime, err := time.ParseInLocation("2006-01-02 15:04:05", extime.Value, loc)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodePasswordExpired, constant.ErrTypePasswordExpired, err)
|
||||
return
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/service"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func SafetyAuth() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if err := service.NewIAuthService().SafetyStatus(c); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, nil)
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,7 @@ func (s *BaseRouter) InitBaseRouter(Router *gin.RouterGroup) {
|
||||
baseRouter.GET("/captcha", baseApi.Captcha)
|
||||
baseRouter.POST("/mfalogin", baseApi.MFALogin)
|
||||
baseRouter.POST("/login", baseApi.Login)
|
||||
baseRouter.GET("/status", baseApi.CheckIsFirstLogin)
|
||||
baseRouter.POST("/init", baseApi.InitUserInfo)
|
||||
baseRouter.GET("/issafety", baseApi.CheckIsSafety)
|
||||
baseRouter.POST("/logout", baseApi.LogOut)
|
||||
baseRouter.GET("/demo", baseApi.CheckIsDemo)
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ func (f *FileRouter) InitFileRouter(Router *gin.RouterGroup) {
|
||||
fileRouter.POST("/del", baseApi.DeleteFile)
|
||||
fileRouter.POST("/batch/del", baseApi.BatchDeleteFile)
|
||||
fileRouter.POST("/mode", baseApi.ChangeFileMode)
|
||||
fileRouter.POST("/owner", baseApi.ChangeFileOwner)
|
||||
fileRouter.POST("/compress", baseApi.CompressFile)
|
||||
fileRouter.POST("/decompress", baseApi.DeCompressFile)
|
||||
fileRouter.POST("/content", baseApi.GetContent)
|
||||
|
||||
@@ -23,6 +23,8 @@ func (s *SettingRouter) InitSettingRouter(Router *gin.RouterGroup) {
|
||||
settingRouter.GET("/search/available", baseApi.GetSystemAvailable)
|
||||
settingRouter.POST("/update", baseApi.UpdateSetting)
|
||||
settingRouter.POST("/port/update", baseApi.UpdatePort)
|
||||
settingRouter.POST("/ssl/update", baseApi.UpdateSSL)
|
||||
settingRouter.GET("/ssl/info", baseApi.LoadFromCert)
|
||||
settingRouter.POST("/password/update", baseApi.UpdatePassword)
|
||||
settingRouter.POST("/time/sync", baseApi.SyncTime)
|
||||
settingRouter.POST("/monitor/clean", baseApi.CleanMonitor)
|
||||
|
||||
@@ -51,5 +51,12 @@ func (a *WebsiteRouter) InitWebsiteRouter(Router *gin.RouterGroup) {
|
||||
|
||||
groupRouter.POST("/dir/update", baseApi.UpdateSiteDir)
|
||||
groupRouter.POST("/dir/permission", baseApi.UpdateSiteDirPermission)
|
||||
|
||||
groupRouter.POST("/proxies", baseApi.GetProxyConfig)
|
||||
groupRouter.POST("/proxies/update", baseApi.UpdateProxyConfig)
|
||||
groupRouter.POST("/proxies/file", baseApi.UpdateProxyConfigFile)
|
||||
|
||||
groupRouter.POST("/auths", baseApi.GetAuthConfig)
|
||||
groupRouter.POST("/auths/update", baseApi.UpdateAuthConfig)
|
||||
}
|
||||
}
|
||||
|
||||
+38
-15
@@ -1,8 +1,11 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/init/app"
|
||||
@@ -43,22 +46,42 @@ func Start() {
|
||||
|
||||
rootRouter := router.Routers()
|
||||
address := fmt.Sprintf(":%s", global.CONF.System.Port)
|
||||
s := initServer(address, rootRouter)
|
||||
global.LOG.Infof("server run success on %s", global.CONF.System.Port)
|
||||
if err := s.ListenAndServe(); err != nil {
|
||||
global.LOG.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
type server interface {
|
||||
ListenAndServe() error
|
||||
}
|
||||
|
||||
func initServer(address string, router *gin.Engine) server {
|
||||
s := endless.NewServer(address, router)
|
||||
s := endless.NewServer(address, rootRouter)
|
||||
s.ReadHeaderTimeout = 20 * time.Second
|
||||
s.WriteTimeout = 60 * time.Second
|
||||
s.MaxHeaderBytes = 1 << 20
|
||||
return s
|
||||
|
||||
if global.CONF.System.SSL == "disable" {
|
||||
global.LOG.Infof("server run success on %s with http", global.CONF.System.Port)
|
||||
if err := s.ListenAndServe(); err != nil {
|
||||
global.LOG.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
certificate, err := os.ReadFile(global.CONF.System.BaseDir + "/1panel/secret/server.crt")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
key, err := os.ReadFile(global.CONF.System.BaseDir + "/1panel/secret/server.key")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
cert, err := tls.X509KeyPair(certificate, key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
s := &http.Server{
|
||||
Addr: address,
|
||||
Handler: rootRouter,
|
||||
TLSConfig: &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
},
|
||||
}
|
||||
|
||||
global.LOG.Infof("server run success on %s with https", global.CONF.System.Port)
|
||||
if err := s.ListenAndServeTLS("", ""); err != nil {
|
||||
global.LOG.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,32 @@ func ExecWithTimeOut(cmdStr string, timeout time.Duration) (string, error) {
|
||||
return stdout.String(), nil
|
||||
}
|
||||
|
||||
func ExecCronjobWithTimeOut(cmdStr string, timeout time.Duration) (string, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
cmd := exec.Command("bash", "-c", cmdStr)
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
err := cmd.Run()
|
||||
if ctx.Err() == context.DeadlineExceeded {
|
||||
return "", buserr.New(constant.ErrCmdTimeout)
|
||||
}
|
||||
|
||||
errMsg := ""
|
||||
if len(stderr.String()) != 0 {
|
||||
errMsg = fmt.Sprintf("stderr:\n %s", stderr.String())
|
||||
}
|
||||
if len(stdout.String()) != 0 {
|
||||
if len(errMsg) != 0 {
|
||||
errMsg = fmt.Sprintf("%s \n\n; stdout:\n %s", errMsg, stdout.String())
|
||||
} else {
|
||||
errMsg = fmt.Sprintf("stdout\n: %s", stdout.String())
|
||||
}
|
||||
}
|
||||
return errMsg, err
|
||||
}
|
||||
|
||||
func Execf(cmdStr string, a ...interface{}) (string, error) {
|
||||
cmd := exec.Command("bash", "-c", fmt.Sprintf(cmdStr, a...))
|
||||
var stdout, stderr bytes.Buffer
|
||||
@@ -91,11 +117,6 @@ func Execf(cmdStr string, a ...interface{}) (string, error) {
|
||||
}
|
||||
|
||||
func HasNoPasswordSudo() bool {
|
||||
cmd := exec.Command("sudo", "-v")
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
cmd2 := exec.Command("sudo", "-n", "ls")
|
||||
err2 := cmd2.Run()
|
||||
return err2 == nil
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func CompareVersion(version1 string, version2 string) bool {
|
||||
@@ -134,3 +135,11 @@ func LoadSizeUnit(value float64) string {
|
||||
}
|
||||
return fmt.Sprintf("%v", value)
|
||||
}
|
||||
|
||||
func LoadTimeZone() string {
|
||||
loc := time.Now().Location()
|
||||
if _, err := time.LoadLocation(loc.String()); err != nil {
|
||||
return "Asia/Shanghai"
|
||||
}
|
||||
return loc.String()
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
@@ -106,7 +107,7 @@ func (f FileOp) SaveFile(dst string, content string, mode fs.FileMode) error {
|
||||
}
|
||||
defer file.Close()
|
||||
write := bufio.NewWriter(file)
|
||||
_, _ = write.WriteString(string(content))
|
||||
_, _ = write.WriteString(content)
|
||||
write.Flush()
|
||||
return nil
|
||||
}
|
||||
@@ -119,6 +120,37 @@ func (f FileOp) Chown(dst string, uid int, gid int) error {
|
||||
return f.Fs.Chown(dst, uid, gid)
|
||||
}
|
||||
|
||||
func (f FileOp) ChownR(dst string, uid string, gid string, sub bool) error {
|
||||
cmdStr := fmt.Sprintf("chown %s:%s %s", uid, gid, dst)
|
||||
if sub {
|
||||
cmdStr = fmt.Sprintf("chown -R %s:%s %s", uid, gid, dst)
|
||||
}
|
||||
if cmd.HasNoPasswordSudo() {
|
||||
cmdStr = fmt.Sprintf("sudo %s", cmdStr)
|
||||
}
|
||||
if msg, err := cmd.ExecWithTimeOut(cmdStr, 2*time.Second); err != nil {
|
||||
if msg != "" {
|
||||
return errors.New(msg)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f FileOp) ChmodR(dst string, mode int64) error {
|
||||
cmdStr := fmt.Sprintf("chmod -R %v %s", fmt.Sprintf("%04o", mode), dst)
|
||||
if cmd.HasNoPasswordSudo() {
|
||||
cmdStr = fmt.Sprintf("sudo %s", cmdStr)
|
||||
}
|
||||
if msg, err := cmd.ExecWithTimeOut(cmdStr, 2*time.Second); err != nil {
|
||||
if msg != "" {
|
||||
return errors.New(msg)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f FileOp) Rename(oldName string, newName string) error {
|
||||
return f.Fs.Rename(oldName, newName)
|
||||
}
|
||||
@@ -322,17 +354,21 @@ func (f FileOp) CopyFile(src, dst string) error {
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
srcInfo, err := f.Fs.Stat(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dstPath := path.Join(dst, srcInfo.Name())
|
||||
if src == dstPath {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = f.Fs.MkdirAll(filepath.Dir(dst), 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
srcInfo, err := f.Fs.Stat(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dstFile, err := f.Fs.OpenFile(path.Join(dst, srcInfo.Name()), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775)
|
||||
dstFile, err := f.Fs.OpenFile(dstPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -446,6 +482,9 @@ func (f FileOp) Decompress(srcFile string, dst string, cType CompressType) error
|
||||
|
||||
handler := func(ctx context.Context, archFile archiver.File) error {
|
||||
info := archFile.FileInfo
|
||||
if strings.HasPrefix(archFile.NameInArchive, "__MACOSX") {
|
||||
return nil
|
||||
}
|
||||
filePath := filepath.Join(dst, archFile.NameInArchive)
|
||||
if archFile.FileInfo.IsDir() {
|
||||
if err := f.Fs.MkdirAll(filePath, info.Mode()); err != nil {
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
package files
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
@@ -73,6 +76,8 @@ func NewFileInfo(op FileOption) (*FileInfo, error) {
|
||||
Extension: filepath.Ext(info.Name()),
|
||||
IsHidden: IsHidden(op.Path),
|
||||
Mode: fmt.Sprintf("%04o", info.Mode().Perm()),
|
||||
User: GetUsername(info.Sys().(*syscall.Stat_t).Uid),
|
||||
Group: GetGroup(info.Sys().(*syscall.Stat_t).Gid),
|
||||
MimeType: GetMimeType(op.Path),
|
||||
}
|
||||
if file.IsSymlink {
|
||||
@@ -93,35 +98,39 @@ func NewFileInfo(op FileOption) (*FileInfo, error) {
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func (f *FileInfo) search(dir, showHidden bool, af afero.Afero, search string, count int) ([]FileSearchInfo, error) {
|
||||
var files []FileSearchInfo
|
||||
if err := afero.Walk(af, f.Path, func(path string, info fs.FileInfo, err error) error {
|
||||
if info != nil {
|
||||
|
||||
if dir && !info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if !showHidden && IsHidden(info.Name()) {
|
||||
return nil
|
||||
}
|
||||
|
||||
lowerName := strings.ToLower(info.Name())
|
||||
lowerSearch := strings.ToLower(search)
|
||||
if strings.Contains(lowerName, lowerSearch) {
|
||||
files = append(files, FileSearchInfo{
|
||||
Path: path,
|
||||
FileInfo: info,
|
||||
})
|
||||
if len(files) > count {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
func (f *FileInfo) search(search string, count int) (files []FileSearchInfo, total int, err error) {
|
||||
cmd := exec.Command("find", f.Path, "-name", search)
|
||||
output, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return files, nil
|
||||
if err = cmd.Start(); err != nil {
|
||||
return
|
||||
}
|
||||
defer cmd.Wait()
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
scanner := bufio.NewScanner(output)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
fmt.Println(line)
|
||||
info, err := os.Stat(line)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
total++
|
||||
if total > count {
|
||||
continue
|
||||
}
|
||||
files = append(files, FileSearchInfo{
|
||||
Path: line,
|
||||
FileInfo: info,
|
||||
})
|
||||
}
|
||||
if err = scanner.Err(); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f *FileInfo) listChildren(dir, showHidden, containSub bool, search string, page, pageSize int) error {
|
||||
@@ -129,10 +138,11 @@ func (f *FileInfo) listChildren(dir, showHidden, containSub bool, search string,
|
||||
var (
|
||||
files []FileSearchInfo
|
||||
err error
|
||||
total int
|
||||
)
|
||||
|
||||
if search != "" && containSub {
|
||||
files, err = f.search(dir, showHidden, *afs, search, page*pageSize)
|
||||
files, total, err = f.search(search, page*pageSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -156,10 +166,8 @@ func (f *FileInfo) listChildren(dir, showHidden, containSub bool, search string,
|
||||
if dir && !df.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
name := df.Name()
|
||||
fPath := path.Join(df.Path, df.Name())
|
||||
|
||||
if search != "" {
|
||||
if containSub {
|
||||
fPath = df.Path
|
||||
@@ -172,12 +180,10 @@ func (f *FileInfo) listChildren(dir, showHidden, containSub bool, search string,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !showHidden && IsHidden(name) {
|
||||
continue
|
||||
}
|
||||
f.ItemTotal++
|
||||
|
||||
isSymlink, isInvalidLink := false, false
|
||||
if IsSymlink(df.Mode()) {
|
||||
isSymlink = true
|
||||
@@ -201,19 +207,22 @@ func (f *FileInfo) listChildren(dir, showHidden, containSub bool, search string,
|
||||
Extension: filepath.Ext(name),
|
||||
Path: fPath,
|
||||
Mode: fmt.Sprintf("%04o", df.Mode().Perm()),
|
||||
MimeType: GetMimeType(fPath),
|
||||
User: GetUsername(df.Sys().(*syscall.Stat_t).Uid),
|
||||
Group: GetGroup(df.Sys().(*syscall.Stat_t).Gid),
|
||||
}
|
||||
|
||||
if isSymlink {
|
||||
file.LinkPath = GetSymlink(fPath)
|
||||
}
|
||||
|
||||
if df.Size() > 0 {
|
||||
file.MimeType = GetMimeType(fPath)
|
||||
}
|
||||
if isInvalidLink {
|
||||
file.Type = "invalid_link"
|
||||
}
|
||||
items = append(items, file)
|
||||
}
|
||||
|
||||
f.ItemTotal = total
|
||||
start := (page - 1) * pageSize
|
||||
end := pageSize + start
|
||||
var result []*FileInfo
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package files
|
||||
|
||||
import (
|
||||
"github.com/gabriel-vasile/mimetype"
|
||||
"github.com/spf13/afero"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@@ -13,11 +15,19 @@ func IsSymlink(mode os.FileMode) bool {
|
||||
}
|
||||
|
||||
func GetMimeType(path string) string {
|
||||
mime, err := mimetype.DetectFile(path)
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return mime.String()
|
||||
defer file.Close()
|
||||
|
||||
buffer := make([]byte, 512)
|
||||
_, err = file.Read(buffer)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
mimeType := http.DetectContentType(buffer)
|
||||
return mimeType
|
||||
}
|
||||
|
||||
func GetSymlink(path string) string {
|
||||
@@ -28,6 +38,22 @@ func GetSymlink(path string) string {
|
||||
return linkPath
|
||||
}
|
||||
|
||||
func GetUsername(uid uint32) string {
|
||||
usr, err := user.LookupId(strconv.Itoa(int(uid)))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return usr.Username
|
||||
}
|
||||
|
||||
func GetGroup(gid uint32) string {
|
||||
usr, err := user.LookupGroupId(strconv.Itoa(int(gid)))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return usr.Name
|
||||
}
|
||||
|
||||
func ScanDir(fs afero.Fs, path string, dirMap *sync.Map, wg *sync.WaitGroup) {
|
||||
afs := &afero.Afero{Fs: fs}
|
||||
files, _ := afs.ReadDir(path)
|
||||
|
||||
@@ -62,41 +62,6 @@ func (b *Block) UpdateDirective(key string, params []string) {
|
||||
b.Directives = directives
|
||||
}
|
||||
|
||||
//func (b *Block) UpdateDirectiveBySecondKey(name string, key string, directive Directive) {
|
||||
//
|
||||
// directives := b.GetDirectives()
|
||||
//
|
||||
// index := -1
|
||||
// for i, dir := range directives {
|
||||
// if dir.GetName() == name && dir.GetParameters()[0] == key {
|
||||
// index = i
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// if index > -1 {
|
||||
// directives[index] = &directive
|
||||
// } else {
|
||||
// directives = append(directives, &directive)
|
||||
// }
|
||||
// b.Directives = directives
|
||||
//}
|
||||
|
||||
//func (b *Block) RemoveDirectives(names []string) {
|
||||
// nameMaps := make(map[string]struct{}, len(names))
|
||||
// for _, name := range names {
|
||||
// nameMaps[name] = struct{}{}
|
||||
// }
|
||||
// directives := b.GetDirectives()
|
||||
// var newDirectives []IDirective
|
||||
// for _, dir := range directives {
|
||||
// if _, ok := nameMaps[dir.GetName()]; ok {
|
||||
// continue
|
||||
// }
|
||||
// newDirectives = append(newDirectives, dir)
|
||||
// }
|
||||
// b.Directives = newDirectives
|
||||
//}
|
||||
|
||||
func (b *Block) RemoveDirective(key string, params []string) {
|
||||
directives := b.GetDirectives()
|
||||
var newDirectives []IDirective
|
||||
|
||||
@@ -46,6 +46,7 @@ var repeatKeys = map[string]struct {
|
||||
"proxy_set_header": {},
|
||||
"location": {},
|
||||
"include": {},
|
||||
"sub_filter": {},
|
||||
}
|
||||
|
||||
func IsRepeatKey(key string) bool {
|
||||
|
||||
@@ -1,32 +1,240 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Location struct {
|
||||
*Directive
|
||||
Modifier string
|
||||
Match string
|
||||
Modifier string
|
||||
Match string
|
||||
Cache bool
|
||||
ProxyPass string
|
||||
Host string
|
||||
CacheTime int
|
||||
CacheUint string
|
||||
Comment string
|
||||
Directives []IDirective
|
||||
Line int
|
||||
Parameters []string
|
||||
Replaces map[string]string
|
||||
}
|
||||
|
||||
func NewLocation(directive *Directive) *Location {
|
||||
func NewLocation(directive IDirective) *Location {
|
||||
location := &Location{
|
||||
Modifier: "",
|
||||
Match: "",
|
||||
Directive: directive,
|
||||
Modifier: "",
|
||||
Match: "",
|
||||
}
|
||||
if directive.GetBlock() != nil {
|
||||
directive.Comment = directive.GetBlock().GetComment()
|
||||
}
|
||||
|
||||
if len(directive.Parameters) == 0 {
|
||||
directives := make([]IDirective, 0)
|
||||
if len(directive.GetParameters()) == 0 {
|
||||
panic("no enough parameter for location")
|
||||
}
|
||||
|
||||
if len(directive.Parameters) == 1 {
|
||||
location.Match = directive.Parameters[0]
|
||||
return location
|
||||
} else if len(directive.Parameters) == 2 {
|
||||
location.Match = directive.Parameters[1]
|
||||
location.Modifier = directive.Parameters[0]
|
||||
return location
|
||||
for _, dir := range directive.GetBlock().GetDirectives() {
|
||||
directives = append(directives, dir)
|
||||
params := dir.GetParameters()
|
||||
switch dir.GetName() {
|
||||
case "proxy_pass":
|
||||
location.ProxyPass = params[0]
|
||||
case "proxy_set_header":
|
||||
if params[0] == "Host" {
|
||||
location.Host = params[1]
|
||||
}
|
||||
case "proxy_cache":
|
||||
location.Cache = true
|
||||
case "if":
|
||||
if params[0] == "(" && params[1] == "$uri" && params[2] == "~*" {
|
||||
dirs := dir.GetBlock().GetDirectives()
|
||||
for _, di := range dirs {
|
||||
if di.GetName() == "expires" {
|
||||
re := regexp.MustCompile(`^(\d+)(\w+)$`)
|
||||
matches := re.FindStringSubmatch(di.GetParameters()[0])
|
||||
if matches == nil {
|
||||
continue
|
||||
}
|
||||
cacheTime, err := strconv.Atoi(matches[1])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
unit := matches[2]
|
||||
location.CacheUint = unit
|
||||
location.CacheTime = cacheTime
|
||||
}
|
||||
}
|
||||
}
|
||||
case "sub_filter":
|
||||
if location.Replaces == nil {
|
||||
location.Replaces = make(map[string]string, 0)
|
||||
}
|
||||
location.Replaces[strings.Trim(params[0], "\"")] = strings.Trim(params[1], "\"")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
params := directive.GetParameters()
|
||||
if len(params) == 1 {
|
||||
location.Match = params[0]
|
||||
} else if len(params) == 2 {
|
||||
location.Match = params[1]
|
||||
location.Modifier = params[0]
|
||||
}
|
||||
location.Parameters = directive.GetParameters()
|
||||
location.Line = directive.GetLine()
|
||||
location.Comment = directive.GetComment()
|
||||
location.Directives = directives
|
||||
return location
|
||||
}
|
||||
|
||||
func (l *Location) GetName() string {
|
||||
return "location"
|
||||
}
|
||||
|
||||
func (l *Location) GetParameters() []string {
|
||||
return l.Parameters
|
||||
}
|
||||
|
||||
func (l *Location) GetBlock() IBlock {
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *Location) GetComment() string {
|
||||
return l.Comment
|
||||
}
|
||||
|
||||
func (l *Location) GetLine() int {
|
||||
return l.Line
|
||||
}
|
||||
|
||||
func (l *Location) GetDirectives() []IDirective {
|
||||
return l.Directives
|
||||
}
|
||||
|
||||
func (l *Location) FindDirectives(directiveName string) []IDirective {
|
||||
directives := make([]IDirective, 0)
|
||||
for _, directive := range l.Directives {
|
||||
if directive.GetName() == directiveName {
|
||||
directives = append(directives, directive)
|
||||
}
|
||||
if directive.GetBlock() != nil {
|
||||
directives = append(directives, directive.GetBlock().FindDirectives(directiveName)...)
|
||||
}
|
||||
}
|
||||
return directives
|
||||
}
|
||||
|
||||
func (l *Location) UpdateDirective(key string, params []string) {
|
||||
if key == "" || len(params) == 0 {
|
||||
return
|
||||
}
|
||||
directives := l.Directives
|
||||
index := -1
|
||||
for i, dir := range directives {
|
||||
if dir.GetName() == key {
|
||||
if IsRepeatKey(key) {
|
||||
oldParams := dir.GetParameters()
|
||||
if !(len(oldParams) > 0 && oldParams[0] == params[0]) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
newDirective := &Directive{
|
||||
Name: key,
|
||||
Parameters: params,
|
||||
}
|
||||
if index > -1 {
|
||||
directives[index] = newDirective
|
||||
} else {
|
||||
directives = append(directives, newDirective)
|
||||
}
|
||||
l.Directives = directives
|
||||
}
|
||||
|
||||
func (l *Location) RemoveDirective(key string, params []string) {
|
||||
directives := l.Directives
|
||||
var newDirectives []IDirective
|
||||
for _, dir := range directives {
|
||||
if dir.GetName() == key {
|
||||
if len(params) > 0 {
|
||||
oldParams := dir.GetParameters()
|
||||
if oldParams[0] == params[0] {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
newDirectives = append(newDirectives, dir)
|
||||
}
|
||||
l.Directives = newDirectives
|
||||
}
|
||||
|
||||
func (l *Location) ChangePath(Modifier string, Match string) {
|
||||
if Match != "" && Modifier != "" {
|
||||
l.Parameters = []string{Modifier, Match}
|
||||
}
|
||||
if Match != "" && Modifier == "" {
|
||||
l.Parameters = []string{Match}
|
||||
}
|
||||
l.Modifier = Modifier
|
||||
l.Match = Match
|
||||
}
|
||||
|
||||
func (l *Location) AddCache(cacheTime int, cacheUint string) {
|
||||
l.RemoveDirective("add_header", []string{"Cache-Control", "no-cache"})
|
||||
l.RemoveDirective("if", []string{"(", "$uri", "~*", `"\.(gif|png|jpg|css|js|woff|woff2)$"`, ")"})
|
||||
directives := l.GetDirectives()
|
||||
newDir := &Directive{
|
||||
Name: "if",
|
||||
Parameters: []string{"(", "$uri", "~*", `"\.(gif|png|jpg|css|js|woff|woff2)$"`, ")"},
|
||||
Block: &Block{},
|
||||
}
|
||||
block := &Block{}
|
||||
block.Directives = append(block.Directives, &Directive{
|
||||
Name: "expires",
|
||||
Parameters: []string{strconv.Itoa(cacheTime) + cacheUint},
|
||||
})
|
||||
newDir.Block = block
|
||||
directives = append(directives, newDir)
|
||||
l.Directives = directives
|
||||
l.UpdateDirective("proxy_ignore_headers", []string{"Set-Cookie", "Cache-Control", "expires"})
|
||||
l.UpdateDirective("proxy_cache", []string{"proxy_cache_panel"})
|
||||
l.UpdateDirective("proxy_cache_key", []string{"$host$uri$is_args$args"})
|
||||
l.UpdateDirective("proxy_cache_valid", []string{"200", "304", "301", "302", "10m"})
|
||||
l.Cache = true
|
||||
l.CacheTime = cacheTime
|
||||
l.CacheUint = cacheUint
|
||||
}
|
||||
|
||||
func (l *Location) RemoveCache() {
|
||||
l.RemoveDirective("if", []string{"(", "$uri", "~*", `"\.(gif|png|jpg|css|js|woff|woff2)$"`, ")"})
|
||||
l.RemoveDirective("proxy_ignore_headers", []string{"Set-Cookie"})
|
||||
l.RemoveDirective("proxy_cache", []string{"proxy_cache_panel"})
|
||||
l.RemoveDirective("proxy_cache_key", []string{"$host$uri$is_args$args"})
|
||||
l.RemoveDirective("proxy_cache_valid", []string{"200"})
|
||||
|
||||
l.UpdateDirective("add_header", []string{"Cache-Control", "no-cache"})
|
||||
|
||||
l.CacheTime = 0
|
||||
l.CacheUint = ""
|
||||
l.Cache = false
|
||||
}
|
||||
|
||||
func (l *Location) AddSubFilter(subFilters map[string]string) {
|
||||
l.RemoveDirective("sub_filter", []string{})
|
||||
l.Replaces = subFilters
|
||||
for k, v := range subFilters {
|
||||
l.UpdateDirective("sub_filter", []string{fmt.Sprintf(`"%s"`, k), fmt.Sprintf(`"%s"`, v)})
|
||||
}
|
||||
l.UpdateDirective("proxy_set_header", []string{"Accept-Encoding", `""`})
|
||||
l.UpdateDirective("sub_filter_once", []string{"off"})
|
||||
}
|
||||
|
||||
func (l *Location) RemoveSubFilter() {
|
||||
l.RemoveDirective("sub_filter", []string{})
|
||||
l.RemoveDirective("proxy_set_header", []string{"Accept-Encoding", `""`})
|
||||
l.RemoveDirective("sub_filter_once", []string{"off"})
|
||||
l.Replaces = nil
|
||||
}
|
||||
|
||||
@@ -116,22 +116,14 @@ func (s *Server) RemoveDirective(key string, params []string) {
|
||||
directives := s.Directives
|
||||
var newDirectives []IDirective
|
||||
for _, dir := range directives {
|
||||
if key == "location" {
|
||||
if location, ok := dir.(*Location); ok {
|
||||
if len(params) == 2 && location.Match == params[1] && location.Modifier == params[0] {
|
||||
continue
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if dir.GetName() == key {
|
||||
if len(params) > 0 {
|
||||
oldParams := dir.GetParameters()
|
||||
if oldParams[0] == params[0] {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
if dir.GetName() == key {
|
||||
if len(params) > 0 {
|
||||
oldParams := dir.GetParameters()
|
||||
if oldParams[0] == params[0] {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
newDirectives = append(newDirectives, dir)
|
||||
@@ -319,5 +311,4 @@ func (s *Server) AddHTTP2HTTPS() {
|
||||
})
|
||||
newDir.Block = block
|
||||
s.UpdateDirectiveBySecondKey("if", "($scheme", newDir)
|
||||
|
||||
}
|
||||
|
||||
@@ -74,7 +74,6 @@ func DumpBlock(b components.IBlock, style *Style, startLine int) string {
|
||||
}
|
||||
|
||||
directives := b.GetDirectives()
|
||||
|
||||
for i, directive := range directives {
|
||||
|
||||
if directive.GetLine() > line {
|
||||
|
||||
@@ -84,7 +84,6 @@ func (p *Parser) Parse() *components.Config {
|
||||
}
|
||||
|
||||
func (p *Parser) parseBlock() *components.Block {
|
||||
|
||||
context := &components.Block{
|
||||
Comment: "",
|
||||
Directives: make([]components.IDirective, 0),
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
package ssl
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
)
|
||||
|
||||
func GenerateSSL(domain string) error {
|
||||
rootPrivateKey, _ := rsa.GenerateKey(rand.Reader, 2048)
|
||||
ipItem := net.ParseIP(domain)
|
||||
isIP := false
|
||||
if len(ipItem) != 0 {
|
||||
isIP = true
|
||||
}
|
||||
|
||||
rootTemplate := x509.Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
Subject: pkix.Name{CommonName: "1Panel Root CA"},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(10, 0, 0),
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
KeyUsage: x509.KeyUsageCertSign,
|
||||
}
|
||||
if isIP {
|
||||
rootTemplate.IPAddresses = []net.IP{ipItem}
|
||||
} else {
|
||||
rootTemplate.DNSNames = []string{domain}
|
||||
}
|
||||
|
||||
rootCertBytes, _ := x509.CreateCertificate(rand.Reader, &rootTemplate, &rootTemplate, &rootPrivateKey.PublicKey, rootPrivateKey)
|
||||
rootCertBlock := &pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: rootCertBytes,
|
||||
}
|
||||
|
||||
interPrivateKey, _ := rsa.GenerateKey(rand.Reader, 2048)
|
||||
interTemplate := x509.Certificate{
|
||||
SerialNumber: big.NewInt(2),
|
||||
Subject: pkix.Name{CommonName: "1Panel Intermediate CA"},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(10, 0, 0),
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
KeyUsage: x509.KeyUsageCertSign,
|
||||
}
|
||||
if isIP {
|
||||
interTemplate.IPAddresses = []net.IP{ipItem}
|
||||
} else {
|
||||
interTemplate.DNSNames = []string{domain}
|
||||
}
|
||||
|
||||
interCertBytes, _ := x509.CreateCertificate(rand.Reader, &interTemplate, &rootTemplate, &interPrivateKey.PublicKey, rootPrivateKey)
|
||||
interCertBlock := &pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: interCertBytes,
|
||||
}
|
||||
|
||||
clientPrivateKey, _ := rsa.GenerateKey(rand.Reader, 2048)
|
||||
clientTemplate := x509.Certificate{
|
||||
SerialNumber: big.NewInt(3),
|
||||
Subject: pkix.Name{CommonName: domain},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(10, 0, 0),
|
||||
KeyUsage: x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
}
|
||||
if isIP {
|
||||
clientTemplate.IPAddresses = []net.IP{ipItem}
|
||||
} else {
|
||||
clientTemplate.DNSNames = []string{domain}
|
||||
}
|
||||
|
||||
clientCertBytes, _ := x509.CreateCertificate(rand.Reader, &clientTemplate, &interTemplate, &clientPrivateKey.PublicKey, interPrivateKey)
|
||||
clientCertBlock := &pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: clientCertBytes,
|
||||
}
|
||||
|
||||
pemBytes := []byte{}
|
||||
pemBytes = append(pemBytes, pem.EncodeToMemory(clientCertBlock)...)
|
||||
pemBytes = append(pemBytes, pem.EncodeToMemory(interCertBlock)...)
|
||||
pemBytes = append(pemBytes, pem.EncodeToMemory(rootCertBlock)...)
|
||||
certOut, err := os.OpenFile(global.CONF.System.BaseDir+"/1panel/secret/server.crt.tmp", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer certOut.Close()
|
||||
if _, err := certOut.Write(pemBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keyOut, err := os.OpenFile(global.CONF.System.BaseDir+"/1panel/secret/server.key.tmp", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer keyOut.Close()
|
||||
if err := pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(clientPrivateKey)}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -21,9 +21,8 @@ type LocalCommand struct {
|
||||
closeSignal syscall.Signal
|
||||
closeTimeout time.Duration
|
||||
|
||||
cmd *exec.Cmd
|
||||
pty *os.File
|
||||
ptyClosed chan struct{}
|
||||
cmd *exec.Cmd
|
||||
pty *os.File
|
||||
}
|
||||
|
||||
func NewCommand(commands string) (*LocalCommand, error) {
|
||||
@@ -33,15 +32,13 @@ func NewCommand(commands string) (*LocalCommand, error) {
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to start command")
|
||||
}
|
||||
ptyClosed := make(chan struct{})
|
||||
|
||||
lcmd := &LocalCommand{
|
||||
closeSignal: DefaultCloseSignal,
|
||||
closeTimeout: DefaultCloseTimeout,
|
||||
|
||||
cmd: cmd,
|
||||
pty: pty,
|
||||
ptyClosed: ptyClosed,
|
||||
cmd: cmd,
|
||||
pty: pty,
|
||||
}
|
||||
|
||||
return lcmd, nil
|
||||
@@ -57,16 +54,10 @@ func (lcmd *LocalCommand) Write(p []byte) (n int, err error) {
|
||||
|
||||
func (lcmd *LocalCommand) Close() error {
|
||||
if lcmd.cmd != nil && lcmd.cmd.Process != nil {
|
||||
_ = lcmd.cmd.Process.Signal(lcmd.closeSignal)
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-lcmd.ptyClosed:
|
||||
return nil
|
||||
case <-lcmd.closeTimeoutC():
|
||||
_ = lcmd.cmd.Process.Signal(syscall.SIGKILL)
|
||||
}
|
||||
_ = lcmd.cmd.Process.Kill()
|
||||
}
|
||||
_ = lcmd.pty.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lcmd *LocalCommand) ResizeTerminal(width int, height int) error {
|
||||
@@ -100,11 +91,3 @@ func (lcmd *LocalCommand) Wait(quitChan chan bool) {
|
||||
setQuit(quitChan)
|
||||
}
|
||||
}
|
||||
|
||||
func (lcmd *LocalCommand) closeTimeoutC() <-chan time.Time {
|
||||
if lcmd.closeTimeout >= 0 {
|
||||
return time.After(lcmd.closeTimeout)
|
||||
}
|
||||
|
||||
return make(chan time.Time)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(resetMFACmd)
|
||||
RootCmd.AddCommand(resetSSLCmd)
|
||||
RootCmd.AddCommand(resetEntranceCmd)
|
||||
}
|
||||
|
||||
var resetMFACmd = &cobra.Command{
|
||||
Use: "reset-mfa",
|
||||
Short: "关闭 1Panel 两步验证",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
db, err := loadDBConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return setSettingByKey(db, "MFAStatus", "disable")
|
||||
},
|
||||
}
|
||||
|
||||
var resetSSLCmd = &cobra.Command{
|
||||
Use: "reset-https",
|
||||
Short: "取消 1Panel https 方式登录",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
db, err := loadDBConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return setSettingByKey(db, "SSL", "disable")
|
||||
},
|
||||
}
|
||||
var resetEntranceCmd = &cobra.Command{
|
||||
Use: "reset-entrance",
|
||||
Short: "取消 1Panel 安全入口",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
db, err := loadDBConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return setSettingByKey(db, "SecurityEntrance", "")
|
||||
},
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/server"
|
||||
cmdUtils "github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/spf13/cobra"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -28,8 +32,32 @@ type setting struct {
|
||||
About string `json:"about" gorm:"type:longText"`
|
||||
}
|
||||
|
||||
func loadDBConn() (*gorm.DB, error) {
|
||||
stdout, err := cmdUtils.Exec("grep '^BASE_DIR=' /usr/bin/1pctl | cut -d'=' -f2")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("handle load `BASE_DIR` failed, err: %v", err)
|
||||
}
|
||||
baseDir := strings.ReplaceAll(stdout, "\n", "")
|
||||
if len(baseDir) == 0 {
|
||||
return nil, fmt.Errorf("error `BASE_DIR` find in /usr/bin/1pctl \n")
|
||||
}
|
||||
if strings.HasSuffix(baseDir, "/") {
|
||||
baseDir = baseDir[:strings.LastIndex(baseDir, "/")]
|
||||
}
|
||||
|
||||
db, err := gorm.Open(sqlite.Open(baseDir+"/1panel/db/1Panel.db"), &gorm.Config{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("init my db conn failed, err: %v \n", err)
|
||||
}
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func getSettingByKey(db *gorm.DB, key string) string {
|
||||
var setting setting
|
||||
_ = db.Where("key = ?", key).First(&setting).Error
|
||||
return setting.Value
|
||||
}
|
||||
|
||||
func setSettingByKey(db *gorm.DB, key, value string) error {
|
||||
return db.Model(&setting{}).Where("key = ?", key).Updates(map[string]interface{}{"value": value}).Error
|
||||
}
|
||||
|
||||
@@ -2,14 +2,10 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
cmdUtils "github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
|
||||
"github.com/spf13/cobra"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -20,25 +16,15 @@ var userinfoCmd = &cobra.Command{
|
||||
Use: "user-info",
|
||||
Short: "获取用户信息",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
stdout, err := cmdUtils.Exec("grep '^BASE_DIR=' /usr/bin/1pctl | cut -d'=' -f2")
|
||||
db, err := loadDBConn()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
baseDir := strings.ReplaceAll(stdout, "\n", "")
|
||||
if len(baseDir) == 0 {
|
||||
fmt.Printf("error `BASE_DIR` find in /usr/bin/1pctl \n")
|
||||
}
|
||||
if strings.HasSuffix(baseDir, "/") {
|
||||
baseDir = baseDir[:strings.LastIndex(baseDir, "/")]
|
||||
}
|
||||
|
||||
db, err := gorm.Open(sqlite.Open(baseDir+"/1panel/db/1Panel.db"), &gorm.Config{})
|
||||
if err != nil {
|
||||
fmt.Printf("init my db conn failed, err: %v \n", err)
|
||||
return fmt.Errorf("init my db conn failed, err: %v \n", err)
|
||||
}
|
||||
user := getSettingByKey(db, "UserName")
|
||||
password := getSettingByKey(db, "Password")
|
||||
port := getSettingByKey(db, "ServerPort")
|
||||
ssl := getSettingByKey(db, "SSL")
|
||||
entrance := getSettingByKey(db, "SecurityEntrance")
|
||||
enptrySetting := getSettingByKey(db, "EncryptKey")
|
||||
|
||||
p := ""
|
||||
@@ -52,6 +38,8 @@ var userinfoCmd = &cobra.Command{
|
||||
fmt.Printf("username: %s\n", user)
|
||||
fmt.Printf("password: %s\n", p)
|
||||
fmt.Printf("port: %s\n", port)
|
||||
fmt.Printf("ssl: %s\n", ssl)
|
||||
fmt.Printf("entrance: %s\n", entrance)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
+12
-18
@@ -2,12 +2,12 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
cmdUtils "github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/configs"
|
||||
"github.com/1Panel-dev/1Panel/cmd/server/conf"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -18,27 +18,21 @@ var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "获取系统版本信息",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
stdout, err := cmdUtils.Exec("grep '^BASE_DIR=' /usr/bin/1pctl | cut -d'=' -f2")
|
||||
db, err := loadDBConn()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
baseDir := strings.ReplaceAll(stdout, "\n", "")
|
||||
if len(baseDir) == 0 {
|
||||
fmt.Printf("error `BASE_DIR` find in /usr/bin/1pctl \n")
|
||||
}
|
||||
if strings.HasSuffix(baseDir, "/") {
|
||||
baseDir = baseDir[:strings.LastIndex(baseDir, "/")]
|
||||
}
|
||||
|
||||
db, err := gorm.Open(sqlite.Open(baseDir+"/1panel/db/1Panel.db"), &gorm.Config{})
|
||||
if err != nil {
|
||||
fmt.Printf("init my db conn failed, err: %v \n", err)
|
||||
return err
|
||||
}
|
||||
version := getSettingByKey(db, "SystemVersion")
|
||||
appStoreVersion := getSettingByKey(db, "AppStoreVersion")
|
||||
|
||||
fmt.Printf("1panel version: %s\n", version)
|
||||
fmt.Printf("appstore version: %s\n", appStoreVersion)
|
||||
config := configs.ServerConfig{}
|
||||
if err := yaml.Unmarshal(conf.AppYaml, &config); err != nil {
|
||||
return fmt.Errorf("unmarshal conf.App.Yaml failed, errL %v", err)
|
||||
} else {
|
||||
fmt.Printf("mode: %s\n", config.System.Mode)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
+608
-53
@@ -799,27 +799,13 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/init": {
|
||||
"post": {
|
||||
"description": "初始化用户",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"/auth/issafety": {
|
||||
"get": {
|
||||
"description": "获取系统安全登录状态",
|
||||
"tags": [
|
||||
"Auth"
|
||||
],
|
||||
"summary": "Init user",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.InitUser"
|
||||
}
|
||||
}
|
||||
],
|
||||
"summary": "Load safety status",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
@@ -908,20 +894,6 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/status": {
|
||||
"get": {
|
||||
"description": "判断是否为首次登录",
|
||||
"tags": [
|
||||
"Auth"
|
||||
],
|
||||
"summary": "Check is First login",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/containers": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -4522,6 +4494,50 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/files/owner": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "修改文件用户/组",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"File"
|
||||
],
|
||||
"summary": "Change file owner",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.FileRoleUpdate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [],
|
||||
"bodyKeys": [
|
||||
"path",
|
||||
"user",
|
||||
"group"
|
||||
],
|
||||
"formatEN": "Change owner [paths] =\u003e [user]/[group]",
|
||||
"formatZH": "修改用户/组 [paths] =\u003e [user]/[group]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/files/rename": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -7384,6 +7400,70 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/settings/ssl/info": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取证书信息",
|
||||
"tags": [
|
||||
"System Setting"
|
||||
],
|
||||
"summary": "Load system cert info",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.SettingInfo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/settings/ssl/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "修改系统 ssl 登录",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"System Setting"
|
||||
],
|
||||
"summary": "Update system ssl",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.SSLUpdate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [],
|
||||
"bodyKeys": [
|
||||
"ssl"
|
||||
],
|
||||
"formatEN": "update system ssl =\u003e [ssl]",
|
||||
"formatZH": "修改系统 ssl =\u003e [ssl]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/settings/time/sync": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -7857,6 +7937,72 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/auths": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取密码访问配置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website"
|
||||
],
|
||||
"summary": "Get AuthBasic conf",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.NginxAuthReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/auths/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "更新密码访问配置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website"
|
||||
],
|
||||
"summary": "Get AuthBasic conf",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.NginxAuthUpdate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/check": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -8083,6 +8229,57 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/dir/permission": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "更新网站目录权限",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website"
|
||||
],
|
||||
"summary": "Update Site Dir permission",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.WebsiteUpdateDirPermission"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [
|
||||
{
|
||||
"db": "websites",
|
||||
"input_colume": "id",
|
||||
"input_value": "id",
|
||||
"isList": false,
|
||||
"output_colume": "primary_domain",
|
||||
"output_value": "domain"
|
||||
}
|
||||
],
|
||||
"bodyKeys": [
|
||||
"id"
|
||||
],
|
||||
"formatEN": "Update domain [domain] dir permission",
|
||||
"formatZH": "更新网站 [domain] 目录权限",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/dir/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -8111,7 +8308,7 @@ var doc = `{
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
@@ -8773,6 +8970,141 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/proxies": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取反向代理配置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website"
|
||||
],
|
||||
"summary": "Get proxy conf",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.WebsiteProxyReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/proxies/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "修改反向代理配置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website"
|
||||
],
|
||||
"summary": "Update proxy conf",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.WebsiteProxyConfig"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [
|
||||
{
|
||||
"db": "websites",
|
||||
"input_colume": "id",
|
||||
"input_value": "id",
|
||||
"isList": false,
|
||||
"output_colume": "primary_domain",
|
||||
"output_value": "domain"
|
||||
}
|
||||
],
|
||||
"bodyKeys": [
|
||||
"id"
|
||||
],
|
||||
"formatEN": "Update domain [domain] proxy config",
|
||||
"formatZH": "修改网站 [domain] 反向代理配置 ",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/proxy/file": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "更新反向代理文件",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website"
|
||||
],
|
||||
"summary": "Update proxy file",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.NginxProxyUpdate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [
|
||||
{
|
||||
"db": "websites",
|
||||
"input_colume": "id",
|
||||
"input_value": "websiteID",
|
||||
"isList": false,
|
||||
"output_colume": "primary_domain",
|
||||
"output_value": "domain"
|
||||
}
|
||||
],
|
||||
"bodyKeys": [
|
||||
"websiteID"
|
||||
],
|
||||
"formatEN": "Nginx conf proxy file update [domain]",
|
||||
"formatZH": "更新反向代理文件 [domain]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/rewrite": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -9659,6 +9991,9 @@ var doc = `{
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"withFile": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -10696,21 +11031,6 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.InitUser": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"password"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.InspectReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -10734,6 +11054,9 @@ var doc = `{
|
||||
"captchaID": {
|
||||
"type": "string"
|
||||
},
|
||||
"ignoreCaptcha": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -11122,10 +11445,16 @@ var doc = `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"containerPort": {
|
||||
"type": "integer"
|
||||
"type": "string"
|
||||
},
|
||||
"hostIP": {
|
||||
"type": "string"
|
||||
},
|
||||
"hostPort": {
|
||||
"type": "integer"
|
||||
"type": "string"
|
||||
},
|
||||
"protocol": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -11351,6 +11680,36 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SSLUpdate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ssl"
|
||||
],
|
||||
"properties": {
|
||||
"cert": {
|
||||
"type": "string"
|
||||
},
|
||||
"domain": {
|
||||
"type": "string"
|
||||
},
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"ssl": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"enable",
|
||||
"disable"
|
||||
]
|
||||
},
|
||||
"sslID": {
|
||||
"type": "integer"
|
||||
},
|
||||
"sslType": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SearchForTree": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -11530,6 +11889,12 @@ var doc = `{
|
||||
"sessionTimeout": {
|
||||
"type": "string"
|
||||
},
|
||||
"ssl": {
|
||||
"type": "string"
|
||||
},
|
||||
"sslType": {
|
||||
"type": "string"
|
||||
},
|
||||
"systemVersion": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -11565,7 +11930,8 @@ var doc = `{
|
||||
],
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"maxLength": 256
|
||||
},
|
||||
"from": {
|
||||
"type": "string",
|
||||
@@ -11584,7 +11950,8 @@ var doc = `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"maxLength": 256
|
||||
},
|
||||
"from": {
|
||||
"type": "string"
|
||||
@@ -11621,7 +11988,8 @@ var doc = `{
|
||||
],
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"maxLength": 256
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
@@ -11942,6 +12310,9 @@ var doc = `{
|
||||
"expireDate": {
|
||||
"type": "string"
|
||||
},
|
||||
"group": {
|
||||
"type": "string"
|
||||
},
|
||||
"httpConfig": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -11981,6 +12352,9 @@ var doc = `{
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"user": {
|
||||
"type": "string"
|
||||
},
|
||||
"webSiteGroupId": {
|
||||
"type": "integer"
|
||||
},
|
||||
@@ -12315,6 +12689,9 @@ var doc = `{
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"sub": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -12469,6 +12846,29 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.FileRoleUpdate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"group",
|
||||
"path",
|
||||
"sub",
|
||||
"user"
|
||||
],
|
||||
"properties": {
|
||||
"group": {
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"sub": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"user": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.FileWget": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -12503,6 +12903,43 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.NginxAuthReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"websiteID"
|
||||
],
|
||||
"properties": {
|
||||
"websiteID": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.NginxAuthUpdate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"operate",
|
||||
"password",
|
||||
"username",
|
||||
"websiteID"
|
||||
],
|
||||
"properties": {
|
||||
"operate": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"remark": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"websiteID": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.NginxConfigFileUpdate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -12540,6 +12977,25 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.NginxProxyUpdate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"content",
|
||||
"name",
|
||||
"websiteID"
|
||||
],
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"websiteID": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.NginxRewriteReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -13044,6 +13500,80 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.WebsiteProxyConfig": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"cache",
|
||||
"cacheTime",
|
||||
"cacheUnit",
|
||||
"enable",
|
||||
"id",
|
||||
"match",
|
||||
"modifier",
|
||||
"name",
|
||||
"operate",
|
||||
"proxyHost",
|
||||
"proxyPass"
|
||||
],
|
||||
"properties": {
|
||||
"cache": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"cacheTime": {
|
||||
"type": "integer"
|
||||
},
|
||||
"cacheUnit": {
|
||||
"type": "string"
|
||||
},
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"enable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"filePath": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"match": {
|
||||
"type": "string"
|
||||
},
|
||||
"modifier": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"operate": {
|
||||
"type": "string"
|
||||
},
|
||||
"proxyHost": {
|
||||
"type": "string"
|
||||
},
|
||||
"proxyPass": {
|
||||
"type": "string"
|
||||
},
|
||||
"replaces": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.WebsiteProxyReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.WebsiteResourceReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -13186,6 +13716,25 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.WebsiteUpdateDirPermission": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"group",
|
||||
"id",
|
||||
"user"
|
||||
],
|
||||
"properties": {
|
||||
"group": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"user": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.WebsiteWafReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -13572,6 +14121,9 @@ var doc = `{
|
||||
"expireDate": {
|
||||
"type": "string"
|
||||
},
|
||||
"group": {
|
||||
"type": "string"
|
||||
},
|
||||
"httpConfig": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -13617,6 +14169,9 @@ var doc = `{
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"user": {
|
||||
"type": "string"
|
||||
},
|
||||
"webSiteGroupId": {
|
||||
"type": "integer"
|
||||
},
|
||||
|
||||
+608
-53
@@ -785,27 +785,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/init": {
|
||||
"post": {
|
||||
"description": "初始化用户",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"/auth/issafety": {
|
||||
"get": {
|
||||
"description": "获取系统安全登录状态",
|
||||
"tags": [
|
||||
"Auth"
|
||||
],
|
||||
"summary": "Init user",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.InitUser"
|
||||
}
|
||||
}
|
||||
],
|
||||
"summary": "Load safety status",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
@@ -894,20 +880,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/status": {
|
||||
"get": {
|
||||
"description": "判断是否为首次登录",
|
||||
"tags": [
|
||||
"Auth"
|
||||
],
|
||||
"summary": "Check is First login",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/containers": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -4508,6 +4480,50 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/files/owner": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "修改文件用户/组",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"File"
|
||||
],
|
||||
"summary": "Change file owner",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.FileRoleUpdate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [],
|
||||
"bodyKeys": [
|
||||
"path",
|
||||
"user",
|
||||
"group"
|
||||
],
|
||||
"formatEN": "Change owner [paths] =\u003e [user]/[group]",
|
||||
"formatZH": "修改用户/组 [paths] =\u003e [user]/[group]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/files/rename": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -7370,6 +7386,70 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/settings/ssl/info": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取证书信息",
|
||||
"tags": [
|
||||
"System Setting"
|
||||
],
|
||||
"summary": "Load system cert info",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.SettingInfo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/settings/ssl/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "修改系统 ssl 登录",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"System Setting"
|
||||
],
|
||||
"summary": "Update system ssl",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.SSLUpdate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [],
|
||||
"bodyKeys": [
|
||||
"ssl"
|
||||
],
|
||||
"formatEN": "update system ssl =\u003e [ssl]",
|
||||
"formatZH": "修改系统 ssl =\u003e [ssl]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/settings/time/sync": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -7843,6 +7923,72 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/auths": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取密码访问配置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website"
|
||||
],
|
||||
"summary": "Get AuthBasic conf",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.NginxAuthReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/auths/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "更新密码访问配置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website"
|
||||
],
|
||||
"summary": "Get AuthBasic conf",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.NginxAuthUpdate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/check": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -8069,6 +8215,57 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/dir/permission": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "更新网站目录权限",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website"
|
||||
],
|
||||
"summary": "Update Site Dir permission",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.WebsiteUpdateDirPermission"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [
|
||||
{
|
||||
"db": "websites",
|
||||
"input_colume": "id",
|
||||
"input_value": "id",
|
||||
"isList": false,
|
||||
"output_colume": "primary_domain",
|
||||
"output_value": "domain"
|
||||
}
|
||||
],
|
||||
"bodyKeys": [
|
||||
"id"
|
||||
],
|
||||
"formatEN": "Update domain [domain] dir permission",
|
||||
"formatZH": "更新网站 [domain] 目录权限",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/dir/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -8097,7 +8294,7 @@
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
@@ -8759,6 +8956,141 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/proxies": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取反向代理配置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website"
|
||||
],
|
||||
"summary": "Get proxy conf",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.WebsiteProxyReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/proxies/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "修改反向代理配置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website"
|
||||
],
|
||||
"summary": "Update proxy conf",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.WebsiteProxyConfig"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [
|
||||
{
|
||||
"db": "websites",
|
||||
"input_colume": "id",
|
||||
"input_value": "id",
|
||||
"isList": false,
|
||||
"output_colume": "primary_domain",
|
||||
"output_value": "domain"
|
||||
}
|
||||
],
|
||||
"bodyKeys": [
|
||||
"id"
|
||||
],
|
||||
"formatEN": "Update domain [domain] proxy config",
|
||||
"formatZH": "修改网站 [domain] 反向代理配置 ",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/proxy/file": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "更新反向代理文件",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website"
|
||||
],
|
||||
"summary": "Update proxy file",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.NginxProxyUpdate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [
|
||||
{
|
||||
"db": "websites",
|
||||
"input_colume": "id",
|
||||
"input_value": "websiteID",
|
||||
"isList": false,
|
||||
"output_colume": "primary_domain",
|
||||
"output_value": "domain"
|
||||
}
|
||||
],
|
||||
"bodyKeys": [
|
||||
"websiteID"
|
||||
],
|
||||
"formatEN": "Nginx conf proxy file update [domain]",
|
||||
"formatZH": "更新反向代理文件 [domain]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/rewrite": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -9645,6 +9977,9 @@
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"withFile": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -10682,21 +11017,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.InitUser": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"password"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.InspectReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -10720,6 +11040,9 @@
|
||||
"captchaID": {
|
||||
"type": "string"
|
||||
},
|
||||
"ignoreCaptcha": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -11108,10 +11431,16 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"containerPort": {
|
||||
"type": "integer"
|
||||
"type": "string"
|
||||
},
|
||||
"hostIP": {
|
||||
"type": "string"
|
||||
},
|
||||
"hostPort": {
|
||||
"type": "integer"
|
||||
"type": "string"
|
||||
},
|
||||
"protocol": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -11337,6 +11666,36 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SSLUpdate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ssl"
|
||||
],
|
||||
"properties": {
|
||||
"cert": {
|
||||
"type": "string"
|
||||
},
|
||||
"domain": {
|
||||
"type": "string"
|
||||
},
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"ssl": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"enable",
|
||||
"disable"
|
||||
]
|
||||
},
|
||||
"sslID": {
|
||||
"type": "integer"
|
||||
},
|
||||
"sslType": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SearchForTree": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -11516,6 +11875,12 @@
|
||||
"sessionTimeout": {
|
||||
"type": "string"
|
||||
},
|
||||
"ssl": {
|
||||
"type": "string"
|
||||
},
|
||||
"sslType": {
|
||||
"type": "string"
|
||||
},
|
||||
"systemVersion": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -11551,7 +11916,8 @@
|
||||
],
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"maxLength": 256
|
||||
},
|
||||
"from": {
|
||||
"type": "string",
|
||||
@@ -11570,7 +11936,8 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"maxLength": 256
|
||||
},
|
||||
"from": {
|
||||
"type": "string"
|
||||
@@ -11607,7 +11974,8 @@
|
||||
],
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"maxLength": 256
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
@@ -11928,6 +12296,9 @@
|
||||
"expireDate": {
|
||||
"type": "string"
|
||||
},
|
||||
"group": {
|
||||
"type": "string"
|
||||
},
|
||||
"httpConfig": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -11967,6 +12338,9 @@
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"user": {
|
||||
"type": "string"
|
||||
},
|
||||
"webSiteGroupId": {
|
||||
"type": "integer"
|
||||
},
|
||||
@@ -12301,6 +12675,9 @@
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"sub": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -12455,6 +12832,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.FileRoleUpdate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"group",
|
||||
"path",
|
||||
"sub",
|
||||
"user"
|
||||
],
|
||||
"properties": {
|
||||
"group": {
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"sub": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"user": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.FileWget": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -12489,6 +12889,43 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.NginxAuthReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"websiteID"
|
||||
],
|
||||
"properties": {
|
||||
"websiteID": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.NginxAuthUpdate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"operate",
|
||||
"password",
|
||||
"username",
|
||||
"websiteID"
|
||||
],
|
||||
"properties": {
|
||||
"operate": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"remark": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"websiteID": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.NginxConfigFileUpdate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -12526,6 +12963,25 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.NginxProxyUpdate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"content",
|
||||
"name",
|
||||
"websiteID"
|
||||
],
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"websiteID": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.NginxRewriteReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -13030,6 +13486,80 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.WebsiteProxyConfig": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"cache",
|
||||
"cacheTime",
|
||||
"cacheUnit",
|
||||
"enable",
|
||||
"id",
|
||||
"match",
|
||||
"modifier",
|
||||
"name",
|
||||
"operate",
|
||||
"proxyHost",
|
||||
"proxyPass"
|
||||
],
|
||||
"properties": {
|
||||
"cache": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"cacheTime": {
|
||||
"type": "integer"
|
||||
},
|
||||
"cacheUnit": {
|
||||
"type": "string"
|
||||
},
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"enable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"filePath": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"match": {
|
||||
"type": "string"
|
||||
},
|
||||
"modifier": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"operate": {
|
||||
"type": "string"
|
||||
},
|
||||
"proxyHost": {
|
||||
"type": "string"
|
||||
},
|
||||
"proxyPass": {
|
||||
"type": "string"
|
||||
},
|
||||
"replaces": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.WebsiteProxyReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.WebsiteResourceReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -13172,6 +13702,25 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.WebsiteUpdateDirPermission": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"group",
|
||||
"id",
|
||||
"user"
|
||||
],
|
||||
"properties": {
|
||||
"group": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"user": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.WebsiteWafReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -13558,6 +14107,9 @@
|
||||
"expireDate": {
|
||||
"type": "string"
|
||||
},
|
||||
"group": {
|
||||
"type": "string"
|
||||
},
|
||||
"httpConfig": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -13603,6 +14155,9 @@
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"user": {
|
||||
"type": "string"
|
||||
},
|
||||
"webSiteGroupId": {
|
||||
"type": "integer"
|
||||
},
|
||||
|
||||
+399
-35
@@ -202,6 +202,8 @@ definitions:
|
||||
type: string
|
||||
path:
|
||||
type: string
|
||||
withFile:
|
||||
type: boolean
|
||||
required:
|
||||
- name
|
||||
- operation
|
||||
@@ -902,16 +904,6 @@ definitions:
|
||||
- sourceID
|
||||
- targetName
|
||||
type: object
|
||||
dto.InitUser:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
password:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- password
|
||||
type: object
|
||||
dto.InspectReq:
|
||||
properties:
|
||||
id:
|
||||
@@ -927,6 +919,8 @@ definitions:
|
||||
type: string
|
||||
captchaID:
|
||||
type: string
|
||||
ignoreCaptcha:
|
||||
type: boolean
|
||||
name:
|
||||
type: string
|
||||
password:
|
||||
@@ -1185,9 +1179,13 @@ definitions:
|
||||
dto.PortHelper:
|
||||
properties:
|
||||
containerPort:
|
||||
type: integer
|
||||
type: string
|
||||
hostIP:
|
||||
type: string
|
||||
hostPort:
|
||||
type: integer
|
||||
type: string
|
||||
protocol:
|
||||
type: string
|
||||
type: object
|
||||
dto.PortRuleOperate:
|
||||
properties:
|
||||
@@ -1338,6 +1336,26 @@ definitions:
|
||||
used_memory_rss:
|
||||
type: string
|
||||
type: object
|
||||
dto.SSLUpdate:
|
||||
properties:
|
||||
cert:
|
||||
type: string
|
||||
domain:
|
||||
type: string
|
||||
key:
|
||||
type: string
|
||||
ssl:
|
||||
enum:
|
||||
- enable
|
||||
- disable
|
||||
type: string
|
||||
sslID:
|
||||
type: integer
|
||||
sslType:
|
||||
type: string
|
||||
required:
|
||||
- ssl
|
||||
type: object
|
||||
dto.SearchForTree:
|
||||
properties:
|
||||
info:
|
||||
@@ -1457,6 +1475,10 @@ definitions:
|
||||
type: string
|
||||
sessionTimeout:
|
||||
type: string
|
||||
ssl:
|
||||
type: string
|
||||
sslType:
|
||||
type: string
|
||||
systemVersion:
|
||||
type: string
|
||||
theme:
|
||||
@@ -1478,6 +1500,7 @@ definitions:
|
||||
dto.SnapshotCreate:
|
||||
properties:
|
||||
description:
|
||||
maxLength: 256
|
||||
type: string
|
||||
from:
|
||||
enum:
|
||||
@@ -1494,6 +1517,7 @@ definitions:
|
||||
dto.SnapshotImport:
|
||||
properties:
|
||||
description:
|
||||
maxLength: 256
|
||||
type: string
|
||||
from:
|
||||
type: string
|
||||
@@ -1516,6 +1540,7 @@ definitions:
|
||||
dto.UpdateDescription:
|
||||
properties:
|
||||
description:
|
||||
maxLength: 256
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
@@ -1728,6 +1753,8 @@ definitions:
|
||||
type: boolean
|
||||
expireDate:
|
||||
type: string
|
||||
group:
|
||||
type: string
|
||||
httpConfig:
|
||||
type: string
|
||||
id:
|
||||
@@ -1754,6 +1781,8 @@ definitions:
|
||||
type: string
|
||||
updatedAt:
|
||||
type: string
|
||||
user:
|
||||
type: string
|
||||
webSiteGroupId:
|
||||
type: integer
|
||||
webSiteSSL:
|
||||
@@ -1974,6 +2003,8 @@ definitions:
|
||||
type: integer
|
||||
path:
|
||||
type: string
|
||||
sub:
|
||||
type: boolean
|
||||
required:
|
||||
- mode
|
||||
- path
|
||||
@@ -2079,6 +2110,22 @@ definitions:
|
||||
- newName
|
||||
- oldName
|
||||
type: object
|
||||
request.FileRoleUpdate:
|
||||
properties:
|
||||
group:
|
||||
type: string
|
||||
path:
|
||||
type: string
|
||||
sub:
|
||||
type: boolean
|
||||
user:
|
||||
type: string
|
||||
required:
|
||||
- group
|
||||
- path
|
||||
- sub
|
||||
- user
|
||||
type: object
|
||||
request.FileWget:
|
||||
properties:
|
||||
name:
|
||||
@@ -2102,6 +2149,31 @@ definitions:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
type: object
|
||||
request.NginxAuthReq:
|
||||
properties:
|
||||
websiteID:
|
||||
type: integer
|
||||
required:
|
||||
- websiteID
|
||||
type: object
|
||||
request.NginxAuthUpdate:
|
||||
properties:
|
||||
operate:
|
||||
type: string
|
||||
password:
|
||||
type: string
|
||||
remark:
|
||||
type: string
|
||||
username:
|
||||
type: string
|
||||
websiteID:
|
||||
type: integer
|
||||
required:
|
||||
- operate
|
||||
- password
|
||||
- username
|
||||
- websiteID
|
||||
type: object
|
||||
request.NginxConfigFileUpdate:
|
||||
properties:
|
||||
backup:
|
||||
@@ -2127,6 +2199,19 @@ definitions:
|
||||
required:
|
||||
- websiteId
|
||||
type: object
|
||||
request.NginxProxyUpdate:
|
||||
properties:
|
||||
content:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
websiteID:
|
||||
type: integer
|
||||
required:
|
||||
- content
|
||||
- name
|
||||
- websiteID
|
||||
type: object
|
||||
request.NginxRewriteReq:
|
||||
properties:
|
||||
name:
|
||||
@@ -2465,6 +2550,58 @@ definitions:
|
||||
- id
|
||||
- type
|
||||
type: object
|
||||
request.WebsiteProxyConfig:
|
||||
properties:
|
||||
cache:
|
||||
type: boolean
|
||||
cacheTime:
|
||||
type: integer
|
||||
cacheUnit:
|
||||
type: string
|
||||
content:
|
||||
type: string
|
||||
enable:
|
||||
type: boolean
|
||||
filePath:
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
match:
|
||||
type: string
|
||||
modifier:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
operate:
|
||||
type: string
|
||||
proxyHost:
|
||||
type: string
|
||||
proxyPass:
|
||||
type: string
|
||||
replaces:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
required:
|
||||
- cache
|
||||
- cacheTime
|
||||
- cacheUnit
|
||||
- enable
|
||||
- id
|
||||
- match
|
||||
- modifier
|
||||
- name
|
||||
- operate
|
||||
- proxyHost
|
||||
- proxyPass
|
||||
type: object
|
||||
request.WebsiteProxyReq:
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
required:
|
||||
- id
|
||||
type: object
|
||||
request.WebsiteResourceReq:
|
||||
properties:
|
||||
id:
|
||||
@@ -2560,6 +2697,19 @@ definitions:
|
||||
- id
|
||||
- siteDir
|
||||
type: object
|
||||
request.WebsiteUpdateDirPermission:
|
||||
properties:
|
||||
group:
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
user:
|
||||
type: string
|
||||
required:
|
||||
- group
|
||||
- id
|
||||
- user
|
||||
type: object
|
||||
request.WebsiteWafReq:
|
||||
properties:
|
||||
key:
|
||||
@@ -2816,6 +2966,8 @@ definitions:
|
||||
type: string
|
||||
expireDate:
|
||||
type: string
|
||||
group:
|
||||
type: string
|
||||
httpConfig:
|
||||
type: string
|
||||
id:
|
||||
@@ -2846,6 +2998,8 @@ definitions:
|
||||
type: string
|
||||
updatedAt:
|
||||
type: string
|
||||
user:
|
||||
type: string
|
||||
webSiteGroupId:
|
||||
type: integer
|
||||
webSiteSSL:
|
||||
@@ -3392,22 +3546,13 @@ paths:
|
||||
summary: Check System isDemo
|
||||
tags:
|
||||
- Auth
|
||||
/auth/init:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 初始化用户
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.InitUser'
|
||||
/auth/issafety:
|
||||
get:
|
||||
description: 获取系统安全登录状态
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
summary: Init user
|
||||
summary: Load safety status
|
||||
tags:
|
||||
- Auth
|
||||
/auth/login:
|
||||
@@ -3461,15 +3606,6 @@ paths:
|
||||
summary: User login with mfa
|
||||
tags:
|
||||
- Auth
|
||||
/auth/status:
|
||||
get:
|
||||
description: 判断是否为首次登录
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
summary: Check is First login
|
||||
tags:
|
||||
- Auth
|
||||
/containers:
|
||||
post:
|
||||
consumes:
|
||||
@@ -5761,6 +5897,35 @@ paths:
|
||||
formatEN: Move [oldPaths] => [newPath]
|
||||
formatZH: 移动文件 [oldPaths] => [newPath]
|
||||
paramKeys: []
|
||||
/files/owner:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 修改文件用户/组
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/request.FileRoleUpdate'
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Change file owner
|
||||
tags:
|
||||
- File
|
||||
x-panel-log:
|
||||
BeforeFuntions: []
|
||||
bodyKeys:
|
||||
- path
|
||||
- user
|
||||
- group
|
||||
formatEN: Change owner [paths] => [user]/[group]
|
||||
formatZH: 修改用户/组 [paths] => [user]/[group]
|
||||
paramKeys: []
|
||||
/files/rename:
|
||||
post:
|
||||
consumes:
|
||||
@@ -7578,6 +7743,46 @@ paths:
|
||||
summary: Page system snapshot
|
||||
tags:
|
||||
- System Setting
|
||||
/settings/ssl/info:
|
||||
get:
|
||||
description: 获取证书信息
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/dto.SettingInfo'
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Load system cert info
|
||||
tags:
|
||||
- System Setting
|
||||
/settings/ssl/update:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 修改系统 ssl 登录
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.SSLUpdate'
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Update system ssl
|
||||
tags:
|
||||
- System Setting
|
||||
x-panel-log:
|
||||
BeforeFuntions: []
|
||||
bodyKeys:
|
||||
- ssl
|
||||
formatEN: update system ssl => [ssl]
|
||||
formatZH: 修改系统 ssl => [ssl]
|
||||
paramKeys: []
|
||||
/settings/time/sync:
|
||||
post:
|
||||
description: 系统时间同步
|
||||
@@ -7879,6 +8084,46 @@ paths:
|
||||
summary: Page website acme accounts
|
||||
tags:
|
||||
- Website Acme
|
||||
/websites/auths:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 获取密码访问配置
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/request.NginxAuthReq'
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Get AuthBasic conf
|
||||
tags:
|
||||
- Website
|
||||
/websites/auths/update:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 更新密码访问配置
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/request.NginxAuthUpdate'
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Get AuthBasic conf
|
||||
tags:
|
||||
- Website
|
||||
/websites/check:
|
||||
post:
|
||||
consumes:
|
||||
@@ -8023,6 +8268,39 @@ paths:
|
||||
formatEN: Delete website [domain]
|
||||
formatZH: 删除网站 [domain]
|
||||
paramKeys: []
|
||||
/websites/dir/permission:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 更新网站目录权限
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/request.WebsiteUpdateDirPermission'
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Update Site Dir permission
|
||||
tags:
|
||||
- Website
|
||||
x-panel-log:
|
||||
BeforeFuntions:
|
||||
- db: websites
|
||||
input_colume: id
|
||||
input_value: id
|
||||
isList: false
|
||||
output_colume: primary_domain
|
||||
output_value: domain
|
||||
bodyKeys:
|
||||
- id
|
||||
formatEN: Update domain [domain] dir permission
|
||||
formatZH: 更新网站 [domain] 目录权限
|
||||
paramKeys: []
|
||||
/websites/dir/update:
|
||||
post:
|
||||
consumes:
|
||||
@@ -8037,7 +8315,7 @@ paths:
|
||||
$ref: '#/definitions/request.WebsiteUpdateDir'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
description: ""
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Update Site Dir
|
||||
@@ -8464,6 +8742,92 @@ paths:
|
||||
formatEN: Nginx conf update [domain]
|
||||
formatZH: php 配置修改 [domain]
|
||||
paramKeys: []
|
||||
/websites/proxies:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 获取反向代理配置
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/request.WebsiteProxyReq'
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Get proxy conf
|
||||
tags:
|
||||
- Website
|
||||
/websites/proxies/update:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 修改反向代理配置
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/request.WebsiteProxyConfig'
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Update proxy conf
|
||||
tags:
|
||||
- Website
|
||||
x-panel-log:
|
||||
BeforeFuntions:
|
||||
- db: websites
|
||||
input_colume: id
|
||||
input_value: id
|
||||
isList: false
|
||||
output_colume: primary_domain
|
||||
output_value: domain
|
||||
bodyKeys:
|
||||
- id
|
||||
formatEN: Update domain [domain] proxy config
|
||||
formatZH: '修改网站 [domain] 反向代理配置 '
|
||||
paramKeys: []
|
||||
/websites/proxy/file:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 更新反向代理文件
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/request.NginxProxyUpdate'
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Update proxy file
|
||||
tags:
|
||||
- Website
|
||||
x-panel-log:
|
||||
BeforeFuntions:
|
||||
- db: websites
|
||||
input_colume: id
|
||||
input_value: websiteID
|
||||
isList: false
|
||||
output_colume: primary_domain
|
||||
output_value: domain
|
||||
bodyKeys:
|
||||
- websiteID
|
||||
formatEN: Nginx conf proxy file update [domain]
|
||||
formatZH: 更新反向代理文件 [domain]
|
||||
paramKeys: []
|
||||
/websites/rewrite:
|
||||
post:
|
||||
consumes:
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
proxy_temp_path /www/common/proxy/proxy_temp_dir;
|
||||
proxy_cache_path /www/common/proxy/proxy_cache_dir levels=1:2 keys_zone=proxy_cache_panel:20m inactive=1d max_size=5g;
|
||||
client_body_buffer_size 512k;
|
||||
proxy_connect_timeout 60;
|
||||
proxy_read_timeout 60;
|
||||
proxy_send_timeout 60;
|
||||
proxy_buffer_size 32k;
|
||||
proxy_buffers 4 64k;
|
||||
proxy_busy_buffers_size 128k;
|
||||
proxy_temp_file_write_size 128k;
|
||||
proxy_next_upstream error timeout invalid_header http_500 http_503 http_404;
|
||||
proxy_cache proxy_cache_panel;
|
||||
@@ -19,3 +19,12 @@ var IndexPHP []byte
|
||||
|
||||
//go:embed rewrite/*
|
||||
var Rewrites embed.FS
|
||||
|
||||
//go:embed cache.conf
|
||||
var Cache []byte
|
||||
|
||||
//go:embed proxy.conf
|
||||
var Proxy []byte
|
||||
|
||||
//go:embed proxy_cache.conf
|
||||
var ProxyCache []byte
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
location ^~ /test {
|
||||
proxy_pass http://1panel.cloud/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header REMOTE-HOST $remote_addr;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_http_version 1.1;
|
||||
|
||||
add_header X-Cache $upstream_cache_status;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
proxy_ignore_headers Set-Cookie Cache-Control expires;
|
||||
proxy_cache cache_one;
|
||||
proxy_cache_key $host$uri$is_args$args;
|
||||
proxy_cache_valid 200 304 301 302 10m;
|
||||
@@ -0,0 +1,10 @@
|
||||
set $static_fileg 0;
|
||||
if ( $uri ~* "\.(gif|png|jpg|css|js|woff|woff2)$" )
|
||||
{
|
||||
set $static_fileg 1;
|
||||
expires 1m;
|
||||
}
|
||||
if ( $static_fileg = 0 )
|
||||
{
|
||||
add_header Cache-Control no-cache;
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
|
||||
server_name ko.wp-1.com;
|
||||
|
||||
index index.php index.html index.htm default.php default.htm default.html;
|
||||
|
||||
Binary file not shown.
Vendored
+1
@@ -90,6 +90,7 @@ declare module 'vue' {
|
||||
TableSetting: typeof import('./src/components/table-setting/index.vue')['default']
|
||||
Terminal: typeof import('./src/components/terminal/index.vue')['default']
|
||||
Tooltip: typeof import('./src/components/tooltip/index.vue')['default']
|
||||
Unsafe: typeof import('./src/components/error-message/unsafe.vue')['default']
|
||||
Upload: typeof import('./src/components/upload/index.vue')['default']
|
||||
VCharts: typeof import('./src/components/v-charts/index.vue')['default']
|
||||
}
|
||||
|
||||
Generated
+2567
-2090
File diff suppressed because it is too large
Load Diff
@@ -29,8 +29,8 @@
|
||||
"axios": "^0.27.2",
|
||||
"echarts": "^5.3.0",
|
||||
"echarts-liquidfill": "^3.1.0",
|
||||
"element-plus": "^2.2.32",
|
||||
"fit2cloud-ui-plus": "^1.0.4",
|
||||
"element-plus": "^2.3.4",
|
||||
"fit2cloud-ui-plus": "^1.0.7",
|
||||
"js-base64": "^3.7.2",
|
||||
"js-md5": "^0.7.3",
|
||||
"md-editor-v3": "^2.7.2",
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import i18n from '@/lang';
|
||||
import router from '@/routers';
|
||||
import { MsgError } from '@/utils/message';
|
||||
import { GlobalStore } from '@/store';
|
||||
const globalStore = GlobalStore();
|
||||
|
||||
export const checkStatus = (status: number, msg: string): void => {
|
||||
switch (status) {
|
||||
@@ -11,7 +13,7 @@ export const checkStatus = (status: number, msg: string): void => {
|
||||
MsgError(msg ? msg : i18n.global.t('commons.res.notFound'));
|
||||
break;
|
||||
case 403:
|
||||
router.replace({ path: '/login' });
|
||||
router.replace({ name: 'entrance', params: { code: globalStore.entrance } });
|
||||
MsgError(msg ? msg : i18n.global.t('commons.res.forbidden'));
|
||||
break;
|
||||
case 500:
|
||||
|
||||
@@ -26,7 +26,6 @@ class RequestHttp {
|
||||
...config.headers,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...config,
|
||||
};
|
||||
@@ -43,17 +42,12 @@ class RequestHttp {
|
||||
globalStore.setCsrfToken(response.headers['x-csrf-token']);
|
||||
}
|
||||
if (data.code == ResultEnum.OVERDUE || data.code == ResultEnum.FORBIDDEN) {
|
||||
router.replace({
|
||||
path: '/login',
|
||||
router.push({
|
||||
name: 'entrance',
|
||||
params: { code: globalStore.entrance },
|
||||
});
|
||||
return Promise.reject(data);
|
||||
}
|
||||
if (data.code == ResultEnum.UNSAFETY) {
|
||||
router.replace({
|
||||
path: '/login',
|
||||
});
|
||||
return data;
|
||||
}
|
||||
if (data.code == ResultEnum.EXPIRED) {
|
||||
router.push({ name: 'Expired' });
|
||||
return data;
|
||||
|
||||
@@ -2,6 +2,7 @@ export namespace Login {
|
||||
export interface ReqLoginForm {
|
||||
name: string;
|
||||
password: string;
|
||||
ignoreCaptcha: boolean;
|
||||
captcha: string;
|
||||
captchaID: string;
|
||||
authMethod: string;
|
||||
@@ -17,10 +18,6 @@ export namespace Login {
|
||||
token: string;
|
||||
mfaStatus: string;
|
||||
}
|
||||
export interface InitUser {
|
||||
name: string;
|
||||
password: string;
|
||||
}
|
||||
export interface ResCaptcha {
|
||||
imagePath: string;
|
||||
captchaID: string;
|
||||
|
||||
@@ -27,8 +27,11 @@ export namespace Container {
|
||||
restartPolicy: string;
|
||||
}
|
||||
export interface Port {
|
||||
containerPort: number;
|
||||
hostPort: number;
|
||||
host: string;
|
||||
hostIP: string;
|
||||
containerPort: string;
|
||||
hostPort: string;
|
||||
protocol: string;
|
||||
}
|
||||
export interface Volume {
|
||||
sourceDir: string;
|
||||
@@ -42,7 +45,9 @@ export namespace Container {
|
||||
createTime: string;
|
||||
state: string;
|
||||
runTime: string;
|
||||
|
||||
cpuPercent: number;
|
||||
memoryPercent: number;
|
||||
ports: Array<string>;
|
||||
isFromApp: boolean;
|
||||
isFromCompose: boolean;
|
||||
}
|
||||
@@ -203,6 +208,7 @@ export namespace Container {
|
||||
name: string;
|
||||
operation: string;
|
||||
path: string;
|
||||
withFile: boolean;
|
||||
}
|
||||
export interface ComposeUpdate {
|
||||
name: string;
|
||||
|
||||
@@ -54,6 +54,7 @@ export namespace File {
|
||||
isLink?: boolean;
|
||||
isSymlink?: boolean;
|
||||
linkPath?: boolean;
|
||||
sub?: boolean;
|
||||
}
|
||||
|
||||
export interface FileDelete {
|
||||
@@ -90,6 +91,13 @@ export namespace File {
|
||||
newName: string;
|
||||
}
|
||||
|
||||
export interface FileOwner {
|
||||
path: string;
|
||||
user: string;
|
||||
group: string;
|
||||
sub: boolean;
|
||||
}
|
||||
|
||||
export interface FileWget {
|
||||
path: string;
|
||||
name: string;
|
||||
|
||||
@@ -15,6 +15,8 @@ export namespace Setting {
|
||||
language: string;
|
||||
|
||||
serverPort: number;
|
||||
ssl: string;
|
||||
sslType: string;
|
||||
securityEntrance: string;
|
||||
expirationDays: number;
|
||||
expirationTime: string;
|
||||
@@ -34,6 +36,22 @@ export namespace Setting {
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
export interface SSLUpdate {
|
||||
ssl: string;
|
||||
domain: string;
|
||||
sslType: string;
|
||||
cert: string;
|
||||
key: string;
|
||||
sslID: number;
|
||||
}
|
||||
export interface SSLInfo {
|
||||
domain: string;
|
||||
timeout: string;
|
||||
rootPath: string;
|
||||
cert: string;
|
||||
key: string;
|
||||
sslID: number;
|
||||
}
|
||||
export interface PasswordUpdate {
|
||||
oldPassword: string;
|
||||
newPassword: string;
|
||||
|
||||
@@ -307,4 +307,57 @@ export namespace Website {
|
||||
user: string;
|
||||
group: string;
|
||||
}
|
||||
|
||||
export interface ProxyReq {
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface ProxyConfig {
|
||||
id: number;
|
||||
operate: string;
|
||||
enable: boolean;
|
||||
cache: boolean;
|
||||
cacheTime: number;
|
||||
cacheUnit: string;
|
||||
name: string;
|
||||
modifier: string;
|
||||
match: string;
|
||||
proxyPass: string;
|
||||
proxyHost: string;
|
||||
filePath?: string;
|
||||
replaces?: ProxReplace;
|
||||
content?: string;
|
||||
}
|
||||
|
||||
export interface ProxReplace {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export interface ProxyFileUpdate {
|
||||
websiteID: number;
|
||||
name: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface AuthReq {
|
||||
websiteID: number;
|
||||
}
|
||||
|
||||
export interface NginxAuth {
|
||||
username: string;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
export interface AuthConfig {
|
||||
enable: boolean;
|
||||
items: NginxAuth[];
|
||||
}
|
||||
|
||||
export interface NginxAuthConfig {
|
||||
websiteID: number;
|
||||
operate: string;
|
||||
username: string;
|
||||
password: string;
|
||||
remark: string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ export const GetAppInstalled = (search: App.AppInstalledSearch) => {
|
||||
};
|
||||
|
||||
export const InstalledOp = (op: App.AppInstalledOp) => {
|
||||
return http.post<any>('apps/installed/op', op);
|
||||
return http.post<any>('apps/installed/op', op, 40000);
|
||||
};
|
||||
|
||||
export const SyncInstalledApp = () => {
|
||||
|
||||
@@ -25,12 +25,8 @@ export const loginStatus = () => {
|
||||
return http.get<any>('/info');
|
||||
};
|
||||
|
||||
export const checkIsFirst = () => {
|
||||
return http.get<boolean>('/auth/status');
|
||||
};
|
||||
|
||||
export const initUser = (params: Login.InitUser) => {
|
||||
return http.post(`/auth/init`, params);
|
||||
export const checkIsSafety = (code: string) => {
|
||||
return http.get<boolean>(`/auth/issafety?code=${code}`);
|
||||
};
|
||||
|
||||
export const checkIsDemo = () => {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ResPage, SearchWithPage } from '../interface';
|
||||
import { Container } from '../interface/container';
|
||||
|
||||
export const searchContainer = (params: Container.ContainerSearch) => {
|
||||
return http.post<ResPage<Container.ContainerInfo>>(`/containers/search`, params);
|
||||
return http.post<ResPage<Container.ContainerInfo>>(`/containers/search`, params, 400000);
|
||||
};
|
||||
export const createContainer = (params: Container.ContainerCreate) => {
|
||||
return http.post(`/containers`, params, 3000000);
|
||||
|
||||
@@ -67,6 +67,10 @@ export const RenameRile = (params: File.FileRename) => {
|
||||
return http.post<File.File>('files/rename', params);
|
||||
};
|
||||
|
||||
export const ChangeOwner = (params: File.FileOwner) => {
|
||||
return http.post<File.File>('files/owner', params);
|
||||
};
|
||||
|
||||
export const WgetFile = (params: File.FileWget) => {
|
||||
return http.post<File.FileWgetRes>('files/wget', params);
|
||||
};
|
||||
|
||||
@@ -24,6 +24,13 @@ export const updatePort = (param: Setting.PortUpdate) => {
|
||||
return http.post(`/settings/port/update`, param);
|
||||
};
|
||||
|
||||
export const updateSSL = (param: Setting.SSLUpdate) => {
|
||||
return http.post(`/settings/ssl/update`, param);
|
||||
};
|
||||
export const loadSSLInfo = () => {
|
||||
return http.get<Setting.SSLInfo>(`/settings/ssl/info`);
|
||||
};
|
||||
|
||||
export const handleExpired = (param: Setting.PasswordUpdate) => {
|
||||
return http.post(`/settings/expired/handle`, param);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import http from '@/api';
|
||||
import { ReqPage, ResPage } from '../interface';
|
||||
import { Website } from '../interface/Website';
|
||||
import { Website } from '../interface/website';
|
||||
import { File } from '../interface/file';
|
||||
|
||||
export const SearchWebsites = (req: Website.WebSiteSearch) => {
|
||||
@@ -186,3 +186,23 @@ export const UpdateWebsiteDir = (req: Website.DirUpdate) => {
|
||||
export const UpdateWebsiteDirPermission = (req: Website.DirPermissionUpdate) => {
|
||||
return http.post<any>(`/websites/dir/permission`, req);
|
||||
};
|
||||
|
||||
export const GetProxyConfig = (req: Website.ProxyReq) => {
|
||||
return http.post<Website.ProxyConfig[]>(`/websites/proxies`, req);
|
||||
};
|
||||
|
||||
export const OperateProxyConfig = (req: Website.ProxyReq) => {
|
||||
return http.post<any>(`/websites/proxies/update`, req);
|
||||
};
|
||||
|
||||
export const UpdateProxyConfigFile = (req: Website.ProxyFileUpdate) => {
|
||||
return http.post<any>(`/websites/proxies/file`, req);
|
||||
};
|
||||
|
||||
export const GetAuthConfig = (req: Website.AuthReq) => {
|
||||
return http.post<Website.AuthConfig>(`/websites/auths`, req);
|
||||
};
|
||||
|
||||
export const OperateAuthConfig = (req: Website.NginxAuthConfig) => {
|
||||
return http.post<any>(`/websites/auths/update`, req);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<svg width="250" height="250" viewBox="0 0 250 250" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_17_668)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M137.1 72.5238C137.1 49.0386 118.062 30 94.5763 30H79.7382C56.2529 30 37.2144 49.0386 37.2144 72.5238V127.805H137.1V72.5238ZM79.7381 46.2857H94.5762L95.1468 46.2918C109.374 46.5953 120.814 58.2236 120.814 72.5238L120.812 111.517H53.5V72.5238L53.5061 71.9532C53.8096 57.7258 65.4379 46.2857 79.7381 46.2857Z" fill="#B5BEC8"/>
|
||||
<rect x="6" y="98.6719" width="162.314" height="120.695" rx="3" fill="#D6DAE1"/>
|
||||
<path d="M89.3762 198.557C108.914 198.557 124.752 182.719 124.752 163.181C124.752 143.643 108.914 127.805 89.3762 127.805C69.8385 127.805 54 143.643 54 163.181C54 182.719 69.8385 198.557 89.3762 198.557Z" fill="white"/>
|
||||
<path d="M94.3999 170.798C95.4168 170.279 96.3558 169.6 97.1781 168.778C99.241 166.715 100.4 163.917 100.4 161C100.4 158.083 99.241 155.285 97.1781 153.222C95.1152 151.159 92.3173 150 89.3999 150C86.4825 150 83.6846 151.159 81.6217 153.222C79.5588 155.285 78.3999 158.083 78.3999 161C78.3999 163.917 79.5588 166.715 81.6217 168.778C82.444 169.6 83.3831 170.279 84.3999 170.798V178C84.3999 179.105 85.2953 180 86.3999 180H92.3999C93.5045 180 94.3999 179.105 94.3999 178V170.798Z" fill="#B5BEC8"/>
|
||||
<rect x="187.043" y="77.8623" width="12.4857" height="60.3476" rx="3.45" fill="#B5BEC8"/>
|
||||
<path d="M193.286 87C182.243 87 173.291 95.9522 173.291 106.995C173.291 118.038 182.243 126.99 193.286 126.99C204.329 126.99 213.281 118.038 213.281 106.995C213.281 95.9522 204.329 87 193.286 87Z" fill="#B5BEC8"/>
|
||||
<path d="M193.286 97.8574C198.332 97.8574 202.424 101.949 202.424 106.996C202.424 112.042 198.332 116.134 193.286 116.134C188.239 116.134 184.147 112.042 184.147 106.996C184.147 101.949 188.239 97.8574 193.286 97.8574Z" fill="white"/>
|
||||
<path d="M137.552 134.048H215.724C221.72 134.048 226.581 138.909 226.581 144.905V208.51C226.581 214.506 221.72 219.367 215.724 219.367H126.695V219.367V144.905C126.695 138.909 131.556 134.048 137.552 134.048Z" fill="#D6DAE1"/>
|
||||
<rect x="143.343" y="134.048" width="99.8857" height="85.319" rx="3" fill="#E7EBED"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M167.41 179.828C167.41 165.537 178.995 153.952 193.286 153.952C207.577 153.952 219.162 165.537 219.162 179.828C219.162 194.119 207.577 205.705 193.286 205.705C178.995 205.705 167.41 194.119 167.41 179.828ZM217.353 179.828C217.353 166.537 206.578 155.762 193.286 155.762C179.994 155.762 169.219 166.537 169.219 179.828C169.219 193.12 179.994 203.895 193.286 203.895C206.578 203.895 217.353 193.12 217.353 179.828Z" fill="#050B3C"/>
|
||||
<path d="M177.234 162.5L213.003 193.794L211.812 195.156L176.042 163.862L177.234 162.5Z" fill="#2E344D"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_17_668">
|
||||
<rect width="250" height="250" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
@@ -7,7 +7,7 @@
|
||||
<a href="https://fit2cloud.com/" target="_blank">Copyright © 2014-2023 FIT2CLOUD 飞致云</a>
|
||||
</div>
|
||||
<div class="footer-right">
|
||||
<SystemUpgrade />
|
||||
<SystemUpgrade :footer="true" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -80,7 +80,7 @@ const logout = () => {
|
||||
})
|
||||
.then(() => {
|
||||
systemLogOut();
|
||||
router.push({ name: 'login' });
|
||||
router.push({ name: 'entrance', params: { code: globalStore.entrance } });
|
||||
globalStore.setLogStatus(false);
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
})
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
</template>
|
||||
|
||||
<div class="complex-table__body">
|
||||
<fu-table v-bind="$attrs" @selection-change="handleSelectionChange">
|
||||
<fu-table v-bind="$attrs" ref="tableRef" @selection-change="handleSelectionChange">
|
||||
<slot></slot>
|
||||
</fu-table>
|
||||
</div>
|
||||
@@ -52,6 +52,7 @@ defineProps({
|
||||
});
|
||||
const emit = defineEmits(['search', 'update:selects']);
|
||||
const condition = ref({});
|
||||
const tableRef = ref();
|
||||
function search(conditions: any, e: any) {
|
||||
if (conditions) {
|
||||
condition.value = conditions;
|
||||
@@ -62,6 +63,13 @@ function search(conditions: any, e: any) {
|
||||
function handleSelectionChange(row: any) {
|
||||
emit('update:selects', row);
|
||||
}
|
||||
|
||||
function clearSelects() {
|
||||
tableRef.value.refElTable.clearSelection();
|
||||
}
|
||||
defineExpose({
|
||||
clearSelects,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<div class="not-container">
|
||||
<img src="@/assets/images/unsafe.svg" class="not-img" alt="404" />
|
||||
<div class="not-detail">
|
||||
<h2>{{ $t('commons.login.notSafe') }}</h2>
|
||||
<h4>{{ $t('commons.login.safeEntrance1') }}</h4>
|
||||
<div>
|
||||
<h4>{{ $t('commons.login.safeEntrance2') }}</h4>
|
||||
<div style="cursor: pointer; float: left">
|
||||
<el-icon color="#409EFC" style="margin-left: 5px; margin-top: 33px" :size="18" @click="onCopy()">
|
||||
<DocumentCopy />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="404">
|
||||
import i18n from '@/lang';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
|
||||
const onCopy = () => {
|
||||
let input = document.createElement('input');
|
||||
input.value = '1pctl user-info';
|
||||
document.body.appendChild(input);
|
||||
input.select();
|
||||
document.execCommand('Copy');
|
||||
document.body.removeChild(input);
|
||||
MsgSuccess(i18n.global.t('commons.msg.copySuccess'));
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.not-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.not-img {
|
||||
margin-top: 300px;
|
||||
}
|
||||
.not-detail {
|
||||
margin-top: 300px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
h2,
|
||||
h4 {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 60px;
|
||||
color: #434e59;
|
||||
}
|
||||
h4 {
|
||||
margin: 30px 0 20px;
|
||||
float: left;
|
||||
font-size: 19px;
|
||||
font-weight: normal;
|
||||
color: #848587;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,9 +1,24 @@
|
||||
<template>
|
||||
<div>
|
||||
<span class="version">{{ version }}</span>
|
||||
<el-badge is-dot class="item" v-if="version !== 'Waiting' && globalStore.hasNewVersion">
|
||||
<el-button style="margin-top: -8px" type="primary" link @click="onLoadUpgradeInfo">
|
||||
{{ $t('setting.upgradeCheck') }}
|
||||
<span v-if="props.footer">
|
||||
<el-button type="primary" link @click="toForum">
|
||||
<span>{{ $t('setting.forum') }}</span>
|
||||
</el-button>
|
||||
<el-divider direction="vertical" />
|
||||
<el-button type="primary" link @click="toDoc">
|
||||
<span>{{ $t('setting.doc2') }}</span>
|
||||
</el-button>
|
||||
<el-divider direction="vertical" />
|
||||
</span>
|
||||
<span class="version">{{ $t('setting.currentVersion') + version }}</span>
|
||||
<el-badge
|
||||
is-dot
|
||||
class="item"
|
||||
v-if="version !== 'Waiting' && globalStore.hasNewVersion"
|
||||
style="margin-top: -6px"
|
||||
>
|
||||
<el-button type="primary" link @click="onLoadUpgradeInfo">
|
||||
<span style="font-size: 14px">({{ $t('setting.hasNewVersion') }})</span>
|
||||
</el-button>
|
||||
</el-badge>
|
||||
<el-button
|
||||
@@ -13,11 +28,10 @@
|
||||
link
|
||||
@click="onLoadUpgradeInfo"
|
||||
>
|
||||
{{ $t('setting.upgradeCheck') }}
|
||||
({{ $t('setting.upgradeCheck') }})
|
||||
</el-button>
|
||||
<el-tag v-if="version === 'Waiting'" round style="margin-left: 10px">{{ $t('setting.upgrading') }}</el-tag>
|
||||
</div>
|
||||
|
||||
<el-drawer :close-on-click-modal="false" :key="refresh" v-model="drawerVisiable" size="50%" append-to-body>
|
||||
<template #header>
|
||||
<DrawerHeader :header="$t('setting.upgrade')" :back="handleClose" />
|
||||
@@ -50,6 +64,7 @@
|
||||
</el-drawer>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||
import { getSettingInfo, loadReleaseNotes, loadUpgradeInfo, upgrade } from '@/api/modules/setting';
|
||||
import MdEditor from 'md-editor-v3';
|
||||
import i18n from '@/lang';
|
||||
@@ -61,12 +76,17 @@ import { ElMessageBox } from 'element-plus';
|
||||
const globalStore = GlobalStore();
|
||||
|
||||
const version = ref();
|
||||
let loading = ref(false);
|
||||
const loading = ref(false);
|
||||
const drawerVisiable = ref(false);
|
||||
const upgradeInfo = ref();
|
||||
const refresh = ref();
|
||||
|
||||
const upgradeVersion = ref();
|
||||
const props = defineProps({
|
||||
footer: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const search = async () => {
|
||||
const res = await getSettingInfo();
|
||||
@@ -77,6 +97,14 @@ const handleClose = () => {
|
||||
drawerVisiable.value = false;
|
||||
};
|
||||
|
||||
const toDoc = () => {
|
||||
window.open('https://1panel.cn/docs/', '_blank');
|
||||
};
|
||||
|
||||
const toForum = () => {
|
||||
window.open('https://bbs.fit2cloud.com/c/1p/7', '_blank');
|
||||
};
|
||||
|
||||
const onLoadUpgradeInfo = async () => {
|
||||
loading.value = true;
|
||||
await loadUpgradeInfo()
|
||||
|
||||
@@ -47,7 +47,7 @@ const checkName = (rule: any, value: any, callback: any) => {
|
||||
if (value === '' || typeof value === 'undefined' || value == null) {
|
||||
callback(new Error(i18n.global.t('commons.rule.commonName')));
|
||||
} else {
|
||||
const reg = /^[a-zA-Z0-9\u4e00-\u9fa5]{1}[a-zA-Z0-9_.\u4e00-\u9fa5-]{0,30}$/;
|
||||
const reg = /^[a-zA-Z0-9\u4e00-\u9fa5]{1}[a-zA-Z0-9_.\u4e00-\u9fa5-]{1,29}$/;
|
||||
if (!reg.test(value) && value !== '') {
|
||||
callback(new Error(i18n.global.t('commons.rule.commonName')));
|
||||
} else {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user