mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-09 16:23:27 +08:00
- 修复 Windows、macOS 与关于页的品牌图标展示及资源裁切问题 - 补齐 SQLite 表记录数与 SELECT 查询结果渲染 - 增加品牌图标和 SQLite 回归测试
103 lines
3.2 KiB
Go
103 lines
3.2 KiB
Go
package app
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
"image"
|
|
_ "image/png"
|
|
"strings"
|
|
|
|
"GoNavi-Wails/internal/connection"
|
|
"GoNavi-Wails/internal/logger"
|
|
)
|
|
|
|
const applicationBrandIconMaxPNGBytes = 4 * 1024 * 1024
|
|
|
|
var (
|
|
errApplicationBrandIconPayloadEmpty = errors.New("empty icon payload")
|
|
errApplicationBrandIconPayloadInvalid = errors.New("invalid PNG icon payload")
|
|
)
|
|
|
|
// SetApplicationBrandIcon updates the OS application icon (macOS Dock) from a
|
|
// PNG payload. Frontend may pass raw base64 or a data URL (data:image/png;base64,...).
|
|
func (a *App) SetApplicationBrandIcon(imageBase64 string) (result connection.QueryResult) {
|
|
defer func() {
|
|
if recovered := recover(); recovered != nil {
|
|
logger.Errorf("设置应用图标失败:%v", recovered)
|
|
result = connection.QueryResult{
|
|
Success: false,
|
|
Message: a.appText("app.backend.error.set_brand_icon_failed", map[string]any{
|
|
"detail": fmt.Sprint(recovered),
|
|
}),
|
|
}
|
|
}
|
|
}()
|
|
|
|
png, err := decodeApplicationBrandIconPayload(imageBase64)
|
|
if err != nil {
|
|
key := "app.backend.error.set_brand_icon_invalid"
|
|
if errors.Is(err, errApplicationBrandIconPayloadEmpty) {
|
|
key = "app.backend.error.set_brand_icon_empty"
|
|
}
|
|
return connection.QueryResult{Success: false, Message: a.appText(key, map[string]any{
|
|
"detail": err.Error(),
|
|
})}
|
|
}
|
|
if err := setApplicationIconPNG(png); err != nil {
|
|
return connection.QueryResult{Success: false, Message: a.appText("app.backend.error.set_brand_icon_failed", map[string]any{
|
|
"detail": err.Error(),
|
|
})}
|
|
}
|
|
return connection.QueryResult{Success: true, Message: a.appText("app.backend.message.brand_icon_updated", nil)}
|
|
}
|
|
|
|
func decodeApplicationBrandIconPayload(imageBase64 string) ([]byte, error) {
|
|
raw := strings.TrimSpace(imageBase64)
|
|
if raw == "" {
|
|
return nil, errApplicationBrandIconPayloadEmpty
|
|
}
|
|
if idx := strings.Index(raw, ","); idx >= 0 && strings.Contains(strings.ToLower(raw[:idx]), "base64") {
|
|
raw = raw[idx+1:]
|
|
}
|
|
raw = strings.Map(func(r rune) rune {
|
|
if r == '\n' || r == '\r' || r == ' ' || r == '\t' {
|
|
return -1
|
|
}
|
|
return r
|
|
}, raw)
|
|
if raw == "" {
|
|
return nil, errApplicationBrandIconPayloadEmpty
|
|
}
|
|
if len(raw) > base64.StdEncoding.EncodedLen(applicationBrandIconMaxPNGBytes+1) {
|
|
return nil, fmt.Errorf("%w: payload exceeds %d MiB", errApplicationBrandIconPayloadInvalid, applicationBrandIconMaxPNGBytes/(1024*1024))
|
|
}
|
|
|
|
var decodeErr error
|
|
for _, encoding := range []*base64.Encoding{
|
|
base64.StdEncoding,
|
|
base64.RawStdEncoding,
|
|
base64.URLEncoding,
|
|
base64.RawURLEncoding,
|
|
} {
|
|
png, err := encoding.DecodeString(raw)
|
|
if err != nil {
|
|
decodeErr = err
|
|
continue
|
|
}
|
|
if len(png) > applicationBrandIconMaxPNGBytes {
|
|
return nil, fmt.Errorf("%w: decoded image exceeds %d MiB", errApplicationBrandIconPayloadInvalid, applicationBrandIconMaxPNGBytes/(1024*1024))
|
|
}
|
|
if _, format, err := image.DecodeConfig(bytes.NewReader(png)); err != nil || format != "png" {
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: %v", errApplicationBrandIconPayloadInvalid, err)
|
|
}
|
|
return nil, fmt.Errorf("%w: expected PNG image", errApplicationBrandIconPayloadInvalid)
|
|
}
|
|
return png, nil
|
|
}
|
|
|
|
return nil, fmt.Errorf("%w: %v", errApplicationBrandIconPayloadInvalid, decodeErr)
|
|
}
|