mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 13:47:15 +08:00
feat: website controller
This commit is contained in:
@@ -1,30 +1,40 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/goravel/framework/facades"
|
||||
"panel/app/models"
|
||||
"panel/packages/helper"
|
||||
|
||||
"panel/app/services"
|
||||
)
|
||||
|
||||
type WebsiteController struct {
|
||||
//Dependent services
|
||||
website services.Website
|
||||
setting services.Setting
|
||||
backup services.Backup
|
||||
}
|
||||
|
||||
func NewWebsiteController() *WebsiteController {
|
||||
return &WebsiteController{
|
||||
//Inject services
|
||||
website: services.NewWebsiteImpl(),
|
||||
setting: services.NewSettingImpl(),
|
||||
backup: services.NewBackupImpl(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *WebsiteController) List(ctx http.Context) {
|
||||
func (c *WebsiteController) List(ctx http.Context) {
|
||||
limit := ctx.Request().QueryInt("limit")
|
||||
page := ctx.Request().QueryInt("page")
|
||||
|
||||
var websites []models.Website
|
||||
var total int64
|
||||
err := facades.Orm().Query().Paginate(page, limit, &websites, &total)
|
||||
total, websites, err := c.website.List(page, limit)
|
||||
if err != nil {
|
||||
facades.Log().Error("[面板][WebsiteController] 查询网站列表失败 ", err)
|
||||
facades.Log().Error("[面板][WebsiteController] 获取网站列表失败 ", err)
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
return
|
||||
}
|
||||
@@ -34,3 +44,562 @@ func (r *WebsiteController) List(ctx http.Context) {
|
||||
"items": websites,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *WebsiteController) Add(ctx http.Context) {
|
||||
validator, err := ctx.Request().Validate(map[string]string{
|
||||
"name": "required|regex:^[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)*$",
|
||||
"domain": "required",
|
||||
"php": "required",
|
||||
"db": "required",
|
||||
"db_type": "required_if:db,1",
|
||||
"db_name": "required_if:db,1",
|
||||
"db_user": "required_if:db,1",
|
||||
"db_password": "required_if:db,1",
|
||||
})
|
||||
if err != nil {
|
||||
Error(ctx, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
return
|
||||
}
|
||||
|
||||
var website services.PanelWebsite
|
||||
err = ctx.Request().Bind(&website)
|
||||
if err != nil {
|
||||
Error(ctx, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
newSite, err := c.website.Add(website)
|
||||
if err != nil {
|
||||
facades.Log().Error("[面板][WebsiteController] 添加网站失败 ", err)
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
return
|
||||
}
|
||||
|
||||
Success(ctx, newSite)
|
||||
}
|
||||
|
||||
func (c *WebsiteController) Delete(ctx http.Context) {
|
||||
validator, err := ctx.Request().Validate(map[string]string{
|
||||
"id": "required|int",
|
||||
})
|
||||
if err != nil {
|
||||
Error(ctx, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
return
|
||||
}
|
||||
|
||||
id := ctx.Request().InputInt("id")
|
||||
err = c.website.Delete(id)
|
||||
if err != nil {
|
||||
facades.Log().Error("[面板][WebsiteController] 删除网站失败 ", err)
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
return
|
||||
}
|
||||
|
||||
Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (c *WebsiteController) GetDefaultConfig(ctx http.Context) {
|
||||
index := helper.ReadFile("/www/server/openresty/html/index.html")
|
||||
stop := helper.ReadFile("/www/server/openresty/html/stop.html")
|
||||
|
||||
Success(ctx, http.Json{
|
||||
"index": index,
|
||||
"stop": stop,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *WebsiteController) SaveDefaultConfig(ctx http.Context) {
|
||||
index := ctx.Request().Input("index")
|
||||
stop := ctx.Request().Input("stop")
|
||||
|
||||
if !helper.WriteFile("/www/server/openresty/html/index.html", index, 0644) {
|
||||
facades.Log().Error("[面板][WebsiteController] 保存默认配置失败")
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
return
|
||||
}
|
||||
|
||||
if !helper.WriteFile("/www/server/openresty/html/stop.html", stop, 0644) {
|
||||
facades.Log().Error("[面板][WebsiteController] 保存默认配置失败")
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
return
|
||||
}
|
||||
|
||||
Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (c *WebsiteController) GetConfig(ctx http.Context) {
|
||||
id := ctx.Request().InputInt("id")
|
||||
if id == 0 {
|
||||
Error(ctx, http.StatusBadRequest, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
config, err := c.website.GetConfig(id)
|
||||
if err != nil {
|
||||
facades.Log().Error("[面板][WebsiteController] 获取网站配置失败 ", err)
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
return
|
||||
}
|
||||
|
||||
Success(ctx, config)
|
||||
}
|
||||
|
||||
func (c *WebsiteController) SaveConfig(ctx http.Context) {
|
||||
validator, err := ctx.Request().Validate(map[string]string{
|
||||
"id": "required|int",
|
||||
})
|
||||
if err != nil {
|
||||
Error(ctx, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
return
|
||||
}
|
||||
|
||||
id := ctx.Request().InputInt("id")
|
||||
var website models.Website
|
||||
if facades.Orm().Query().Where("id", id).FirstOrFail(&website) != nil {
|
||||
Error(ctx, http.StatusBadRequest, "网站不存在")
|
||||
return
|
||||
}
|
||||
|
||||
if !website.Status {
|
||||
Error(ctx, http.StatusBadRequest, "网站已停用,请先启用")
|
||||
return
|
||||
}
|
||||
|
||||
// 原文
|
||||
raw := helper.ReadFile("/www/server/panel/vhost/openresty/" + website.Name + ".conf")
|
||||
if strings.TrimSpace(raw) != strings.TrimSpace(ctx.Request().Input("raw")) {
|
||||
helper.WriteFile("/www/server/panel/vhost/openresty/"+website.Name+".conf", ctx.Request().Input("raw"), 0644)
|
||||
Success(ctx, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 目录
|
||||
path := ctx.Request().Input("path")
|
||||
if !helper.Exists(path) {
|
||||
Error(ctx, http.StatusBadRequest, "网站目录不存在")
|
||||
return
|
||||
}
|
||||
website.Path = path
|
||||
|
||||
// 域名
|
||||
domain := "server_name"
|
||||
domains := strings.Split(ctx.Request().Input("domain"), "\n")
|
||||
if len(domains) == 0 {
|
||||
Error(ctx, http.StatusBadRequest, "域名不能为空")
|
||||
return
|
||||
}
|
||||
for _, v := range domains {
|
||||
if v == "" {
|
||||
continue
|
||||
}
|
||||
domain += " " + v
|
||||
}
|
||||
domain += ";"
|
||||
domainConfigOld := helper.Cut(raw, "# server_name标记位开始", "# server_name标记位结束")
|
||||
if len(strings.TrimSpace(domainConfigOld)) == 0 {
|
||||
Error(ctx, http.StatusBadRequest, "配置文件中缺少server_name标记位")
|
||||
return
|
||||
}
|
||||
raw = strings.Replace(raw, domainConfigOld, "\n "+domain+"\n ", -1)
|
||||
|
||||
// 端口
|
||||
var port strings.Builder
|
||||
ports := strings.Split(ctx.Request().Input("port"), "\n")
|
||||
if len(ports) == 0 {
|
||||
Error(ctx, http.StatusBadRequest, "端口不能为空")
|
||||
return
|
||||
}
|
||||
for i, v := range ports {
|
||||
if _, err := strconv.Atoi(v); err != nil && v != "443 ssl http2" {
|
||||
Error(ctx, http.StatusBadRequest, "端口格式错误")
|
||||
return
|
||||
}
|
||||
if v == "443" && ctx.Request().InputBool("ssl") {
|
||||
v = "443 ssl http2"
|
||||
}
|
||||
if i != len(ports)-1 {
|
||||
port.WriteString(" listen " + v + ";\n")
|
||||
} else {
|
||||
port.WriteString(" listen " + v + ";")
|
||||
}
|
||||
}
|
||||
portConfigOld := helper.Cut(raw, "# port标记位开始", "# port标记位结束")
|
||||
if len(strings.TrimSpace(portConfigOld)) == 0 {
|
||||
Error(ctx, http.StatusBadRequest, "配置文件中缺少port标记位")
|
||||
return
|
||||
}
|
||||
raw = strings.Replace(raw, portConfigOld, "\n"+port.String()+"\n ", -1)
|
||||
|
||||
// 运行目录
|
||||
root := helper.Cut(raw, "# root标记位开始", "# root标记位结束")
|
||||
if len(strings.TrimSpace(root)) == 0 {
|
||||
Error(ctx, http.StatusBadRequest, "配置文件中缺少root标记位")
|
||||
return
|
||||
}
|
||||
match := regexp.MustCompile(`root\s+(.+);`).FindStringSubmatch(root)
|
||||
if len(match) != 2 {
|
||||
Error(ctx, http.StatusBadRequest, "配置文件中root标记位格式错误")
|
||||
return
|
||||
}
|
||||
rootNew := strings.Replace(root, match[1], path, -1)
|
||||
raw = strings.Replace(raw, root, rootNew, -1)
|
||||
|
||||
// 默认文件
|
||||
index := helper.Cut(raw, "# index标记位开始", "# index标记位结束")
|
||||
if len(strings.TrimSpace(index)) == 0 {
|
||||
Error(ctx, http.StatusBadRequest, "配置文件中缺少index标记位")
|
||||
return
|
||||
}
|
||||
match = regexp.MustCompile(`index\s+(.+);`).FindStringSubmatch(index)
|
||||
if len(match) != 2 {
|
||||
Error(ctx, http.StatusBadRequest, "配置文件中index标记位格式错误")
|
||||
return
|
||||
}
|
||||
indexNew := strings.Replace(index, match[1], ctx.Request().Input("index"), -1)
|
||||
raw = strings.Replace(raw, index, indexNew, -1)
|
||||
|
||||
// 防跨站
|
||||
if !strings.HasSuffix(path, "/") {
|
||||
path += "/"
|
||||
}
|
||||
if ctx.Request().InputBool("open_basedir") {
|
||||
helper.WriteFile(path+".user.ini", "open_basedir="+path+":/tmp/", 0644)
|
||||
} else {
|
||||
if helper.Exists(path + ".user.ini") {
|
||||
helper.RemoveFile(path + ".user.ini")
|
||||
}
|
||||
}
|
||||
|
||||
// WAF
|
||||
waf := ctx.Request().Input("waf")
|
||||
wafMode := ctx.Request().Input("waf_mode", "DYNAMIC")
|
||||
wafCcDeny := ctx.Request().Input("waf_cc_deny", "rate=1000r/m duration=60m")
|
||||
wafCache := ctx.Request().Input("waf_cache", "capacity=50")
|
||||
wafConfig := `
|
||||
# waf标记位开始
|
||||
waf ` + waf + `;
|
||||
waf_rule_path /www/server/openresty/ngx_waf/assets/rules/;
|
||||
waf_mode ` + wafMode + `;
|
||||
waf_cc_deny ` + wafCcDeny + `;
|
||||
waf_cache ` + wafCache + `;
|
||||
|
||||
`
|
||||
wafConfigOld := helper.Cut(raw, "# waf标记位开始", "# waf标记位结束")
|
||||
if len(strings.TrimSpace(wafConfigOld)) != 0 {
|
||||
raw = strings.Replace(raw, wafConfigOld, "", -1)
|
||||
}
|
||||
raw = strings.Replace(raw, "# waf标记位开始", wafConfig, -1)
|
||||
|
||||
// SSL
|
||||
ssl := ctx.Request().InputBool("ssl")
|
||||
website.Ssl = ssl
|
||||
if ssl {
|
||||
helper.WriteFile("/www/server/vhost/ssl/"+website.Name+".pem", ctx.Request().Input("ssl_certificate"), 0644)
|
||||
helper.WriteFile("/www/server/vhost/ssl/"+website.Name+".key", ctx.Request().Input("ssl_certificate_key"), 0644)
|
||||
sslConfig := `
|
||||
# ssl标记位开始
|
||||
ssl_certificate /www/server/vhost/ssl/` + website.Name + `.pem;
|
||||
ssl_certificate_key /www/server/vhost/ssl/` + website.Name + `.key;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_tickets off;
|
||||
ssl_dhparam /etc/ssl/certs/dhparam.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
|
||||
`
|
||||
if ctx.Request().InputBool("http_redirect") {
|
||||
sslConfig += `
|
||||
|
||||
# http重定向标记位开始
|
||||
if (\$server_port !~ 443){
|
||||
return 301 https://\$host\$request_uri;
|
||||
}
|
||||
error_page 497 https://\$host\$request_uri;
|
||||
# http重定向标记位结束
|
||||
|
||||
`
|
||||
}
|
||||
if ctx.Request().InputBool("hsts") {
|
||||
sslConfig += `
|
||||
|
||||
# hsts标记位开始
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
# hsts标记位结束
|
||||
|
||||
`
|
||||
}
|
||||
sslConfigOld := helper.Cut(raw, "# ssl标记位开始", "# ssl标记位结束")
|
||||
if len(strings.TrimSpace(sslConfigOld)) != 0 {
|
||||
raw = strings.Replace(raw, sslConfigOld, "", -1)
|
||||
}
|
||||
raw = strings.Replace(raw, "# ssl标记位开始", sslConfig, -1)
|
||||
} else {
|
||||
sslConfigOld := helper.Cut(raw, "# ssl标记位开始", "# ssl标记位结束")
|
||||
if len(strings.TrimSpace(sslConfigOld)) != 0 {
|
||||
raw = strings.Replace(raw, sslConfigOld, "", -1)
|
||||
}
|
||||
}
|
||||
|
||||
if website.Php != ctx.Request().InputInt("php") {
|
||||
website.Php = ctx.Request().InputInt("php")
|
||||
phpConfigOld := helper.Cut(raw, "# php标记位开始", "# php标记位结束")
|
||||
phpConfig := `
|
||||
include enable-php` + strconv.Itoa(website.Php) + `.conf;
|
||||
|
||||
`
|
||||
if len(strings.TrimSpace(phpConfigOld)) != 0 {
|
||||
raw = strings.Replace(raw, phpConfigOld, phpConfig, -1)
|
||||
}
|
||||
}
|
||||
|
||||
err = facades.Orm().Query().Save(&website)
|
||||
if err != nil {
|
||||
facades.Log().Error("[面板][WebsiteController] 保存网站配置失败 ", err)
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
return
|
||||
}
|
||||
|
||||
helper.WriteFile("/www/server/vhost/"+website.Name+".conf", raw, 0644)
|
||||
helper.WriteFile("/www/server/vhost/rewrite/"+website.Name+".conf", ctx.Request().Input("rewrite"), 0644)
|
||||
helper.ExecShell("systemctl reload openresty")
|
||||
|
||||
Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (c *WebsiteController) ClearSiteLpg(ctx http.Context) {
|
||||
id := ctx.Request().InputInt("id")
|
||||
if id == 0 {
|
||||
Error(ctx, http.StatusBadRequest, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
website := models.Website{}
|
||||
err := facades.Orm().Query().Where("id", id).Get(&website)
|
||||
if err != nil {
|
||||
facades.Log().Error("[面板][WebsiteController] 获取网站信息失败 ", err)
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
return
|
||||
}
|
||||
|
||||
helper.RemoveFile("/www/wwwlogs/" + website.Name + ".log")
|
||||
|
||||
Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (c *WebsiteController) UpdateRemark(ctx http.Context) {
|
||||
id := ctx.Request().InputInt("id")
|
||||
if id == 0 {
|
||||
Error(ctx, http.StatusBadRequest, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
website := models.Website{}
|
||||
err := facades.Orm().Query().Where("id", id).Get(&website)
|
||||
if err != nil {
|
||||
facades.Log().Error("[面板][WebsiteController] 获取网站信息失败 ", err)
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
return
|
||||
}
|
||||
|
||||
website.Remark = ctx.Request().Input("remark")
|
||||
err = facades.Orm().Query().Save(&website)
|
||||
if err != nil {
|
||||
facades.Log().Error("[面板][WebsiteController] 保存网站备注失败 ", err)
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
return
|
||||
}
|
||||
|
||||
Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (c *WebsiteController) BackupList(ctx http.Context) {
|
||||
|
||||
backupList, err := c.backup.WebsiteList()
|
||||
if err != nil {
|
||||
facades.Log().Error("[面板][WebsiteController] 获取网站备份列表失败 ", err)
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
return
|
||||
}
|
||||
|
||||
Success(ctx, backupList)
|
||||
}
|
||||
|
||||
func (c *WebsiteController) CreateBackup(ctx http.Context) {
|
||||
id := ctx.Request().InputInt("id")
|
||||
if id == 0 {
|
||||
Error(ctx, http.StatusBadRequest, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
website := models.Website{}
|
||||
err := facades.Orm().Query().Where("id", id).Get(&website)
|
||||
if err != nil {
|
||||
facades.Log().Error("[面板][WebsiteController] 获取网站信息失败 ", err)
|
||||
Error(ctx, http.StatusInternalServerError, "获取网站信息失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = c.backup.WebSiteBackup(website)
|
||||
if err != nil {
|
||||
facades.Log().Error("[面板][WebsiteController] 备份网站失败 ", err)
|
||||
Error(ctx, http.StatusInternalServerError, "备份网站失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (c *WebsiteController) ResetConfig(ctx http.Context) {
|
||||
id := ctx.Request().InputInt("id")
|
||||
if id == 0 {
|
||||
Error(ctx, http.StatusBadRequest, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
website := models.Website{}
|
||||
if err := facades.Orm().Query().Where("id", id).Get(&website); err != nil {
|
||||
facades.Log().Error("[面板][WebsiteController] 获取网站信息失败 ", err)
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
return
|
||||
}
|
||||
|
||||
website.Status = true
|
||||
website.Ssl = false
|
||||
if err := facades.Orm().Query().Save(&website); err != nil {
|
||||
facades.Log().Error("[面板][WebsiteController] 保存网站配置失败 ", err)
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
return
|
||||
}
|
||||
|
||||
raw := fmt.Sprintf(`
|
||||
# 配置文件中的标记位请勿随意修改,改错将导致面板无法识别!
|
||||
# 有自定义配置需求的,请将自定义的配置写在各标记位下方。
|
||||
server
|
||||
{
|
||||
# port标记位开始
|
||||
listen 80;
|
||||
# port标记位结束
|
||||
# server_name标记位开始
|
||||
server_name localhost;
|
||||
# server_name标记位结束
|
||||
# index标记位开始
|
||||
index index.php index.html;
|
||||
# index标记位结束
|
||||
# root标记位开始
|
||||
root %s;
|
||||
# root标记位结束
|
||||
|
||||
# ssl标记位开始
|
||||
# ssl标记位结束
|
||||
|
||||
# php标记位开始
|
||||
include enable-php-%d.conf;
|
||||
# php标记位结束
|
||||
|
||||
# waf标记位开始
|
||||
waf on;
|
||||
waf_rule_path /www/server/nginx/ngx_waf/assets/rules/;
|
||||
waf_mode DYNAMIC;
|
||||
waf_cc_deny rate=1000r/m duration=60m;
|
||||
waf_cache capacity=50;
|
||||
# waf标记位结束
|
||||
|
||||
# 错误页配置,可自行设置
|
||||
#error_page 404 /404.html;
|
||||
#error_page 502 /502.html;
|
||||
|
||||
# 伪静态规则引入,修改后将导致面板设置的伪静态规则失效
|
||||
include /www/server/vhost/rewrite/%s.conf;
|
||||
|
||||
# 面板默认禁止访问部分敏感目录,可自行修改
|
||||
location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn)
|
||||
{
|
||||
return 404;
|
||||
}
|
||||
# 面板默认不记录静态资源的访问日志并开启1小时浏览器缓存,可自行修改
|
||||
location ~ .*\.(js|css)$
|
||||
{
|
||||
expires 1h;
|
||||
error_log /dev/null;
|
||||
access_log /dev/null;
|
||||
}
|
||||
|
||||
access_log /www/wwwlogs/%s.log;
|
||||
error_log /www/wwwlogs/%s.log;
|
||||
}
|
||||
|
||||
`, website.Path, website.Php, website.Name, website.Name, website.Name)
|
||||
|
||||
helper.WriteFile("/www/server/vhost/"+website.Name+".conf", raw, 0644)
|
||||
helper.WriteFile("/www/server/vhost/rewrite"+website.Name+".conf", "", 0644)
|
||||
helper.ExecShell("systemctl reload openresty")
|
||||
|
||||
Success(ctx, nil)
|
||||
}
|
||||
|
||||
func (c *WebsiteController) SetStatus(ctx http.Context) {
|
||||
id := ctx.Request().InputInt("id")
|
||||
if id == 0 {
|
||||
Error(ctx, http.StatusBadRequest, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
website := models.Website{}
|
||||
if err := facades.Orm().Query().Where("id", id).Get(&website); err != nil {
|
||||
facades.Log().Error("[面板][WebsiteController] 获取网站信息失败 ", err)
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
return
|
||||
}
|
||||
|
||||
website.Status = ctx.Request().InputBool("status")
|
||||
if err := facades.Orm().Query().Save(&website); err != nil {
|
||||
facades.Log().Error("[面板][WebsiteController] 保存网站配置失败 ", err)
|
||||
Error(ctx, http.StatusInternalServerError, "系统内部错误")
|
||||
return
|
||||
}
|
||||
|
||||
raw := helper.ReadFile("/www/server/vhost/" + website.Name + ".conf")
|
||||
|
||||
// 运行目录
|
||||
rootConfig := helper.Cut(raw, "# root标记位开始", "# root标记位结束")
|
||||
match := regexp.MustCompile(`root\s+(.+);`).FindStringSubmatch(rootConfig)
|
||||
if len(match) == 2 {
|
||||
if website.Status {
|
||||
root := regexp.MustCompile(`# root\s+(.+);`).FindStringSubmatch(rootConfig)
|
||||
raw = strings.ReplaceAll(raw, rootConfig, "root "+root[1]+";")
|
||||
} else {
|
||||
raw = strings.ReplaceAll(raw, rootConfig, "root /www/server/openresty/html;\n# root "+match[1]+";\n")
|
||||
}
|
||||
}
|
||||
|
||||
// 默认文件
|
||||
indexConfig := helper.Cut(raw, "# index标记位开始", "# index标记位结束")
|
||||
match = regexp.MustCompile(`index\s+(.+);`).FindStringSubmatch(indexConfig)
|
||||
if len(match) == 2 {
|
||||
if website.Status {
|
||||
index := regexp.MustCompile(`# index\s+(.+);`).FindStringSubmatch(indexConfig)
|
||||
raw = strings.ReplaceAll(raw, indexConfig, "index "+index[1]+";")
|
||||
} else {
|
||||
raw = strings.ReplaceAll(raw, indexConfig, "index stop.html;\n# index "+match[1]+";\n")
|
||||
}
|
||||
}
|
||||
|
||||
helper.WriteFile("/www/server/vhost/"+website.Name+".conf", raw, 0644)
|
||||
helper.ExecShell("systemctl reload openresty")
|
||||
|
||||
Success(ctx, nil)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,76 @@
|
||||
// Package services 备份服务
|
||||
package services
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/goravel/framework/support/carbon"
|
||||
|
||||
"panel/app/models"
|
||||
"panel/packages/helper"
|
||||
)
|
||||
|
||||
type Backup interface {
|
||||
BackupWebSite(string) error
|
||||
BackupDatabase(string, string) error
|
||||
WebsiteList() ([]BackupFile, error)
|
||||
WebSiteBackup(website models.Website) error
|
||||
}
|
||||
|
||||
type BackupFile struct {
|
||||
Name string `json:"name"`
|
||||
Size string `json:"size"`
|
||||
}
|
||||
|
||||
type BackupImpl struct {
|
||||
setting Setting
|
||||
}
|
||||
|
||||
func NewBackupImpl() *BackupImpl {
|
||||
return &BackupImpl{
|
||||
setting: NewSettingImpl(),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *BackupImpl) WebsiteList() ([]BackupFile, error) {
|
||||
path := s.setting.Get(models.SettingKeyBackupPath)
|
||||
if len(path) == 0 {
|
||||
return []BackupFile{}, nil
|
||||
}
|
||||
|
||||
path += "/website"
|
||||
|
||||
files, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return []BackupFile{}, err
|
||||
}
|
||||
var backupList []BackupFile
|
||||
for _, file := range files {
|
||||
info, err := file.Info()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
backupList = append(backupList, BackupFile{
|
||||
Name: file.Name(),
|
||||
Size: helper.FormatBytes(float64(info.Size())),
|
||||
})
|
||||
}
|
||||
|
||||
return backupList, nil
|
||||
}
|
||||
|
||||
func (s *BackupImpl) WebSiteBackup(website models.Website) error {
|
||||
backupPath := s.setting.Get(models.SettingKeyBackupPath)
|
||||
if len(backupPath) == 0 {
|
||||
return errors.New("未正确配置备份路径")
|
||||
}
|
||||
|
||||
backupPath += "/website"
|
||||
if !helper.Exists(backupPath) {
|
||||
helper.Mkdir(backupPath, 0644)
|
||||
}
|
||||
|
||||
backupFile := backupPath + "/" + website.Name + carbon.Now().ToShortDateTimeString() + ".zip"
|
||||
helper.ExecShell("cd " + website.Path + " && zip -r " + backupFile + " .")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ package services
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/goravel/framework/facades"
|
||||
@@ -14,7 +15,10 @@ import (
|
||||
)
|
||||
|
||||
type Website interface {
|
||||
List() ([]models.Website, error)
|
||||
List(page int, limit int) (int64, []models.Website, error)
|
||||
Add(website PanelWebsite) (models.Website, error)
|
||||
Delete(id int) error
|
||||
GetConfig(id int) (WebsiteSetting, error)
|
||||
}
|
||||
|
||||
type PanelWebsite struct {
|
||||
@@ -40,6 +44,7 @@ type WebsiteSetting struct {
|
||||
Root string `json:"root"`
|
||||
Path string `json:"path"`
|
||||
Index string `json:"index"`
|
||||
Php int `json:"php"`
|
||||
OpenBasedir bool `json:"open_basedir"`
|
||||
Ssl bool `json:"ssl"`
|
||||
SslCertificate string `json:"ssl_certificate"`
|
||||
@@ -192,7 +197,7 @@ server
|
||||
#error_page 502 /502.html;
|
||||
|
||||
# 伪静态规则引入,修改后将导致面板设置的伪静态规则失效
|
||||
include /www/server/vhost/openresty/rewrite/%s.conf;
|
||||
include /www/server/vhost/rewrite/%s.conf;
|
||||
|
||||
# 面板默认禁止访问部分敏感目录,可自行修改
|
||||
location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn)
|
||||
@@ -226,9 +231,9 @@ server
|
||||
}
|
||||
|
||||
// Delete 删除网站
|
||||
func (r *WebsiteImpl) Delete(name string) error {
|
||||
func (r *WebsiteImpl) Delete(id int) error {
|
||||
var website models.Website
|
||||
if err := facades.Orm().Query().Where("name", name).First(&website); err != nil {
|
||||
if err := facades.Orm().Query().Where("id", id).First(&website); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -248,3 +253,94 @@ func (r *WebsiteImpl) Delete(name string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetConfig 获取网站配置
|
||||
func (r *WebsiteImpl) GetConfig(id int) (WebsiteSetting, error) {
|
||||
var website models.Website
|
||||
if err := facades.Orm().Query().Where("id", id).First(&website); err != nil {
|
||||
return WebsiteSetting{}, err
|
||||
}
|
||||
|
||||
config := helper.ReadFile("/www/server/panel/vhost/openresty/" + website.Name + ".conf")
|
||||
|
||||
var setting WebsiteSetting
|
||||
setting.Name = website.Name
|
||||
setting.Path = website.Path
|
||||
setting.Ssl = website.Ssl
|
||||
setting.Php = website.Php
|
||||
setting.Raw = config
|
||||
|
||||
ports := helper.Cut(config, "# port标记位开始", "# port标记位结束")
|
||||
matches := regexp.MustCompile(`listen\s+(.*);`).FindAllStringSubmatch(ports, -1)
|
||||
for _, match := range matches {
|
||||
if len(match) < 2 {
|
||||
continue
|
||||
}
|
||||
setting.Ports = append(setting.Ports, match[1])
|
||||
}
|
||||
serverName := helper.Cut(config, "# server_name标记位开始", "# server_name标记位结束")
|
||||
match := regexp.MustCompile(`server_name\s+(.*);`).FindStringSubmatch(serverName)
|
||||
if len(match) > 1 {
|
||||
setting.Domains = strings.Split(match[1], " ")
|
||||
}
|
||||
root := helper.Cut(config, "# root标记位开始", "# root标记位结束")
|
||||
match = regexp.MustCompile(`root\s+(.*);`).FindStringSubmatch(root)
|
||||
if len(match) > 1 {
|
||||
setting.Root = match[1]
|
||||
}
|
||||
index := helper.Cut(config, "# index标记位开始", "# index标记位结束")
|
||||
match = regexp.MustCompile(`index\s+(.*);`).FindStringSubmatch(index)
|
||||
if len(match) > 1 {
|
||||
setting.Index = match[1]
|
||||
}
|
||||
|
||||
if helper.Exists(setting.Root + "/.user.ini") {
|
||||
userIni := helper.ReadFile(setting.Path + "/.user.ini")
|
||||
if strings.Contains(userIni, "open_basedir") {
|
||||
setting.OpenBasedir = true
|
||||
} else {
|
||||
setting.OpenBasedir = false
|
||||
}
|
||||
} else {
|
||||
setting.OpenBasedir = false
|
||||
}
|
||||
|
||||
if setting.Ssl {
|
||||
ssl := helper.Cut(config, "# ssl标记位开始", "# ssl标记位结束")
|
||||
match = regexp.MustCompile(`ssl_certificate\s+(.*);`).FindStringSubmatch(ssl)
|
||||
if len(match) > 1 {
|
||||
setting.SslCertificate = helper.ReadFile(match[1])
|
||||
}
|
||||
match = regexp.MustCompile(`ssl_certificate_key\s+(.*);`).FindStringSubmatch(ssl)
|
||||
if len(match) > 1 {
|
||||
setting.SslCertificateKey = helper.ReadFile(match[1])
|
||||
}
|
||||
setting.HttpRedirect = strings.Contains(ssl, "# http重定向标记位")
|
||||
setting.Hsts = strings.Contains(ssl, "# hsts标记位")
|
||||
} else {
|
||||
setting.SslCertificate = ""
|
||||
setting.SslCertificateKey = ""
|
||||
setting.HttpRedirect = false
|
||||
setting.Hsts = false
|
||||
}
|
||||
|
||||
waf := helper.Cut(config, "# waf标记位开始", "# waf标记位结束")
|
||||
setting.Waf = strings.Contains(waf, "waf on;")
|
||||
match = regexp.MustCompile(`waf_mode\s+(.+);`).FindStringSubmatch(waf)
|
||||
if len(match) > 1 {
|
||||
setting.WafMode = match[1]
|
||||
}
|
||||
match = regexp.MustCompile(`waf_cc_deny\s+(.+);`).FindStringSubmatch(waf)
|
||||
if len(match) > 1 {
|
||||
setting.WafCcDeny = match[1]
|
||||
}
|
||||
match = regexp.MustCompile(`waf_cache\s+(.+);`).FindStringSubmatch(waf)
|
||||
if len(match) > 1 {
|
||||
setting.WafCache = match[1]
|
||||
}
|
||||
|
||||
setting.Rewrite = helper.ReadFile("/www/server/panel/vhost/openresty/rewrite/" + website.Name + ".conf")
|
||||
setting.Log = helper.ExecShell("tail -n 100 /www/wwwlogs/" + website.Name + ".log")
|
||||
|
||||
return setting, nil
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ func FormatBytes(size float64) string {
|
||||
}
|
||||
|
||||
// Cut 裁剪字符串
|
||||
func Cut(begin, end, str string) string {
|
||||
func Cut(str, begin, end string) string {
|
||||
bIndex := strings.Index(str, begin)
|
||||
eIndex := strings.Index(str, end)
|
||||
if bIndex == -1 || eIndex == -1 || bIndex > eIndex {
|
||||
|
||||
@@ -60,5 +60,5 @@ func (s *StringHelperTestSuite) TestFormatBytes() {
|
||||
}
|
||||
|
||||
func (s *StringHelperTestSuite) TestCut() {
|
||||
s.Equal("aoZ", Cut("H", "i", "HaoZi"))
|
||||
s.Equal("aoZ", Cut("HaoZi", "H", "i"))
|
||||
}
|
||||
|
||||
@@ -169,9 +169,9 @@ rm -rf src
|
||||
mkdir -p /www/wwwroot/default
|
||||
mkdir -p /www/wwwlogs
|
||||
mkdir -p /www/server/vhost
|
||||
mkdir -p /www/server/vhost/openresty
|
||||
mkdir -p /www/server/vhost/openresty/rewrite
|
||||
mkdir -p /www/server/vhost/openresty/ssl
|
||||
mkdir -p /www/server/vhost
|
||||
mkdir -p /www/server/vhost/rewrite
|
||||
mkdir -p /www/server/vhost/ssl
|
||||
|
||||
# 写入主配置文件
|
||||
cat >${openrestyPath}/conf/nginx.conf <<EOF
|
||||
@@ -264,7 +264,7 @@ http {
|
||||
fastcgi_param SCRIPT_FILENAME \$fastcgi_script_name;
|
||||
}
|
||||
}
|
||||
include /www/server/vhost/openresty/*.conf;
|
||||
include /www/server/vhost/*.conf;
|
||||
}
|
||||
EOF
|
||||
# 写入pathinfo配置文件
|
||||
@@ -318,7 +318,7 @@ chown -R www:www /www/wwwroot
|
||||
chmod -R 644 /www/server/vhost
|
||||
|
||||
# 写入无php配置文件
|
||||
echo "" >${openrestyPath}/conf/enable-php-00.conf
|
||||
echo "" >${openrestyPath}/conf/enable-php-0.conf
|
||||
# 写入代理默认配置文件
|
||||
cat >${openrestyPath}/conf/proxy.conf <<EOF
|
||||
proxy_temp_path ${openrestyPath}/proxy_temp_dir;
|
||||
|
||||
@@ -57,7 +57,7 @@ if [ -d "/www/server/php/82" ]; then
|
||||
fi
|
||||
|
||||
# 写入 phpMyAdmin 配置文件
|
||||
cat >/www/server/vhost/openresty/phpmyadmin.conf <<EOF
|
||||
cat >/www/server/vhost/phpmyadmin.conf <<EOF
|
||||
# 配置文件中的标记位请勿随意修改,改错将导致面板无法识别!
|
||||
# 有自定义配置需求的,请将自定义的配置写在各标记位下方。
|
||||
server
|
||||
@@ -100,8 +100,8 @@ server
|
||||
}
|
||||
EOF
|
||||
# 设置文件权限
|
||||
chown -R root:root /www/server/vhost/openresty/phpmyadmin.conf
|
||||
chmod -R 644 /www/server/vhost/openresty/phpmyadmin.conf
|
||||
chown -R root:root /www/server/vhost/phpmyadmin.conf
|
||||
chmod -R 644 /www/server/vhost/phpmyadmin.conf
|
||||
|
||||
# 放行端口
|
||||
firewall-cmd --permanent --zone=public --add-port=888/tcp >/dev/null 2>&1
|
||||
|
||||
@@ -22,7 +22,7 @@ setupPath="/www"
|
||||
phpmyadminPath="${setupPath}/wwwroot/phpmyadmin"
|
||||
|
||||
|
||||
rm -rf /www/server/vhost/openresty/phpmyadmin.conf
|
||||
rm -rf /www/server/vhost/phpmyadmin.conf
|
||||
rm -rf ${phpmyadminPath}
|
||||
panel deletePlugin phpmyadmin
|
||||
systemctl reload openresty
|
||||
|
||||
Reference in New Issue
Block a user