ci(backend): 调整 embed server
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -81,7 +81,12 @@ build/
|
|||||||
release/
|
release/
|
||||||
|
|
||||||
# 后端嵌入的前端构建产物
|
# 后端嵌入的前端构建产物
|
||||||
|
# Keep a placeholder file so `//go:embed all:dist` always has a match in CI/lint,
|
||||||
|
# while still ignoring generated frontend build outputs.
|
||||||
backend/internal/web/dist/
|
backend/internal/web/dist/
|
||||||
|
!backend/internal/web/dist/
|
||||||
|
backend/internal/web/dist/*
|
||||||
|
!backend/internal/web/dist/.keep
|
||||||
|
|
||||||
# 后端运行时缓存数据
|
# 后端运行时缓存数据
|
||||||
backend/data/
|
backend/data/
|
||||||
|
|||||||
10
backend/.github/workflows/ci.yml
vendored
10
backend/.github/workflows/ci.yml
vendored
@@ -14,10 +14,11 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: backend/go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
cache: true
|
cache: true
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
|
working-directory: backend
|
||||||
run: go test ./...
|
run: go test ./...
|
||||||
|
|
||||||
golangci-lint:
|
golangci-lint:
|
||||||
@@ -26,11 +27,12 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: backend/go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
cache: true
|
cache: true
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v6
|
uses: golangci/golangci-lint-action@v9
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: v2.7
|
||||||
args: --timeout=5m
|
args: --timeout=5m
|
||||||
|
working-directory: backend
|
||||||
|
|||||||
@@ -1,6 +1,16 @@
|
|||||||
.PHONY: wire
|
.PHONY: wire build build-embed
|
||||||
|
|
||||||
wire:
|
wire:
|
||||||
@echo "生成 Wire 代码..."
|
@echo "生成 Wire 代码..."
|
||||||
@cd cmd/server && go generate
|
@cd cmd/server && go generate
|
||||||
@echo "Wire 代码生成完成"
|
@echo "Wire 代码生成完成"
|
||||||
|
|
||||||
|
build:
|
||||||
|
@echo "构建后端(不嵌入前端)..."
|
||||||
|
@go build -o bin/server ./cmd/server
|
||||||
|
@echo "构建完成: bin/server"
|
||||||
|
|
||||||
|
build-embed:
|
||||||
|
@echo "构建后端(嵌入前端)..."
|
||||||
|
@go build -tags embed -o bin/server ./cmd/server
|
||||||
|
@echo "构建完成: bin/server (with embedded frontend)"
|
||||||
20
backend/internal/web/embed_off.go
Normal file
20
backend/internal/web/embed_off.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
//go:build !embed
|
||||||
|
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ServeEmbeddedFrontend() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
c.String(http.StatusNotFound, "Frontend not embedded. Build with -tags embed to include frontend.")
|
||||||
|
c.Abort()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func HasEmbeddedFrontend() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build embed
|
||||||
|
|
||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -13,8 +15,6 @@ import (
|
|||||||
//go:embed all:dist
|
//go:embed all:dist
|
||||||
var frontendFS embed.FS
|
var frontendFS embed.FS
|
||||||
|
|
||||||
// ServeEmbeddedFrontend returns a Gin handler that serves embedded frontend assets
|
|
||||||
// and handles SPA routing by falling back to index.html for non-API routes.
|
|
||||||
func ServeEmbeddedFrontend() gin.HandlerFunc {
|
func ServeEmbeddedFrontend() gin.HandlerFunc {
|
||||||
distFS, err := fs.Sub(frontendFS, "dist")
|
distFS, err := fs.Sub(frontendFS, "dist")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -25,7 +25,6 @@ func ServeEmbeddedFrontend() gin.HandlerFunc {
|
|||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
path := c.Request.URL.Path
|
path := c.Request.URL.Path
|
||||||
|
|
||||||
// Skip API and gateway routes
|
|
||||||
if strings.HasPrefix(path, "/api/") ||
|
if strings.HasPrefix(path, "/api/") ||
|
||||||
strings.HasPrefix(path, "/v1/") ||
|
strings.HasPrefix(path, "/v1/") ||
|
||||||
strings.HasPrefix(path, "/setup/") ||
|
strings.HasPrefix(path, "/setup/") ||
|
||||||
@@ -34,7 +33,6 @@ func ServeEmbeddedFrontend() gin.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to serve static file
|
|
||||||
cleanPath := strings.TrimPrefix(path, "/")
|
cleanPath := strings.TrimPrefix(path, "/")
|
||||||
if cleanPath == "" {
|
if cleanPath == "" {
|
||||||
cleanPath = "index.html"
|
cleanPath = "index.html"
|
||||||
@@ -47,7 +45,6 @@ func ServeEmbeddedFrontend() gin.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPA fallback: serve index.html for all other routes
|
|
||||||
serveIndexHTML(c, distFS)
|
serveIndexHTML(c, distFS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,7 +69,6 @@ func serveIndexHTML(c *gin.Context, fsys fs.FS) {
|
|||||||
c.Abort()
|
c.Abort()
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasEmbeddedFrontend checks if frontend assets are embedded
|
|
||||||
func HasEmbeddedFrontend() bool {
|
func HasEmbeddedFrontend() bool {
|
||||||
_, err := frontendFS.ReadFile("dist/index.html")
|
_, err := frontendFS.ReadFile("dist/index.html")
|
||||||
return err == nil
|
return err == nil
|
||||||
Reference in New Issue
Block a user