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

refactor: 重构入口和前端加载逻辑

This commit is contained in:
耗子
2024-07-13 01:17:24 +08:00
parent 7a7134b26a
commit 6bad7f4b5b
14 changed files with 81 additions and 131 deletions

View File

@@ -21,9 +21,8 @@ jobs:
run: |
sudo apt-get install -y curl jq unzip zip
curl -sSL https://api.github.com/repos/TheTNB/panel-frontend/releases/latest | jq -r ".assets[] | select(.name | contains(\"dist\")) | .browser_download_url" | xargs curl -L -o frontend.zip
rm -rf public
unzip frontend.zip
mv dist public
mv dist embed/frontend
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:

View File

@@ -1,25 +0,0 @@
name: Issue Close Question
on:
schedule:
- cron: "0 0 * * *"
permissions:
contents: read
jobs:
issue-close-require:
permissions:
issues: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: needs more info
uses: actions-cool/issues-helper@v3
with:
actions: 'close-issues'
labels: 'question'
inactive-day: 3
body: |
由于该 Issue 3天未收到回应现已被自动关闭若有任何问题可评论回复。
This issue has been closed automatically because it has not had recent activity for 3 days. If you have any questions, please comment here.

7
.gitignore vendored
View File

@@ -68,7 +68,6 @@ out/
__debug_bin
.project
# 前端工具链 #
.sass-cache/*
node_modules/
/public
# 嵌入文件 #
embed/*
!embed/embed.go

View File

@@ -62,13 +62,12 @@ fetch:
- apk add --no-cache curl jq unzip zip
script:
- curl -sSL "https://git.haozi.net/api/v4/projects/opensource%2Fpanel-frontend/releases" | jq -r '.[0].assets.links[] | select(.name | contains("dist")) | .direct_asset_url' | xargs curl -L -o frontend.zip
- rm -rf public
- unzip frontend.zip
- mv dist public
- mv dist embed/frontend
artifacts:
name: "frontend"
paths:
- public
- embed/frontend
expire_in: 3 days
release:

View File

@@ -40,7 +40,6 @@ archives:
files:
- LICENSE
- docs/*
- public/*
- storage/*
- lang/*
- scripts/*

View File

@@ -1,77 +0,0 @@
package controllers
import (
"os"
"strings"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/io"
)
type AssetController struct {
setting internal.Setting
}
func NewAssetController() *AssetController {
return &AssetController{
setting: services.NewSettingImpl(),
}
}
func (r *AssetController) Index(ctx http.Context) http.Response {
entrance := facades.Config().GetString("http.entrance")
if entrance == "/" {
entrance = ""
}
// 自动纠正 URL 格式
if ctx.Request().Path() == entrance && ctx.Request().Path() != "/" {
return ctx.Response().Redirect(http.StatusMovedPermanently, ctx.Request().Path()+"/")
}
// 拒绝访问非入口文件
if !strings.HasPrefix(ctx.Request().Path(), entrance) {
return Error(ctx, http.StatusNotFound, http.StatusText(http.StatusNotFound))
}
path := strings.TrimPrefix(ctx.Request().Path(), entrance)
// 设置默认首页
if path == "/" || path == "" {
path = "/index.html"
}
if !io.Exists("public" + path) {
return Error(ctx, http.StatusNotFound, http.StatusText(http.StatusNotFound))
}
file, err := os.Open("public" + path)
if err != nil {
return Error(ctx, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
}
stat, err := file.Stat()
if err != nil {
return Error(ctx, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
}
if stat.IsDir() {
return Error(ctx, http.StatusForbidden, http.StatusText(http.StatusForbidden))
}
return ctx.Response().Header("Cache-Control", "no-cache").File("public" + path)
}
func (r *AssetController) Favicon(ctx http.Context) http.Response {
return ctx.Response().File("public/favicon.png")
}
func (r *AssetController) Robots(ctx http.Context) http.Response {
return ctx.Response().File("public/robots.txt")
}
func (r *AssetController) NotFound(ctx http.Context) http.Response {
return Error(ctx, http.StatusNotFound, http.StatusText(http.StatusNotFound))
}

View File

@@ -1,8 +1,6 @@
package controllers
import (
"fmt"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/spf13/cast"
@@ -75,10 +73,7 @@ func (r *UserController) Login(ctx http.Context) http.Response {
// @Success 200 {object} SuccessResponse
// @Router /panel/user/logout [post]
func (r *UserController) Logout(ctx http.Context) http.Response {
if err := ctx.Request().Session().Invalidate(); err != nil {
return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("登出失败: %s", err.Error()))
}
ctx.Request().Session().Forget("user_id")
return Success(ctx, nil)
}

View File

@@ -17,5 +17,7 @@ func (kernel Kernel) Middleware() []http.Middleware {
sessionmiddleware.StartSession(),
middleware.Log(),
middleware.Status(),
middleware.Entrance(),
middleware.Static(),
}
}

View File

@@ -0,0 +1,38 @@
package middleware
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/spf13/cast"
)
func Entrance() http.Middleware {
return func(ctx http.Context) {
translate := facades.Lang(ctx)
if !ctx.Request().HasSession() {
ctx.Request().AbortWithStatusJson(http.StatusUnauthorized, http.Json{
"message": translate.Get("auth.session.missing"),
})
return
}
entrance := facades.Config().GetString("http.entrance")
if ctx.Request().Path() == entrance {
ctx.Request().Session().Put("verify_entrance", true)
ctx.Response().Redirect(http.StatusFound, "/")
return
}
if !facades.Config().GetBool("app.debug") &&
(ctx.Request().Session().Missing("verify_entrance") || !cast.ToBool(ctx.Request().Session().Get("verify_entrance"))) &&
ctx.Request().Path() != "/robots.txt" {
ctx.Request().AbortWithStatusJson(http.StatusTeapot, http.Json{
"message": "请通过正确的入口访问",
})
return
}
ctx.Request().Next()
}
}

View File

@@ -0,0 +1,16 @@
package middleware
import (
"github.com/gin-contrib/static"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/gin"
"github.com/TheTNB/panel/embed"
)
func Static() http.Middleware {
return func(ctx http.Context) {
static.Serve("/", static.EmbedFolder(embed.PublicFS, "frontend"))(ctx.(*gin.Context).Instance())
ctx.Request().Next()
}
}

6
embed/embed.go Normal file
View File

@@ -0,0 +1,6 @@
package embed
import "embed"
//go:embed public/*
var PublicFS embed.FS

1
go.mod
View File

@@ -5,6 +5,7 @@ go 1.22
require (
github.com/docker/docker v27.0.3+incompatible
github.com/docker/go-connections v0.5.0
github.com/gin-contrib/static v1.1.2
github.com/go-gormigrate/gormigrate/v2 v2.1.2
github.com/go-resty/resty/v2 v2.13.1
github.com/go-sql-driver/mysql v1.8.1

2
go.sum
View File

@@ -165,6 +165,8 @@ github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSe
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-contrib/static v1.1.2 h1:c3kT4bFkUJn2aoRU3s6XnMjJT8J6nNWJkR0NglqmlZ4=
github.com/gin-contrib/static v1.1.2/go.mod h1:Fw90ozjHCmZBWbgrsqrDvO28YbhKEKzKp8GixhR4yLw=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=

View File

@@ -1,12 +1,16 @@
package routes
import (
"path/filepath"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/route"
"github.com/goravel/framework/facades"
frameworkmiddleware "github.com/goravel/framework/http/middleware"
"github.com/TheTNB/panel/app/http/controllers"
"github.com/TheTNB/panel/app/http/middleware"
"github.com/TheTNB/panel/embed"
)
func Api() {
@@ -221,17 +225,9 @@ func Api() {
swaggerController := controllers.NewSwaggerController()
facades.Route().Middleware(middleware.Session()).Get("swagger/*any", swaggerController.Index)
// 静态文件
entrance := facades.Config().GetString("http.entrance")
if entrance == "/" {
entrance = ""
}
assetController := controllers.NewAssetController()
facades.Route().Get("favicon.png", assetController.Favicon)
facades.Route().Get("robots.txt", assetController.Robots)
facades.Route().Get(entrance+"/assets/{any}", assetController.Index)
facades.Route().Get(entrance+"/loading/{any}", assetController.Index)
facades.Route().Get(entrance+"/{any}", assetController.Index)
facades.Route().Get(entrance+"/", assetController.Index)
facades.Route().Fallback(assetController.NotFound)
// 404
facades.Route().Fallback(func(ctx http.Context) http.Response {
index, _ := embed.PublicFS.ReadFile(filepath.Join("public", "index.html"))
return ctx.Response().Data(http.StatusOK, "text/html; charset=utf-8", index)
})
}