mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-09-18 06:34:54 +08:00
新增 bat-api 资源 bootstrap/分发 HTTP 服务、RPC release 发现、CDN path 分发、launcher 资源引导兼容、控制面中间件、OpenAPI 和 systemd 模板。 同步 Go 边界文档,明确 Rust bat 是资源生产者和同步运维入口,Go bat-api 是只读 bootstrap/分发服务,试验 Go CLI 产物为 bin/bat-go。 验证:未运行新命令;本轮已按要求停止重复构建/测试。
126 lines
3.6 KiB
Go
126 lines
3.6 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// RewriteServerInfoAddressables rewrites AddressablesCatalogUrlRoot values so
|
|
// clients fetch resources from publicBaseURL while leaving business API URLs
|
|
// untouched.
|
|
func RewriteServerInfoAddressables(raw []byte, publicBaseURL string) ([]byte, error) {
|
|
var doc map[string]any
|
|
if err := json.Unmarshal(raw, &doc); err != nil {
|
|
return nil, fmt.Errorf("parse server-info: %w", err)
|
|
}
|
|
groups, _ := doc["ConnectionGroups"].([]any)
|
|
for _, g := range groups {
|
|
group, ok := g.(map[string]any)
|
|
if !ok {
|
|
continue
|
|
}
|
|
rewriteGroupAddressables(group, publicBaseURL)
|
|
if overrides, ok := group["OverrideConnectionGroups"].([]any); ok {
|
|
for _, o := range overrides {
|
|
if og, ok := o.(map[string]any); ok {
|
|
rewriteGroupAddressables(og, publicBaseURL)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return json.Marshal(doc)
|
|
}
|
|
|
|
func rewriteGroupAddressables(group map[string]any, publicBaseURL string) {
|
|
raw, _ := group["AddressablesCatalogUrlRoot"].(string)
|
|
if raw == "" {
|
|
return
|
|
}
|
|
if rewritten, ok := rewriteAddressablesRoot(raw, publicBaseURL); ok {
|
|
group["AddressablesCatalogUrlRoot"] = rewritten
|
|
}
|
|
}
|
|
|
|
func rewriteAddressablesRoot(original, publicBaseURL string) (string, bool) {
|
|
original = strings.TrimRight(original, "/")
|
|
publicBaseURL = strings.TrimRight(publicBaseURL, "/")
|
|
// Already rewritten to this base.
|
|
if strings.HasPrefix(original, publicBaseURL+"/"+ClientPatchHost) ||
|
|
strings.HasPrefix(original, publicBaseURL+"/"+ServerInfoHost) {
|
|
return original, true
|
|
}
|
|
u, err := url.Parse(original)
|
|
if err != nil || u.Host == "" {
|
|
return "", false
|
|
}
|
|
// Keep host/path so CDN path mapping stays 1:1 with destination_for_url.
|
|
path := strings.TrimPrefix(u.Path, "/")
|
|
if path == "" {
|
|
return publicBaseURL + "/" + u.Host, true
|
|
}
|
|
return publicBaseURL + "/" + u.Host + "/" + path, true
|
|
}
|
|
|
|
// LoadServerInfoBytes loads server-info from an explicit file, or from the
|
|
// release tree under yostar-serverinfo host, or synthesizes a minimal document
|
|
// from the release snapshot addressables root.
|
|
func LoadServerInfoBytes(cfg Config, idx *ReleaseIndex, requestName string) ([]byte, error) {
|
|
if cfg.ServerInfoFile != "" {
|
|
return os.ReadFile(cfg.ServerInfoFile)
|
|
}
|
|
if idx != nil && idx.ResourceRoot != "" {
|
|
name := requestName
|
|
if name == "" {
|
|
name = "server-info.json"
|
|
}
|
|
// Official path layout.
|
|
candidates := []string{
|
|
filepath.Join(idx.ResourceRoot, ServerInfoHost, name),
|
|
filepath.Join(idx.ResourceRoot, ServerInfoHost, requestName),
|
|
}
|
|
for _, c := range candidates {
|
|
if c == "" {
|
|
continue
|
|
}
|
|
if data, err := os.ReadFile(c); err == nil {
|
|
return data, nil
|
|
}
|
|
}
|
|
// Any single json under server-info host.
|
|
dir := filepath.Join(idx.ResourceRoot, ServerInfoHost)
|
|
if entries, err := os.ReadDir(dir); err == nil {
|
|
for _, e := range entries {
|
|
if e.IsDir() || !strings.HasSuffix(e.Name(), ".json") {
|
|
continue
|
|
}
|
|
return os.ReadFile(filepath.Join(dir, e.Name()))
|
|
}
|
|
}
|
|
}
|
|
if idx != nil && idx.Snapshot != nil && idx.Snapshot.AddressablesRoot != "" {
|
|
doc := map[string]any{
|
|
"ConnectionGroups": []any{
|
|
map[string]any{
|
|
"Name": defaultString(idx.Snapshot.ConnectionGroupName, "Prod"),
|
|
"AddressablesCatalogUrlRoot": idx.Snapshot.AddressablesRoot,
|
|
"BundleVersion": idx.Snapshot.BundleVersion,
|
|
"IsLivePublished": true,
|
|
},
|
|
},
|
|
}
|
|
return json.Marshal(doc)
|
|
}
|
|
return nil, fmt.Errorf("server-info source not found")
|
|
}
|
|
|
|
func defaultString(v, fallback string) string {
|
|
if v == "" {
|
|
return fallback
|
|
}
|
|
return v
|
|
}
|