2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 17:17:13 +08:00

feat: openresty plugin

This commit is contained in:
耗子
2023-06-24 22:27:41 +08:00
parent 1c0fb1d298
commit 8b262cf289
50 changed files with 1604 additions and 1686 deletions

View File

@@ -4,7 +4,6 @@
[![Test](https://github.com/HaoZi-Team/Panel/actions/workflows/test.yml/badge.svg)](https://github.com/HaoZi-Team/Panel/actions)
[![Report Card](https://goreportcard.com/badge/github.com/HaoZi-Team/Panel)](https://goreportcard.com/report/github.com/HaoZi-Team/Panel)
[![Codecov](https://codecov.io/gh/HaoZi-Team/Panel/branch/main/graph/badge.svg?token=XFT5NGNSRG)](https://codecov.io/gh/HaoZi-Team/Panel)
[![Doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/HaoZi-Team/Panel)
![License](https://img.shields.io/github/license/HaoZi-Team/Panel)
这是耗子Linux面板的开源仓库基于Apache License 2.0协议进行开源,目前处于积极开发状态。
@@ -21,7 +20,7 @@
低配机器建议使用 `Debian`,资源占用较 `RHEL` 系更低。其他机器建议使用 `RockyLinux` | `AlmaLinux`,维护周期更长。
不在下表中的其他 `RHEL` 系统,可自行尝试安装,但不保证能够正常运行,且不提供技术支持。
不在下表中的其他同版本的 `RHEL` 系统OpenCloudOS、Anolis、CentOS Stream等,可自行尝试安装,但不保证能够正常运行,且不提供技术支持。
| 系统 | 版本 |
|------------|----|

View File

@@ -11,7 +11,7 @@ func Success(ctx http.Context, data any) {
}
func Error(ctx http.Context, code int, message any) {
ctx.Response().Json(code, http.Json{
ctx.Response().Json(http.StatusOK, http.Json{
"code": code,
"message": message,
})

View File

@@ -79,3 +79,32 @@ func (r *InfoController) SystemInfo(ctx http.Context) {
"panel_version": facades.Config().GetString("panel.version"),
})
}
func (r *InfoController) InstalledDbAndPhp(ctx http.Context) {
var php []models.Plugin
err := facades.Orm().Query().Where("slug like ?", "php%").Find(&php)
if err != nil {
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
}
var mysql models.Plugin
mysqlInstalled := true
err = facades.Orm().Query().Where("slug like ?", "mysql%").FirstOrFail(&mysql)
if err != nil {
mysqlInstalled = false
}
var postgresql models.Plugin
postgresqlInstalled := true
err = facades.Orm().Query().Where("slug like ?", "postgresql%").FirstOrFail(&postgresql)
if err != nil {
postgresqlInstalled = false
}
Success(ctx, http.Json{
"php": php,
"mysql": mysqlInstalled,
"postgresql": postgresqlInstalled,
})
}

View File

@@ -0,0 +1,36 @@
package controllers
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"panel/app/models"
)
type WebsiteController struct {
//Dependent services
}
func NewWebsiteController() *WebsiteController {
return &WebsiteController{
//Inject services
}
}
func (r *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)
if err != nil {
facades.Log().Error("[面板][WebsiteController] 查询网站列表失败 ", err)
Error(ctx, http.StatusInternalServerError, "系统内部错误")
return
}
Success(ctx, http.Json{
"total": total,
"items": websites,
})
}

View File

@@ -3,9 +3,10 @@ package services
import (
"testing"
"github.com/goravel/framework/testing/mock"
"github.com/stretchr/testify/suite"
"github.com/goravel/framework/testing/mock"
"panel/app/models"
)

21
go.mod
View File

@@ -8,7 +8,9 @@ require (
github.com/gookit/color v1.5.3
github.com/goravel/framework v1.12.3
github.com/iancoleman/strcase v0.2.0
github.com/imroc/req/v3 v3.37.1
github.com/mojocn/base64Captcha v1.3.5
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/spf13/cast v1.5.1
github.com/stretchr/testify v1.8.4
)
@@ -29,6 +31,7 @@ require (
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae // indirect
github.com/RichardKnop/machinery/v2 v2.0.11 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/aws/aws-sdk-go v1.37.16 // indirect
github.com/bytedance/sonic v1.9.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
@@ -45,6 +48,7 @@ require (
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gaukas/godicttls v0.0.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.9.1 // indirect
github.com/glebarez/go-sqlite v1.21.1 // indirect
@@ -57,6 +61,7 @@ require (
github.com/go-redsync/redsync/v4 v4.0.4 // indirect
github.com/go-sql-driver/mysql v1.7.1 // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
@@ -66,10 +71,12 @@ require (
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/gomodule/redigo v2.0.0+incompatible // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect
github.com/google/s2a-go v0.1.3 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.3.0 // indirect
@@ -107,6 +114,7 @@ require (
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/onsi/ginkgo/v2 v2.11.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/opencontainers/runc v1.1.5 // indirect
@@ -116,11 +124,15 @@ require (
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-19 v0.3.2 // indirect
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
github.com/quic-go/quic-go v0.36.0 // indirect
github.com/refraction-networking/utls v1.3.2 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
@@ -149,14 +161,15 @@ require (
go.uber.org/atomic v1.11.0 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.10.0 // indirect
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
golang.org/x/image v0.0.0-20190802002840-cff245a6509b // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/mod v0.11.0 // indirect
golang.org/x/net v0.11.0 // indirect
golang.org/x/oauth2 v0.7.0 // indirect
golang.org/x/sync v0.2.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.9.0 // indirect
golang.org/x/text v0.10.0 // indirect
golang.org/x/tools v0.9.1 // indirect
golang.org/x/tools v0.10.0 // indirect
google.golang.org/api v0.122.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230525234025-438c736192d0 // indirect

49
go.sum
View File

@@ -77,6 +77,8 @@ github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae h1:DcFpTQBYQ9C
github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae/go.mod h1:rJJ84PyA/Wlmw1hO+xTzV2wsSUon6J5ktg0g8BF2PuU=
github.com/RichardKnop/machinery/v2 v2.0.11 h1:BTfLGOmOju3W/OtlZmLX26OjYNZsU4PJo04pQReycdc=
github.com/RichardKnop/machinery/v2 v2.0.11/go.mod h1:b5Q6cT/w7YLlIl4Vi+jpdEoyYiqhTgx+0USoKb1wzqU=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48=
github.com/aws/aws-sdk-go v1.37.16 h1:Q4YOP2s00NpB9wfmTDZArdcLRuG9ijbnoAwTW3ivleI=
@@ -153,6 +155,8 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gaukas/godicttls v0.0.3 h1:YNDIf0d9adcxOijiLrEzpfZGAkNwLRzPaG6OjU7EITk=
github.com/gaukas/godicttls v0.0.3/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
github.com/gertd/go-pluralize v0.2.1 h1:M3uASbVjMnTsPb0PNqg+E/24Vwigyo/tvyMTtAlLgiA=
github.com/gertd/go-pluralize v0.2.1/go.mod h1:rbYaKDbsXxmRfr8uygAEKhOWsjyrrqrkHVpZvoOp8zk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@@ -172,6 +176,7 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
@@ -202,6 +207,8 @@ github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrt
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
@@ -259,6 +266,8 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -312,7 +321,8 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 h1:hR7/MlvK23p6+lIw9SN1TigNLn9ZnF3W4SYRKq2gAHs=
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.3 h1:FAgZmpLl/SXurPEZyCMPBIiiYeTbqfjlbdnCNTAkbGE=
github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
@@ -372,6 +382,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
github.com/imroc/req/v3 v3.37.1 h1:HUs5/jazZWTlTGMs3PCV15vqQq/ha9fY1NV+RYACrxI=
github.com/imroc/req/v3 v3.37.1/go.mod h1:DECzjVIrj6jcUr5n6e+z0ygmCO93rx4Jy0RjOEe1YCI=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
@@ -474,12 +486,14 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108
github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48=
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
@@ -507,6 +521,16 @@ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qR
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U=
github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E=
github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
github.com/quic-go/quic-go v0.36.0 h1:JIrO7p7Ug6hssFcARjWDiqS2RAKJHCiwPxBAA989rbI=
github.com/quic-go/quic-go v0.36.0/go.mod h1:zPetvwDlILVxt15n3hr3Gf/I3mDf7LpLKPhR4Ez0AZQ=
github.com/refraction-networking/utls v1.3.2 h1:o+AkWB57mkcoW36ET7uJ002CpBWHu0KPxi6vzxvPnv8=
github.com/refraction-networking/utls v1.3.2/go.mod h1:fmoaOww2bxzzEpIKOebIsnBvjQpqP7L2vcm/9KUfm/E=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
@@ -618,6 +642,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
@@ -677,7 +702,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 h1:pVgRXcIictcr+lBQIFeiwuwtDIs4eL21OuM9nyAADmo=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
@@ -703,10 +729,11 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -743,6 +770,7 @@ golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
@@ -774,10 +802,11 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -832,6 +861,7 @@ golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -932,10 +962,11 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg=
golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -1,7 +1,19 @@
package controllers
import (
"os"
"os/exec"
"regexp"
"strings"
"time"
"github.com/imroc/req/v3"
"github.com/spf13/cast"
"github.com/goravel/framework/contracts/http"
"panel/app/http/controllers"
"panel/packages/helpers"
)
type OpenRestyController struct {
@@ -14,8 +26,282 @@ func NewOpenrestyController() *OpenRestyController {
}
}
func (r *OpenRestyController) Show(ctx http.Context) {
ctx.Response().Success().Json(http.Json{
"Hello": "Goravel",
})
// Status 获取运行状态
func (r *OpenRestyController) Status(ctx http.Context) {
cmd := exec.Command("bash", "-c", "systemctl status openresty | grep Active | grep -v grep | awk '{print $2}'")
out, err := cmd.CombinedOutput()
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
status := strings.TrimSpace(string(out))
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty状态失败")
return
}
if status == "active" {
controllers.Success(ctx, true)
} else {
controllers.Success(ctx, false)
}
}
// Reload 重载配置
func (r *OpenRestyController) Reload(ctx http.Context) {
cmd := exec.Command("bash", "-c", "systemctl reload openresty")
out, err := cmd.CombinedOutput()
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, err.Error())
return
}
cmd = exec.Command("bash", "-c", "systemctl status openresty | grep Active | grep -v grep | awk '{print $2}'")
out, err = cmd.CombinedOutput()
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, err.Error())
return
}
status := strings.TrimSpace(string(out))
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty状态失败")
return
}
if status == "active" {
controllers.Success(ctx, true)
} else {
controllers.Error(ctx, 1, "重载OpenResty失败: "+string(out))
}
}
// Start 启动OpenResty
func (r *OpenRestyController) Start(ctx http.Context) {
cmd := exec.Command("bash", "-c", "systemctl start openresty")
out, err := cmd.CombinedOutput()
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, err.Error())
return
}
cmd = exec.Command("bash", "-c", "systemctl status openresty | grep Active | grep -v grep | awk '{print $2}'")
out, err = cmd.CombinedOutput()
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, err.Error())
return
}
status := strings.TrimSpace(string(out))
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty状态失败")
return
}
if status == "active" {
controllers.Success(ctx, true)
} else {
controllers.Error(ctx, 1, "启动OpenResty失败: "+string(out))
}
}
// Stop 停止OpenResty
func (r *OpenRestyController) Stop(ctx http.Context) {
cmd := exec.Command("bash", "-c", "systemctl stop openresty")
out, err := cmd.CombinedOutput()
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, err.Error())
return
}
cmd = exec.Command("bash", "-c", "systemctl status openresty | grep Active | grep -v grep | awk '{print $2}'")
out, err = cmd.CombinedOutput()
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, err.Error())
return
}
status := strings.TrimSpace(string(out))
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty状态失败")
return
}
if status == "active" {
controllers.Success(ctx, true)
} else {
controllers.Error(ctx, 1, "停止OpenResty失败: "+string(out))
}
}
// Restart 重启OpenResty
func (r *OpenRestyController) Restart(ctx http.Context) {
cmd := exec.Command("bash", "-c", "systemctl restart openresty")
out, err := cmd.CombinedOutput()
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, err.Error())
return
}
cmd = exec.Command("bash", "-c", "systemctl status openresty | grep Active | grep -v grep | awk '{print $2}'")
out, err = cmd.CombinedOutput()
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, err.Error())
return
}
status := strings.TrimSpace(string(out))
if len(status) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty状态失败")
return
}
if status == "active" {
controllers.Success(ctx, true)
} else {
controllers.Error(ctx, 1, "重启OpenResty失败: "+string(out))
}
}
// GetConfig 获取配置
func (r *OpenRestyController) GetConfig(ctx http.Context) {
config, err := os.ReadFile("/www/server/openresty/conf/nginx.conf")
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty配置失败")
return
}
controllers.Success(ctx, string(config))
}
// SaveConfig 保存配置
func (r *OpenRestyController) SaveConfig(ctx http.Context) {
config := ctx.Request().Input("config")
if len(config) == 0 {
controllers.Error(ctx, http.StatusInternalServerError, "配置不能为空")
return
}
oldConfig, err := os.ReadFile("/www/server/openresty/conf/nginx.conf")
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty配置失败")
return
}
err = os.WriteFile("/www/server/openresty/conf/nginx.conf", []byte(config), 0644)
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, "保存OpenResty配置失败")
return
}
cmd := exec.Command("bash", "-c", "openresty -t 2>&1")
out, err := cmd.CombinedOutput()
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, "检查OpenResty配置失败")
return
}
if !strings.Contains(string(out), "test is successful") {
err = os.WriteFile("/www/server/openresty/conf/nginx.conf", oldConfig, 0644)
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, "保存OpenResty配置失败")
return
}
controllers.Error(ctx, http.StatusForbidden, "OpenResty配置有误请修正后再保存: "+string(out))
}
cmd = exec.Command("bash", "-c", "systemctl reload openresty")
_, err = cmd.CombinedOutput()
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, "重载OpenResty配置失败")
return
}
controllers.Success(ctx, true)
}
// ErrorLog 获取错误日志
func (r *OpenRestyController) ErrorLog(ctx http.Context) {
cmd := exec.Command("bash", "-c", "tail -n 100 /www/wwwlogs/nginx_error.log")
out, err := cmd.CombinedOutput()
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty错误日志失败")
return
}
controllers.Success(ctx, string(out))
}
// ClearErrorLog 清空错误日志
func (r *OpenRestyController) ClearErrorLog(ctx http.Context) {
cmd := exec.Command("bash", "-c", "echo '' > /www/wwwlogs/nginx_error.log")
_, err := cmd.CombinedOutput()
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, "清空OpenResty错误日志失败")
return
}
controllers.Success(ctx, true)
}
// Load 获取负载
func (r *OpenRestyController) Load(ctx http.Context) {
client := req.C().SetTimeout(10 * time.Second)
resp, err := client.R().Get("http://127.0.0.1/nginx_status")
if err != nil || !resp.IsSuccessState() {
controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty负载失败")
return
}
raw := resp.String()
var data map[int]map[string]any
cmd := exec.Command("bash", "-c", "ps aux | grep nginx | grep 'worker process' | wc -l")
out, err := cmd.CombinedOutput()
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty负载失败")
return
}
workers := strings.TrimSpace(string(out))
data[0]["name"] = "工作进程"
data[0]["value"] = workers
cmd = exec.Command("bash", "-c", "ps aux | grep nginx | grep 'worker process' | awk '{memsum+=$6};END {print memsum}'")
out, err = cmd.CombinedOutput()
if err != nil {
controllers.Error(ctx, http.StatusInternalServerError, "获取OpenResty负载失败")
return
}
mem := helpers.FormatBytes(cast.ToFloat64(strings.TrimSpace(string(out))))
data[1]["name"] = "内存占用"
data[1]["value"] = mem
match := regexp.MustCompile(`Active connections:\s+(\d+)`).FindStringSubmatch(raw)
if len(match) == 2 {
data[2]["name"] = "活跃连接数"
data[2]["value"] = match[1]
}
match = regexp.MustCompile(`server accepts handled requests\s+(\d+)\s+(\d+)\s+(\d+)`).FindStringSubmatch(raw)
if len(match) == 4 {
data[3]["name"] = "总连接次数"
data[3]["value"] = match[1]
data[4]["name"] = "总握手次数"
data[4]["value"] = match[2]
data[5]["name"] = "总请求次数"
data[5]["value"] = match[3]
}
match = regexp.MustCompile(`Reading:\s+(\d+)\s+Writing:\s+(\d+)\s+Waiting:\s+(\d+)`).FindStringSubmatch(raw)
if len(match) == 4 {
data[6]["name"] = "请求数"
data[6]["value"] = match[1]
data[7]["name"] = "响应数"
data[7]["value"] = match[2]
data[8]["name"] = "驻留进程"
data[8]["value"] = match[3]
}
controllers.Success(ctx, data)
}

View File

@@ -6,6 +6,8 @@ const (
Description = "OpenResty® 是一款基于 NGINX 和 LuaJIT 的 Web 平台。"
Slug = "openresty"
Version = "1.21.4.1"
Requires = ""
Excludes = ""
)
func Boot() {

View File

@@ -0,0 +1,250 @@
<!--
Name: Openresty管理器
Author: 耗子
Date: 2023-06-24
-->
<title>OpenResty</title>
<div class="layui-fluid" id="component-tabs">
<div class="layui-row">
<div class="layui-col-md12">
<div class="layui-card">
<div class="layui-card-header">OpenResty管理</div>
<div class="layui-card-body">
<div class="layui-tab">
<ul class="layui-tab-title">
<li class="layui-this">运行状态</li>
<li>配置修改</li>
<li>负载状态</li>
<li>错误日志</li>
</ul>
<div class="layui-tab-content">
<div class="layui-tab-item layui-show">
<blockquote id="openresty-status" class="layui-elem-quote layui-quote-nm">当前状态:<span
class="layui-badge layui-bg-black">获取中</span></blockquote>
<div class="layui-btn-container" style="padding-top: 30px;">
<button id="openresty-start" class="layui-btn">启动</button>
<button id="openresty-stop" class="layui-btn layui-btn-danger">停止</button>
<button id="openresty-restart" class="layui-btn layui-btn-warm">重启</button>
<button id="openresty-reload" class="layui-btn layui-btn-normal">重载</button>
</div>
</div>
<div class="layui-tab-item">
<blockquote class="layui-elem-quote">此处修改的是OpenResty主配置文件如果你不了解各参数的含义请不要随意修改<br>
提示Ctrl+F 搜索关键字Ctrl+S 保存Ctrl+H 查找替换!
</blockquote>
<div id="openresty-config-editor"
style="height: 600px;"></div>
<div class="layui-btn-container" style="padding-top: 30px;">
<button id="openresty-config-save" class="layui-btn">保存</button>
</div>
</div>
<div class="layui-tab-item">
<table class="layui-hide" id="openresty-load-status"></table>
</div>
<div class="layui-tab-item">
<div class="layui-btn-container">
<button id="openresty-clean-error-log" class="layui-btn">清空日志</button>
</div>
<pre id="openresty-error-log" class="layui-code">
获取中...
</pre>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
let openresty_config_editor
layui.use(['index', 'code', 'table'], function () {
let $ = layui.$
, admin = layui.admin
, element = layui.element
, code = layui.code
, table = layui.table
admin.req({
url: '/api/plugins/openresty/status'
, method: 'get'
, success: function (result) {
if (result.code !== 0) {
console.log('耗子Linux面板OpenResty运行状态获取失败接口返回' + result)
return false
}
if (result.data) {
$('#openresty-status').html('当前状态:<span class="layui-badge layui-bg-green">运行中</span>')
} else {
$('#openresty-status').html('当前状态:<span class="layui-badge layui-bg-red">已停止</span>')
}
}
})
// 获取openresty错误日志并渲染
admin.req({
url: '/api/plugins/openresty/errorLog'
, method: 'get'
, success: function (result) {
if (result.code !== 0) {
console.log('耗子Linux面板OpenResty错误日志获取失败接口返回' + result)
$('#openresty-error-log').text('OpenResty错误日志获取失败请刷新重试')
code({
elem: '#openresty-error-log'
, title: 'error.log'
, encode: true
, about: false
})
return false
}
$('#openresty-error-log').text(result.data)
code({
elem: '#openresty-error-log'
, title: 'error.log'
, encode: true
, about: false
})
}
})
// 获取openresty配置并渲染
admin.req({
url: '/api/plugins/openresty/config'
, method: 'get'
, success: function (result) {
if (result.code !== 0) {
console.log('耗子Linux面板OpenResty主配置获取失败接口返回' + result)
return false
}
$('#openresty-config-editor').text(result.data)
openresty_config_editor = ace.edit('openresty-config-editor', {
mode: 'ace/mode/nginx',
selectionStyle: 'text'
})
}
})
// 获取openresty负载状态并渲染
table.render({
elem: '#openresty-load-status'
, url: '/api/plugins/openresty/load'
, cols: [[
{ field: 'name', width: '80%', title: '属性', }
, { field: 'value', width: '20%', title: '当前值' }
]]
})
element.render()
// 事件监听
$('#openresty-start').click(function () {
admin.popup({
title: '<span style="color: red;">警告</span>'
,
shade: 0
,
anim: -1
,
area: ['300px', '200px']
,
id: 'layadmin-layer-skin-openresty-start'
,
skin: 'layui-anim layui-anim-upbit'
,
content: '面板的正常访问依赖OpenResty因此不支持在面板启动OpenResty如您确需操作请在SSH执行<span class="layui-badge-rim">systemctl start nginx</span>以启动OpenResty'
})
})
$('#openresty-stop').click(function () {
admin.popup({
title: '<span style="color: red;">警告</span>'
,
shade: 0
,
anim: -1
,
area: ['300px', '200px']
,
id: 'layadmin-layer-skin-openresty-stop'
,
skin: 'layui-anim layui-anim-upbit'
,
content: '面板的正常访问依赖OpenResty因此不支持在面板停止OpenResty如您确需操作请在SSH执行<span class="layui-badge-rim">systemctl stop nginx</span>以停止OpenResty'
})
})
$('#openresty-restart').click(function () {
layer.confirm('重启OpenResty有可能导致面板短时间无法访问是否继续重启', {
btn: ['重启', '取消']
}, function () {
index = layer.msg('正在重启OpenResty...', { icon: 16, time: 0 })
admin.req({
url: '/api/plugins/openresty/restart'
, method: 'post'
, beforeSend: function () {
layer.msg('已发送重启请求,请稍后刷新确认重启状态。')
}
, success: function (result) {
layer.close(index)
if (result.code !== 0) {
console.log('耗子Linux面板OpenResty重启失败接口返回' + result)
return false
}
admin.events.refresh()
layer.alert('OpenResty重启成功')
}
})
})
})
$('#openresty-reload').click(function () {
index = layer.msg('正在重载OpenResty...', { icon: 16, time: 0 })
admin.req({
url: '/api/plugins/openresty/reload'
, method: 'post'
, success: function (result) {
layer.close(index)
if (result.code !== 0) {
console.log('耗子Linux面板OpenResty重载失败接口返回' + result)
return false
}
layer.alert('OpenResty重载成功')
}
})
})
$('#openresty-config-save').click(function () {
index = layer.msg('正在保存OpenResty主配置...', { icon: 16, time: 0 })
admin.req({
url: '/api/plugins/openresty/config'
, method: 'post'
, data: {
config: openresty_config_editor.getValue()
}
, success: function (result) {
layer.close(index)
if (result.code !== 0) {
console.log('耗子Linux面板OpenResty配置保存失败接口返回' + result)
return false
}
layer.alert('OpenResty配置保存成功')
}
})
})
$('#openresty-clean-error-log').click(function () {
index = layer.msg('正在清空OpenResty错误日志...', { icon: 16, time: 0 })
admin.req({
url: '/api/plugins/openresty/cleanErrorLog'
, method: 'post'
, success: function (result) {
layer.close(index)
if (result.code !== 0) {
console.log('耗子Linux面板OpenResty错误日志清空失败接口返回' + result)
return false
}
admin.events.refresh()
layer.msg('OpenResty错误日志已清空')
}
})
})
})
</script>

View File

@@ -1,22 +1,27 @@
package openresty
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/route"
"github.com/goravel/framework/facades"
"panel/app/http/middleware"
"panel/plugins/openresty/http/controllers"
)
func Route() {
facades.Route().Prefix("api/plugins/openresty").Group(func(route route.Route) {
route.Get("/openresty", func(ctx http.Context) {
ctx.Response().Json(http.StatusOK, http.Json{
"Hello": "Openresty",
})
})
facades.Route().Prefix("api/plugins/openresty").Middleware(middleware.Jwt()).Group(func(route route.Route) {
openRestyController := controllers.NewOpenrestyController()
route.Get("/openresty/users/{id}", openRestyController.Show)
route.Get("status", openRestyController.Status)
route.Post("reload", openRestyController.Reload)
route.Post("start", openRestyController.Start)
route.Post("stop", openRestyController.Stop)
route.Post("restart", openRestyController.Restart)
route.Get("load", openRestyController.Load)
route.Get("config", openRestyController.GetConfig)
route.Post("config", openRestyController.SaveConfig)
route.Get("errorLog", openRestyController.ErrorLog)
route.Get("cleanErrorLog", openRestyController.ClearErrorLog)
})
facades.Route().StaticFile("panel/views/plugins/openresty.html", "plugins/openresty/public/views/openresty.html")
}

View File

@@ -41,7 +41,7 @@
<script src="https://cdnjs.cdn.haozi.net/echarts/5.4.2/echarts.min.js"></script>
<script>
layui.config({
base: 'res/', // 静态资源所在路径
base: 'panel/', // 静态资源所在路径
version: new Date().getTime()
}).use(['index', 'setter'], function () {
var admin = layui.admin

View File

@@ -417,7 +417,7 @@ layui.define('view', function (exports) {
admin.popupRight({
id: 'LAY_adminPopupTheme'
, success: function () {
view(this.id).render('system/theme')
view(this.id).render('ui/theme')
}
})
}

View File

@@ -154,7 +154,7 @@ layui.define('admin', function (exports) {
if (admin.pageType === 'console') { //后台主体页
renderPage()
} else { //初始控制台结构
container.render('layout').done(function () {
container.render('ui/layout').done(function () {
renderPage()
layui.element.render()

View File

@@ -201,9 +201,9 @@ layui.define(['laytpl', 'layer'], function (exports) {
}
if (e.status === 404) {
that.render('template/tips/404')
that.render('ui/404')
} else {
that.render('template/tips/error')
that.render('ui/error')
}
that.render.isError = true

View File

Before

Width:  |  Height:  |  Size: 832 B

After

Width:  |  Height:  |  Size: 832 B

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 455 B

After

Width:  |  Height:  |  Size: 455 B

View File

Before

Width:  |  Height:  |  Size: 457 B

After

Width:  |  Height:  |  Size: 457 B

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@@ -0,0 +1,43 @@
<!-- 主题设置模板 -->
<script type="text/html" template lay-done="layui.data.theme();">
{{#
var local = layui.data(layui.setter.tableName)
,theme = local.theme || {}
,themeColorIndex = parseInt((theme && theme.color) ? theme.color.index : 0) || 0;
}}
<div class="layui-card-header">
配色方案
</div>
<div class="layui-card-body layadmin-setTheme">
<ul class="layadmin-setTheme-color">
{{# layui.each(layui.setter.theme.color, function(index, item){ }}
<li layadmin-event="setTheme" data-index="{{ index }}" data-alias="{{ item.alias }}"
{{ index=== themeColorIndex ?
'class="layui-this"' : '' }} title="{{ item.alias }}">
<div class="layadmin-setTheme-header" style="background-color: {{ item.header }};"></div>
<div class="layadmin-setTheme-side" style="background-color: {{ item.main }};">
<div class="layadmin-setTheme-logo" style="background-color: {{ item.logo }};"></div>
</div>
</li>
{{# }); }}
</ul>
</div>
</script>
<script>
layui.data.theme = function () {
layui.use('form', function () {
var form = layui.form
, admin = layui.admin
//隐藏开关
form.on('switch(system-theme-sideicon)', function () {
admin.theme({
hideSideIcon: this.checked
})
})
})
}
</script>

View File

@@ -0,0 +1,166 @@
<!--
Name: 网站 - 添加
Author: 耗子
Date: 2023-06-24
-->
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
<form class="layui-form" action="" lay-filter="add-website-form">
<div class="layui-form-item">
<label class="layui-form-label">网站名</label>
<div class="layui-input-block">
<input type="text" name="name" lay-verify="required" placeholder="请输入网站名(英文,设置后不可修改)"
autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">域名</label>
<div class="layui-input-block">
<textarea name="domain" lay-verify="required"
placeholder="请输入域名一行一个支持泛域名格式yourdomain.com:88 端口不填则默认80端口"
class="layui-textarea"></textarea>
</div>
</div>
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">PHP版本</label>
<div class="layui-input-block">
<select name="php" lay-filter="add-website-php">
<option value="00" selected="">不使用</option>
{{# layui.each(d.params.php, function(index, item){ }}
<option value="{{ item }}">{{ item }}</option>
{{# }); }}
</select>
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">数据库</label>
<div class="layui-input-block">
<select name="db_type" lay-filter="add-website-db">
<option value="false" selected="">不使用</option>
{{# if(d.params.mysql){ }}
<option value="mysql">MySQL</option>
{{# } }}
{{# if(d.params.postgresql){ }}
<option value="postgresql">PostgreSQL</option>
{{# } }}
</select>
</div>
</div>
</div>
<div id="add-website-db-info" class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">数据库名</label>
<div class="layui-input-inline">
<input type="text" name="db_name" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">数据库用户</label>
<div class="layui-input-inline">
<input type="text" name="db_username" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">数据库密码</label>
<div class="layui-input-inline">
<input id="add-website-db-password" type="text" name="db_password" autocomplete="off"
class="layui-input">
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">目录</label>
<div class="layui-input-block">
<input type="text" name="path"
placeholder="请输入网站根目录(不填默认为/www/wwwroot/网站名)"
autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">备注</label>
<div class="layui-input-block">
<textarea name="note" placeholder="请输入备注内容,可以为空。" class="layui-textarea"></textarea>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<div class="layui-footer">
<button class="layui-btn" lay-submit="" lay-filter="add-website-submit">立即提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
</div>
</form>
</script>
<script>
layui.data.sendParams = function (params) {
layui.use(['admin', 'form', 'laydate'], function () {
var $ = layui.$
, admin = layui.admin
, layer = layui.layer
, table = layui.table
, form = layui.form
$('#add-website-db-info').hide()
form.render()
$('#add-website-db-password').hover(function () {
layer.tips('必须8位以上大小写数字特殊符号混合', '#add-website-db-password', {
tips: 1,
time: 0
})
}, function () {
layer.closeAll('tips')
})
form.on('select(add-website-db)', function (data) {
if (data.value === '') {
$('#add-website-db-info').hide()
return false
}
if (data.value === 'mysql') {
$('#add-website-db-info').show()
$('input[name="db_name"]').val($('input[name="name"]').val() + '_mysql')
$('input[name="db_username"]').val($('input[name="name"]').val() + '_mysql')
} else if (data.value === 'postgresql') {
$('#add-website-db-info').show()
$('input[name="db_name"]').val($('input[name="name"]').val() + '_postgresql')
$('input[name="db_username"]').val($('input[name="name"]').val() + '_postgresql')
}
})
// 提交
form.on('submit(add-website-submit)', function (data) {
// 判断db_type是否为空
if (data.field.db_type === '') {
data.field.db = 0
} else {
data.field.db = 1
}
admin.req({
url: '/api/panel/website/add'
, method: 'post'
, data: data.field
, success: function (result) {
if (result.code !== 0) {
console.log('耗子Linux面板新网站添加失败接口返回' + result)
layer.msg('新网站添加失败!')
return false
}
table.reload('website-list')
layer.alert('新网站添加成功!', {
icon: 1
, title: '提示'
, btn: ['确定']
, yes: function (index) {
layer.closeAll()
//location.reload();
}
})
}
})
return false
})
})
}
</script>

View File

@@ -0,0 +1,82 @@
<!--
Name: 网站 - 全局设置
Author: 耗子
Date: 2023-06-24
-->
<script type="text/html" template lay-url="/api/panel/website/getDefaultSettings"
lay-done="layui.data.sendParams(d.params)">
<div class="layui-tab">
<ul class="layui-tab-title">
<li class="layui-this">默认页</li>
<li>停止页</li>
</ul>
<div class="layui-tab-content">
<div class="layui-tab-item layui-show">
<!-- 默认页 -->
<blockquote class="layui-elem-quote layui-quote-nm">
设置站点未找到时的提示页面
</blockquote>
<div id="index-editor" style="height: 400px;">{{ d.data.index }}</div>
</div>
<div class="layui-tab-item">
<!-- 停止页 -->
<blockquote class="layui-elem-quote layui-quote-nm">
设置站点停止时的提示页面设置后需重新开关网站方可生效
</blockquote>
<div id="stop-editor" style="height: 400px;">{{ d.data.stop }}</div>
</div>
</div>
</div>
<div class="layui-footer">
<button id="save-website-default-settings" class="layui-btn">保存设置</button>
</div>
</script>
<script>
let indexEditor = '';
let stopEditor = '';
layui.data.sendParams = function (params) {
layui.use(['admin', 'form', 'laydate', 'code'], function () {
var $ = layui.$
, admin = layui.admin
, element = layui.element
, layer = layui.layer
, laydate = layui.laydate
, code = layui.code;
indexEditor = ace.edit("index-editor", {
mode: "ace/mode/html",
selectionStyle: "text"
});
stopEditor = ace.edit("stop-editor", {
mode: "ace/mode/html",
selectionStyle: "text"
});
$('#save-website-default-settings').click(function () {
layer.load();
admin.req({
url: '/api/panel/website/saveDefaultSettings'
, type: 'post'
, data: {
index: indexEditor.getValue(),
stop: stopEditor.getValue()
}
, success: function (res) {
layer.closeAll('loading');
if (res.code === 0) {
layer.msg('保存成功', {icon: 1});
setTimeout(function () {
admin.render();
}, 1000);
} else {
layer.msg(res.msg, {icon: 2});
}
}
, error: function (xhr, status, error) {
console.log('耗子Linux面板ajax请求出错错误' + error);
}
});
});
});
};
</script>

View File

@@ -0,0 +1,379 @@
<!--
Name: 网站 - 编辑
Author: 耗子
Date: 2023-06-24
-->
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
<div class="layui-tab" lay-filter="website-edit-tab">
<ul class="layui-tab-title">
<li class="layui-this">域名端口</li>
<li>基本设置</li>
<li>防火墙</li>
<li>SSL</li>
<li>伪静态</li>
<li>配置原文</li>
<li>访问日志</li>
</ul>
<div class="layui-tab-content">
<div class="layui-tab-item layui-show">
<!-- 域名绑定 -->
<div class="layui-form layui-form-pane">
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">域名</label>
<div class="layui-input-block">
<textarea name="domain" lay-verify="required"
placeholder="请输入域名,一行一个支持泛域名"
class="layui-textarea">{{ d.params.config.domain }}</textarea>
</div>
</div>
<hr>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">端口</label>
<div class="layui-input-block">
<textarea name="port" lay-verify="required"
placeholder="请输入访问端口,一行一个"
class="layui-textarea">{{ d.params.config.port }}</textarea>
</div>
</div>
</div>
</div>
<div class="layui-tab-item">
<!-- 基本设置 -->
<div class="layui-form layui-form-pane">
<div class="layui-form-item">
<label class="layui-form-label">网站目录</label>
<div class="layui-input-block">
<input type="text" name="path" autocomplete="off" placeholder="请输入网站目录"
class="layui-input" value="{{ d.params.config.path }}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">运行目录</label>
<div class="layui-input-block">
<input type="text" name="root" autocomplete="off"
placeholder="请输入网站运行目录Laravel等程序需要"
class="layui-input" value="{{ d.params.config.root }}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">默认文档</label>
<div class="layui-input-block">
<input type="text" name="index" autocomplete="off" placeholder="请输入默认文档,以空格隔开"
class="layui-input" value="{{ d.params.config.index }}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">PHP版本</label>
<div class="layui-input-block">
<select name="php" lay-filter="website-php">
{{# layui.each(d.params.php_version, function(index, item){ }}
{{# if(item == d.params.config.php){ }}
<option value="{{ item }}" selected="">{{ item }}</option>
{{# }else{ }}
<option value="{{ item }}">{{ item }}</option>
{{# } }}
{{# }); }}
</select>
</div>
</div>
<div class="layui-form-item" pane="">
<label class="layui-form-label">防跨站攻击</label>
<div class="layui-input-block">
<input type="checkbox" name="open_basedir" lay-skin="switch" lay-text="ON|OFF"
{{ d.params.config.open_basedir== 1 ? 'checked' : '' }}>
</div>
</div>
</div>
</div>
<div class="layui-tab-item">
<!-- 防火墙 -->
<blockquote class="layui-elem-quote layui-quote-nm">
面板自带开源的 ngx_waf 防火墙<br>文档参考<a
href="https://docs.addesp.com/ngx_waf/zh-cn/advance/directive.html"
target="_blank">https://docs.addesp.com/ngx_waf/zh-cn/advance/directive.html</a>
</blockquote>
<div class="layui-form layui-form-pane">
<div class="layui-form-item" pane="">
<label class="layui-form-label">总开关</label>
<div class="layui-input-inline">
<input type="checkbox" name="waf" lay-skin="switch" lay-text="ON|OFF"
{{ d.params.config.waf== 1 ? 'checked' : '' }}>
</div>
<div class="layui-form-mid layui-word-aux">只有打开了总开关下面的设置才会生效</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">模式</label>
<div class="layui-input-block">
<input type="text" name="waf_mode" autocomplete="off" placeholder="DYNAMIC"
class="layui-input" value="{{ d.params.config.waf_mode }}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">CC</label>
<div class="layui-input-block">
<input type="text" name="waf_cc_deny" autocomplete="off"
placeholder="rate=1000r/m duration=60m"
class="layui-input" value="{{ d.params.config.waf_cc_deny }}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">缓存</label>
<div class="layui-input-block">
<input type="text" name="waf_cache" autocomplete="off" placeholder="capacity=50"
class="layui-input" value="{{ d.params.config.waf_cache }}">
</div>
</div>
</div>
</div>
<div class="layui-tab-item">
<!-- SSL -->
<div class="layui-form layui-form-pane">
<div class="layui-form-item" pane="">
<label class="layui-form-label">总开关</label>
<div class="layui-input-inline">
<input type="checkbox" name="ssl" lay-skin="switch" lay-text="ON|OFF"
{{ d.params.config.ssl== 1 ? 'checked' : '' }}>
</div>
<div class="layui-form-mid layui-word-aux">只有打开了总开关下面的设置才会生效</div>
</div>
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">HTTP跳转</label>
<div class="layui-input-block">
<input type="checkbox" name="http_redirect" lay-skin="switch" lay-text="ON|OFF"
{{ d.params.config.http_redirect== 1 ? 'checked' : '' }}>
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">HSTS</label>
<div class="layui-input-inline">
<input type="checkbox" name="hsts" lay-skin="switch" lay-text="ON|OFF"
{{ d.params.config.hsts== 1 ? 'checked' : '' }}>
</div>
</div>
<div class="layui-inline">
<div class="layui-input-inline">
<button id="issue-ssl" class="layui-btn layui-btn-sm">签发免费SSL证书</button>
</div>
</div>
</div>
<div class="layui-form-item layui-form-text">
{{# if(d.params.config.ssl == 1){ }}
<label class="layui-form-label">证书 <span style="color: red; float: right;">剩余有效期{{ d.params.config.ssl_date }}</span></label>
{{# }else{ }}
<label class="layui-form-label">证书</label>
{{# } }}
<div class="layui-input-block">
<textarea name="ssl_certificate" placeholder="请输入pem证书文件的内容"
class="layui-textarea">{{ d.params.config.ssl_certificate }}</textarea>
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">私钥</label>
<div class="layui-input-block">
<textarea name="ssl_certificate_key" placeholder="请输入key私钥文件的内容"
class="layui-textarea">{{ d.params.config.ssl_certificate_key }}</textarea>
</div>
</div>
</div>
</div>
<div class="layui-tab-item">
<!-- 伪静态 -->
<blockquote class="layui-elem-quote layui-quote-nm">
设置伪静态规则填入 <code>location</code>
</blockquote>
<div id="rewrite-editor" style="height: 400px;">{{ d.params.config.rewrite }}</div>
</div>
<div class="layui-tab-item">
<!-- 配置原文 -->
<blockquote class="layui-elem-quote layui-quote-nm">
如果您不了解配置规则请勿随意修改否则可能会导致网站无法访问或面板功能异常如果已经遇到问题可尝试
<button id="site-config-restore" class="layui-btn layui-btn-xs">重置配置</button>
<br>
如果你修改了原文那么点击保存后其余的修改将不会生效
</blockquote>
<div id="config-editor" style="height: 400px;">{{ d.params.config.config_raw }}</div>
</div>
<div class="layui-tab-item">
<!-- 访问日志 -->
<button id="clean-site-log" class="layui-btn">清空日志</button>
<pre id="website-log" class="layui-code" lay-options="{about: '{{ d.params.config.name }}.log'}">{{ d.params.config.log }}</pre>
</div>
</div>
</div>
<div class="layui-footer">
<button id="save-site-config" class="layui-btn">保存设置</button>
</div>
</script>
<script>
let rewriteEditor = ''
let configEditor = ''
layui.data.sendParams = function (params) {
layui.use(['admin', 'form', 'laydate', 'code'], function () {
var $ = layui.$
, admin = layui.admin
, layer = layui.layer
, code = layui.code
, form = layui.form
, element = layui.element
form.render()
element.render()
element.on('tab(website-edit-tab)', function (data) {
if (data.index === 6) {
// 隐藏保存按钮
$('.layui-footer').hide()
} else {
$('.layui-footer').show()
}
})
rewriteEditor = ace.edit('rewrite-editor', {
mode: 'ace/mode/nginx',
selectionStyle: 'text'
})
configEditor = ace.edit('config-editor', {
mode: 'ace/mode/nginx',
selectionStyle: 'text'
})
code({
elem: '#website-log'
, encode: true
, about: false
})
$('#clean-site-log').click(function () {
layer.confirm('确定要清空日志吗?', function (index) {
layer.close(index)
layer.load()
admin.req({
url: '/api/panel/website/clearSiteLog'
, type: 'post'
, data: { name: params.config.name }
, success: function (res) {
layer.closeAll('loading')
if (res.code === 0) {
layer.msg('已清空', { icon: 1 })
setTimeout(function () {
admin.render()
}, 1000)
} else {
layer.msg(res.msg, { icon: 2 })
}
}
})
})
})
$('#save-site-config').click(function () {
layer.load()
var port = $('textarea[name="port"]').val()
var reg = new RegExp(/\n443.*\n?/)
// 如果开启了https就自动添加443端口
if ($('input[name="ssl"]').prop('checked') && !reg.test(port)) {
port = port + '\n443'
}
// 如果关闭了https就自动删除443端口
if (!$('input[name="ssl"]').prop('checked') && reg.test(port)) {
// 正则替换
port = port.replace(/443.*\n?/, '')
}
admin.req({
url: '/api/panel/website/saveSiteSettings'
, type: 'post'
, data: {
name: params.config.name,
config: {
domain: $('textarea[name="domain"]').val(),
port: port,
ssl: $('input[name="ssl"]').prop('checked') ? 1 : 0,
http_redirect: $('input[name="http_redirect"]').prop('checked') ? 1 : 0,
hsts: $('input[name="hsts"]').prop('checked') ? 1 : 0,
ssl_certificate: $('textarea[name="ssl_certificate"]').val(),
ssl_certificate_key: $('textarea[name="ssl_certificate_key"]').val(),
path: $('input[name="path"]').val(),
root: $('input[name="root"]').val(),
index: $('input[name="index"]').val(),
php: $('select[name="php"]').val(),
open_basedir: $('input[name="open_basedir"]').prop('checked') ? 1 : 0,
waf: $('input[name="waf"]').prop('checked') ? 1 : 0,
waf_mode: $('input[name="waf_mode"]').val(),
waf_cc_deny: $('input[name="waf_cc_deny"]').val(),
waf_cache: $('input[name="waf_cache"]').val(),
rewrite: rewriteEditor.getValue(),
config_raw: configEditor.getValue()
}
}
, success: function (res) {
layer.closeAll('loading')
if (res.code === 0) {
layer.msg('保存成功', { icon: 1 })
setTimeout(function () {
admin.render()
}, 1000)
} else {
layer.msg(res.msg, { icon: 2 })
}
}
})
})
// 重置配置
$('#site-config-restore').click(function () {
layer.confirm('高风险操作,网站配置重置后所有配置均需重新设置,确定要重置配置吗?', function (index) {
index = layer.msg('重置网站配置', {
icon: 16
, time: 0
})
admin.req({
url: '/api/panel/website/resetSiteConfig'
, type: 'post'
, data: { name: params.config.name }
, success: function (res) {
layer.close(index)
if (res.code === 0) {
layer.alert('重置成功,你需要重新添加域名/端口绑定,设置各配置参数!', function (index) {
admin.render()
layer.close(index)
})
} else {
layer.msg(res.msg, { icon: 2 })
}
}
})
})
})
// 监听签发证书按钮
$('#issue-ssl').click(function () {
layer.confirm('确定要申请签发免费SSL证书吗', function (index) {
index = layer.msg('正在签发证书,可能需要较长时间,请勿操作...', {
icon: 16
, time: 0
})
admin.req({
url: '/api/panel/website/issueSsl'
, type: 'post'
, data: {
name: params.config.name
, type: 'lets'
}
, success: function (res) {
layer.close(index)
if (res.code === 0) {
layer.msg('签发成功', { icon: 1 })
setTimeout(function () {
admin.render()
}, 1000)
} else {
layer.alert(res.msg, { icon: 2 })
}
}
})
})
})
})
}
</script>

View File

@@ -0,0 +1,241 @@
<!--
Name: 网站 - 列表
Author: 耗子
Date: 2023-06-24
-->
<title>网站</title>
<div class="layui-fluid">
<div class="layui-row layui-col-space15">
<div class="layui-col-md12">
<div class="layui-card">
<div class="layui-card-header">网站列表</div>
<div class="layui-card-body">
<table class="layui-hide" id="website-list" lay-filter="website-list"></table>
<!-- 顶部工具栏 -->
<script type="text/html" id="website-list-bar">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm" lay-event="website_add">添加网站</button>
<button class="layui-btn layui-btn-sm" lay-event="website_default_settings">全局设置
</button>
</div>
</script>
<!-- 右侧网站设置和删除网站 -->
<script type="text/html" id="website-control">
<a class="layui-btn layui-btn-warm layui-btn-xs" lay-event="backup">备份</a>
<a class="layui-btn layui-btn-xs" lay-event="edit">设置</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
</script>
<!-- 网站运行状态开关 -->
<script type="text/html" id="website-run">
<input type="checkbox" name="run" lay-skin="switch" lay-text="ON|OFF"
lay-filter="website-run-checkbox"
value="{{ d.status }}" data-website-name="{{ d.name }}"
{{ d.status== 1 ? 'checked' : '' }}>
</script>
<!-- 网站SSL状态 -->
<script type="text/html" id="website-ssl">
{{ d.ssl == 1 ? '已开启' : '未开启' }}
</script>
</div>
</div>
</div>
</div>
</div>
<script>
let php, mysql, postgresql
layui.use(['admin', 'table', 'form', 'view'], function () {
var admin = layui.admin
, table = layui.table
, form = layui.form
, view = layui.view
// 获取已安装的PHP和DB版本
admin.req({
url: '/api/panel/info/installedDbAndPhp'
, method: 'get'
, success: function (result) {
php = result.data.php
mysql = result.data.mysql
postgresql = result.data.postgresql
}
})
table.render({
elem: '#website-list'
, url: '/api/panel/website/list'
, toolbar: '#website-list-bar'
, title: '网站列表'
, cols: [[
{ field: 'name', title: '网站名', width: 150, unresize: true, sort: true }
, { field: 'run', title: '运行', width: 100, templet: '#website-run', unresize: true }
, { field: 'path', title: '目录', width: 250 }
, { field: 'php', title: 'PHP', width: 60 }
, { field: 'ssl', title: 'SSL', width: 110, templet: '#website-ssl' }
, { field: 'note', title: '备注', edit: 'textarea' }
, { fixed: 'right', title: '操作', unresize: true, toolbar: '#website-control', width: 180 }
]]
, parseData: function (res) {
return {
'code': res.code,
'msg': res.message,
'count': res.data.total,
'data': res.data.items
}
}
, page: true
})
// 头工具栏事件
table.on('toolbar(website-list)', function (obj) {
if (obj.event === 'website_add') {
admin.popup({
title: '添加网站'
, area: ['80%', '60%']
, id: 'LAY-popup-website-add'
, success: function (layer, index) {
view(this.id).render('website/add', {
php: php,
mysql: mysql,
postgresql: postgresql
}).done(function () {
form.render(null, 'LAY-popup-website-add')
})
}
})
} else if (obj.event === 'website_default_settings') {
admin.popup({
title: '全局设置'
, area: ['80%', '80%']
, id: 'LAY-popup-website-add'
, success: function (layer, index) {
view(this.id).render('website/default_settings', {}).done(function () {
form.render(null, 'LAY-popup-website-default-settings')
})
}
})
}
})
// 行工具事件
table.on('tool(website-list)', function (obj) {
let data = obj.data
if (obj.event === 'del') {
layer.confirm('删除网站将一并删除站点目录(不包括数据库),是否继续?', function (index) {
admin.req({
url: '/api/panel/website/delete'
, method: 'post'
, data: data
, success: function (result) {
if (result.code !== 0) {
console.log('耗子Linux面板网站删除失败接口返回' + result)
layer.msg('网站删除失败,请刷新重试!')
return false
}
obj.del()
layer.alert('网站' + data.name + '删除成功!')
}
})
layer.close(index)
})
} else if (obj.event === 'edit') {
let config
admin.req({
url: '/api/panel/website/getSiteSettings?name=' + data.name
, method: 'get'
, beforeSend: function (request) {
layer.load()
}
, success: function (result) {
if (result.code !== 0) {
console.log('耗子Linux面板网站设置获取失败接口返回' + result)
layer.alert('网站设置获取失败!')
return false
}
config = result.data
layer.closeAll('loading')
// 打开编辑网站页面
admin.popup({
title: '编辑网站 - ' + data.name
, area: ['80%', '80%']
, id: 'LAY-popup-website-edit'
, success: function (layero, index) {
view(this.id).render('website/edit', {
php: php,
mysql: mysql,
postgresql: postgresql,
data: data,
config: config
}).done(function () {
form.render(null, 'LAY-popup-website-edit')
})
}
})
}
})
} else if (obj.event === 'backup') {
// 打开备份页面
admin.popup({
title: '备份管理 - ' + data.name
, area: ['70%', '80%']
, id: 'LAY-popup-website-backup'
, success: function (layero, index) {
view(this.id).render('website/backup', {
data: data
}).done(function () {
form.render(null, 'LAY-popup-website-backup')
})
}
})
}
})
// 网站备注编辑
table.on('edit(website-list)', function (obj) {
let value = obj.value // 得到修改后的值
, data = obj.data // 得到行数据
admin.req({
url: '/api/panel/website/updateSiteNote'
, method: 'post'
, data: {
name: data.name,
note: value
}
, success: function (result) {
if (result.code !== 0) {
console.log('耗子Linux面板网站备注更新失败接口返回' + result)
layer.msg('网站备注更新失败,请刷新重试!')
return false
}
layer.alert('网站 ' + data.name + ' 备注更新成功!')
}
})
})
// 网站运行状态操作
form.on('switch(website-run-checkbox)', function (obj) {
let $ = layui.$
let website_name = $(this).data('website-name')
let status = obj.elem.checked ? 1 : 0
admin.req({
url: '/api/panel/website/setSiteStatus'
, method: 'post'
, data: {
name: website_name,
status: status
}
, success: function (result) {
if (result.code !== 0) {
console.log('耗子Linux面板网站运行状态设置失败接口返回' + result)
layer.msg('网站运行状态设置失败,请刷新重试!')
return false
}
layer.alert('网站 ' + website_name + ' 运行状态设置成功!')
}
})
})
})
</script>

View File

@@ -1,368 +0,0 @@
<title>通讯录</title>
<script type="text/html" template>
<link rel="stylesheet" href="{{ layui.setter.paths.base }}style/template.css?v={{ layui.admin.v }}" media="all">
</script>
<div class="layui-card layadmin-header">
<div class="layui-breadcrumb" lay-filter="breadcrumb">
<a lay-href="">主页</a>
<a><cite>页面</cite></a>
<a><cite>通讯录</cite></a>
</div>
</div>
<div class="layui-fluid layadmin-maillist-fluid">
<div class="layui-row layui-col-space15">
<div class="layui-col-md4 layui-col-sm6">
<div class="layadmin-contact-box">
<div class="layui-col-md4 layui-col-sm6">
<a href="javascript:;">
<div class="layadmin-text-center">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/character.jpg">
</script>
<div class="layadmin-maillist-img layadmin-font-blod">演员</div>
</div>
</a>
</div>
<div class="layui-col-md8 layadmin-padding-left20 layui-col-sm6">
<a href="javascript:;">
<h3 class="layadmin-title">
<strong>张三</strong>
</h3>
<p class="layadmin-textimg">
<i class="layui-icon layui-icon-location"></i>
上海
</p>
</a>
<div class="layadmin-address">
<a href="javascript:;">
<strong>Zhang san</strong>
<br>
E-mail:xxx@baidu.com
<br>
Weibo: https://weibo.com/123
<br>
<addr title="phone">Tel:</addr>
(123) 456-7890
</a>
</div>
</div>
</div>
</div>
<div class="layui-col-md4 layui-col-sm6">
<div class="layadmin-contact-box">
<div class="layui-col-md4 layui-col-sm6">
<a href="javascript:;">
<div class="layadmin-text-center">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/character.jpg">
</script>
<div class="layadmin-maillist-img layadmin-font-blod">演员</div>
</div>
</a>
</div>
<div class="layui-col-md8 layadmin-padding-left20 layui-col-sm6">
<a href="javascript:;">
<h3 class="layadmin-title">
<strong>张三</strong>
</h3>
<p class="layadmin-textimg">
<i class="layui-icon layui-icon-location"></i>
上海
</p>
</a>
<div class="layadmin-address">
<a href="javascript:;">
<strong>Zhang san</strong>
<br>
E-mail:xxx@baidu.com
<br>
Weibo: https://weibo.com/123
<br>
<addr title="phone">Tel:</addr>
(123) 456-7890
</a>
</div>
</div>
</div>
</div>
<div class="layui-col-md4 layui-col-sm6">
<div class="layadmin-contact-box">
<div class="layui-col-md4 layui-col-sm6">
<a href="javascript:;">
<div class="layadmin-text-center">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/character.jpg">
</script>
<div class="layadmin-maillist-img layadmin-font-blod">演员</div>
</div>
</a>
</div>
<div class="layui-col-md8 layadmin-padding-left20 layui-col-sm6">
<a href="javascript:;">
<h3 class="layadmin-title">
<strong>张三</strong>
</h3>
<p class="layadmin-textimg">
<i class="layui-icon layui-icon-location"></i>
上海
</p>
</a>
<div class="layadmin-address">
<a href="javascript:;">
<strong>Zhang san</strong>
<br>
E-mail:xxx@baidu.com
<br>
Weibo: https://weibo.com/123
<br>
<addr title="phone">Tel:</addr>
(123) 456-7890
</a>
</div>
</div>
</div>
</div>
<div class="layui-col-md4 layui-col-sm6">
<div class="layadmin-contact-box">
<div class="layui-col-md4 layui-col-sm6">
<a href="javascript:;">
<div class="layadmin-text-center">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/character.jpg">
</script>
<div class="layadmin-maillist-img layadmin-font-blod">演员</div>
</div>
</a>
</div>
<div class="layui-col-md8 layadmin-padding-left20 layui-col-sm6">
<a href="javascript:;">
<h3 class="layadmin-title">
<strong>张三</strong>
</h3>
<p class="layadmin-textimg">
<i class="layui-icon layui-icon-location"></i>
上海
</p>
</a>
<div class="layadmin-address">
<a href="javascript:;">
<strong>Zhang san</strong>
<br>
E-mail:xxx@baidu.com
<br>
Weibo: https://weibo.com/123
<br>
<addr title="phone">Tel:</addr>
(123) 456-7890
</a>
</div>
</div>
</div>
</div>
<div class="layui-col-md4 layui-col-sm6">
<div class="layadmin-contact-box">
<div class="layui-col-md4 layui-col-sm6">
<a href="javascript:;">
<div class="layadmin-text-center">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/character.jpg">
</script>
<div class="layadmin-maillist-img layadmin-font-blod">演员</div>
</div>
</a>
</div>
<div class="layui-col-md8 layadmin-padding-left20 layui-col-sm6">
<a href="javascript:;">
<h3 class="layadmin-title">
<strong>张三</strong>
</h3>
<p class="layadmin-textimg">
<i class="layui-icon layui-icon-location"></i>
上海
</p>
</a>
<div class="layadmin-address">
<a href="javascript:;">
<strong>Zhang san</strong>
<br>
E-mail:xxx@baidu.com
<br>
Weibo: https://weibo.com/123
<br>
<addr title="phone">Tel:</addr>
(123) 456-7890
</a>
</div>
</div>
</div>
</div>
<div class="layui-col-md4 layui-col-sm6">
<div class="layadmin-contact-box">
<div class="layui-col-md4 layui-col-sm6">
<a href="javascript:;">
<div class="layadmin-text-center">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/character.jpg">
</script>
<div class="layadmin-maillist-img layadmin-font-blod">演员</div>
</div>
</a>
</div>
<div class="layui-col-md8 layadmin-padding-left20 layui-col-sm6">
<a href="javascript:;">
<h3 class="layadmin-title">
<strong>张三</strong>
</h3>
<p class="layadmin-textimg">
<i class="layui-icon layui-icon-location"></i>
上海
</p>
</a>
<div class="layadmin-address">
<a href="javascript:;">
<strong>Zhang san</strong>
<br>
E-mail:xxx@baidu.com
<br>
Weibo: https://weibo.com/123
<br>
<addr title="phone">Tel:</addr>
(123) 456-7890
</a>
</div>
</div>
</div>
</div>
<div class="layui-col-md4 layui-col-sm6">
<div class="layadmin-contact-box">
<div class="layui-col-md4 layui-col-sm6">
<a href="javascript:;">
<div class="layadmin-text-center">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/character.jpg">
</script>
<div class="layadmin-maillist-img layadmin-font-blod">演员</div>
</div>
</a>
</div>
<div class="layui-col-md8 layadmin-padding-left20 layui-col-sm6">
<a href="javascript:;">
<h3 class="layadmin-title">
<strong>张三</strong>
</h3>
<p class="layadmin-textimg">
<i class="layui-icon layui-icon-location"></i>
上海
</p>
</a>
<div class="layadmin-address">
<a href="javascript:;">
<strong>Zhang san</strong>
<br>
E-mail:xxx@baidu.com
<br>
Weibo: https://weibo.com/123
<br>
<addr title="phone">Tel:</addr>
(123) 456-7890
</a>
</div>
</div>
</div>
</div>
<div class="layui-col-md4 layui-col-sm6">
<div class="layadmin-contact-box">
<div class="layui-col-md4 layui-col-sm6">
<a href="javascript:;">
<div class="layadmin-text-center">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/character.jpg">
</script>
<div class="layadmin-maillist-img layadmin-font-blod">演员</div>
</div>
</a>
</div>
<div class="layui-col-md8 layadmin-padding-left20 layui-col-sm6">
<a href="javascript:;">
<h3 class="layadmin-title">
<strong>张三</strong>
</h3>
<p class="layadmin-textimg">
<i class="layui-icon layui-icon-location"></i>
上海
</p>
</a>
<div class="layadmin-address">
<a href="javascript:;">
<strong>Zhang san</strong>
<br>
E-mail:xxx@baidu.com
<br>
Weibo: https://weibo.com/123
<br>
<addr title="phone">Tel:</addr>
(123) 456-7890
</a>
</div>
</div>
</div>
</div>
<div class="layui-col-md4 layui-col-sm6">
<div class="layadmin-contact-box">
<div class="layui-col-md4 layui-col-sm6">
<a href="javascript:;">
<div class="layadmin-text-center">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/character.jpg">
</script>
<div class="layadmin-maillist-img layadmin-font-blod">演员</div>
</div>
</a>
</div>
<div class="layui-col-md8 layadmin-padding-left20 layui-col-sm6">
<a href="javascript:;">
<h3 class="layadmin-title">
<strong>张三</strong>
</h3>
<p class="layadmin-textimg">
<i class="layui-icon layui-icon-location"></i>
上海
</p>
</a>
<div class="layadmin-address">
<a href="javascript:;">
<strong>Zhang san</strong>
<br>
E-mail:xxx@baidu.com
<br>
Weibo: https://weibo.com/123
<br>
<addr title="phone">Tel:</addr>
(123) 456-7890
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
layui.use(['admin'], function () {
var $ = layui.$
, admin = layui.admin
})
</script>

View File

@@ -1,203 +0,0 @@
<title>客户列表</title>
<script type="text/html" template>
<link rel="stylesheet" href="{{ layui.setter.paths.base }}style/template.css?v={{ layui.admin.v }}" media="all">
</script>
<div class="layui-card layadmin-header">
<div class="layui-breadcrumb" lay-filter="breadcrumb">
<a lay-href="">主页</a>
<a><cite>页面</cite></a>
<a><cite>客户列表</cite></a>
</div>
</div>
<div class="layui-fluid">
<div class="layadmin-caller">
<form class="layui-form caller-seach" action="">
<i class="layui-icon layui-icon-search caller-seach-icon caller-icon"></i>
<input type="text" name="title" required lay-verify="required" placeholder="请输入标题" autocomplete="off"
class="layui-input caller-pl32">
<i class="layui-icon layui-icon-close caller-dump-icon caller-icon"></i>
</form>
<div class="layui-tab layui-tab-brief caller-tab" lay-filter="docDemoTabBrief">
<ul class="layui-tab-title">
<li class="layui-this">所有联系人</li>
<li>联系方式</li>
<li>联系地址</li>
</ul>
</div>
<div class="caller-contar">
<div class="caller-item">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png" alt=""
class="caller-img caller-fl">
</script>
<div class="caller-main caller-fl">
<p><strong>张三</strong> <em>最近联系1 小时前</em></p>
<p class="caller-adds"><i class="layui-icon layui-icon-location"></i>浙江省广州市越秀区中山六路109
</p>
<div class="caller-iconset">
<i class="layui-icon layui-icon-login-wechat"></i>
<i class="layui-icon layui-icon-login-qq"></i>
<i class="layui-icon layui-icon-login-weibo"></i>
</div>
</div>
<button class="layui-btn layui-btn-sm caller-fr">
<i class="layui-icon layui-icon-ok"></i>
已收藏
</button>
</div>
<div class="caller-item">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png" alt=""
class="caller-img caller-fl">
</script>
<div class="caller-main caller-fl">
<p><strong>张三</strong> <em>最近联系1 小时前</em></p>
<p class="caller-adds"><i class="layui-icon layui-icon-location"></i>浙江省广州市越秀区中山六路109
</p>
<div class="caller-iconset">
<i class="layui-icon layui-icon-login-wechat"></i>
<i class="layui-icon layui-icon-login-qq"></i>
<i class="layui-icon layui-icon-login-weibo"></i>
</div>
</div>
<button class="layui-btn layui-btn-sm layui-btn layui-btn-primary caller-fr">
已收藏
</button>
</div>
<div class="caller-item">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png" alt=""
class="caller-img caller-fl">
</script>
<div class="caller-main caller-fl">
<p><strong>张三</strong> <em>最近联系1 小时前</em></p>
<p class="caller-adds"><i class="layui-icon layui-icon-location"></i>浙江省广州市越秀区中山六路109
</p>
<div class="caller-iconset">
<i class="layui-icon layui-icon-login-wechat"></i>
<i class="layui-icon layui-icon-login-qq"></i>
<i class="layui-icon layui-icon-login-weibo"></i>
</div>
</div>
<button class="layui-btn layui-btn-sm caller-fr">
<i class="layui-icon layui-icon-ok"></i>
已收藏
</button>
</div>
<div class="caller-item">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png" alt=""
class="caller-img caller-fl">
</script>
<div class="caller-main caller-fl">
<p><strong>张三</strong> <em>最近联系1 小时前</em></p>
<p class="caller-adds"><i class="layui-icon layui-icon-location"></i>浙江省广州市越秀区中山六路109
</p>
<div class="caller-iconset">
<i class="layui-icon layui-icon-login-wechat"></i>
<i class="layui-icon layui-icon-login-qq"></i>
<i class="layui-icon layui-icon-login-weibo"></i>
</div>
</div>
<button class="layui-btn layui-btn-sm layui-btn layui-btn-primary caller-fr">
已收藏
</button>
</div>
<div class="caller-item">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png" alt=""
class="caller-img caller-fl">
</script>
<div class="caller-main caller-fl">
<p><strong>张三</strong> <em>最近联系1 小时前</em></p>
<p class="caller-adds"><i class="layui-icon layui-icon-location"></i>浙江省广州市越秀区中山六路109
</p>
<div class="caller-iconset">
<i class="layui-icon layui-icon-login-wechat"></i>
<i class="layui-icon layui-icon-login-qq"></i>
<i class="layui-icon layui-icon-login-weibo"></i>
</div>
</div>
<button class="layui-btn layui-btn-sm caller-fr">
<i class="layui-icon layui-icon-ok"></i>
已收藏
</button>
</div>
<div class="caller-item">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png" alt=""
class="caller-img caller-fl">
</script>
<div class="caller-main caller-fl">
<p><strong>张三</strong> <em>最近联系1 小时前</em></p>
<p class="caller-adds"><i class="layui-icon layui-icon-location"></i>浙江省广州市越秀区中山六路109
</p>
<div class="caller-iconset">
<i class="layui-icon layui-icon-login-wechat"></i>
<i class="layui-icon layui-icon-login-qq"></i>
<i class="layui-icon layui-icon-login-weibo"></i>
</div>
</div>
<button class="layui-btn layui-btn-sm layui-btn layui-btn-primary caller-fr">
已收藏
</button>
</div>
<div class="caller-item">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png" alt=""
class="caller-img caller-fl">
</script>
<div class="caller-main caller-fl">
<p><strong>张三</strong> <em>最近联系1 小时前</em></p>
<p class="caller-adds"><i class="layui-icon layui-icon-location"></i>浙江省广州市越秀区中山六路109
</p>
<div class="caller-iconset">
<i class="layui-icon layui-icon-login-wechat"></i>
<i class="layui-icon layui-icon-login-qq"></i>
<i class="layui-icon layui-icon-login-weibo"></i>
</div>
</div>
<button class="layui-btn layui-btn-sm caller-fr">
<i class="layui-icon layui-icon-ok"></i>
已收藏
</button>
</div>
<div class="caller-item">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png" alt=""
class="caller-img caller-fl">
</script>
<div class="caller-main caller-fl">
<p><strong>张三</strong> <em>最近联系1 小时前</em></p>
<p class="caller-adds"><i class="layui-icon layui-icon-location"></i>浙江省广州市越秀区中山六路109
</p>
<div class="caller-iconset">
<i class="layui-icon layui-icon-login-wechat"></i>
<i class="layui-icon layui-icon-login-qq"></i>
<i class="layui-icon layui-icon-login-weibo"></i>
</div>
</div>
<button class="layui-btn layui-btn-sm layui-btn layui-btn-primary caller-fr">
已收藏
</button>
</div>
</div>
<div id="demo-template-caller1"></div>
</div>
</div>
<script>
layui.use(['admin', 'laypage'], function () {
var $ = layui.$
, admin = layui.admin
, laypage = layui.laypage
laypage.render({
elem: 'demo-template-caller1'
, count: 70 //数据总数
})
})
</script>

View File

@@ -1,422 +0,0 @@
<title>商品列表</title>
<script type="text/html" template>
<link rel="stylesheet" href="{{ layui.setter.paths.base }}style/template.css?v={{ layui.admin.v }}" media="all">
</script>
<div class="layui-card layadmin-header">
<div class="layui-breadcrumb" lay-filter="breadcrumb">
<a lay-href="">主页</a>
<a><cite>页面</cite></a>
<a><cite>商品列表</cite></a>
</div>
</div>
<div class="layui-fluid layadmin-cmdlist-fluid">
<div class="layui-row layui-col-space30">
<div class="layui-col-md2 layui-col-sm4">
<div class="cmdlist-container">
<a href="javascript:;">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</a>
<a href="javascript:;">
<div class="cmdlist-text">
<p class="info">标题标题标题标题标题标题标题标题标题标题标题标题标题标题</p>
<div class="price">
<b>¥79</b>
<p>
¥
<del>130</del>
</p>
<span class="flow">
<i class="layui-icon layui-icon-rate"></i>
433
</span>
</div>
</div>
</a>
</div>
</div>
<div class="layui-col-md2 layui-col-sm4">
<div class="cmdlist-container">
<a href="javascript:;">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</a>
<a href="javascript:;">
<div class="cmdlist-text">
<p class="info">标题标题标题标题标题标题标题标题标题标题标题标题标题标题</p>
<div class="price">
<b>¥79</b>
<p>
¥
<del>130</del>
</p>
<span class="flow">
<i class="layui-icon layui-icon-rate"></i>
433
</span>
</div>
</div>
</a>
</div>
</div>
<div class="layui-col-md2 layui-col-sm4">
<div class="cmdlist-container">
<a href="javascript:;">
<script type="text/html" template><img
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png"></script>
</a>
<a href="javascript:;">
<div class="cmdlist-text">
<p class="info">标题标题标题标题标题标题标题标题标题标题标题标题标题标题</p>
<div class="price">
<b>¥79</b>
<p>
¥
<del>130</del>
</p>
<span class="flow">
<i class="layui-icon layui-icon-rate"></i>
433
</span>
</div>
</div>
</a>
</div>
</div>
<div class="layui-col-md2 layui-col-sm4">
<div class="cmdlist-container">
<a href="javascript:;">
<script type="text/html" template><img
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png"></script>
</a>
<a href="javascript:;">
<div class="cmdlist-text">
<p class="info">标题标题标题标题标题标题标题标题标题标题标题标题标题标题</p>
<div class="price">
<b>¥79</b>
<p>
¥
<del>130</del>
</p>
<span class="flow">
<i class="layui-icon layui-icon-rate"></i>
433
</span>
</div>
</div>
</a>
</div>
</div>
<div class="layui-col-md2 layui-col-sm4">
<div class="cmdlist-container">
<a href="javascript:;">
<script type="text/html" template><img
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png"></script>
</a>
<a href="javascript:;">
<div class="cmdlist-text">
<p class="info">标题标题标题标题标题标题标题标题标题标题标题标题标题标题</p>
<div class="price">
<b>¥79</b>
<p>
¥
<del>130</del>
</p>
<span class="flow">
<i class="layui-icon layui-icon-rate"></i>
433
</span>
</div>
</div>
</a>
</div>
</div>
<div class="layui-col-md2 layui-col-sm4">
<div class="cmdlist-container">
<a href="javascript:;">
<script type="text/html" template><img
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png"></script>
</a>
<a href="javascript:;">
<div class="cmdlist-text">
<p class="info">标题标题标题标题标题标题标题标题标题标题标题标题标题标题</p>
<div class="price">
<b>¥79</b>
<p>
¥
<del>130</del>
</p>
<span class="flow">
<i class="layui-icon layui-icon-rate"></i>
433
</span>
</div>
</div>
</a>
</div>
</div>
<div class="layui-col-md2 layui-col-sm4">
<div class="cmdlist-container">
<a href="javascript:;">
<script type="text/html" template><img
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png"></script>
</a>
<a href="javascript:;">
<div class="cmdlist-text">
<p class="info">标题标题标题标题标题标题标题标题标题标题标题标题标题标题</p>
<div class="price">
<b>¥79</b>
<p>
¥
<del>130</del>
</p>
<span class="flow">
<i class="layui-icon layui-icon-rate"></i>
433
</span>
</div>
</div>
</a>
</div>
</div>
<div class="layui-col-md2 layui-col-sm4">
<div class="cmdlist-container">
<a href="javascript:;">
<script type="text/html" template><img
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png"></script>
</a>
<a href="javascript:;">
<div class="cmdlist-text">
<p class="info">标题标题标题标题标题标题标题标题标题标题标题标题标题标题</p>
<div class="price">
<b>¥79</b>
<p>
¥
<del>130</del>
</p>
<span class="flow">
<i class="layui-icon layui-icon-rate"></i>
433
</span>
</div>
</div>
</a>
</div>
</div>
<div class="layui-col-md2 layui-col-sm4">
<div class="cmdlist-container">
<a href="javascript:;">
<script type="text/html" template><img
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png"></script>
</a>
<a href="javascript:;">
<div class="cmdlist-text">
<p class="info">标题标题标题标题标题标题标题标题标题标题标题标题标题标题</p>
<div class="price">
<b>¥79</b>
<p>
¥
<del>130</del>
</p>
<span class="flow">
<i class="layui-icon layui-icon-rate"></i>
433
</span>
</div>
</div>
</a>
</div>
</div>
<div class="layui-col-md2 layui-col-sm4">
<div class="cmdlist-container">
<a href="javascript:;">
<script type="text/html" template><img
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png"></script>
</a>
<a href="javascript:;">
<div class="cmdlist-text">
<p class="info">标题标题标题标题标题标题标题标题标题标题标题标题标题标题</p>
<div class="price">
<b>¥79</b>
<p>
¥
<del>130</del>
</p>
<span class="flow">
<i class="layui-icon layui-icon-rate"></i>
433
</span>
</div>
</div>
</a>
</div>
</div>
<div class="layui-col-md2 layui-col-sm4">
<div class="cmdlist-container">
<a href="javascript:;">
<script type="text/html" template><img
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png"></script>
</a>
<a href="javascript:;">
<div class="cmdlist-text">
<p class="info">标题标题标题标题标题标题标题标题标题标题标题标题标题标题</p>
<div class="price">
<b>¥79</b>
<p>
¥
<del>130</del>
</p>
<span class="flow">
<i class="layui-icon layui-icon-rate"></i>
433
</span>
</div>
</div>
</a>
</div>
</div>
<div class="layui-col-md2 layui-col-sm4">
<div class="cmdlist-container">
<a href="javascript:;">
<script type="text/html" template><img
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png"></script>
</a>
<a href="javascript:;">
<div class="cmdlist-text">
<p class="info">标题标题标题标题标题标题标题标题标题标题标题标题标题标题</p>
<div class="price">
<b>¥79</b>
<p>
¥
<del>130</del>
</p>
<span class="flow">
<i class="layui-icon layui-icon-rate"></i>
433
</span>
</div>
</div>
</a>
</div>
</div>
<div class="layui-col-md2 layui-col-sm4">
<div class="cmdlist-container">
<a href="javascript:;">
<script type="text/html" template><img
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png"></script>
</a>
<a href="javascript:;">
<div class="cmdlist-text">
<p class="info">标题标题标题标题标题标题标题标题标题标题标题标题标题标题</p>
<div class="price">
<b>¥79</b>
<p>
¥
<del>130</del>
</p>
<span class="flow">
<i class="layui-icon layui-icon-rate"></i>
433
</span>
</div>
</div>
</a>
</div>
</div>
<div class="layui-col-md2 layui-col-sm4">
<div class="cmdlist-container">
<a href="javascript:;">
<script type="text/html" template><img
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png"></script>
</a>
<a href="javascript:;">
<div class="cmdlist-text">
<p class="info">标题标题标题标题标题标题标题标题标题标题标题标题标题标题</p>
<div class="price">
<b>¥79</b>
<p>
¥
<del>130</del>
</p>
<span class="flow">
<i class="layui-icon layui-icon-rate"></i>
433
</span>
</div>
</div>
</a>
</div>
</div>
<div class="layui-col-md2 layui-col-sm4">
<div class="cmdlist-container">
<a href="javascript:;">
<script type="text/html" template><img
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png"></script>
</a>
<a href="javascript:;">
<div class="cmdlist-text">
<p class="info">标题标题标题标题标题标题标题标题标题标题标题标题标题标题</p>
<div class="price">
<b>¥79</b>
<p>
¥
<del>130</del>
</p>
<span class="flow">
<i class="layui-icon layui-icon-rate"></i>
433
</span>
</div>
</div>
</a>
</div>
</div>
<div class="layui-col-md2 layui-col-sm4">
<div class="cmdlist-container">
<a href="javascript:;">
<script type="text/html" template><img
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png"></script>
</a>
<a href="javascript:;">
<div class="cmdlist-text">
<p class="info">标题标题标题标题标题标题标题标题标题标题标题标题标题标题</p>
<div class="price">
<b>¥79</b>
<p>
¥
<del>130</del>
</p>
<span class="flow">
<i class="layui-icon layui-icon-rate"></i>
433
</span>
</div>
</div>
</a>
</div>
</div>
<div class="layui-col-md12 layui-col-sm12">
<div id="demo0"></div>
</div>
</div>
</div>
<script>
layui.use(['admin', 'laypage', 'layer'], function () {
var $ = layui.$
, admin = layui.admin
, laypage = layui.laypage
, layer = layui.layer
laypage.render({
elem: 'demo0'
, count: 50 //数据总数
})
})
</script>

View File

@@ -1,100 +0,0 @@
<title>留言板</title>
<script type="text/html" template>
<link rel="stylesheet" href="{{ layui.setter.paths.base }}style/template.css?v={{ layui.admin.v }}" media="all">
</script>
<div class="layui-card layadmin-header">
<div class="layui-breadcrumb" lay-filter="breadcrumb">
<a lay-href="">主页</a>
<a><cite>页面</cite></a>
<a><cite>留言板</cite></a>
</div>
</div>
<div class="layui-fluid layadmin-message-fluid">
<div class="layui-row">
<div class="layui-col-md12">
<div class="layui-form">
<div class="layui-form-item layui-form-text">
<div class="layui-input-block">
<textarea name="desc" placeholder="请输入内容" class="layui-textarea"></textarea>
</div>
</div>
<div class="layui-form-item" style="overflow: hidden;">
<div class="layui-input-block layui-input-right">
<button class="layui-btn" lay-submit lay-filter="formDemo">发表</button>
</div>
<div class="layadmin-messag-icon">
<a href="javascript:;"><i class="layui-icon layui-icon-face-smile-b"></i></a>
<a href="javascript:;"><i class="layui-icon layui-icon-picture"></i></a>
<a href="javascript:;"><i class="layui-icon layui-icon-link"></i></a>
</div>
</div>
</div>
</div>
<div class="layui-col-md12 layadmin-homepage-list-imgtxt message-content">
<div class="media-body">
<a href="javascript:;" class="media-left" style="float: left;">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png" height="46px"
width="46px">
</script>
</a>
<div class="pad-btm">
<p class="fontColor"><a href="javascript:;">张三</a></p>
<p class="min-font">
<span class="layui-breadcrumb" lay-separator="-">
<a href="javascript:;" class="layui-icon layui-icon-cellphone"></a>
<a href="javascript:;">从移动</a>
<a href="javascript:;">11分钟前</a>
</span>
</p>
</div>
<p class="message-text">
内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容</p>
</div>
<div class="media-body">
<a href="javascript:;" class="media-left" style="float: left;">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png" height="46px"
width="46px">
</script>
</a>
<div class="pad-btm">
<p class="fontColor"><a href="javascript:;">张三</a></p>
<p class="min-font">
<span class="layui-breadcrumb" lay-separator="-">
<a href="javascript:;" class="layui-icon layui-icon-cellphone"></a>
<a href="javascript:;">从移动</a>
<a href="javascript:;">11分钟前</a>
</span>
</p>
</div>
<p class="message-text">
内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容</p>
</div>
<div class="layui-row message-content-btn">
<a href="javascript:;" class="layui-btn">更多</a>
</div>
</div>
</div>
</div>
<script>
layui.use(['admin', 'layer', 'form'], function () {
var $ = layui.$
, admin = layui.admin
, element = layui.element
, form = layui.form
element.render()
form.on('submit(formDemo)', function (data) {
// layer.alert(layui.util.escape(JSON.stringify(data.field)));
return false
})
})
</script>

View File

@@ -1,443 +0,0 @@
<title>个人主页</title>
<script type="text/html" template>
<link rel="stylesheet" href="{{ layui.setter.paths.base }}style/template.css?v={{ layui.admin.v }}" media="all">
</script>
<div class="layui-card layadmin-header">
<div class="layui-breadcrumb" lay-filter="breadcrumb">
<a lay-href="">主页</a>
<a><cite>页面</cite></a>
<a><cite>个人主页</cite></a>
</div>
</div>
<div class="layui-fluid layadmin-homepage-fluid">
<div class="layui-row layui-col-space8">
<div class="layui-col-md2">
<div class="layadmin-homepage-panel layadmin-homepage-shadow">
<div class="layui-card text-center">
<div class="layui-card-body">
<div class="layadmin-homepage-pad-ver">
<script type="text/html" template>
<img class="layadmin-homepage-pad-img"
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png" width="96"
height="96">
</script>
</div>
<h4 class="layadmin-homepage-font">张三</h4>
<p class="layadmin-homepage-min-font">中国非知名前端</p>
<div class="layadmin-homepage-pad-ver">
<a href="javascript:;" class="layui-icon layui-icon-cellphone"></a>
<a href="javascript:;" class="layui-icon layui-icon-vercode"></a>
<a href="javascript:;" class="layui-icon layui-icon-login-wechat"></a>
<a href="javascript:;" class="layui-icon layui-icon-login-qq"></a>
</div>
<button class="layui-btn layui-btn-fluid">跟随</button>
</div>
</div>
<p class="layadmin-homepage-about">
关于我
</p>
<ul class="layadmin-homepage-list-group">
<li class="list-group-item"><i class="layui-icon layui-icon-location"></i>中国上海</li>
<li class="list-group-item"><a href="javascript:;" class="color"><i
class="layui-icon layui-icon-snowflake"></i><span style="word-wrap:break-word;">https://weibo.com/123</span></a>
</li>
</ul>
<div class="layadmin-homepage-pad-hor">
<mdall>介绍介绍介绍介绍介绍介绍介绍介绍介绍介绍介绍介绍介绍介绍介绍介绍介绍介绍介绍介绍</mdall>
</div>
<p class="layadmin-homepage-about">
技能
</p>
<ul class="layadmin-homepage-list-inline">
<a href="javascript:;" class="layui-btn layui-btn-primary">前端</a>
<a href="javascript:;" class="layui-btn layui-btn-primary">主持人</a>
<a href="javascript:;" class="layui-btn layui-btn-primary">摄影师</a>
<a href="javascript:;" class="layui-btn layui-btn-primary">导演</a>
<a href="javascript:;" class="layui-btn layui-btn-primary">公共人物</a>
</ul>
</div>
</div>
<div class="layui-col-md10">
<div class="layui-fluid layadmin-homepage-content">
<div class="layui-row layadmin-homepage-padding15">
<hr class="new-section-xs"></hr>
<div class="layui-col-md7 layadmin-homepage-padding8">
<div class="layui-row layadmin-homepage-text-center">
<div class="layui-col-md3 layui-col-sm3 layui-col-xs3">
<p class="h4">52K</p>
<mdall>关注</mdall>
</div>
<div class="layui-col-md3 layui-col-sm3 layui-col-xs3">
<p class="h4">72K</p>
<mdall>以下</mdall>
</div>
<div class="layui-col-md3 layui-col-sm3 layui-col-xs3">
<p class="h4">5343</p>
<mdall>照片</mdall>
</div>
<div class="layui-col-md3 layui-col-sm3 layui-col-xs3">
<p class="h4"><i class="layui-icon layui-icon-rate-solid"></i>4.5</p>
<mdall>排行</mdall>
</div>
</div>
</div>
<div class="layui-col-md5">
<a href="javascript:;" class="layui-btn layui-btn-normal">下载简历</a>
<a href="javascript:;" class="layui-btn">发信息</a>
</div>
</div>
<div class="layui-row layui-col-space20 layadmin-homepage-list-imgtxt">
<div class="layui-col-md9">
<div class="grid-demo">
<div class="panel-body layadmin-homepage-shadow">
<a href="javascript:;" class="media-left">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png"
height="46px" width="46px">
</script>
</a>
<div class="media-body">
<div class="pad-btm">
<p class="fontColor"><a href="javascript:;">张三</a><span>普通照片</span>添加了新的<span>照片</span>
</p>
<p class="min-font">
<span class="layui-breadcrumb" lay-separator="-">
<a href="javascript:;" class="layui-icon layui-icon-cellphone"></a>
<a href="javascript:;">从移动</a>
<a href="javascript:;">11分钟前</a>
</span>
</p>
</div>
<p>
内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容</p>
<script type="text/html" template>
<img class="h-img"
src="{{ layui.setter.paths.base }}style/imgs/template/huge.jpg">
</script>
<div class="media">
<div class="media-right">
<ul class="list-inline">
<li>
<i class="layui-icon layui-icon-camera-fill"></i>
<span>107</span>
</li>
<li>
<i class="layui-icon layui-icon-group"></i>
<span>95</span>
</li>
</ul>
</div>
<div class="media-left">
<ul class="list-inline">
<li>
<a href="javascript:;">
<script type="text/html" template>
<img class="img-xs"
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</a>
</li>
<li>
<a href="javascript:;">
<script type="text/html" template>
<img class="img-xs"
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</a>
</li>
<li>
<a href="javascript:;">
<script type="text/html" template>
<img class="img-xs"
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</a>
</li>
<li>
<a href="javascript:;" class="">
14+
</a>
</li>
</ul>
</div>
</div>
<div class="media-list">
<div class="media-item">
<a href="javascript:;" class="media-item-left">
<script type="text/html" template>
<img class="img-xs"
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</a>
<div class="media-text">
<div>
<a href="javascript:;">张三</a>
<mdall> 3月21日 19:56</mdall>
</div>
<div>一次难忘更难得的拍摄经历 谢谢</div>
</div>
</div>
<div class="media-item">
<a href="javascript:;" class="media-item-left">
<script type="text/html" template>
<img class="img-xs"
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</a>
<div class="media-text">
<div>
<a href="javascript:;">张三</a>
<mdall> 3月21日 19:56</mdall>
</div>
<div>一次难忘更难得的拍摄经历 谢谢</div>
</div>
</div>
<div class="media-item">
<a href="javascript:;" class="media-item-left">
<script type="text/html" template>
<img class="img-xs"
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</a>
<div class="media-text">
<div>
<a href="javascript:;">张三</a>
<mdall> 3月21日 19:56</mdall>
</div>
<div>一次难忘更难得的拍摄经历 谢谢</div>
</div>
</div>
<a href="javascript:;" class="layui-btn">更多</a>
</div>
</div>
</div>
<div class="panel-body layadmin-homepage-shadow">
<a href="javascript:;" class="media-left">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png"
height="46px" width="46px">
</script>
</a>
<div class="media-body">
<div class="pad-btm">
<p class="fontColor"><a href="javascript:;">张三</a><span>普通照片</span>添加了新的<span>照片</span>
</p>
<p class="min-font">
<span class="layui-breadcrumb" lay-separator="-">
<a href="javascript:;" class="layui-icon layui-icon-cellphone"></a>
<a href="javascript:;">从移动</a>
<a href="javascript:;">11分钟前</a>
</span>
</p>
</div>
<p>
内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容</p>
<div class="media">
<div class="media-right">
<ul class="list-inline">
<li>
<i class="layui-icon layui-icon-camera-fill"></i>
<span>107</span>
</li>
<li>
<i class="layui-icon layui-icon-group"></i>
<span>95</span>
</li>
</ul>
</div>
<div class="media-left">
<ul class="list-inline">
<li>
<a href="javascript:;">
<script type="text/html" template>
<img class="img-xs"
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</a>
</li>
<li>
<a href="javascript:;">
<script type="text/html" template>
<img class="img-xs"
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</a>
</li>
<li>
<a href="javascript:;">
<script type="text/html" template>
<img class="img-xs"
src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</a>
</li>
<li>
<a href="javascript:;" class="">
14+
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="layui-col-md3">
<div class="grid-demo">
<div class="layui-card homepage-top">
<div class="layui-card-header">
<h3 class="panel-title">
<i class="layui-icon layui-icon-picture"></i>
画廊
</h3>
</div>
<div class="layui-card-body">
<div class="layui-row layui-col-space15">
<div class="layui-col-md4">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</div>
<div class="layui-col-md4">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</div>
<div class="layui-col-md4">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</div>
<div class="layui-col-md4">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</div>
<div class="layui-col-md4">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</div>
<div class="layui-col-md4">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</div>
<div class="layui-col-md4">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</div>
<div class="layui-col-md4">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</div>
<div class="layui-col-md4">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</div>
<div class="layui-col-md12 margin-top10">
<a href="javascript:;" class="layui-btn layui-btn-primary">更多</a>
</div>
</div>
</div>
</div>
<div class="layui-card homepage-bottom">
<div class="layui-card-header">
<h3 class="panel-title">
<i class="layui-icon layui-icon-user"></i>
私信
</h3>
</div>
<div class="layui-card-body">
<a href="javascript:;" class="layadmin-privateletterlist-item">
<div class="meida-left">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</div>
<div class="meida-right">
<p>张三</p>
<mdall>人生是一场修行,不要轻易交白卷</mdall>
</div>
</a>
<a href="javascript:;" class="layadmin-privateletterlist-item">
<div class="meida-left">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</div>
<div class="meida-right">
<p>张三</p>
<mdall>人生是一场修行,不要轻易交白卷</mdall>
</div>
</a>
<a href="javascript:;" class="layadmin-privateletterlist-item">
<div class="meida-left">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</div>
<div class="meida-right">
<p>张三</p>
<mdall>人生是一场修行,不要轻易交白卷</mdall>
</div>
</a>
<a href="javascript:;" class="layadmin-privateletterlist-item">
<div class="meida-left">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</div>
<div class="meida-right">
<p>张三</p>
<mdall>人生是一场修行,不要轻易交白卷</mdall>
</div>
</a>
<a href="javascript:;" class="layadmin-privateletterlist-item">
<div class="meida-left">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</div>
<div class="meida-right">
<p>张三</p>
<mdall>人生是一场修行,不要轻易交白卷</mdall>
</div>
</a>
<a href="javascript:;" class="layadmin-privateletterlist-item">
<div class="meida-left">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/template/portrait.png">
</script>
</div>
<div class="meida-right">
<p>张三</p>
<mdall>人生是一场修行,不要轻易交白卷</mdall>
</div>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
layui.use(['admin'], function () {
var $ = layui.$
, admin = layui.admin
})
</script>

View File

@@ -1,100 +0,0 @@
<title>搜索结果</title>
<script type="text/html" template>
<link rel="stylesheet" href="{{ layui.setter.paths.base }}style/template.css?v={{ layui.admin.v }}" media="all">
</script>
<div class="layui-card layadmin-header">
<div class="layui-breadcrumb" lay-filter="breadcrumb">
<a lay-href="">主页</a>
<a><cite>页面</cite></a>
<a><cite>搜索结果</cite></a>
</div>
</div>
<div class="layui-fluid">
<div class="layui-row layui-col-space15">
<div class="layui-col-md12">
<div class="layui-card layadmin-serach-main">
<div class="layui-card-header">
<p style="font-size: 18px;">
<span style="color: #01AAED">关键词</span> 查询到
<strong>66</strong> 个结果
</p>
<p class="layadmin-font-em">耗时350ms</p>
</div>
<div class="layui-card-body">
<ul class="layadmin-serach-list layui-text">
<li>
<div class="layui-serachlist-text">
<h3><a href="http://www.baidu.com#/admin/" target="_blank">前端的进化?</a></h3>
<p>
在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。</p>
</div>
</li>
<li>
<a href="http://www.baidu.com#/admin/" target="_blank" class="layui-serachlist-cover">
<script type="text/html" template>
<img src="{{ layui.setter.paths.base }}style/imgs/layui-logo.jpg">
</script>
</a>
<div class="layui-serachlist-text">
<h3><a href="http://www.baidu.com#/admin/" target="_blank">前端 UI</a></h3>
<p> UI 设计(或称界面设计)是指对软件的人机交互、操作逻辑、界面美观的整体设计。 UI 设计分为实体
UI和虚拟 UI互联网常用的 UI 设计是虚拟 UI UI即 User Interface (用户界面)的简称。</p>
</div>
</li>
<li>
<div class="layui-serachlist-text">
<h3><a href="http://www.baidu.com#/admin/" target="_blank">前端的进化?</a></h3>
<p>
在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。</p>
<p>
<span class="layui-badge layui-bg-green">前端</span>
<span class="layui-badge layui-bg-blue">性别</span>
<span class="layui-badge layui-bg-orange"></span>
</p>
</div>
</li>
<li>
<div class="layui-serachlist-text">
<h3>
<span class="layui-badge">热点</span>
<a href="http://www.baidu.com#/admin/" target="_blank"> UI 设计</a>
</h3>
<p> UI 设计(或称界面设计)是指对软件的人机交互、操作逻辑、界面美观的整体设计。 UI 设计分为实体
UI和虚拟 UI互联网常用的 UI 设计是虚拟 UI UI即 User Interface (用户界面)的简称。</p>
</div>
</li>
<li>
<div class="layui-serachlist-text">
<h3><a href="http://www.baidu.com#/admin/" target="_blank">前端的进化?</a></h3>
<p>
在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。在前端技术的快速发展中找准你的定位,在思考与实践中沉淀。。</p>
</div>
</li>
</ul>
<div id="LAY-template-search-page" style="text-align: center;"></div>
</div>
</div>
</div>
</div>
</div>
<script>
layui.use(['admin', 'laypage'], function () {
var laypage = layui.laypage
laypage.render({
elem: 'LAY-template-search-page'
, count: 66
, jump: function (obj, first) {
if (!first) {
layer.msg('第' + obj.curr + '页')
}
}
})
})
</script>

View File

@@ -1,12 +0,0 @@
<title>出错了</title>
<div class="layui-fluid">
<div class="layadmin-tips">
<i class="layui-icon" face>&#xe664;</i>
<div class="layui-text" style="font-size: 20px;">
好像出错了呢
</div>
</div>
</div>

View File

@@ -1,2 +0,0 @@
该目录存放【模板】的视图文件

View File

@@ -1,2 +1,2 @@
User-agent: *
Disallow:
Disallow: /

View File

@@ -18,6 +18,7 @@ func Web() {
r.Middleware(middleware.Jwt()).Get("homePlugins", infoController.HomePlugins)
r.Middleware(middleware.Jwt()).Get("nowMonitor", infoController.NowMonitor)
r.Middleware(middleware.Jwt()).Get("systemInfo", infoController.SystemInfo)
r.Middleware(middleware.Jwt()).Get("installedDbAndPhp", infoController.InstalledDbAndPhp)
})
r.Prefix("user").Group(func(r route.Route) {
userController := controllers.NewUserController()
@@ -28,6 +29,10 @@ func Web() {
taskController := controllers.NewTaskController()
r.Get("status", taskController.Status)
})
r.Prefix("website").Middleware(middleware.Jwt()).Group(func(r route.Route) {
websiteController := controllers.NewWebsiteController()
r.Get("list", websiteController.List)
})
})
facades.Route().Fallback(func(ctx http.Context) {