fix(dev): load runtime metadata without frontend build artifacts
This commit is contained in:
1 parent
c705d1ab33
commit
11f5e4fcf4
6 files changed
+96
-16
No files matched your search
@@ -69,6 +69,7 @@ scripts/dev.sh --host # 前端监听所有网卡,手机访问 http://<电脑
|
|||||||
```
|
```
|
||||||
|
|
||||||
浏览器打开 http://localhost:5173 ,首次使用直接设置登录密码。端口与数据目录可用 `API_PORT`、`WEB_PORT`、`RAWMARK_DATA` 覆盖,详见 `scripts/dev.sh --help`。
|
浏览器打开 http://localhost:5173 ,首次使用直接设置登录密码。端口与数据目录可用 `API_PORT`、`WEB_PORT`、`RAWMARK_DATA` 覆盖,详见 `scripts/dev.sh --help`。
|
||||||
|
开发模式从源码统计可选字体,日志导出的版本标为 `dev`,不读取已有 `web/dist`;生产模式使用前端构建清单与页面构建版本。
|
||||||
|
|
||||||
检查:
|
检查:
|
||||||
|
|
||||||
|
|||||||
+2
-16
@@ -6,7 +6,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -18,13 +17,13 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"rawmark/internal/backup"
|
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"rawmark/internal/api"
|
"rawmark/internal/api"
|
||||||
"rawmark/internal/auth"
|
"rawmark/internal/auth"
|
||||||
|
"rawmark/internal/backup"
|
||||||
"rawmark/internal/events"
|
"rawmark/internal/events"
|
||||||
"rawmark/internal/search"
|
"rawmark/internal/search"
|
||||||
"rawmark/internal/store"
|
"rawmark/internal/store"
|
||||||
@@ -84,23 +83,10 @@ func run(addr string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("读取内置字体统计失败: %w", err)
|
return fmt.Errorf("读取内置字体统计失败: %w", err)
|
||||||
}
|
}
|
||||||
// 构建标识与前端入口一致,不另建版本来源。
|
buildVersion, err := web.BuildVersion()
|
||||||
static := web.Dist()
|
|
||||||
html, err := fs.ReadFile(static, "index.html")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
buildVersion := ""
|
|
||||||
marker := []byte(`<meta name="rawmark-version" content="`)
|
|
||||||
if start := bytes.Index(html, marker); start >= 0 {
|
|
||||||
rest := html[start+len(marker):]
|
|
||||||
if end := bytes.IndexByte(rest, '"'); end >= 0 {
|
|
||||||
buildVersion = string(rest[:end])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if buildVersion == "" {
|
|
||||||
return fmt.Errorf("缺少构建版本")
|
|
||||||
}
|
|
||||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||||
defer stop()
|
defer stop()
|
||||||
for {
|
for {
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"embed"
|
"embed"
|
||||||
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,3 +21,19 @@ func Dist() fs.FS {
|
|||||||
}
|
}
|
||||||
return sub
|
return sub
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BuildVersion 与页面入口使用同一构建标识,生产产物缺失时明确失败。
|
||||||
|
func BuildVersion() (string, error) {
|
||||||
|
html, err := fs.ReadFile(Dist(), "index.html")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
marker := []byte(`<meta name="rawmark-version" content="`)
|
||||||
|
if start := bytes.Index(html, marker); start >= 0 {
|
||||||
|
rest := html[start+len(marker):]
|
||||||
|
if end := bytes.IndexByte(rest, '"'); end > 0 {
|
||||||
|
return string(rest[:end]), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("缺少构建版本")
|
||||||
|
}
|
||||||
@@ -5,6 +5,8 @@ package web
|
|||||||
import (
|
import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Dist 在开发模式下直接读取磁盘上的 web/dist(未构建时为空,页面由 Vite 开发服务器提供),
|
// Dist 在开发模式下直接读取磁盘上的 web/dist(未构建时为空,页面由 Vite 开发服务器提供),
|
||||||
@@ -12,3 +14,31 @@ import (
|
|||||||
func Dist() fs.FS {
|
func Dist() fs.FS {
|
||||||
return os.DirFS("web/dist")
|
return os.DirFS("web/dist")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 开发环境由 Vite 提供页面,不依赖 web/dist 中可能过期的构建产物。
|
||||||
|
func BuildVersion() (string, error) { return "dev", nil }
|
||||||
|
|
||||||
|
// FontStorage 按 Vite 构建使用的 fonts.css 统计可选字体,不包含 Maple 与 KaTeX。
|
||||||
|
func FontStorage() (int64, error) {
|
||||||
|
root := filepath.Join("web", "src", "themes")
|
||||||
|
css, err := os.ReadFile(filepath.Join(root, "fonts.css"))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
paths := regexp.MustCompile(`url\(['"]?(\.?/?fonts/[^)'"]+)`).FindAllSubmatch(css, -1)
|
||||||
|
seen := map[string]bool{}
|
||||||
|
var size int64
|
||||||
|
for _, match := range paths {
|
||||||
|
name := string(match[1])
|
||||||
|
if seen[name] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[name] = true
|
||||||
|
info, err := os.Stat(filepath.Join(root, name))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
size += info.Size()
|
||||||
|
}
|
||||||
|
return size, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
//go:build dev
|
||||||
|
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDevelopmentMetadataDoesNotRequireBuild(t *testing.T) {
|
||||||
|
root := t.TempDir()
|
||||||
|
t.Chdir(root)
|
||||||
|
themes := filepath.Join("web", "src", "themes")
|
||||||
|
if err := os.MkdirAll(filepath.Join(themes, "fonts"), 0700); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
css := `@font-face { src: url(./fonts/a.woff2); }
|
||||||
|
@font-face { src: url('./fonts/a.woff2'); }
|
||||||
|
@font-face { src: url("./fonts/b.woff2"); }`
|
||||||
|
if err := os.WriteFile(filepath.Join(themes, "fonts.css"), []byte(css), 0600); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for name, data := range map[string]string{"a.woff2": "abc", "b.woff2": "defgh"} {
|
||||||
|
if err := os.WriteFile(filepath.Join(themes, "fonts", name), []byte(data), 0600); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
version, err := BuildVersion()
|
||||||
|
if err != nil || version != "dev" {
|
||||||
|
t.Fatalf("开发版本不应依赖构建产物:%q,%v", version, err)
|
||||||
|
}
|
||||||
|
size, err := FontStorage()
|
||||||
|
if err != nil || size != 8 {
|
||||||
|
t.Fatalf("开发字体统计应按源文件去重:%d,%v", size, err)
|
||||||
|
}
|
||||||
|
if err = os.Remove(filepath.Join(themes, "fonts", "b.woff2")); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err = FontStorage(); err == nil {
|
||||||
|
t.Fatal("字体源文件缺失时不得返回零值伪装成功")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build !dev
|
||||||
|
|
||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
Reference in new issue
Block a user