package config import ( "farga/lib" "log" "net/url" "os" "strconv" "strings" "time" "github.com/joho/godotenv" ) type Config struct { SQLitePath string // path relative to the executable where the database is at. CSRFKey string // random string at least 32 bytes long. Domain string // domain used both for the frontend and SSH Port uint // HTTP/S port SSHPort uint // SSH port PublicUrl string // Full url of the frontend, including HTTP and no trailing slash. http://localhost:3333 is an example. AllowSignups bool // boolean. Invites allow to signup users. InviteTokenLength uint // length of token. 8 -> "abc12345". If set to 0 it will error out. InviteTokenDuration time.Duration // how much an invite should last: 7d or 4h or 2h45m HackatimeClientID string // optional field for hackatime oauth HackatimeClientSecret string // optional field for hackatime oauth } var C Config func New() Config { err := godotenv.Load() if err != nil { log.Println("\x1b[33mNo .env file found, using system env\x1b[0m") // orange then reset } DOMAIN := os.Getenv("DOMAIN") PORT := os.Getenv("PORT") SSH_PORT := os.Getenv("SSH_PORT") CSRF_KEY := os.Getenv("CSRF_KEY") SQLITE_PATH := os.Getenv("SQLITE_PATH") ALLOW_SIGNUPS := os.Getenv("ALLOW_SIGNUPS") PUBLIC_URL := os.Getenv("PUBLIC_URL") HACKATIME_CLIENT_ID := os.Getenv("HACKATIME_CLIENT_ID") HACKATIME_CLIENT_SECRET := os.Getenv("HACKATIME_CLIENT_SECRET") INVITE_TOKEN_LENGTH := os.Getenv("INVITE_TOKEN_LENGTH") if DOMAIN == "" || PUBLIC_URL == "" || PORT == "" || SQLITE_PATH == "" { log.Fatal("You're missing one (or more) of the following env vars: 'SQLITE_PATH, DOMAIN, PORT, PUBLIC_URL'.") } Port, err := strconv.ParseUint(PORT, 10, 32) if Port == 0 || err != nil { log.Fatal("Invalid PORT env var.") } SSHPort, err := strconv.ParseUint(SSH_PORT, 10, 32) if SSHPort == 0 || err != nil { log.Fatal("Invalid SSH_PORT env var.") } // assume HTTP scheme if none provided. if !strings.HasPrefix(PUBLIC_URL, "http://") && !strings.HasPrefix(PUBLIC_URL, "https://") { PUBLIC_URL = "http://" + PUBLIC_URL } ParsedPublicUrl, err := url.ParseRequestURI(PUBLIC_URL) if err != nil { log.Fatal("Invalid PUBLIC_URL env var.") } var InviteTokenLength uint64 = 16 // sensible default with 80 bits of entropy. if INVITE_TOKEN_LENGTH != "" { InviteTokenLength, err = strconv.ParseUint(INVITE_TOKEN_LENGTH, 10, 32) if InviteTokenLength == 0 || err != nil { log.Fatal("Invalid INVITE_TOKEN_LENGTH env var.") } } if CSRF_KEY == "" { env, err := godotenv.Read(".env") if err != nil { log.Panic(err) } // generate a new, safe CSRF key and write to .env. // it magically formats the .env too. CSRF_KEY = lib.Base64Token(64) env["CSRF_KEY"] = CSRF_KEY godotenv.Write(env, ".env") } var AllowSignups bool if ALLOW_SIGNUPS != "" { AllowSignups, err = strconv.ParseBool(ALLOW_SIGNUPS) if err != nil { log.Fatal("Invalid ALLOW_SIGNUPS value. Set it to either 'true' or 'false'") } } // 24 hours * 2 days = 48 hours InviteTokenDuration, err := time.ParseDuration("48h") // todo: check if not negative if err != nil { log.Fatal("Invalid token duration value.") } C = Config{ SQLitePath: SQLITE_PATH, AllowSignups: AllowSignups, Port: uint(Port), SSHPort: uint(SSHPort), CSRFKey: CSRF_KEY, Domain: DOMAIN, PublicUrl: strings.TrimSuffix(ParsedPublicUrl.String(), "/"), // omit trailing slash HackatimeClientID: HACKATIME_CLIENT_ID, HackatimeClientSecret: HACKATIME_CLIENT_SECRET, InviteTokenLength: uint(InviteTokenLength), InviteTokenDuration: InviteTokenDuration, } return C }