mirror of
https://github.com/databasus/databasus.git
synced 2026-04-06 00:32:03 +02:00
417 lines
12 KiB
Go
417 lines
12 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/ilyakaznacheev/cleanenv"
|
|
"github.com/joho/godotenv"
|
|
|
|
env_utils "databasus-backend/internal/util/env"
|
|
"databasus-backend/internal/util/logger"
|
|
"databasus-backend/internal/util/tools"
|
|
)
|
|
|
|
var log = logger.GetLogger()
|
|
|
|
const (
|
|
AppModeWeb = "web"
|
|
AppModeBackground = "background"
|
|
)
|
|
|
|
type EnvVariables struct {
|
|
IsTesting bool
|
|
EnvMode env_utils.EnvMode `env:"ENV_MODE" required:"true"`
|
|
PostgresesInstallDir string `env:"POSTGRES_INSTALL_DIR"`
|
|
MysqlInstallDir string `env:"MYSQL_INSTALL_DIR"`
|
|
MariadbInstallDir string `env:"MARIADB_INSTALL_DIR"`
|
|
MongodbInstallDir string `env:"MONGODB_INSTALL_DIR"`
|
|
|
|
// Internal database
|
|
DatabaseDsn string `env:"DATABASE_DSN" required:"true"`
|
|
// Internal Valkey
|
|
ValkeyHost string `env:"VALKEY_HOST" required:"true"`
|
|
ValkeyPort string `env:"VALKEY_PORT" required:"true"`
|
|
ValkeyUsername string `env:"VALKEY_USERNAME" required:"true"`
|
|
ValkeyPassword string `env:"VALKEY_PASSWORD" required:"true"`
|
|
ValkeyIsSsl bool `env:"VALKEY_IS_SSL" required:"true"`
|
|
|
|
IsCloud bool `env:"IS_CLOUD"`
|
|
TestLocalhost string `env:"TEST_LOCALHOST"`
|
|
|
|
ShowDbInstallationVerificationLogs bool `env:"SHOW_DB_INSTALLATION_VERIFICATION_LOGS"`
|
|
IsSkipExternalResourcesTests bool `env:"IS_SKIP_EXTERNAL_RESOURCES_TESTS"`
|
|
|
|
IsManyNodesMode bool `env:"IS_MANY_NODES_MODE"`
|
|
IsPrimaryNode bool `env:"IS_PRIMARY_NODE"`
|
|
IsProcessingNode bool `env:"IS_PROCESSING_NODE"`
|
|
NodeNetworkThroughputMBs int `env:"NODE_NETWORK_THROUGHPUT_MBPS"`
|
|
|
|
DataFolder string
|
|
TempFolder string
|
|
SecretKeyPath string
|
|
|
|
// Billing (always tax-exclusive)
|
|
PricePerGBCents int64 `env:"PRICE_PER_GB_CENTS"`
|
|
MinStorageGB int
|
|
MaxStorageGB int
|
|
TrialDuration time.Duration
|
|
TrialStorageGB int
|
|
GracePeriod time.Duration
|
|
// Paddle billing
|
|
IsPaddleSandbox bool `env:"IS_PADDLE_SANDBOX"`
|
|
PaddleApiKey string `env:"PADDLE_API_KEY"`
|
|
PaddleWebhookSecret string `env:"PADDLE_WEBHOOK_SECRET"`
|
|
PaddlePriceID string `env:"PADDLE_PRICE_ID"`
|
|
PaddleClientToken string `env:"PADDLE_CLIENT_TOKEN"`
|
|
|
|
TestGoogleDriveClientID string `env:"TEST_GOOGLE_DRIVE_CLIENT_ID"`
|
|
TestGoogleDriveClientSecret string `env:"TEST_GOOGLE_DRIVE_CLIENT_SECRET"`
|
|
TestGoogleDriveTokenJSON string `env:"TEST_GOOGLE_DRIVE_TOKEN_JSON"`
|
|
|
|
TestPostgres12Port string `env:"TEST_POSTGRES_12_PORT"`
|
|
TestPostgres13Port string `env:"TEST_POSTGRES_13_PORT"`
|
|
TestPostgres14Port string `env:"TEST_POSTGRES_14_PORT"`
|
|
TestPostgres15Port string `env:"TEST_POSTGRES_15_PORT"`
|
|
TestPostgres16Port string `env:"TEST_POSTGRES_16_PORT"`
|
|
TestPostgres17Port string `env:"TEST_POSTGRES_17_PORT"`
|
|
TestPostgres18Port string `env:"TEST_POSTGRES_18_PORT"`
|
|
|
|
TestMinioPort string `env:"TEST_MINIO_PORT"`
|
|
TestMinioConsolePort string `env:"TEST_MINIO_CONSOLE_PORT"`
|
|
|
|
TestAzuriteBlobPort string `env:"TEST_AZURITE_BLOB_PORT"`
|
|
|
|
TestNASPort string `env:"TEST_NAS_PORT"`
|
|
TestFTPPort string `env:"TEST_FTP_PORT"`
|
|
TestSFTPPort string `env:"TEST_SFTP_PORT"`
|
|
|
|
TestMysql57Port string `env:"TEST_MYSQL_57_PORT"`
|
|
TestMysql80Port string `env:"TEST_MYSQL_80_PORT"`
|
|
TestMysql84Port string `env:"TEST_MYSQL_84_PORT"`
|
|
TestMysql90Port string `env:"TEST_MYSQL_90_PORT"`
|
|
|
|
TestMariadb55Port string `env:"TEST_MARIADB_55_PORT"`
|
|
TestMariadb101Port string `env:"TEST_MARIADB_101_PORT"`
|
|
TestMariadb102Port string `env:"TEST_MARIADB_102_PORT"`
|
|
TestMariadb103Port string `env:"TEST_MARIADB_103_PORT"`
|
|
TestMariadb104Port string `env:"TEST_MARIADB_104_PORT"`
|
|
TestMariadb105Port string `env:"TEST_MARIADB_105_PORT"`
|
|
TestMariadb106Port string `env:"TEST_MARIADB_106_PORT"`
|
|
TestMariadb1011Port string `env:"TEST_MARIADB_1011_PORT"`
|
|
TestMariadb114Port string `env:"TEST_MARIADB_114_PORT"`
|
|
TestMariadb118Port string `env:"TEST_MARIADB_118_PORT"`
|
|
TestMariadb120Port string `env:"TEST_MARIADB_120_PORT"`
|
|
|
|
TestMongodb40Port string `env:"TEST_MONGODB_40_PORT"`
|
|
TestMongodb42Port string `env:"TEST_MONGODB_42_PORT"`
|
|
TestMongodb44Port string `env:"TEST_MONGODB_44_PORT"`
|
|
TestMongodb50Port string `env:"TEST_MONGODB_50_PORT"`
|
|
TestMongodb60Port string `env:"TEST_MONGODB_60_PORT"`
|
|
TestMongodb70Port string `env:"TEST_MONGODB_70_PORT"`
|
|
TestMongodb82Port string `env:"TEST_MONGODB_82_PORT"`
|
|
|
|
// oauth
|
|
GitHubClientID string `env:"GITHUB_CLIENT_ID"`
|
|
GitHubClientSecret string `env:"GITHUB_CLIENT_SECRET"`
|
|
GoogleClientID string `env:"GOOGLE_CLIENT_ID"`
|
|
GoogleClientSecret string `env:"GOOGLE_CLIENT_SECRET"`
|
|
|
|
// Cloudflare Turnstile
|
|
CloudflareTurnstileSecretKey string `env:"CLOUDFLARE_TURNSTILE_SECRET_KEY"`
|
|
CloudflareTurnstileSiteKey string `env:"CLOUDFLARE_TURNSTILE_SITE_KEY"`
|
|
|
|
// testing Supabase
|
|
TestSupabaseHost string `env:"TEST_SUPABASE_HOST"`
|
|
TestSupabasePort string `env:"TEST_SUPABASE_PORT"`
|
|
TestSupabaseUsername string `env:"TEST_SUPABASE_USERNAME"`
|
|
TestSupabasePassword string `env:"TEST_SUPABASE_PASSWORD"`
|
|
TestSupabaseDatabase string `env:"TEST_SUPABASE_DATABASE"`
|
|
|
|
// SMTP configuration (optional)
|
|
SMTPHost string `env:"SMTP_HOST"`
|
|
SMTPPort int `env:"SMTP_PORT"`
|
|
SMTPUser string `env:"SMTP_USER"`
|
|
SMTPPassword string `env:"SMTP_PASSWORD"`
|
|
SMTPFrom string `env:"SMTP_FROM"`
|
|
|
|
// Application URL (optional) - used for email links
|
|
DatabasusURL string `env:"DATABASUS_URL"`
|
|
}
|
|
|
|
var (
|
|
env EnvVariables
|
|
once sync.Once
|
|
)
|
|
|
|
func GetEnv() *EnvVariables {
|
|
once.Do(loadEnvVariables)
|
|
return &env
|
|
}
|
|
|
|
func loadEnvVariables() {
|
|
// Get current working directory
|
|
cwd, err := os.Getwd()
|
|
if err != nil {
|
|
log.Warn("could not get current working directory", "error", err)
|
|
cwd = "."
|
|
}
|
|
|
|
backendRoot := cwd
|
|
for {
|
|
if _, err := os.Stat(filepath.Join(backendRoot, "go.mod")); err == nil {
|
|
break
|
|
}
|
|
|
|
parent := filepath.Dir(backendRoot)
|
|
if parent == backendRoot {
|
|
break
|
|
}
|
|
|
|
backendRoot = parent
|
|
}
|
|
|
|
envPaths := []string{
|
|
filepath.Join(cwd, ".env"),
|
|
filepath.Join(backendRoot, ".env"),
|
|
}
|
|
|
|
var loaded bool
|
|
for _, path := range envPaths {
|
|
log.Info("Trying to load .env", "path", path)
|
|
if err := godotenv.Load(path); err == nil {
|
|
log.Info("Successfully loaded .env", "path", path)
|
|
loaded = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !loaded {
|
|
log.Error("Error loading .env file: could not find .env in any location")
|
|
os.Exit(1)
|
|
}
|
|
|
|
err = cleanenv.ReadEnv(&env)
|
|
if err != nil {
|
|
log.Error("Configuration could not be loaded", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Set default value for ShowDbInstallationVerificationLogs if not defined
|
|
if os.Getenv("SHOW_DB_INSTALLATION_VERIFICATION_LOGS") == "" {
|
|
env.ShowDbInstallationVerificationLogs = true
|
|
}
|
|
|
|
// Set default value for IsSkipExternalTests if not defined
|
|
if os.Getenv("IS_SKIP_EXTERNAL_RESOURCES_TESTS") == "" {
|
|
env.IsSkipExternalResourcesTests = false
|
|
}
|
|
|
|
// Set default value for IsCloud if not defined
|
|
if os.Getenv("IS_CLOUD") == "" {
|
|
env.IsCloud = false
|
|
}
|
|
|
|
for _, arg := range os.Args {
|
|
if strings.Contains(arg, "test") {
|
|
env.IsTesting = true
|
|
break
|
|
}
|
|
}
|
|
|
|
// Check for external database override
|
|
if externalDsn := os.Getenv("DANGEROUS_EXTERNAL_DATABASE_DSN"); externalDsn != "" {
|
|
log.Warn(
|
|
"Using DANGEROUS_EXTERNAL_DATABASE_DSN - connecting to external database instead of internal PostgreSQL",
|
|
)
|
|
env.DatabaseDsn = externalDsn
|
|
}
|
|
|
|
if env.DatabaseDsn == "" {
|
|
log.Error("DATABASE_DSN is empty")
|
|
os.Exit(1)
|
|
}
|
|
|
|
if env.EnvMode == "" {
|
|
log.Error("ENV_MODE is empty")
|
|
os.Exit(1)
|
|
}
|
|
if env.EnvMode != "development" && env.EnvMode != "production" {
|
|
log.Error("ENV_MODE is invalid", "mode", env.EnvMode)
|
|
os.Exit(1)
|
|
}
|
|
log.Info("ENV_MODE loaded", "mode", env.EnvMode)
|
|
|
|
env.PostgresesInstallDir = filepath.Join(backendRoot, "tools", "postgresql")
|
|
tools.VerifyPostgresesInstallation(
|
|
log,
|
|
env.EnvMode,
|
|
env.PostgresesInstallDir,
|
|
env.ShowDbInstallationVerificationLogs,
|
|
)
|
|
|
|
env.MysqlInstallDir = filepath.Join(backendRoot, "tools", "mysql")
|
|
tools.VerifyMysqlInstallation(
|
|
log,
|
|
env.EnvMode,
|
|
env.MysqlInstallDir,
|
|
env.ShowDbInstallationVerificationLogs,
|
|
)
|
|
|
|
env.MariadbInstallDir = filepath.Join(backendRoot, "tools", "mariadb")
|
|
tools.VerifyMariadbInstallation(
|
|
log,
|
|
env.EnvMode,
|
|
env.MariadbInstallDir,
|
|
env.ShowDbInstallationVerificationLogs,
|
|
)
|
|
|
|
env.MongodbInstallDir = filepath.Join(backendRoot, "tools", "mongodb")
|
|
tools.VerifyMongodbInstallation(
|
|
log,
|
|
env.EnvMode,
|
|
env.MongodbInstallDir,
|
|
env.ShowDbInstallationVerificationLogs,
|
|
)
|
|
|
|
if env.NodeNetworkThroughputMBs == 0 {
|
|
env.NodeNetworkThroughputMBs = 125 // 1 Gbit/s
|
|
}
|
|
|
|
if !env.IsManyNodesMode {
|
|
env.IsPrimaryNode = true
|
|
env.IsProcessingNode = true
|
|
}
|
|
|
|
if env.TestLocalhost == "" {
|
|
env.TestLocalhost = "localhost"
|
|
}
|
|
|
|
// Valkey
|
|
if env.ValkeyHost == "" {
|
|
log.Error("VALKEY_HOST is empty")
|
|
os.Exit(1)
|
|
}
|
|
if env.ValkeyPort == "" {
|
|
log.Error("VALKEY_PORT is empty")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Check for external Valkey override
|
|
if externalValkeyHost := os.Getenv("DANGEROUS_VALKEY_HOST"); externalValkeyHost != "" {
|
|
log.Warn(
|
|
"Using DANGEROUS_VALKEY_* variables - connecting to external Valkey instead of internal instance",
|
|
)
|
|
env.ValkeyHost = externalValkeyHost
|
|
|
|
if externalValkeyPort := os.Getenv("DANGEROUS_VALKEY_PORT"); externalValkeyPort != "" {
|
|
env.ValkeyPort = externalValkeyPort
|
|
}
|
|
if externalValkeyUsername := os.Getenv("DANGEROUS_VALKEY_USERNAME"); externalValkeyUsername != "" {
|
|
env.ValkeyUsername = externalValkeyUsername
|
|
}
|
|
if externalValkeyPassword := os.Getenv("DANGEROUS_VALKEY_PASSWORD"); externalValkeyPassword != "" {
|
|
env.ValkeyPassword = externalValkeyPassword
|
|
}
|
|
if externalValkeyIsSsl := os.Getenv("DANGEROUS_VALKEY_IS_SSL"); externalValkeyIsSsl != "" {
|
|
env.ValkeyIsSsl = externalValkeyIsSsl == "true"
|
|
}
|
|
}
|
|
|
|
// Store the data and temp folders one level below the root
|
|
// (projectRoot/databasus-data -> /databasus-data)
|
|
env.DataFolder = filepath.Join(filepath.Dir(backendRoot), "databasus-data", "backups")
|
|
env.TempFolder = filepath.Join(filepath.Dir(backendRoot), "databasus-data", "temp")
|
|
env.SecretKeyPath = filepath.Join(filepath.Dir(backendRoot), "databasus-data", "secret.key")
|
|
|
|
if env.IsTesting {
|
|
if env.TestPostgres12Port == "" {
|
|
log.Error("TEST_POSTGRES_12_PORT is empty")
|
|
os.Exit(1)
|
|
}
|
|
if env.TestPostgres13Port == "" {
|
|
log.Error("TEST_POSTGRES_13_PORT is empty")
|
|
os.Exit(1)
|
|
}
|
|
if env.TestPostgres14Port == "" {
|
|
log.Error("TEST_POSTGRES_14_PORT is empty")
|
|
os.Exit(1)
|
|
}
|
|
if env.TestPostgres15Port == "" {
|
|
log.Error("TEST_POSTGRES_15_PORT is empty")
|
|
os.Exit(1)
|
|
}
|
|
if env.TestPostgres16Port == "" {
|
|
log.Error("TEST_POSTGRES_16_PORT is empty")
|
|
os.Exit(1)
|
|
}
|
|
if env.TestPostgres17Port == "" {
|
|
log.Error("TEST_POSTGRES_17_PORT is empty")
|
|
os.Exit(1)
|
|
}
|
|
if env.TestPostgres18Port == "" {
|
|
log.Error("TEST_POSTGRES_18_PORT is empty")
|
|
os.Exit(1)
|
|
}
|
|
|
|
if env.TestMinioPort == "" {
|
|
log.Error("TEST_MINIO_PORT is empty")
|
|
os.Exit(1)
|
|
}
|
|
if env.TestMinioConsolePort == "" {
|
|
log.Error("TEST_MINIO_CONSOLE_PORT is empty")
|
|
os.Exit(1)
|
|
}
|
|
|
|
if env.TestAzuriteBlobPort == "" {
|
|
log.Error("TEST_AZURITE_BLOB_PORT is empty")
|
|
os.Exit(1)
|
|
}
|
|
|
|
if env.TestNASPort == "" {
|
|
log.Error("TEST_NAS_PORT is empty")
|
|
os.Exit(1)
|
|
}
|
|
|
|
}
|
|
|
|
// Billing
|
|
if env.IsCloud {
|
|
if env.PricePerGBCents == 0 {
|
|
log.Error("PRICE_PER_GB_CENTS is empty or zero")
|
|
os.Exit(1)
|
|
}
|
|
|
|
if env.PaddleApiKey == "" {
|
|
log.Error("PADDLE_API_KEY is empty")
|
|
os.Exit(1)
|
|
}
|
|
|
|
if env.PaddleWebhookSecret == "" {
|
|
log.Error("PADDLE_WEBHOOK_SECRET is empty")
|
|
os.Exit(1)
|
|
}
|
|
|
|
if env.PaddlePriceID == "" {
|
|
log.Error("PADDLE_PRICE_ID is empty")
|
|
os.Exit(1)
|
|
}
|
|
|
|
if env.PaddleClientToken == "" {
|
|
log.Error("PADDLE_CLIENT_TOKEN is empty")
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
env.MinStorageGB = 20
|
|
env.MaxStorageGB = 10_000
|
|
env.TrialDuration = 24 * time.Hour
|
|
env.TrialStorageGB = 20
|
|
env.GracePeriod = 30 * 24 * time.Hour
|
|
|
|
log.Info("Environment variables loaded successfully!")
|
|
}
|