opengist/internal/config/config.go

286 lines
7.6 KiB
Go
Raw Normal View History

2023-03-14 15:22:52 +00:00
package config
import (
2023-03-24 13:41:08 +00:00
"fmt"
"io"
"net/url"
2023-03-14 15:22:52 +00:00
"os"
"path/filepath"
2023-06-07 18:50:30 +00:00
"reflect"
"slices"
2023-03-24 13:41:08 +00:00
"strconv"
"strings"
"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"`
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
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"
c.LogOutput = "stdout,file"
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, out io.Writer) 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
}
if err = loadConfigFromYaml(c, configPath, out); err != nil {
2023-06-07 18:50:30 +00:00
return err
2023-03-14 15:22:52 +00:00
}
if err = loadConfigFromEnv(c, out); err != nil {
2023-06-07 18:50:30 +00:00
return err
2023-03-14 15:22:52 +00:00
}
2023-04-06 23:52:56 +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")
}
if err = checks(c); err != nil {
return err
}
2023-03-14 15:22:52 +00:00
C = c
2024-01-23 19:24:01 +00:00
if err = os.Setenv("OG_OPENGIST_HOME_INTERNAL", GetHomeDir()); err != nil {
return err
}
2023-03-14 15:22:52 +00:00
return nil
}
func InitLog() {
if err := os.MkdirAll(filepath.Join(GetHomeDir(), "log"), 0755); err != nil {
panic(err)
}
var level zerolog.Level
level, err := zerolog.ParseLevel(C.LogLevel)
2023-03-14 15:22:52 +00:00
if err != nil {
level = zerolog.InfoLevel
}
var logWriters []io.Writer
logOutputTypes := utils.RemoveDuplicates[string](
strings.Split(strings.ToLower(C.LogOutput), ","),
)
for _, logOutputType := range logOutputTypes {
logOutputType = strings.TrimSpace(logOutputType)
if !slices.Contains([]string{"stdout", "file"}, logOutputType) {
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
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")
}
// 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, out io.Writer) error {
2023-06-07 18:50:30 +00:00
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.Fprintln(out, "No YAML config file found at "+absolutePath)
2023-06-07 18:50:30 +00:00
} else {
_, _ = fmt.Fprintln(out, "Using YAML config file: "+absolutePath)
2023-06-07 18:50:30 +00:00
// 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.Fprintln(out, "No YAML config file specified.")
2023-06-07 18:50:30 +00:00
}
return nil
}
func loadConfigFromEnv(c *config, out io.Writer) error {
2023-06-07 18:50:30 +00:00
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.Fprintln(out, "Using environment variables config: "+strings.Join(envVars, ", "))
2023-06-07 18:50:30 +00:00
} else {
_, _ = fmt.Fprintln(out, "No environment variables config specified.")
2023-06-07 18:50:30 +00:00
}
return nil
}
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
}
return nil
}