2023-03-14 15:22:52 +00:00
package config
import (
2023-03-24 13:41:08 +00:00
"fmt"
2023-12-27 16:24:52 +00:00
"io"
2023-07-03 14:31:12 +00:00
"net/url"
2023-03-14 15:22:52 +00:00
"os"
"path/filepath"
2023-06-07 18:50:30 +00:00
"reflect"
2024-01-04 04:11:46 +00:00
"slices"
2023-03-24 13:41:08 +00:00
"strconv"
"strings"
2023-06-18 15:01:27 +00:00
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/thomiceli/opengist/internal/utils"
"gopkg.in/yaml.v3"
2023-03-14 15:22:52 +00:00
)
2024-01-11 21:55:40 +00:00
var OpengistVersion = "1.7-dev"
2023-03-14 15:22:52 +00:00
var C * config
2023-04-06 23:52:56 +00:00
// Not using nested structs because the library
// doesn't support dot notation in this case sadly
2023-03-14 15:22:52 +00:00
type config struct {
2023-06-07 18:50:30 +00:00
LogLevel string ` yaml:"log-level" env:"OG_LOG_LEVEL" `
2023-12-27 16:24:52 +00:00
LogOutput string ` yaml:"log-output" env:"OG_LOG_OUTPUT" `
2023-06-07 18:50:30 +00:00
ExternalUrl string ` yaml:"external-url" env:"OG_EXTERNAL_URL" `
OpengistHome string ` yaml:"opengist-home" env:"OG_OPENGIST_HOME" `
DBFilename string ` yaml:"db-filename" env:"OG_DB_FILENAME" `
2024-01-04 02:38:15 +00:00
IndexEnabled bool ` yaml:"index.enabled" env:"OG_INDEX_ENABLED" `
IndexDirname string ` yaml:"index.dirname" env:"OG_INDEX_DIRNAME" `
2023-06-07 18:50:30 +00:00
2023-12-27 16:30:42 +00:00
GitDefaultBranch string ` yaml:"git.default-branch" env:"OG_GIT_DEFAULT_BRANCH" `
2023-06-09 13:25:41 +00:00
SqliteJournalMode string ` yaml:"sqlite.journal-mode" env:"OG_SQLITE_JOURNAL_MODE" `
2023-09-18 16:06:27 +00:00
HttpHost string ` yaml:"http.host" env:"OG_HTTP_HOST" `
HttpPort string ` yaml:"http.port" env:"OG_HTTP_PORT" `
HttpGit bool ` yaml:"http.git-enabled" env:"OG_HTTP_GIT_ENABLED" `
2023-06-07 18:50:30 +00:00
SshGit bool ` yaml:"ssh.git-enabled" env:"OG_SSH_GIT_ENABLED" `
SshHost string ` yaml:"ssh.host" env:"OG_SSH_HOST" `
SshPort string ` yaml:"ssh.port" env:"OG_SSH_PORT" `
SshExternalDomain string ` yaml:"ssh.external-domain" env:"OG_SSH_EXTERNAL_DOMAIN" `
SshKeygen string ` yaml:"ssh.keygen-executable" env:"OG_SSH_KEYGEN_EXECUTABLE" `
GithubClientKey string ` yaml:"github.client-key" env:"OG_GITHUB_CLIENT_KEY" `
GithubSecret string ` yaml:"github.secret" env:"OG_GITHUB_SECRET" `
2023-12-18 00:35:44 +00:00
GitlabClientKey string ` yaml:"gitlab.client-key" env:"OG_GITLAB_CLIENT_KEY" `
GitlabSecret string ` yaml:"gitlab.secret" env:"OG_GITLAB_SECRET" `
GitlabUrl string ` yaml:"gitlab.url" env:"OG_GITLAB_URL" `
2023-06-07 18:50:30 +00:00
GiteaClientKey string ` yaml:"gitea.client-key" env:"OG_GITEA_CLIENT_KEY" `
GiteaSecret string ` yaml:"gitea.secret" env:"OG_GITEA_SECRET" `
GiteaUrl string ` yaml:"gitea.url" env:"OG_GITEA_URL" `
2023-09-15 21:56:14 +00:00
OIDCClientKey string ` yaml:"oidc.client-key" env:"OG_OIDC_CLIENT_KEY" `
OIDCSecret string ` yaml:"oidc.secret" env:"OG_OIDC_SECRET" `
OIDCDiscoveryUrl string ` yaml:"oidc.discovery-url" env:"OG_OIDC_DISCOVERY_URL" `
2024-01-20 22:46:47 +00:00
CustomLogo string ` yaml:"custom.logo" env:"OG_CUSTOM_LOGO" `
CustomFavicon string ` yaml:"custom.favicon" env:"OG_CUSTOM_FAVICON" `
2023-03-14 15:22:52 +00:00
}
2023-03-15 00:36:46 +00:00
func configWithDefaults ( ) ( * config , error ) {
c := & config { }
2023-04-06 23:52:56 +00:00
c . LogLevel = "warn"
2023-12-27 16:24:52 +00:00
c . LogOutput = "stdout,file"
2023-10-31 06:23:15 +00:00
c . OpengistHome = ""
2023-03-15 00:36:46 +00:00
c . DBFilename = "opengist.db"
2024-01-04 02:38:15 +00:00
c . IndexEnabled = true
c . IndexDirname = "opengist.index"
2023-03-15 00:36:46 +00:00
2023-06-09 13:25:41 +00:00
c . SqliteJournalMode = "WAL"
2023-04-06 23:52:56 +00:00
c . HttpHost = "0.0.0.0"
c . HttpPort = "6157"
c . HttpGit = true
2023-03-15 09:37:17 +00:00
2023-04-06 23:52:56 +00:00
c . SshGit = true
c . SshHost = "0.0.0.0"
c . SshPort = "2222"
c . SshKeygen = "ssh-keygen"
2023-03-15 00:36:46 +00:00
2023-12-18 00:35:44 +00:00
c . GiteaUrl = "https://gitea.com"
2023-04-17 17:11:32 +00:00
2023-03-15 00:36:46 +00:00
return c , nil
}
func InitConfig ( configPath string ) error {
2023-04-06 23:52:56 +00:00
// Default values
2023-03-15 00:36:46 +00:00
c , err := configWithDefaults ( )
if err != nil {
return err
}
2023-06-07 18:50:30 +00:00
if err = loadConfigFromYaml ( c , configPath ) ; err != nil {
return err
2023-03-14 15:22:52 +00:00
}
2023-06-07 18:50:30 +00:00
if err = loadConfigFromEnv ( c ) ; err != nil {
return err
2023-03-14 15:22:52 +00:00
}
2023-04-06 23:52:56 +00:00
2023-10-31 06:23:15 +00:00
if c . OpengistHome == "" {
homeDir , err := os . UserHomeDir ( )
if err != nil {
return fmt . Errorf ( "opengist home directory is not set and current user home directory could not be determined; please specify the opengist home directory manually via the configuration" )
}
c . OpengistHome = filepath . Join ( homeDir , ".opengist" )
}
2023-07-03 14:31:12 +00:00
if err = checks ( c ) ; err != nil {
return err
}
2023-03-14 15:22:52 +00:00
C = c
return nil
}
func InitLog ( ) {
if err := os . MkdirAll ( filepath . Join ( GetHomeDir ( ) , "log" ) , 0755 ) ; err != nil {
panic ( err )
}
var level zerolog . Level
2023-12-27 16:24:52 +00:00
level , err := zerolog . ParseLevel ( C . LogLevel )
2023-03-14 15:22:52 +00:00
if err != nil {
level = zerolog . InfoLevel
}
2023-12-27 16:24:52 +00:00
var logWriters [ ] io . Writer
logOutputTypes := utils . RemoveDuplicates [ string ] (
strings . Split ( strings . ToLower ( C . LogOutput ) , "," ) ,
)
for _ , logOutputType := range logOutputTypes {
logOutputType = strings . TrimSpace ( logOutputType )
2024-01-04 04:11:46 +00:00
if ! slices . Contains ( [ ] string { "stdout" , "file" } , logOutputType ) {
2023-12-27 16:24:52 +00:00
defer func ( ) { log . Warn ( ) . Msg ( "Invalid log output type: " + logOutputType ) } ( )
continue
}
switch logOutputType {
case "stdout" :
logWriters = append ( logWriters , zerolog . NewConsoleWriter ( ) )
defer func ( ) { log . Debug ( ) . Msg ( "Logging to stdout" ) } ( )
case "file" :
file , err := os . OpenFile ( filepath . Join ( GetHomeDir ( ) , "log" , "opengist.log" ) , os . O_APPEND | os . O_CREATE | os . O_WRONLY , 0644 )
if err != nil {
panic ( err )
}
logWriters = append ( logWriters , file )
defer func ( ) { log . Debug ( ) . Msg ( "Logging to file: " + file . Name ( ) ) } ( )
}
}
if len ( logWriters ) == 0 {
logWriters = append ( logWriters , zerolog . NewConsoleWriter ( ) )
defer func ( ) { log . Warn ( ) . Msg ( "No valid log outputs, defaulting to stdout" ) } ( )
}
multi := zerolog . MultiLevelWriter ( logWriters ... )
2023-05-24 14:40:27 +00:00
log . Logger = zerolog . New ( multi ) . Level ( level ) . With ( ) . Timestamp ( ) . Logger ( )
2023-06-09 13:25:41 +00:00
2024-01-04 04:11:46 +00:00
if ! slices . Contains ( [ ] string { "trace" , "debug" , "info" , "warn" , "error" , "fatal" , "panic" } , strings . ToLower ( C . LogLevel ) ) {
2023-06-09 13:25:41 +00:00
log . Warn ( ) . Msg ( "Invalid log level: " + C . LogLevel )
}
2023-03-14 15:22:52 +00:00
}
2023-03-24 13:41:08 +00:00
func CheckGitVersion ( version string ) ( bool , error ) {
versionParts := strings . Split ( version , "." )
if len ( versionParts ) < 2 {
return false , fmt . Errorf ( "invalid version string" )
}
major , err := strconv . Atoi ( versionParts [ 0 ] )
if err != nil {
return false , fmt . Errorf ( "invalid major version number" )
}
minor , err := strconv . Atoi ( versionParts [ 1 ] )
if err != nil {
return false , fmt . Errorf ( "invalid minor version number" )
}
2023-12-27 16:30:42 +00:00
// Check if version is prior to 2.28
if major < 2 || ( major == 2 && minor < 28 ) {
2023-03-24 13:41:08 +00:00
return false , nil
}
return true , nil
}
2023-03-14 15:22:52 +00:00
func GetHomeDir ( ) string {
absolutePath , _ := filepath . Abs ( C . OpengistHome )
return filepath . Clean ( absolutePath )
}
2023-06-07 18:50:30 +00:00
func loadConfigFromYaml ( c * config , configPath string ) error {
if configPath != "" {
absolutePath , _ := filepath . Abs ( configPath )
absolutePath = filepath . Clean ( absolutePath )
file , err := os . Open ( absolutePath )
if err != nil {
if ! os . IsNotExist ( err ) {
return err
}
fmt . Println ( "No YAML config file found at " + absolutePath )
} else {
fmt . Println ( "Using YAML config file: " + absolutePath )
// Override default values with values from config.yml
d := yaml . NewDecoder ( file )
if err = d . Decode ( & c ) ; err != nil {
return err
}
defer file . Close ( )
}
} else {
fmt . Println ( "No YAML config file specified." )
}
return nil
}
func loadConfigFromEnv ( c * config ) error {
v := reflect . ValueOf ( c ) . Elem ( )
var envVars [ ] string
for i := 0 ; i < v . NumField ( ) ; i ++ {
tag := v . Type ( ) . Field ( i ) . Tag . Get ( "env" )
if tag == "" {
continue
}
envValue := os . Getenv ( strings . ToUpper ( tag ) )
if envValue == "" {
continue
}
switch v . Field ( i ) . Kind ( ) {
case reflect . String :
v . Field ( i ) . SetString ( envValue )
case reflect . Bool :
boolVal , err := strconv . ParseBool ( envValue )
if err != nil {
return err
}
v . Field ( i ) . SetBool ( boolVal )
}
envVars = append ( envVars , tag )
}
if len ( envVars ) > 0 {
fmt . Println ( "Using environment variables config: " + strings . Join ( envVars , ", " ) )
} else {
fmt . Println ( "No environment variables config specified." )
}
return nil
}
2023-07-03 14:31:12 +00:00
func checks ( c * config ) error {
if _ , err := url . Parse ( c . ExternalUrl ) ; err != nil {
return err
}
if _ , err := url . Parse ( c . GiteaUrl ) ; err != nil {
return err
}
2023-09-25 14:08:26 +00:00
if _ , err := url . Parse ( c . OIDCDiscoveryUrl ) ; err != nil {
2023-09-15 21:56:14 +00:00
return err
}
2023-07-03 14:31:12 +00:00
return nil
}