mirror of
https://codeberg.org/VnPower/PixivFE
synced 2024-12-06 19:16:23 +01:00
28a43f2ef8
This reverts commit 01d46aa60b.
218 lines
7.8 KiB
Go
218 lines
7.8 KiB
Go
// Package config provides global server-wide settings.
|
|
package config
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
// TODO: figure out how to properly implement urlx
|
|
// the implementation in 5f8b659b49 causes config.go to segfault due to a nil pointer dereference when the PIXIVFE_IMAGEPROXY env var is not set
|
|
// "github.com/goware/urlx"
|
|
"github.com/sethvargo/go-envconfig"
|
|
|
|
"codeberg.org/vnpower/pixivfe/v2/i18n"
|
|
"codeberg.org/vnpower/pixivfe/v2/server/token_manager"
|
|
)
|
|
|
|
var GlobalConfig ServerConfig
|
|
|
|
// REVISION stores the current version's revision information
|
|
var REVISION string = ""
|
|
|
|
const (
|
|
unknownRevision = "unknown"
|
|
revisionFormat = "date-hash[+dirty]"
|
|
)
|
|
|
|
type ServerConfig struct {
|
|
Version string
|
|
Revision string
|
|
RevisionDate string
|
|
RevisionHash string
|
|
IsDirty bool
|
|
StartingTime string // used in /about page
|
|
|
|
Host string `env:"PIXIVFE_HOST"`
|
|
|
|
// One of the two is required
|
|
Port string `env:"PIXIVFE_PORT"`
|
|
UnixSocket string `env:"PIXIVFE_UNIXSOCKET"`
|
|
|
|
RepoURL string `env:"PIXIVFE_REPO_URL,overwrite"` // used in /about page
|
|
|
|
Token []string `env:"PIXIVFE_TOKEN,required"` // may be multiple tokens. delimiter is ','
|
|
TokenManager *token_manager.TokenManager
|
|
TokenLoadBalancing string `env:"PIXIVFE_TOKEN_LOAD_BALANCING,overwrite"`
|
|
TokenMaxRetries int `env:"PIXIVFE_TOKEN_MAX_RETRIES,overwrite"`
|
|
TokenBaseTimeout time.Duration `env:"PIXIVFE_TOKEN_BASE_TIMEOUT,overwrite"`
|
|
TokenMaxBackoffTime time.Duration `env:"PIXIVFE_TOKEN_MAX_BACKOFF_TIME,overwrite"`
|
|
|
|
// API request level backoff settings
|
|
APIMaxRetries int `env:"PIXIVFE_API_MAX_RETRIES,overwrite"`
|
|
APIBaseTimeout time.Duration `env:"PIXIVFE_API_BASE_TIMEOUT,overwrite"`
|
|
APIMaxBackoffTime time.Duration `env:"PIXIVFE_API_MAX_BACKOFF_TIME,overwrite"`
|
|
|
|
AcceptLanguage string `env:"PIXIVFE_ACCEPTLANGUAGE,overwrite"`
|
|
RequestLimit uint64 `env:"PIXIVFE_REQUESTLIMIT"` // if 0, request limit is disabled
|
|
|
|
ProxyServer_staging string `env:"PIXIVFE_IMAGEPROXY,overwrite"`
|
|
ProxyServer url.URL // proxy server URL, may or may not contain authority part of the URL
|
|
|
|
ProxyCheckEnabled bool `env:"PIXIVFE_PROXY_CHECK_ENABLED,overwrite"`
|
|
ProxyCheckInterval time.Duration `env:"PIXIVFE_PROXY_CHECK_INTERVAL,overwrite"`
|
|
ProxyCheckTimeout time.Duration `env:"PIXIVFE_PROXY_CHECK_TIMEOUT,overwrite"`
|
|
|
|
// Development options
|
|
InDevelopment bool `env:"PIXIVFE_DEV"`
|
|
ResponseSaveLocation string `env:"PIXIVFE_RESPONSE_SAVE_LOCATION,overwrite"`
|
|
}
|
|
|
|
// parseRevision extracts RevisionDate, RevisionHash, and IsDirty status from the Revision string
|
|
func parseRevision(revision string) (date, hash string, isDirty bool) {
|
|
// Check if Revision is empty
|
|
if revision == "" {
|
|
return unknownRevision, unknownRevision, false
|
|
}
|
|
|
|
// Check if Revision is marked as dirty (has uncommitted changes)
|
|
isDirty = strings.HasSuffix(revision, "+dirty")
|
|
if isDirty {
|
|
revision = strings.TrimSuffix(revision, "+dirty")
|
|
}
|
|
|
|
// Split Revision into two parts, RevisionDate and RevisionHash
|
|
parts := strings.Split(revision, "-")
|
|
if len(parts) == 2 {
|
|
return parts[0], parts[1], isDirty
|
|
}
|
|
|
|
// Return unknown date, full string as hash if format doesn't match date-hash
|
|
return unknownRevision, revision, isDirty
|
|
}
|
|
|
|
// validateURL checks if the given URL is valid
|
|
func validateURL(urlString string, urlType string) (*url.URL, error) {
|
|
parsedURL, err := url.Parse(urlString)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Ensure both scheme and host are present in the URL
|
|
if parsedURL.Scheme == "" || parsedURL.Host == "" {
|
|
return nil, i18n.Errorf("%s URL is invalid: %s. Please specify a complete URL with scheme and host, e.g. https://example.com", urlType, urlString)
|
|
}
|
|
if strings.HasSuffix(parsedURL.Path, "/") {
|
|
return nil, i18n.Errorf("%s URL path (%s) cannot end in /: %s. PixivFE does not support this now", urlType, parsedURL.Path, urlString)
|
|
}
|
|
return parsedURL, nil
|
|
}
|
|
|
|
func (s *ServerConfig) LoadConfig() error {
|
|
s.Version = "v2.9"
|
|
s.Revision = REVISION
|
|
|
|
s.RevisionDate, s.RevisionHash, s.IsDirty = parseRevision(REVISION)
|
|
|
|
if REVISION == "" {
|
|
log.Printf("[WARNING] REVISION is not set. Continuing with unknown revision information.\n")
|
|
} else if s.RevisionDate == unknownRevision {
|
|
log.Printf("[WARNING] REVISION format is invalid: %s. Expected format '%s'. Continuing with full revision as hash.\n", REVISION, revisionFormat)
|
|
}
|
|
|
|
log.Printf("PixivFE %s, revision %s\n", s.Version, s.Revision)
|
|
|
|
s.StartingTime = time.Now().UTC().Format("2006-01-02 15:04")
|
|
|
|
// set default values with env:"...,overwrite"
|
|
s.RepoURL = "https://codeberg.org/VnPower/PixivFE"
|
|
|
|
s.AcceptLanguage = "en-US,en;q=0.5"
|
|
s.ProxyServer_staging = BuiltinProxyUrl
|
|
s.ProxyCheckEnabled = true
|
|
s.ProxyCheckInterval = 8 * time.Hour
|
|
s.ProxyCheckTimeout = 4 * time.Second
|
|
s.TokenLoadBalancing = "round-robin"
|
|
s.TokenMaxRetries = 5
|
|
s.TokenBaseTimeout = 1000 * time.Millisecond
|
|
s.TokenMaxBackoffTime = 32000 * time.Millisecond
|
|
|
|
s.APIMaxRetries = 3
|
|
s.APIBaseTimeout = 500 * time.Millisecond
|
|
s.APIMaxBackoffTime = 8000 * time.Millisecond
|
|
|
|
s.ResponseSaveLocation = "/tmp/pixivfe/responses"
|
|
|
|
// load config from from env vars
|
|
if err := envconfig.Process(context.Background(), s); err != nil {
|
|
return err
|
|
}
|
|
|
|
if s.Port == "" && s.UnixSocket == "" {
|
|
log.Fatalln("Either PIXIVFE_PORT or PIXIVFE_UNIXSOCKET has to be set.")
|
|
return i18n.Error("Either PIXIVFE_PORT or PIXIVFE_UNIXSOCKET has to be set")
|
|
}
|
|
|
|
// a check for tokens
|
|
if len(s.Token) < 1 {
|
|
log.Fatalln("PIXIVFE_TOKEN has to be set. Visit https://pixivfe-docs.pages.dev/hosting/hosting-pixivfe for more details.")
|
|
return i18n.Error("PIXIVFE_TOKEN has to be set. Visit https://pixivfe-docs.pages.dev/hosting/hosting-pixivfe for more details")
|
|
}
|
|
|
|
// Validate proxy server URL
|
|
proxyURL, err := validateURL(s.ProxyServer_staging, "Proxy server")
|
|
if err != nil {
|
|
log.Printf("[WARNING] Invalid proxy server URL: %v. Falling back to built-in proxy URL.", err)
|
|
proxyURL, _ = url.Parse(BuiltinProxyUrl) // We know this is valid
|
|
log.Printf("Proxy server set to: %s\n", BuiltinProxyUrl)
|
|
} else {
|
|
log.Printf("Proxy server set to: %s\n", proxyURL.String())
|
|
}
|
|
s.ProxyServer = *proxyURL
|
|
log.Printf("Proxy check interval set to: %v\n", s.ProxyCheckInterval)
|
|
|
|
// Validate repo URL
|
|
repoURL, err := validateURL(s.RepoURL, "Repo")
|
|
if err != nil {
|
|
log.Printf("[WARNING] Invalid repo URL: %v. Using default repo URL.", err)
|
|
s.RepoURL = "https://codeberg.org/VnPower/PixivFE" // Use a default value
|
|
} else {
|
|
s.RepoURL = repoURL.String()
|
|
log.Printf("Repo URL set to: %s\n", s.RepoURL)
|
|
}
|
|
|
|
// Validate TokenLoadBalancing
|
|
switch s.TokenLoadBalancing {
|
|
case "round-robin", "random", "least-recently-used":
|
|
// Valid options
|
|
default:
|
|
log.Printf("[WARNING] Invalid PIXIVFE_TOKEN_LOAD_BALANCING value: %s. Defaulting to 'round-robin'.\n", s.TokenLoadBalancing)
|
|
s.TokenLoadBalancing = "round-robin"
|
|
}
|
|
|
|
// Initialize TokenManager
|
|
s.TokenManager = token_manager.NewTokenManager(s.Token, s.TokenMaxRetries, s.TokenBaseTimeout, s.TokenMaxBackoffTime, s.TokenLoadBalancing)
|
|
log.Printf("Token manager initialized with %d tokens\n", len(s.Token))
|
|
log.Printf("Token manager settings: Max retries: %d, Base timeout: %v, Max backoff time: %v\n", s.TokenMaxRetries, s.TokenBaseTimeout, s.TokenMaxBackoffTime)
|
|
log.Printf("Token load balancing method: %s\n", s.TokenLoadBalancing)
|
|
|
|
log.Printf("API request backoff settings: Max retries: %d, Base timeout: %v, Max backoff time: %v\n", s.APIMaxRetries, s.APIBaseTimeout, s.APIMaxBackoffTime)
|
|
|
|
// Only print ResponseSaveLocation if InDevelopment is set
|
|
if s.InDevelopment {
|
|
log.Printf("Response save location: %s\n", s.ResponseSaveLocation)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *ServerConfig) GetToken() string {
|
|
token := s.TokenManager.GetToken()
|
|
if token == nil {
|
|
log.Println("[WARNING] All tokens are timed out. Using the first available token.")
|
|
return s.Token[0]
|
|
}
|
|
return token.Value
|
|
}
|