main.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. package main
  2. import (
  3. "flag"
  4. "path/filepath"
  5. "strings"
  6. log "github.com/sirupsen/logrus"
  7. "github.com/OliveTin/OliveTin/internal/api"
  8. "github.com/OliveTin/OliveTin/internal/auth"
  9. "github.com/OliveTin/OliveTin/internal/entities"
  10. "github.com/OliveTin/OliveTin/internal/executor"
  11. "github.com/OliveTin/OliveTin/internal/httpservers"
  12. "github.com/OliveTin/OliveTin/internal/installationinfo"
  13. "github.com/OliveTin/OliveTin/internal/oncalendarfile"
  14. "github.com/OliveTin/OliveTin/internal/oncron"
  15. "github.com/OliveTin/OliveTin/internal/onfileindir"
  16. "github.com/OliveTin/OliveTin/internal/onstartup"
  17. "github.com/OliveTin/OliveTin/internal/servicehost"
  18. updatecheck "github.com/OliveTin/OliveTin/internal/updatecheck"
  19. "os"
  20. "strconv"
  21. config "github.com/OliveTin/OliveTin/internal/config"
  22. "github.com/knadh/koanf/parsers/yaml"
  23. "github.com/knadh/koanf/providers/env"
  24. "github.com/knadh/koanf/providers/file"
  25. "github.com/knadh/koanf/v2"
  26. )
  27. var (
  28. cfg *config.Config
  29. version = "dev"
  30. commit = "nocommit"
  31. date = "nodate"
  32. )
  33. func init() {
  34. initLog()
  35. initConfig(initCliFlags())
  36. initCheckEnvironment()
  37. initInstallationInfo()
  38. log.Info("OliveTin initialization complete")
  39. }
  40. func initLog() {
  41. logFormat := os.Getenv("OLIVETIN_LOG_FORMAT")
  42. if logFormat == "json" {
  43. log.SetFormatter(&log.JSONFormatter{})
  44. } else {
  45. log.SetFormatter(&log.TextFormatter{
  46. ForceQuote: true,
  47. DisableTimestamp: true,
  48. })
  49. }
  50. // Use debug this early on to catch details about startup errors. The
  51. // default config will raise the log level later, if not set.
  52. log.SetLevel(log.DebugLevel) // Default to debug, to catch cfg issue
  53. }
  54. func initCliFlags() string {
  55. var configDir string
  56. flag.StringVar(&configDir, "configdir", ".", "Config directory path")
  57. var printVersion bool
  58. flag.BoolVar(&printVersion, "version", false, "Prints the version number and exits")
  59. flag.Parse()
  60. // This log message should be the first log message OliveTin prints.
  61. if printVersion {
  62. logStartupMessage("OliveTin is just printing the startup message")
  63. os.Exit(1)
  64. } else {
  65. logStartupMessage("OliveTin initializing")
  66. }
  67. log.WithFields(log.Fields{
  68. "value": configDir,
  69. }).Debugf("Value of -configdir flag")
  70. return configDir
  71. }
  72. func getBasePort() int {
  73. var err error
  74. defaultPort := 1337
  75. basePort := defaultPort
  76. envPort := os.Getenv("PORT")
  77. if envPort != "" {
  78. basePort, err = strconv.Atoi(os.Getenv("PORT"))
  79. if err != nil {
  80. log.Errorf("Error converting port to int. %s", err)
  81. os.Exit(1)
  82. }
  83. }
  84. if defaultPort != basePort {
  85. log.WithFields(log.Fields{
  86. "basePort": basePort,
  87. }).Debug("Base port")
  88. }
  89. return basePort
  90. }
  91. func getConfigPath(directory string) string {
  92. joinedPath := filepath.Join(directory, "config.yaml")
  93. configPath, err := filepath.Abs(joinedPath)
  94. if err != nil {
  95. log.WithError(err).Warnf("Error getting absolute path for %s", joinedPath)
  96. return joinedPath
  97. }
  98. return configPath
  99. }
  100. func initConfig(configDir string) {
  101. k := koanf.New(".")
  102. err := k.Load(env.Provider(".", ".", nil), nil)
  103. if err != nil {
  104. log.WithFields(log.Fields{
  105. "error": err,
  106. }).Fatalf("Error loading environment variables")
  107. }
  108. directories := []string{
  109. configDir,
  110. }
  111. // Only load additional configs if not in integration test mode
  112. absConfigDir, _ := filepath.Abs(configDir)
  113. if !strings.Contains(absConfigDir, "integration-tests") {
  114. directories = append(directories,
  115. servicehost.GetConfigFilePath(),
  116. "/config", // For containers.
  117. "/etc/OliveTin/",
  118. )
  119. }
  120. var baseConfigPath string
  121. for _, directory := range directories {
  122. configPath := getConfigPath(directory)
  123. found := true
  124. if _, err := os.Stat(configPath); err != nil {
  125. found = false
  126. }
  127. log.WithFields(log.Fields{
  128. "configPath": configPath,
  129. "found": found,
  130. }).Debug("Checking base config path")
  131. if !found {
  132. continue
  133. }
  134. if baseConfigPath == "" {
  135. baseConfigPath = configPath
  136. }
  137. log.WithFields(log.Fields{
  138. "configPath": configPath,
  139. }).Info("Loading config from path")
  140. f := file.Provider(configPath)
  141. if err := k.Load(f, yaml.Parser()); err != nil {
  142. log.Fatalf("error loading config from %s: %v", configPath, err)
  143. os.Exit(1)
  144. }
  145. err := f.Watch(func(evt interface{}, err error) {
  146. log.Infof("config file changed: %v", evt)
  147. errLoad := k.Load(f, yaml.Parser())
  148. if errLoad != nil {
  149. log.WithFields(log.Fields{
  150. "error": errLoad,
  151. }).Fatalf("Error loading config file")
  152. }
  153. config.AppendSource(cfg, k, configPath)
  154. })
  155. if err != nil {
  156. log.WithFields(log.Fields{
  157. "error": err,
  158. }).Fatalf("Error watching config file")
  159. }
  160. break
  161. }
  162. cfg = config.DefaultConfigWithBasePort(getBasePort())
  163. if baseConfigPath == "" {
  164. log.Fatalf("No base config file found")
  165. os.Exit(1)
  166. }
  167. config.AppendSource(cfg, k, baseConfigPath)
  168. }
  169. func initInstallationInfo() {
  170. installationinfo.Config = cfg
  171. installationinfo.Build.Version = version
  172. installationinfo.Build.Commit = commit
  173. installationinfo.Build.Date = date
  174. }
  175. func logStartupMessage(message string) {
  176. log.WithFields(log.Fields{
  177. "version": version,
  178. "commit": commit,
  179. "date": date,
  180. }).Info(message)
  181. }
  182. func initCheckEnvironment() {
  183. warnIfPuidGuid()
  184. }
  185. func warnIfPuidGuid() {
  186. if os.Getenv("PUID") != "" || os.Getenv("PGID") != "" {
  187. log.Warnf("PUID or PGID seem to be set to something, but they are ignored by OliveTin. Please check https://docs.olivetin.app/no-puid-pgid.html")
  188. }
  189. }
  190. func main() {
  191. servicehost.Start(cfg.ServiceHostMode, cfg.ServiceLogs.Directory)
  192. log.WithFields(log.Fields{
  193. "configDir": cfg.GetDir(),
  194. }).Infof("OliveTin started")
  195. log.Debugf("Config: %+v", cfg)
  196. executor := executor.DefaultExecutor(cfg)
  197. executor.RebuildActionMap()
  198. config.AddListener(executor.RebuildActionMap)
  199. executor.LoadLogsFromDisk()
  200. api.RegisterExecutorListener(executor)
  201. entities.AddListener(executor.RebuildActionMap)
  202. go onstartup.Execute(cfg, executor)
  203. go oncron.Schedule(cfg, executor)
  204. go onfileindir.WatchFilesInDirectory(cfg, executor)
  205. go oncalendarfile.Schedule(cfg, executor)
  206. go entities.SetupEntityFileWatchers(cfg)
  207. go updatecheck.StartUpdateChecker(cfg)
  208. // Load persistent sessions from disk
  209. auth.LoadUserSessions(cfg)
  210. httpservers.StartFrontendMux(cfg, executor)
  211. }