优化发布构建资源加载
This commit is contained in:
1 parent
e3dcdb73cc
commit
d3690832f9
16 files changed
+270
-161
No files matched your search
+1
-2
@@ -14,8 +14,7 @@
|
||||
*~
|
||||
|
||||
# 构建产物
|
||||
# bin/agents/ 不能忽略,Agent Docker 镜像构建需要使用
|
||||
web/dist/
|
||||
# bin/agents/ 和 web/dist/ 不能忽略,运行时从本地文件加载
|
||||
web/node_modules/
|
||||
|
||||
# 数据目录
|
||||
|
||||
@@ -51,11 +51,12 @@ jobs:
|
||||
# 验证 index.html 模板代码
|
||||
- name: 验证 index.html 模板代码
|
||||
run: |
|
||||
if grep -q "{{.CustomJS}}" web/dist/index.html && grep -q "{{.CustomCSS}}" web/dist/index.html; then
|
||||
echo "✓ 自定义模板代码已成功添加到 index.html"
|
||||
if grep -Fq "__PIKA_CUSTOM_JS__" web/dist/index.html && grep -Fq "__PIKA_CUSTOM_CSS__" web/dist/index.html && grep -Fq "[[.SystemNameZh]]" web/dist/index.html; then
|
||||
cp web/dist/index.html web/dist/index.html.tmpl
|
||||
echo "✓ index.html 模板已准备完成"
|
||||
else
|
||||
echo "✗ 自定义模板代码未找到,重新执行 patch-html"
|
||||
go run cmd/patch-html/main.go
|
||||
echo "✗ index.html 模板代码未找到"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 列出编译产物
|
||||
@@ -116,4 +117,4 @@ jobs:
|
||||
echo "${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**平台:** linux/amd64" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**平台:** linux/amd64" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -57,11 +57,12 @@ jobs:
|
||||
# 验证 index.html 模板代码
|
||||
- name: 验证 index.html 模板代码
|
||||
run: |
|
||||
if grep -q "{{.CustomJS}}" web/dist/index.html && grep -q "{{.CustomCSS}}" web/dist/index.html; then
|
||||
echo "✓ 自定义模板代码已成功添加到 index.html"
|
||||
if grep -Fq "__PIKA_CUSTOM_JS__" web/dist/index.html && grep -Fq "__PIKA_CUSTOM_CSS__" web/dist/index.html && grep -Fq "[[.SystemNameZh]]" web/dist/index.html; then
|
||||
cp web/dist/index.html web/dist/index.html.tmpl
|
||||
echo "✓ index.html 模板已准备完成"
|
||||
else
|
||||
echo "✗ 自定义模板代码未找到,重新执行 patch-html"
|
||||
go run cmd/patch-html/main.go
|
||||
echo "✗ index.html 模板代码未找到"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 列出编译产物
|
||||
|
||||
@@ -15,6 +15,9 @@ ARG TARGETARCH
|
||||
|
||||
# 从外部编译的产物复制文件
|
||||
COPY ./bin/pika-linux-${TARGETARCH} ./pika
|
||||
COPY ./bin/agents ./bin/agents
|
||||
COPY ./web/dist ./web/dist
|
||||
COPY ./web/public/logo.png ./web/public/logo.png
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 8080
|
||||
|
||||
@@ -9,9 +9,8 @@ GIT_REVISION=$(shell git rev-parse HEAD)
|
||||
GO_VERSION=$(shell go version)
|
||||
BUILD_TIME=$(shell date +%Y-%m-%d_%H:%M:%S)
|
||||
|
||||
# 是否使用最佳压缩(release 时设置为 true)
|
||||
RELEASE ?= false
|
||||
UPX_FLAGS=$(if $(filter true,$(RELEASE)),--best,)
|
||||
# UPX 默认使用快速压缩。可通过 make UPX_FLAGS=... 覆盖。
|
||||
UPX_FLAGS ?=
|
||||
|
||||
# Go 构建参数
|
||||
LDFLAGS=-s -w -X 'github.com/dushixiang/pika/pkg/version.Version=$(VERSION)' -X 'github.com/dushixiang/pika/pkg/version.AgentVersion=$(AGENT_VERSION)'
|
||||
@@ -21,7 +20,6 @@ GOFLAGS=CGO_ENABLED=0
|
||||
# 构建前端
|
||||
build-web:
|
||||
cd web && yarn && yarn build
|
||||
@go run cmd/patch-html/main.go
|
||||
|
||||
# 构建服务端(开发)
|
||||
build-server:
|
||||
@@ -64,11 +62,11 @@ build-agents:
|
||||
@echo "All agents compressed successfully!"
|
||||
@ls -lh bin/agents/
|
||||
|
||||
# 构建所有(release版本,使用最佳压缩)
|
||||
# 构建所有(release版本)
|
||||
build-release:
|
||||
make build-web
|
||||
make build-agents RELEASE=true
|
||||
make build-servers RELEASE=true
|
||||
make build-agents
|
||||
make build-servers
|
||||
|
||||
# 清理编译产物
|
||||
clean:
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package pika
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"io/fs"
|
||||
)
|
||||
|
||||
//go:embed bin/agents/*
|
||||
var agentsFS embed.FS
|
||||
|
||||
func AgentFS() fs.FS {
|
||||
sub, _ := fs.Sub(agentsFS, "bin/agents")
|
||||
return sub
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 读取 web/dist/index.html
|
||||
htmlPath := "web/dist/index.html"
|
||||
content, err := os.ReadFile(htmlPath)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "读取文件失败: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
html := string(content)
|
||||
|
||||
// 检查是否已经包含模板代码(避免重复插入)
|
||||
if strings.Contains(html, "{{.CustomJS}}") {
|
||||
fmt.Println("模板代码已存在,跳过插入")
|
||||
return
|
||||
}
|
||||
|
||||
// 在 </head> 前插入模板代码
|
||||
customTemplate := ` <script type="application/javascript">
|
||||
{{.CustomJS}}
|
||||
</script>
|
||||
<style type="text/css">
|
||||
{{.CustomCSS}}
|
||||
</style>
|
||||
`
|
||||
|
||||
// 替换 </head>
|
||||
newHTML := strings.Replace(html, "</head>", customTemplate+"</head>", 1)
|
||||
|
||||
// 写回文件
|
||||
err = os.WriteFile(htmlPath, []byte(newHTML), 0644)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "写入文件失败: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Println("✓ 成功添加自定义模板到 index.html")
|
||||
}
|
||||
+16
-32
@@ -1,26 +1,22 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/dushixiang/pika/internal/assets"
|
||||
"github.com/dushixiang/pika/internal/config"
|
||||
"github.com/dushixiang/pika/internal/handler"
|
||||
"github.com/dushixiang/pika/internal/migrate"
|
||||
"github.com/dushixiang/pika/internal/models"
|
||||
"github.com/dushixiang/pika/internal/scheduler"
|
||||
"github.com/dushixiang/pika/internal/service"
|
||||
"github.com/dushixiang/pika/pkg/replace"
|
||||
"github.com/dushixiang/pika/pkg/version"
|
||||
"github.com/dushixiang/pika/web"
|
||||
"github.com/google/uuid"
|
||||
"github.com/spf13/afero/mem"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/go-orz/orz"
|
||||
@@ -111,12 +107,14 @@ func setup(app *orz.App) error {
|
||||
go components.PublicIPService.Run(ctx)
|
||||
|
||||
// 设置API
|
||||
setupApi(app, components)
|
||||
if err := setupApi(app, components); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupApi(app *orz.App, components *AppComponents) {
|
||||
func setupApi(app *orz.App, components *AppComponents) error {
|
||||
logger := app.Logger()
|
||||
e := app.GetEcho()
|
||||
|
||||
@@ -130,10 +128,11 @@ func setupApi(app *orz.App, components *AppComponents) {
|
||||
},
|
||||
}))
|
||||
|
||||
indexTemplate, err := template.New("index").Parse(web.IndexHtml())
|
||||
if err != nil {
|
||||
logger.Fatal("failed to parse index.html", zap.Error(err))
|
||||
webDir := assets.WebDir()
|
||||
if err := assets.RenderUIFilesInDir(webDir, components.PropertyService); err != nil {
|
||||
return fmt.Errorf("render ui files: %w", err)
|
||||
}
|
||||
|
||||
// 静态文件服务
|
||||
e.Use(middleware.StaticWithConfig(middleware.StaticConfig{
|
||||
Skipper: func(c echo.Context) bool {
|
||||
@@ -145,34 +144,17 @@ func setupApi(app *orz.App, components *AppComponents) {
|
||||
if strings.HasPrefix(c.Request().RequestURI, "/ws") {
|
||||
return true
|
||||
}
|
||||
// 不暴露模板文件
|
||||
if strings.HasSuffix(c.Request().RequestURI, ".tmpl") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
Index: "index.html",
|
||||
HTML5: true,
|
||||
Browse: false,
|
||||
IgnoreBase: false,
|
||||
Filesystem: replace.FS(http.FS(web.Assets()), func(name string, file http.File) (http.File, error) {
|
||||
if name == "index.html" {
|
||||
fileData := mem.CreateFile(name)
|
||||
fileHandle := mem.NewFileHandle(fileData)
|
||||
|
||||
systemConfig, err := components.PropertyService.GetSystemConfig(context.Background())
|
||||
if err != nil {
|
||||
return file, nil
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
err = indexTemplate.Execute(&buf, systemConfig)
|
||||
if err != nil {
|
||||
return file, err
|
||||
}
|
||||
if _, err := fileHandle.Write(buf.Bytes()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fileHandle, nil
|
||||
}
|
||||
return file, nil
|
||||
}),
|
||||
Filesystem: http.Dir(webDir),
|
||||
}))
|
||||
|
||||
customValidator := CustomValidator{Validator: validator.New()}
|
||||
@@ -321,6 +303,8 @@ func setupApi(app *orz.App, components *AppComponents) {
|
||||
|
||||
// GitHub 认证路由(如果启用)
|
||||
publicApi.POST("/auth/github/callback", components.AccountHandler.GitHubLogin)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func autoMigrate(database *gorm.DB) error {
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package assets
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultWebDir = "web/dist"
|
||||
defaultAgentDir = "bin/agents"
|
||||
defaultLogoPath = "web/public/logo.png"
|
||||
)
|
||||
|
||||
func WebDir() string {
|
||||
if dir := os.Getenv("PIKA_WEB_DIR"); dir != "" {
|
||||
return dir
|
||||
}
|
||||
return defaultWebDir
|
||||
}
|
||||
|
||||
func AgentDir() string {
|
||||
if dir := os.Getenv("PIKA_AGENT_DIR"); dir != "" {
|
||||
return dir
|
||||
}
|
||||
return defaultAgentDir
|
||||
}
|
||||
|
||||
func AgentPath(filename string) string {
|
||||
return filepath.Join(AgentDir(), filename)
|
||||
}
|
||||
|
||||
func DefaultLogoBase64() string {
|
||||
for _, path := range []string{
|
||||
os.Getenv("PIKA_DEFAULT_LOGO_PATH"),
|
||||
defaultLogoPath,
|
||||
filepath.Join(WebDir(), "logo.png"),
|
||||
} {
|
||||
if path == "" {
|
||||
continue
|
||||
}
|
||||
logo, err := os.ReadFile(path)
|
||||
if err == nil && len(logo) > 0 {
|
||||
return "data:image/png;base64," + base64.StdEncoding.EncodeToString(logo)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package assets
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/dushixiang/pika/internal/models"
|
||||
)
|
||||
|
||||
type SystemConfigProvider interface {
|
||||
GetSystemConfig(context.Context) (*models.SystemConfig, error)
|
||||
}
|
||||
|
||||
func RenderUIFiles(provider SystemConfigProvider) error {
|
||||
return RenderUIFilesInDir(WebDir(), provider)
|
||||
}
|
||||
|
||||
func RenderUIFilesInDir(dir string, provider SystemConfigProvider) error {
|
||||
if err := ensureIndexTemplate(dir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
systemConfig, err := provider.GetSystemConfig(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return renderIndexHTML(dir, systemConfig)
|
||||
}
|
||||
|
||||
func ensureIndexTemplate(dir string) error {
|
||||
tmplPath := filepath.Join(dir, "index.html.tmpl")
|
||||
if _, err := os.Stat(tmplPath); err == nil {
|
||||
return nil
|
||||
} else if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
indexPath := filepath.Join(dir, "index.html")
|
||||
src, err := os.ReadFile(indexPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.WriteFile(tmplPath, src, 0644)
|
||||
}
|
||||
|
||||
func renderIndexHTML(dir string, systemConfig *models.SystemConfig) error {
|
||||
tmplPath := filepath.Join(dir, "index.html.tmpl")
|
||||
tmpl, err := template.New(filepath.Base(tmplPath)).Delims("[[", "]]").ParseFiles(tmplPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := struct {
|
||||
*models.SystemConfig
|
||||
}{
|
||||
SystemConfig: systemConfig,
|
||||
}
|
||||
|
||||
outPath := filepath.Join(dir, "index.html")
|
||||
var rendered strings.Builder
|
||||
if err := tmpl.Execute(&rendered, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
html := strings.ReplaceAll(rendered.String(), "/*__PIKA_CUSTOM_JS__*/", systemConfig.CustomJS)
|
||||
html = strings.ReplaceAll(html, "/*__PIKA_CUSTOM_CSS__*/", systemConfig.CustomCSS)
|
||||
|
||||
out, err := os.Create(outPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
_, err = out.WriteString(html)
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package assets
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/dushixiang/pika/internal/models"
|
||||
)
|
||||
|
||||
type testSystemConfigProvider struct {
|
||||
config *models.SystemConfig
|
||||
}
|
||||
|
||||
func (p testSystemConfigProvider) GetSystemConfig(context.Context) (*models.SystemConfig, error) {
|
||||
return p.config, nil
|
||||
}
|
||||
|
||||
func TestRenderUIFilesInDir(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
indexPath := filepath.Join(dir, "index.html")
|
||||
src := `<!doctype html>
|
||||
<title>[[.SystemNameZh]][[if and .SystemNameZh .SystemNameEn]] | [[end]][[.SystemNameEn]]</title>
|
||||
<script>
|
||||
window.SystemConfig = {
|
||||
SystemNameZh: "[[.SystemNameZh]]",
|
||||
SystemNameEn: "[[.SystemNameEn]]",
|
||||
ICPCode: "[[.ICPCode]]",
|
||||
DefaultView: "[[.DefaultView]]",
|
||||
Version: "[[.Version]]",
|
||||
};
|
||||
</script>
|
||||
<script>/*__PIKA_CUSTOM_JS__*/</script>
|
||||
<style>/*__PIKA_CUSTOM_CSS__*/</style>`
|
||||
if err := os.WriteFile(indexPath, []byte(src), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
provider := testSystemConfigProvider{
|
||||
config: &models.SystemConfig{
|
||||
SystemNameZh: "皮卡监控",
|
||||
SystemNameEn: "Pika Monitor",
|
||||
ICPCode: "ICP-1",
|
||||
DefaultView: "grid",
|
||||
CustomJS: `console.log("pika");`,
|
||||
CustomCSS: `body { color: red; }`,
|
||||
Version: "v1.2.3",
|
||||
},
|
||||
}
|
||||
if err := RenderUIFilesInDir(dir, provider); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rendered, err := os.ReadFile(indexPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
html := string(rendered)
|
||||
for _, want := range []string{
|
||||
"<title>皮卡监控 | Pika Monitor</title>",
|
||||
`SystemNameZh: "皮卡监控"`,
|
||||
`Version: "v1.2.3"`,
|
||||
`console.log("pika");`,
|
||||
`body { color: red; }`,
|
||||
} {
|
||||
if !strings.Contains(html, want) {
|
||||
t.Fatalf("rendered index.html missing %q:\n%s", want, html)
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(dir, "index.html.tmpl")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,10 @@ package handler
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/dushixiang/pika"
|
||||
"github.com/dushixiang/pika/internal/assets"
|
||||
"github.com/dushixiang/pika/pkg/version"
|
||||
"github.com/go-orz/orz"
|
||||
"github.com/labstack/echo/v4"
|
||||
@@ -22,6 +23,9 @@ func (h *AgentHandler) GetAgentVersion(c echo.Context) error {
|
||||
// DownloadAgent 下载 Agent 二进制文件
|
||||
func (h *AgentHandler) DownloadAgent(c echo.Context) error {
|
||||
filename := c.Param("filename")
|
||||
if filename == "" || strings.ContainsAny(filename, `/\`) {
|
||||
return orz.NewError(400, "无效的 Agent 文件名")
|
||||
}
|
||||
|
||||
// 校验 API Key
|
||||
apiKey := c.QueryParam("key")
|
||||
@@ -43,12 +47,13 @@ func (h *AgentHandler) DownloadAgent(c echo.Context) error {
|
||||
h.logger.Info("download agent allowed by ip whitelist", zap.String("ip", clientIP), zap.String("filename", filename))
|
||||
}
|
||||
|
||||
// 从嵌入的文件系统读取
|
||||
agentFile, err := pika.AgentFS().Open(fmt.Sprintf("pika-%s", filename))
|
||||
agentFilename := fmt.Sprintf("pika-%s", filename)
|
||||
agentFile, err := os.Open(assets.AgentPath(agentFilename))
|
||||
if err != nil {
|
||||
h.logger.Error("agent binary not found", zap.String("filename", filename), zap.Error(err))
|
||||
return orz.NewError(404, "未找到对应平台的 Agent 二进制文件")
|
||||
}
|
||||
defer agentFile.Close()
|
||||
|
||||
// 设置响应头
|
||||
c.Response().Header().Set("Content-Type", "application/octet-stream")
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/dushixiang/pika/internal/assets"
|
||||
"github.com/dushixiang/pika/internal/models"
|
||||
"github.com/dushixiang/pika/internal/service"
|
||||
"github.com/go-orz/orz"
|
||||
@@ -88,6 +89,12 @@ func (h *PropertyHandler) SetProperty(c echo.Context) error {
|
||||
h.logger.Error("设置属性失败", zap.String("id", id), zap.Error(err))
|
||||
return orz.NewError(500, "设置属性失败")
|
||||
}
|
||||
if id == service.PropertyIDSystemConfig {
|
||||
if err := assets.RenderUIFiles(h.service); err != nil {
|
||||
h.logger.Error("渲染前端模板失败", zap.String("id", id), zap.Error(err))
|
||||
return orz.NewError(500, "渲染前端模板失败")
|
||||
}
|
||||
}
|
||||
|
||||
return orz.Ok(c, orz.Map{})
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/dushixiang/pika/internal/assets"
|
||||
"github.com/dushixiang/pika/internal/models"
|
||||
"github.com/dushixiang/pika/internal/repo"
|
||||
"github.com/dushixiang/pika/pkg/version"
|
||||
"github.com/dushixiang/pika/web"
|
||||
"github.com/go-orz/cache"
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
@@ -347,7 +347,7 @@ func (s *PropertyService) InitializeDefaultConfigs(ctx context.Context) error {
|
||||
Value: models.SystemConfig{
|
||||
SystemNameZh: "皮卡监控",
|
||||
SystemNameEn: "Pika Monitor",
|
||||
LogoBase64: web.DefaultLogoBase64(),
|
||||
LogoBase64: assets.DefaultLogoBase64(),
|
||||
ICPCode: "",
|
||||
DefaultView: "grid",
|
||||
},
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"encoding/base64"
|
||||
"io/fs"
|
||||
)
|
||||
|
||||
//go:embed dist/*
|
||||
var distFS embed.FS
|
||||
|
||||
func Assets() fs.FS {
|
||||
sub, _ := fs.Sub(distFS, "dist")
|
||||
return sub
|
||||
}
|
||||
|
||||
//go:embed dist/index.html
|
||||
var indexHtml string
|
||||
|
||||
func IndexHtml() string {
|
||||
return indexHtml
|
||||
}
|
||||
|
||||
//go:embed public/logo.png
|
||||
var defaultLogo []byte
|
||||
|
||||
func DefaultLogo() []byte {
|
||||
return defaultLogo
|
||||
}
|
||||
|
||||
// DefaultLogoBase64 返回默认 Logo 的 base64 编码(data URI 格式)
|
||||
func DefaultLogoBase64() string {
|
||||
if len(defaultLogo) == 0 {
|
||||
return ""
|
||||
}
|
||||
// 编码为 base64 并添加 data URI 前缀
|
||||
encoded := base64.StdEncoding.EncodeToString(defaultLogo)
|
||||
return "data:image/png;base64," + encoded
|
||||
}
|
||||
+13
-7
@@ -5,16 +5,22 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/api/logo" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>{{.SystemNameZh}}{{if and .SystemNameZh .SystemNameEn}} | {{end}}{{.SystemNameEn}}</title>
|
||||
<title>[[.SystemNameZh]][[if and .SystemNameZh .SystemNameEn]] | [[end]][[.SystemNameEn]]</title>
|
||||
<script type="application/javascript">
|
||||
window.SystemConfig = {
|
||||
SystemNameZh: "{{.SystemNameZh}}",
|
||||
SystemNameEn: "{{.SystemNameEn}}",
|
||||
ICPCode: "{{.ICPCode}}",
|
||||
DefaultView: "{{.DefaultView}}",
|
||||
Version: "{{.Version}}",
|
||||
SystemNameZh: "[[.SystemNameZh]]",
|
||||
SystemNameEn: "[[.SystemNameEn]]",
|
||||
ICPCode: "[[.ICPCode]]",
|
||||
DefaultView: "[[.DefaultView]]",
|
||||
Version: "[[.Version]]",
|
||||
};
|
||||
</script>
|
||||
<script type="application/javascript">
|
||||
/*__PIKA_CUSTOM_JS__*/
|
||||
</script>
|
||||
<style>
|
||||
/*__PIKA_CUSTOM_CSS__*/
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -22,4 +28,4 @@
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
Reference in new issue
Block a user