343 lines
7.7 KiB
Go
343 lines
7.7 KiB
Go
package config
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/assimon/luuu/util/http_client"
|
|
"github.com/spf13/viper"
|
|
"github.com/tidwall/gjson"
|
|
)
|
|
|
|
var (
|
|
HTTPAccessLog bool
|
|
SQLDebug bool
|
|
LogLevel string
|
|
MysqlDns string
|
|
RuntimePath string
|
|
LogSavePath string
|
|
StaticPath string
|
|
StaticFilePath string
|
|
TgBotToken string
|
|
TgProxy string
|
|
TgManage int64
|
|
UsdtRate float64
|
|
RateApiUrl string
|
|
TRON_GRID_API_KEY string
|
|
BuildVersion = "0.0.0-dev"
|
|
BuildCommit = "none"
|
|
BuildDate = "unknown"
|
|
configRootPath string
|
|
explicitConfigPath string
|
|
)
|
|
|
|
func SetConfigPath(path string) {
|
|
explicitConfigPath = strings.TrimSpace(path)
|
|
}
|
|
|
|
func Init() {
|
|
configPath, err := resolveConfigFilePath()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
configRootPath = filepath.Dir(configPath)
|
|
|
|
viper.SetConfigFile(configPath)
|
|
err = viper.ReadInConfig()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
HTTPAccessLog = viper.GetBool("http_access_log")
|
|
SQLDebug = viper.GetBool("sql_debug")
|
|
LogLevel = normalizeLogLevel(viper.GetString("log_level"))
|
|
StaticPath = normalizeStaticURLPath(viper.GetString("static_path"))
|
|
StaticFilePath = filepath.Join(configRootPath, strings.TrimPrefix(StaticPath, "/"))
|
|
RuntimePath = resolvePathFromBase(configRootPath, viper.GetString("runtime_root_path"), filepath.Join(configRootPath, "runtime"))
|
|
LogSavePath = resolvePathFromBase(RuntimePath, viper.GetString("log_save_path"), filepath.Join(RuntimePath, "logs"))
|
|
mustMkdir(RuntimePath)
|
|
mustMkdir(LogSavePath)
|
|
MysqlDns = fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
|
url.QueryEscape(viper.GetString("mysql_user")),
|
|
url.QueryEscape(viper.GetString("mysql_passwd")),
|
|
fmt.Sprintf("%s:%s", viper.GetString("mysql_host"), viper.GetString("mysql_port")),
|
|
viper.GetString("mysql_database"))
|
|
TgBotToken = viper.GetString("tg_bot_token")
|
|
TgProxy = viper.GetString("tg_proxy")
|
|
TgManage = viper.GetInt64("tg_manage")
|
|
|
|
RateApiUrl = GetRateApiUrl()
|
|
TRON_GRID_API_KEY = viper.GetString("tron_grid_api_key")
|
|
}
|
|
|
|
func mustMkdir(path string) {
|
|
if err := os.MkdirAll(path, 0o755); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func normalizeLogLevel(level string) string {
|
|
switch strings.ToLower(strings.TrimSpace(level)) {
|
|
case "debug", "info", "warn", "error":
|
|
return strings.ToLower(strings.TrimSpace(level))
|
|
default:
|
|
return "info"
|
|
}
|
|
}
|
|
|
|
func normalizeStaticURLPath(path string) string {
|
|
path = strings.TrimSpace(path)
|
|
if path == "" || path == "/" {
|
|
return "/static"
|
|
}
|
|
if !strings.HasPrefix(path, "/") {
|
|
path = "/" + path
|
|
}
|
|
return path
|
|
}
|
|
|
|
func resolvePathFromBase(basePath string, path string, fallback string) string {
|
|
path = strings.TrimSpace(path)
|
|
if path == "" {
|
|
return fallback
|
|
}
|
|
if filepath.IsAbs(path) {
|
|
return path
|
|
}
|
|
path = strings.TrimPrefix(path, "/")
|
|
path = strings.TrimPrefix(path, "\\")
|
|
return filepath.Join(basePath, filepath.FromSlash(path))
|
|
}
|
|
|
|
func resolveConfigFilePath() (string, error) {
|
|
if explicitConfigPath != "" {
|
|
return normalizeConfiguredPath(explicitConfigPath)
|
|
}
|
|
if envPath := strings.TrimSpace(os.Getenv("EPUSDT_CONFIG")); envPath != "" {
|
|
return normalizeConfiguredPath(envPath)
|
|
}
|
|
return normalizeConfiguredPath(".env")
|
|
}
|
|
|
|
func normalizeConfiguredPath(input string) (string, error) {
|
|
path := strings.TrimSpace(input)
|
|
if path == "" {
|
|
path = ".env"
|
|
}
|
|
if !filepath.IsAbs(path) {
|
|
cwd, err := os.Getwd()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
path = filepath.Join(cwd, path)
|
|
}
|
|
|
|
info, err := os.Stat(path)
|
|
if err == nil && info.IsDir() {
|
|
path = filepath.Join(path, ".env")
|
|
info, err = os.Stat(path)
|
|
}
|
|
if err != nil {
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return "", fmt.Errorf("config file not found: %s", path)
|
|
}
|
|
return "", err
|
|
}
|
|
if info.IsDir() {
|
|
return "", fmt.Errorf("config path must point to a file, got directory: %s", path)
|
|
}
|
|
return path, nil
|
|
}
|
|
|
|
func GetAppVersion() string {
|
|
return BuildVersion
|
|
}
|
|
|
|
func GetBuildCommit() string {
|
|
return BuildCommit
|
|
}
|
|
|
|
func GetBuildDate() string {
|
|
return BuildDate
|
|
}
|
|
|
|
func GetAppName() string {
|
|
appName := viper.GetString("app_name")
|
|
if appName == "" {
|
|
return "epusdt"
|
|
}
|
|
return appName
|
|
}
|
|
|
|
func GetAppUri() string {
|
|
return viper.GetString("app_uri")
|
|
}
|
|
|
|
func GetApiAuthToken() string {
|
|
return viper.GetString("api_auth_token")
|
|
}
|
|
|
|
func GetRateApiUrl() string {
|
|
rateURL := viper.GetString("api_rate_url")
|
|
if rateURL == "" {
|
|
rateURL = os.Getenv("API_RATE_URL")
|
|
}
|
|
if rateURL == "" {
|
|
log.Println("api_rate_url is empty")
|
|
}
|
|
RateApiUrl = rateURL
|
|
return rateURL
|
|
}
|
|
|
|
func GetRateForCoin(coin string, base string) float64 {
|
|
coin = strings.ToLower(strings.TrimSpace(coin))
|
|
base = strings.ToLower(strings.TrimSpace(base))
|
|
if coin == "" || base == "" {
|
|
return 0
|
|
}
|
|
if coin == base {
|
|
return 1
|
|
}
|
|
if coin == "usdt" {
|
|
switch base {
|
|
case "usd":
|
|
return 1
|
|
case "cny":
|
|
usdtRate := GetUsdtRate()
|
|
if usdtRate > 0 {
|
|
return 1 / usdtRate
|
|
}
|
|
}
|
|
}
|
|
|
|
baseURL := RateApiUrl
|
|
if baseURL == "" {
|
|
baseURL = GetRateApiUrl()
|
|
}
|
|
if baseURL == "" {
|
|
log.Printf("rate api url is empty")
|
|
return 0.0
|
|
}
|
|
if baseURL[len(baseURL)-1] != '/' {
|
|
baseURL += "/"
|
|
}
|
|
|
|
client := http_client.GetHttpClient()
|
|
resp, err := client.R().Get(baseURL + fmt.Sprintf("%s.json", base))
|
|
if err != nil {
|
|
log.Printf("call rate api error: %s", err.Error())
|
|
return 0.0
|
|
}
|
|
if resp.StatusCode() < 200 || resp.StatusCode() >= 300 {
|
|
log.Printf("call rate api unexpected status: %s", resp.Status())
|
|
return 0.0
|
|
}
|
|
|
|
targetRate := 0.0
|
|
gjson.GetBytes(resp.Body(), base).ForEach(func(key, value gjson.Result) bool {
|
|
if key.String() == coin {
|
|
targetRate = value.Float()
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return targetRate
|
|
}
|
|
|
|
func GetUsdtRate() float64 {
|
|
forcedUsdtRate := viper.GetFloat64("forced_usdt_rate")
|
|
if forcedUsdtRate > 0 {
|
|
return forcedUsdtRate
|
|
}
|
|
if UsdtRate <= 0 {
|
|
return 6.4
|
|
}
|
|
return UsdtRate
|
|
}
|
|
|
|
func GetOrderExpirationTime() int {
|
|
timer := viper.GetInt("order_expiration_time")
|
|
if timer <= 0 {
|
|
return 10
|
|
}
|
|
return timer
|
|
}
|
|
|
|
func GetOrderExpirationTimeDuration() time.Duration {
|
|
timer := GetOrderExpirationTime()
|
|
return time.Minute * time.Duration(timer)
|
|
}
|
|
|
|
func GetRuntimeSqlitePath() string {
|
|
filename := viper.GetString("runtime_sqlite_filename")
|
|
if filename == "" {
|
|
filename = "epusdt-runtime.db"
|
|
}
|
|
if filepath.IsAbs(filename) {
|
|
return filename
|
|
}
|
|
filename = strings.TrimPrefix(strings.TrimPrefix(filename, "/"), "\\")
|
|
return filepath.Join(RuntimePath, filepath.FromSlash(filename))
|
|
}
|
|
|
|
func GetPrimarySqlitePath() string {
|
|
filename := strings.TrimSpace(viper.GetString("sqlite_database_filename"))
|
|
if filename == "" {
|
|
return filepath.Join(configRootPath, "epusdt.db")
|
|
}
|
|
if filepath.IsAbs(filename) {
|
|
return filename
|
|
}
|
|
filename = strings.TrimPrefix(strings.TrimPrefix(filename, "/"), "\\")
|
|
return filepath.Join(configRootPath, filepath.FromSlash(filename))
|
|
}
|
|
|
|
func GetQueueConcurrency() int {
|
|
concurrency := viper.GetInt("queue_concurrency")
|
|
if concurrency <= 0 {
|
|
return 10
|
|
}
|
|
return concurrency
|
|
}
|
|
|
|
func GetQueuePollInterval() time.Duration {
|
|
interval := viper.GetInt("queue_poll_interval_ms")
|
|
if interval <= 0 {
|
|
interval = 1000
|
|
}
|
|
return time.Duration(interval) * time.Millisecond
|
|
}
|
|
|
|
func GetOrderNoticeMaxRetry() int {
|
|
retry := viper.GetInt("order_notice_max_retry")
|
|
if retry < 0 {
|
|
return 0
|
|
}
|
|
return retry
|
|
}
|
|
|
|
func GetCallbackRetryBaseDuration() time.Duration {
|
|
seconds := viper.GetInt("callback_retry_base_seconds")
|
|
if seconds <= 0 {
|
|
seconds = 5
|
|
}
|
|
return time.Duration(seconds) * time.Second
|
|
}
|
|
|
|
func GetSolanaRpcUrl() string {
|
|
rpcUrl := viper.GetString("solana_rpc_url")
|
|
if rpcUrl == "" {
|
|
return "https://api.mainnet-beta.solana.com"
|
|
}
|
|
return rpcUrl
|
|
}
|
|
|
|
func GetEthereumWsUrl() string {
|
|
return strings.TrimSpace(viper.GetString("ethereum_ws_url"))
|
|
}
|