- Introduced handlers for the new /setting/* endpoints in the OpenAPI module. - Updated import statements to include the new setting package for better organization. - Enhanced routing capabilities to support the new settings functionality.
255 lines
5.9 KiB
Go
255 lines
5.9 KiB
Go
package setting
|
|
|
|
import (
|
|
_ "embed"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"runtime"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/yaoapp/yao/commercial"
|
|
"github.com/yaoapp/yao/config"
|
|
"github.com/yaoapp/yao/openapi/response"
|
|
"github.com/yaoapp/yao/share"
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
const cdnBase = "https://get.yaoapps.com/yao"
|
|
|
|
// update check cache (package-level, protected by mutex)
|
|
var (
|
|
updateCache *CheckUpdateResult
|
|
updateCacheTime time.Time
|
|
updateMu sync.Mutex
|
|
cacheTTL = 10 * time.Minute
|
|
)
|
|
|
|
// handleSystemInfo returns aggregated system information.
|
|
// GET /setting/system?locale=zh-cn
|
|
func handleSystemInfo(c *gin.Context) {
|
|
locale := strings.ToLower(c.DefaultQuery("locale", "en-us"))
|
|
|
|
env := share.App.Option["env"]
|
|
environment, _ := env.(string)
|
|
if environment == "" {
|
|
environment = config.Conf.Mode
|
|
}
|
|
if environment == "" {
|
|
environment = "production"
|
|
}
|
|
|
|
listen := fmt.Sprintf("%s:%d", config.Conf.Host, config.Conf.Port)
|
|
sessionStore := config.Conf.Session.Store
|
|
if sessionStore == "" {
|
|
sessionStore = "file"
|
|
}
|
|
|
|
lang := langFromLocale(locale)
|
|
lic := commercial.License
|
|
deployment := lic.Edition
|
|
if deployment == "" {
|
|
deployment = "community"
|
|
}
|
|
|
|
var licenseKey string
|
|
if lic.Valid && lic.SerialNumber != "" {
|
|
licenseKey = lic.SerialNumber
|
|
}
|
|
|
|
data := SystemInfoData{
|
|
App: AppInfo{
|
|
Name: share.App.Name,
|
|
Short: share.App.Short,
|
|
Description: share.App.Description,
|
|
Logo: "/api/__yao/app/icons/app.png",
|
|
Version: share.App.Version,
|
|
},
|
|
Deployment: deployment,
|
|
DeploymentLabel: resolveLabel(promFile.Labels.Deployment, deployment, lang, deployment),
|
|
LicenseKey: licenseKey,
|
|
Environment: environment,
|
|
EnvironmentLabel: resolveLabel(promFile.Labels.Environment, environment, lang, environment),
|
|
Server: VersionInfo{
|
|
Version: share.VERSION,
|
|
BuildDate: share.PRVERSION,
|
|
CommitSHA: share.PRVERSION,
|
|
},
|
|
Client: VersionInfo{
|
|
Version: share.CUI,
|
|
BuildDate: share.PRCUI,
|
|
CommitSHA: share.PRCUI,
|
|
},
|
|
Technical: TechnicalInfo{
|
|
Listen: listen,
|
|
DBDriver: config.Conf.DB.Driver,
|
|
SessionStore: sessionStore,
|
|
},
|
|
Promotions: buildPromotions(deployment, locale),
|
|
}
|
|
|
|
response.RespondWithSuccess(c, http.StatusOK, data)
|
|
}
|
|
|
|
//go:embed promotions.yml
|
|
var promotionsYML []byte
|
|
|
|
type promotionEntry struct {
|
|
ID string `yaml:"id"`
|
|
Link string `yaml:"link"`
|
|
I18n map[string]promotionLocale `yaml:"i18n"`
|
|
}
|
|
|
|
type promotionLocale struct {
|
|
Title string `yaml:"title"`
|
|
Desc string `yaml:"desc"`
|
|
Label string `yaml:"label"`
|
|
}
|
|
|
|
type promotionsFile struct {
|
|
Labels struct {
|
|
Deployment map[string]map[string]string `yaml:"deployment"`
|
|
Environment map[string]map[string]string `yaml:"environment"`
|
|
} `yaml:"labels"`
|
|
Community []promotionEntry `yaml:"community"`
|
|
Enterprise []promotionEntry `yaml:"enterprise"`
|
|
Cloud []promotionEntry `yaml:"cloud"`
|
|
}
|
|
|
|
var promFile promotionsFile
|
|
|
|
func init() {
|
|
yaml.Unmarshal(promotionsYML, &promFile)
|
|
}
|
|
|
|
func resolveLabel(m map[string]map[string]string, key, lang, fallback string) string {
|
|
if langs, ok := m[key]; ok {
|
|
if v, ok := langs[lang]; ok {
|
|
return v
|
|
}
|
|
if v, ok := langs["en"]; ok {
|
|
return v
|
|
}
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
func langFromLocale(locale string) string {
|
|
if strings.HasPrefix(locale, "zh") {
|
|
return "zh"
|
|
}
|
|
return "en"
|
|
}
|
|
|
|
func buildPromotions(deployment, locale string) []Promotion {
|
|
lang := langFromLocale(locale)
|
|
|
|
var entries []promotionEntry
|
|
switch deployment {
|
|
case "community":
|
|
entries = promFile.Community
|
|
case "enterprise":
|
|
entries = promFile.Enterprise
|
|
case "cloud":
|
|
entries = promFile.Cloud
|
|
}
|
|
if len(entries) == 0 {
|
|
return nil
|
|
}
|
|
|
|
promos := make([]Promotion, 0, len(entries))
|
|
for _, e := range entries {
|
|
loc, ok := e.I18n[lang]
|
|
if !ok {
|
|
loc = e.I18n["en"]
|
|
}
|
|
promos = append(promos, Promotion{
|
|
ID: e.ID,
|
|
Title: loc.Title,
|
|
Desc: loc.Desc,
|
|
Link: e.Link,
|
|
Label: loc.Label,
|
|
})
|
|
}
|
|
return promos
|
|
}
|
|
|
|
// handleSystemCheckUpdate checks for a newer engine release.
|
|
// Uses the same CDN source as `yao upgrade` and yao-desktop:
|
|
//
|
|
// GET https://get.yaoapps.com/yao/latest.json
|
|
//
|
|
// POST /setting/system/check-update
|
|
func handleSystemCheckUpdate(c *gin.Context) {
|
|
updateMu.Lock()
|
|
if updateCache != nil && time.Since(updateCacheTime) < cacheTTL {
|
|
result := *updateCache
|
|
updateMu.Unlock()
|
|
response.RespondWithSuccess(c, http.StatusOK, result)
|
|
return
|
|
}
|
|
updateMu.Unlock()
|
|
|
|
result := fetchLatestVersion()
|
|
|
|
updateMu.Lock()
|
|
updateCache = &result
|
|
updateCacheTime = time.Now()
|
|
updateMu.Unlock()
|
|
|
|
response.RespondWithSuccess(c, http.StatusOK, result)
|
|
}
|
|
|
|
// cdnLatest mirrors the JSON structure of get.yaoapps.com/yao/latest.json
|
|
// (same format used by cmd/upgrade.go and yao-desktop updater.rs).
|
|
type cdnLatest struct {
|
|
Version string `json:"version"`
|
|
ReleasedAt string `json:"released_at"`
|
|
Assets map[string]string `json:"assets"`
|
|
}
|
|
|
|
func fetchLatestVersion() CheckUpdateResult {
|
|
current := strings.TrimPrefix(share.VERSION, "v")
|
|
base := CheckUpdateResult{HasUpdate: false, CurrentVersion: current}
|
|
|
|
url := cdnBase + "/latest.json"
|
|
client := &http.Client{Timeout: 15 * time.Second}
|
|
req, err := http.NewRequest("GET", url, nil)
|
|
if err != nil {
|
|
return base
|
|
}
|
|
req.Header.Set("User-Agent", fmt.Sprintf("yao/%s", share.VERSION))
|
|
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return base
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return base
|
|
}
|
|
|
|
var data cdnLatest
|
|
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
|
return base
|
|
}
|
|
|
|
latest := strings.TrimPrefix(data.Version, "v")
|
|
if latest == "" {
|
|
return base
|
|
}
|
|
|
|
platformKey := fmt.Sprintf("%s-%s", runtime.GOOS, runtime.GOARCH)
|
|
downloadURL := data.Assets[platformKey]
|
|
|
|
return CheckUpdateResult{
|
|
HasUpdate: latest != current,
|
|
CurrentVersion: current,
|
|
LatestVersion: latest,
|
|
DownloadURL: downloadURL,
|
|
}
|
|
}
|