Merge Go frontend (apiserver + xcecli) into meson build

This commit is contained in:
Rongsong Shen 2026-04-14 11:20:17 +08:00
parent 43dc12a36c
commit c9dc8c4676
205 changed files with 30793 additions and 328 deletions

View file

@ -0,0 +1,293 @@
package utils
import (
"errors"
"fmt"
"log"
"os"
"runtime"
"strconv"
"strings"
"github.com/spf13/viper"
)
type BackendConfig struct {
Address string `yaml:"address"`
Port int `yaml:"port"`
From string `yaml:"from"`
Key string `yaml:"key"`
}
type AppServerConfig struct {
AppConfig AppConfig `yaml:"app"`
Security Security `yaml:"security"`
RateLimit RateLimitConfig `yaml:"rate_limit"`
Authentication Authentication `yaml:"authentication"`
JobConfig JobConfig `yaml:"job"`
CacheConfig CacheConfig `yaml:"cache"`
Backend BackendConfig `yaml:"backend"`
}
type AppConfig struct {
AppName string `yaml:"name"`
AppMode string `yaml:"mode"`
HTTPPort string `yaml:"http_port"`
AppVersion string `yaml:"version"`
}
// TokenConfig is the configuration for the token
type TokenConfig struct {
Type string `yaml:"type"`
SecretKey string `yaml:"secret_key"`
TokenExpireTime int `yaml:"token_expire_time"`
}
// Authentication is the configuration for the authentication
type Authentication struct {
Token TokenConfig
}
type Security struct {
HTTPS HTTPSConfig `yaml:"https"`
}
type HTTPSConfig struct {
Enabled bool `yaml:"enabled"`
Port string `yaml:"port"`
CertFile string `yaml:"cert_file"`
KeyFile string `yaml:"key_file"`
}
type RateLimitConfig struct {
Global EndpointLimit `yaml:"global"`
Endpoints map[string]EndpointLimit `yaml:"endpoints"`
}
type EndpointLimit struct {
Rate int `yaml:"rate"` // requests per second
Burst int `yaml:"burst"` // requests per minute
Window string `yaml:"window"` // Time window (second, minute)
}
type WhitelistConfig struct {
Whitelist []string `yaml:"whitelist"`
}
type FileUploadConfig struct {
MaxSize string `yaml:"max_size"` // maximum file size in MB
AllowedTypes []string `yaml:"allowed_types"` // allowed file types
DisallowedTypes []string `yaml:"disallowed_types"` // disallowed file types
}
type JobConfig struct {
MaxArraySize int `yaml:"max_array_size"`
}
type CORSConfig struct {
Enabled bool `yaml:"enabled"`
AllowedOrigins []string `yaml:"allowed_origins"`
}
type CacheConfig struct {
RefreshInterval int `yaml:"refresh_interval"`
}
var DirPerm os.FileMode
// Add global variables to store configs
var (
AppYamlConfig *AppConfig
AuthenticationYamlConfig *Authentication
HttpsYamlConfig *HTTPSConfig
RateLimitYamlConfig *RateLimitConfig
WhitelistYamlConfig *WhitelistConfig
FileUploadYamlConfig *FileUploadConfig
JobYamlConfig *JobConfig
CacheYamlConfig *CacheConfig
CORSYamlConfig *CORSConfig
BackendYamlConfig *BackendConfig
)
func init() {
if runtime.GOOS == "windows" {
DirPerm = 0666
} else {
DirPerm = 0700
}
}
func InitConf() error {
Debug("Enter InitConf() function")
viper.SetConfigType("yaml")
viper.AddConfigPath(Global_TopDir + "/config")
viper.SetConfigName("app")
// validate the app.yaml file whether is validate
data, err := ValidateYamlFormat(Global_TopDir + "/config/app.yaml")
if err != nil {
Error("Fail to call DiagnoseLogCreationError with error")
return err
}
if data == nil {
Error("Fail to call ValidateYamlFormat and data is nil")
return err
}
err1 := viper.ReadInConfig()
if err1 != nil {
_, ok := err1.(viper.ConfigFileNotFoundError)
if ok {
Error("Can't find the configuration files of app.yaml")
return err1
} else {
log.Println("The configuration file of app.yaml with some error")
return err1
}
}
// parse app section
AppYamlConfig = &AppConfig{
AppName: viper.GetString("app.name"),
AppMode: viper.GetString("app.runmode"),
HTTPPort: viper.GetString("app.http_port"),
AppVersion: viper.GetString("app.version"),
}
// parse authentication section
AuthenticationYamlConfig = &Authentication{
Token: TokenConfig{
Type: viper.GetString("authentication.token.type"),
SecretKey: resolveJWTSecret(viper.GetString("authentication.token.secretKey")),
TokenExpireTime: int(viper.GetFloat64("authentication.token.token_expire_time")),
},
}
// parse security section
HttpsYamlConfig = &HTTPSConfig{
Enabled: viper.GetBool("security.https.enabled"),
Port: viper.GetString("security.https.port"),
CertFile: viper.GetString("security.https.cert_file"),
KeyFile: viper.GetString("security.https.key_file"),
}
// parse rate limit section
RateLimitYamlConfig = &RateLimitConfig{
Global: EndpointLimit{
Rate: viper.GetInt("rate_limit.global.rate"),
Burst: viper.GetInt("rate_limit.global.burst"),
Window: viper.GetString("rate_limit.global.window"),
},
Endpoints: make(map[string]EndpointLimit),
}
// parse endpoints
endpoints := viper.GetStringMap("rate_limit.endpoints")
for path := range endpoints {
RateLimitYamlConfig.Endpoints[path] = EndpointLimit{
Rate: viper.GetInt("rate_limit.endpoints." + path + ".rate"),
Burst: viper.GetInt("rate_limit.endpoints." + path + ".burst"),
Window: viper.GetString("rate_limit.endpoints." + path + ".window"),
}
}
WhitelistYamlConfig = &WhitelistConfig{
Whitelist: viper.GetStringSlice("whitelist"),
}
// parse file upload section
rawMaxSize := viper.GetString("file_upload.max_size")
fileMaxSizeMB, err := ParseMaxSize(rawMaxSize)
if err != nil {
return fmt.Errorf("failed to parse max_size: %v", err)
}
FileUploadYamlConfig = &FileUploadConfig{
MaxSize: strconv.FormatInt(fileMaxSizeMB, 10) + " MB",
AllowedTypes: viper.GetStringSlice("file_upload.allowed_types"),
DisallowedTypes: viper.GetStringSlice("file_upload.disallowed_types"),
}
// parse job config section
// parse job config section
if !viper.IsSet("job") {
// if job section is not set, use default values
JobYamlConfig = &JobConfig{
MaxArraySize: 1000,
}
} else {
maxArraySize := viper.GetInt("job.max_array_size")
if maxArraySize == 0 {
maxArraySize = 1000
}
if maxArraySize < 1 || maxArraySize > 2147483646 {
return fmt.Errorf("invalid job.max_array_size value: %d, must be between 1 and 2147483646", maxArraySize)
}
JobYamlConfig = &JobConfig{
MaxArraySize: maxArraySize,
}
}
// parse cache config section
if !viper.IsSet("cache") {
// if cache section is not set, use default values
CacheYamlConfig = &CacheConfig{
RefreshInterval: 300,
}
} else {
refreshInterval := viper.GetInt("cache.refresh_interval")
if refreshInterval == 0 {
refreshInterval = 300
}
if refreshInterval < 1 || refreshInterval > 300 {
return fmt.Errorf("invalid cache.refresh_interval value: %d, must be between 1 and 300", refreshInterval)
}
CacheYamlConfig = &CacheConfig{
RefreshInterval: refreshInterval,
}
}
CORSYamlConfig = &CORSConfig{
Enabled: viper.GetBool("cors.enabled"),
AllowedOrigins: viper.GetStringSlice("cors.allowed_origins"),
}
BackendYamlConfig = &BackendConfig{
Address: viper.GetString("backend.address"),
Port: viper.GetInt("backend.port"),
From: viper.GetString("backend.from"),
Key: viper.GetString("backend.key"),
}
return nil
}
func ParseMaxSize(maxSize string) (int64, error) {
parts := strings.Fields(maxSize)
if len(parts) != 2 {
return 0, errors.New("invalid max_size format, expected '<number> <unit>'")
}
size, err := strconv.ParseInt(parts[0], 10, 64)
if err != nil {
return 0, errors.New("invalid number in max_size")
}
unit := strings.ToUpper(parts[1])
switch unit {
case "MB":
size *= 1 // MB remains as is
case "GB":
size *= 1024 // convert GB to MB
default:
return 0, errors.New("unsupported unit in max_size, use MB or GB")
}
if size > 10240 {
return 0, errors.New("max_size cannot exceed 10GB (10240 MB)")
}
return size, nil
}
// resolveJWTSecret returns the JWT secret from XCE_JWT_SECRET env var if set and non-empty,
// otherwise falls back to the value from config file.
func resolveJWTSecret(configValue string) string {
if env := os.Getenv("XCE_JWT_SECRET"); env != "" {
return env
}
return configValue
}