conf.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package conf
  5. import (
  6. "net/mail"
  7. "net/url"
  8. "os"
  9. "path"
  10. "path/filepath"
  11. "strconv"
  12. "strings"
  13. "time"
  14. _ "github.com/go-macaron/cache/memcache"
  15. _ "github.com/go-macaron/cache/redis"
  16. "github.com/go-macaron/session"
  17. _ "github.com/go-macaron/session/redis"
  18. "github.com/mcuadros/go-version"
  19. "github.com/pkg/errors"
  20. "gopkg.in/ini.v1"
  21. log "unknwon.dev/clog/v2"
  22. "github.com/gogs/go-libravatar"
  23. "gogs.io/gogs/internal/assets/conf"
  24. "gogs.io/gogs/internal/osutil"
  25. "gogs.io/gogs/internal/user"
  26. )
  27. func init() {
  28. // Initialize the primary logger until logging service is up.
  29. err := log.NewConsole()
  30. if err != nil {
  31. panic("init console logger: " + err.Error())
  32. }
  33. }
  34. // Asset is a wrapper for getting conf assets.
  35. func Asset(name string) ([]byte, error) {
  36. return conf.Asset(name)
  37. }
  38. // AssetDir is a wrapper for getting conf assets.
  39. func AssetDir(name string) ([]string, error) {
  40. return conf.AssetDir(name)
  41. }
  42. // MustAsset is a wrapper for getting conf assets.
  43. func MustAsset(name string) []byte {
  44. return conf.MustAsset(name)
  45. }
  46. // File is the configuration object.
  47. var File *ini.File
  48. // Init initializes configuration from conf assets and given custom configuration file.
  49. // If `customConf` is empty, it falls back to default location, i.e. "<WORK DIR>/custom".
  50. // It is safe to call this function multiple times with desired `customConf`, but it is
  51. // not concurrent safe.
  52. //
  53. // ⚠️ WARNING: Do not print anything in this function other than wanrings.
  54. func Init(customConf string) error {
  55. var err error
  56. File, err = ini.LoadSources(ini.LoadOptions{
  57. IgnoreInlineComment: true,
  58. }, conf.MustAsset("conf/app.ini"))
  59. if err != nil {
  60. return errors.Wrap(err, "parse 'conf/app.ini'")
  61. }
  62. File.NameMapper = ini.SnackCase
  63. if customConf == "" {
  64. customConf = filepath.Join(CustomDir(), "conf", "app.ini")
  65. } else {
  66. customConf, err = filepath.Abs(customConf)
  67. if err != nil {
  68. return errors.Wrap(err, "get absolute path")
  69. }
  70. }
  71. CustomConf = customConf
  72. if osutil.IsFile(customConf) {
  73. if err = File.Append(customConf); err != nil {
  74. return errors.Wrapf(err, "append %q", customConf)
  75. }
  76. } else {
  77. log.Warn("Custom config %q not found. Ignore this warning if you're running for the first time", customConf)
  78. }
  79. if err = File.Section(ini.DefaultSection).MapTo(&App); err != nil {
  80. return errors.Wrap(err, "mapping default section")
  81. }
  82. // ***************************
  83. // ----- Server settings -----
  84. // ***************************
  85. if err = File.Section("server").MapTo(&Server); err != nil {
  86. return errors.Wrap(err, "mapping [server] section")
  87. }
  88. Server.AppDataPath = ensureAbs(Server.AppDataPath)
  89. if !strings.HasSuffix(Server.ExternalURL, "/") {
  90. Server.ExternalURL += "/"
  91. }
  92. Server.URL, err = url.Parse(Server.ExternalURL)
  93. if err != nil {
  94. return errors.Wrapf(err, "parse '[server] EXTERNAL_URL' %q", err)
  95. }
  96. // Subpath should start with '/' and end without '/', i.e. '/{subpath}'.
  97. Server.Subpath = strings.TrimRight(Server.URL.Path, "/")
  98. Server.SubpathDepth = strings.Count(Server.Subpath, "/")
  99. unixSocketMode, err := strconv.ParseUint(Server.UnixSocketPermission, 8, 32)
  100. if err != nil {
  101. return errors.Wrapf(err, "parse '[server] UNIX_SOCKET_PERMISSION' %q", Server.UnixSocketPermission)
  102. }
  103. if unixSocketMode > 0777 {
  104. unixSocketMode = 0666
  105. }
  106. Server.UnixSocketMode = os.FileMode(unixSocketMode)
  107. // ************************
  108. // ----- SSH settings -----
  109. // ************************
  110. SSH.RootPath = filepath.Join(HomeDir(), ".ssh")
  111. SSH.KeyTestPath = os.TempDir()
  112. if err = File.Section("server").MapTo(&SSH); err != nil {
  113. return errors.Wrap(err, "mapping SSH settings from [server] section")
  114. }
  115. SSH.RootPath = ensureAbs(SSH.RootPath)
  116. SSH.KeyTestPath = ensureAbs(SSH.KeyTestPath)
  117. if !SSH.Disabled {
  118. if !SSH.StartBuiltinServer {
  119. if err := os.MkdirAll(SSH.RootPath, 0700); err != nil {
  120. return errors.Wrap(err, "create SSH root directory")
  121. } else if err = os.MkdirAll(SSH.KeyTestPath, 0644); err != nil {
  122. return errors.Wrap(err, "create SSH key test directory")
  123. }
  124. } else {
  125. SSH.RewriteAuthorizedKeysAtStart = false
  126. }
  127. // Check if server is eligible for minimum key size check when user choose to enable.
  128. // Windows server and OpenSSH version lower than 5.1 are forced to be disabled because
  129. // the "ssh-keygen" in Windows does not print key type.
  130. // See https://github.com/gogs/gogs/issues/4507.
  131. if SSH.MinimumKeySizeCheck {
  132. sshVersion, err := openSSHVersion()
  133. if err != nil {
  134. return errors.Wrap(err, "get OpenSSH version")
  135. }
  136. if IsWindowsRuntime() || version.Compare(sshVersion, "5.1", "<") {
  137. log.Warn(`SSH minimum key size check is forced to be disabled because server is not eligible:
  138. 1. Windows server
  139. 2. OpenSSH version is lower than 5.1`)
  140. } else {
  141. SSH.MinimumKeySizes = map[string]int{}
  142. for _, key := range File.Section("ssh.minimum_key_sizes").Keys() {
  143. if key.MustInt() != -1 {
  144. SSH.MinimumKeySizes[strings.ToLower(key.Name())] = key.MustInt()
  145. }
  146. }
  147. }
  148. }
  149. }
  150. // *******************************
  151. // ----- Repository settings -----
  152. // *******************************
  153. Repository.Root = filepath.Join(HomeDir(), "gogs-repositories")
  154. if err = File.Section("repository").MapTo(&Repository); err != nil {
  155. return errors.Wrap(err, "mapping [repository] section")
  156. }
  157. Repository.Root = ensureAbs(Repository.Root)
  158. Repository.Upload.TempPath = ensureAbs(Repository.Upload.TempPath)
  159. // *******************************
  160. // ----- Database settings -----
  161. // *******************************
  162. if err = File.Section("database").MapTo(&Database); err != nil {
  163. return errors.Wrap(err, "mapping [database] section")
  164. }
  165. Database.Path = ensureAbs(Database.Path)
  166. handleDeprecated()
  167. // TODO
  168. sec := File.Section("security")
  169. InstallLock = sec.Key("INSTALL_LOCK").MustBool()
  170. SecretKey = sec.Key("SECRET_KEY").String()
  171. LoginRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt()
  172. CookieUserName = sec.Key("COOKIE_USERNAME").String()
  173. CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").String()
  174. CookieSecure = sec.Key("COOKIE_SECURE").MustBool(false)
  175. ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER")
  176. EnableLoginStatusCookie = sec.Key("ENABLE_LOGIN_STATUS_COOKIE").MustBool(false)
  177. LoginStatusCookieName = sec.Key("LOGIN_STATUS_COOKIE_NAME").MustString("login_status")
  178. // Does not check run user when the install lock is off.
  179. if InstallLock {
  180. currentUser, match := IsRunUserMatchCurrentUser(App.RunUser)
  181. if !match {
  182. log.Fatal("The user configured to run Gogs is %q, but the current user is %q", App.RunUser, currentUser)
  183. }
  184. }
  185. sec = File.Section("attachment")
  186. AttachmentPath = sec.Key("PATH").MustString(filepath.Join(Server.AppDataPath, "attachments"))
  187. if !filepath.IsAbs(AttachmentPath) {
  188. AttachmentPath = path.Join(workDir, AttachmentPath)
  189. }
  190. AttachmentAllowedTypes = strings.Replace(sec.Key("ALLOWED_TYPES").MustString("image/jpeg,image/png"), "|", ",", -1)
  191. AttachmentMaxSize = sec.Key("MAX_SIZE").MustInt64(4)
  192. AttachmentMaxFiles = sec.Key("MAX_FILES").MustInt(5)
  193. AttachmentEnabled = sec.Key("ENABLED").MustBool(true)
  194. TimeFormat = map[string]string{
  195. "ANSIC": time.ANSIC,
  196. "UnixDate": time.UnixDate,
  197. "RubyDate": time.RubyDate,
  198. "RFC822": time.RFC822,
  199. "RFC822Z": time.RFC822Z,
  200. "RFC850": time.RFC850,
  201. "RFC1123": time.RFC1123,
  202. "RFC1123Z": time.RFC1123Z,
  203. "RFC3339": time.RFC3339,
  204. "RFC3339Nano": time.RFC3339Nano,
  205. "Kitchen": time.Kitchen,
  206. "Stamp": time.Stamp,
  207. "StampMilli": time.StampMilli,
  208. "StampMicro": time.StampMicro,
  209. "StampNano": time.StampNano,
  210. }[File.Section("time").Key("FORMAT").MustString("RFC1123")]
  211. sec = File.Section("picture")
  212. AvatarUploadPath = sec.Key("AVATAR_UPLOAD_PATH").MustString(filepath.Join(Server.AppDataPath, "avatars"))
  213. if !filepath.IsAbs(AvatarUploadPath) {
  214. AvatarUploadPath = path.Join(workDir, AvatarUploadPath)
  215. }
  216. RepositoryAvatarUploadPath = sec.Key("REPOSITORY_AVATAR_UPLOAD_PATH").MustString(filepath.Join(Server.AppDataPath, "repo-avatars"))
  217. if !filepath.IsAbs(RepositoryAvatarUploadPath) {
  218. RepositoryAvatarUploadPath = path.Join(workDir, RepositoryAvatarUploadPath)
  219. }
  220. switch source := sec.Key("GRAVATAR_SOURCE").MustString("gravatar"); source {
  221. case "duoshuo":
  222. GravatarSource = "http://gravatar.duoshuo.com/avatar/"
  223. case "gravatar":
  224. GravatarSource = "https://secure.gravatar.com/avatar/"
  225. case "libravatar":
  226. GravatarSource = "https://seccdn.libravatar.org/avatar/"
  227. default:
  228. GravatarSource = source
  229. }
  230. DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool()
  231. EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(true)
  232. if Server.OfflineMode {
  233. DisableGravatar = true
  234. EnableFederatedAvatar = false
  235. }
  236. if DisableGravatar {
  237. EnableFederatedAvatar = false
  238. }
  239. if EnableFederatedAvatar {
  240. LibravatarService = libravatar.New()
  241. parts := strings.Split(GravatarSource, "/")
  242. if len(parts) >= 3 {
  243. if parts[0] == "https:" {
  244. LibravatarService.SetUseHTTPS(true)
  245. LibravatarService.SetSecureFallbackHost(parts[2])
  246. } else {
  247. LibravatarService.SetUseHTTPS(false)
  248. LibravatarService.SetFallbackHost(parts[2])
  249. }
  250. }
  251. }
  252. if err = File.Section("http").MapTo(&HTTP); err != nil {
  253. log.Fatal("Failed to map HTTP settings: %v", err)
  254. } else if err = File.Section("webhook").MapTo(&Webhook); err != nil {
  255. log.Fatal("Failed to map Webhook settings: %v", err)
  256. } else if err = File.Section("release.attachment").MapTo(&Release.Attachment); err != nil {
  257. log.Fatal("Failed to map Release.Attachment settings: %v", err)
  258. } else if err = File.Section("markdown").MapTo(&Markdown); err != nil {
  259. log.Fatal("Failed to map Markdown settings: %v", err)
  260. } else if err = File.Section("smartypants").MapTo(&Smartypants); err != nil {
  261. log.Fatal("Failed to map Smartypants settings: %v", err)
  262. } else if err = File.Section("admin").MapTo(&Admin); err != nil {
  263. log.Fatal("Failed to map Admin settings: %v", err)
  264. } else if err = File.Section("cron").MapTo(&Cron); err != nil {
  265. log.Fatal("Failed to map Cron settings: %v", err)
  266. } else if err = File.Section("git").MapTo(&Git); err != nil {
  267. log.Fatal("Failed to map Git settings: %v", err)
  268. } else if err = File.Section("mirror").MapTo(&Mirror); err != nil {
  269. log.Fatal("Failed to map Mirror settings: %v", err)
  270. } else if err = File.Section("api").MapTo(&API); err != nil {
  271. log.Fatal("Failed to map API settings: %v", err)
  272. } else if err = File.Section("ui").MapTo(&UI); err != nil {
  273. log.Fatal("Failed to map UI settings: %v", err)
  274. } else if err = File.Section("prometheus").MapTo(&Prometheus); err != nil {
  275. log.Fatal("Failed to map Prometheus settings: %v", err)
  276. }
  277. if Mirror.DefaultInterval <= 0 {
  278. Mirror.DefaultInterval = 24
  279. }
  280. Langs = File.Section("i18n").Key("LANGS").Strings(",")
  281. Names = File.Section("i18n").Key("NAMES").Strings(",")
  282. dateLangs = File.Section("i18n.datelang").KeysHash()
  283. ShowFooterBranding = File.Section("other").Key("SHOW_FOOTER_BRANDING").MustBool()
  284. ShowFooterTemplateLoadTime = File.Section("other").Key("SHOW_FOOTER_TEMPLATE_LOAD_TIME").MustBool()
  285. HasRobotsTxt = osutil.IsFile(path.Join(CustomDir(), "robots.txt"))
  286. return nil
  287. }
  288. // MustInit panics if configuration initialization failed.
  289. func MustInit(customConf string) {
  290. err := Init(customConf)
  291. if err != nil {
  292. panic(err)
  293. }
  294. }
  295. // TODO
  296. var (
  297. HTTP struct {
  298. AccessControlAllowOrigin string
  299. }
  300. // Security settings
  301. InstallLock bool
  302. SecretKey string
  303. LoginRememberDays int
  304. CookieUserName string
  305. CookieRememberName string
  306. CookieSecure bool
  307. ReverseProxyAuthUser string
  308. EnableLoginStatusCookie bool
  309. LoginStatusCookieName string
  310. // Database settings
  311. UseSQLite3 bool
  312. UseMySQL bool
  313. UsePostgreSQL bool
  314. UseMSSQL bool
  315. // Webhook settings
  316. Webhook struct {
  317. Types []string
  318. QueueLength int
  319. DeliverTimeout int
  320. SkipTLSVerify bool `ini:"SKIP_TLS_VERIFY"`
  321. PagingNum int
  322. }
  323. // Release settigns
  324. Release struct {
  325. Attachment struct {
  326. Enabled bool
  327. TempPath string
  328. AllowedTypes []string `delim:"|"`
  329. MaxSize int64
  330. MaxFiles int
  331. } `ini:"-"`
  332. }
  333. // Markdown sttings
  334. Markdown struct {
  335. EnableHardLineBreak bool
  336. CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"`
  337. FileExtensions []string
  338. }
  339. // Smartypants settings
  340. Smartypants struct {
  341. Enabled bool
  342. Fractions bool
  343. Dashes bool
  344. LatexDashes bool
  345. AngledQuotes bool
  346. }
  347. // Admin settings
  348. Admin struct {
  349. DisableRegularOrgCreation bool
  350. }
  351. // Picture settings
  352. AvatarUploadPath string
  353. RepositoryAvatarUploadPath string
  354. GravatarSource string
  355. DisableGravatar bool
  356. EnableFederatedAvatar bool
  357. LibravatarService *libravatar.Libravatar
  358. // Log settings
  359. LogRootPath string
  360. LogModes []string
  361. LogConfigs []interface{}
  362. // Attachment settings
  363. AttachmentPath string
  364. AttachmentAllowedTypes string
  365. AttachmentMaxSize int64
  366. AttachmentMaxFiles int
  367. AttachmentEnabled bool
  368. // Time settings
  369. TimeFormat string
  370. // Cache settings
  371. CacheAdapter string
  372. CacheInterval int
  373. CacheConn string
  374. // Session settings
  375. SessionConfig session.Options
  376. CSRFCookieName string
  377. // Cron tasks
  378. Cron struct {
  379. UpdateMirror struct {
  380. Enabled bool
  381. RunAtStart bool
  382. Schedule string
  383. } `ini:"cron.update_mirrors"`
  384. RepoHealthCheck struct {
  385. Enabled bool
  386. RunAtStart bool
  387. Schedule string
  388. Timeout time.Duration
  389. Args []string `delim:" "`
  390. } `ini:"cron.repo_health_check"`
  391. CheckRepoStats struct {
  392. Enabled bool
  393. RunAtStart bool
  394. Schedule string
  395. } `ini:"cron.check_repo_stats"`
  396. RepoArchiveCleanup struct {
  397. Enabled bool
  398. RunAtStart bool
  399. Schedule string
  400. OlderThan time.Duration
  401. } `ini:"cron.repo_archive_cleanup"`
  402. }
  403. // Git settings
  404. Git struct {
  405. Version string `ini:"-"`
  406. DisableDiffHighlight bool
  407. MaxGitDiffLines int
  408. MaxGitDiffLineCharacters int
  409. MaxGitDiffFiles int
  410. GCArgs []string `ini:"GC_ARGS" delim:" "`
  411. Timeout struct {
  412. Migrate int
  413. Mirror int
  414. Clone int
  415. Pull int
  416. GC int `ini:"GC"`
  417. } `ini:"git.timeout"`
  418. }
  419. // Mirror settings
  420. Mirror struct {
  421. DefaultInterval int
  422. }
  423. // API settings
  424. API struct {
  425. MaxResponseItems int
  426. }
  427. // UI settings
  428. UI struct {
  429. ExplorePagingNum int
  430. IssuePagingNum int
  431. FeedMaxCommitNum int
  432. ThemeColorMetaTag string
  433. MaxDisplayFileSize int64
  434. Admin struct {
  435. UserPagingNum int
  436. RepoPagingNum int
  437. NoticePagingNum int
  438. OrgPagingNum int
  439. } `ini:"ui.admin"`
  440. User struct {
  441. RepoPagingNum int
  442. NewsFeedPagingNum int
  443. CommitsPagingNum int
  444. } `ini:"ui.user"`
  445. }
  446. // Prometheus settings
  447. Prometheus struct {
  448. Enabled bool
  449. EnableBasicAuth bool
  450. BasicAuthUsername string
  451. BasicAuthPassword string
  452. }
  453. // I18n settings
  454. Langs []string
  455. Names []string
  456. dateLangs map[string]string
  457. // Highlight settings are loaded in modules/template/hightlight.go
  458. // Other settings
  459. ShowFooterBranding bool
  460. ShowFooterTemplateLoadTime bool
  461. // Global setting objects
  462. HasRobotsTxt bool
  463. )
  464. // DateLang transforms standard language locale name to corresponding value in datetime plugin.
  465. func DateLang(lang string) string {
  466. name, ok := dateLangs[lang]
  467. if ok {
  468. return name
  469. }
  470. return "en"
  471. }
  472. // IsRunUserMatchCurrentUser returns false if configured run user does not match
  473. // actual user that runs the app. The first return value is the actual user name.
  474. // This check is ignored under Windows since SSH remote login is not the main
  475. // method to login on Windows.
  476. func IsRunUserMatchCurrentUser(runUser string) (string, bool) {
  477. if IsWindowsRuntime() {
  478. return "", true
  479. }
  480. currentUser := user.CurrentUsername()
  481. return currentUser, runUser == currentUser
  482. }
  483. // InitLogging initializes the logging service of the application.
  484. func InitLogging() {
  485. LogRootPath = File.Section("log").Key("ROOT_PATH").MustString(filepath.Join(WorkDir(), "log"))
  486. // Because we always create a console logger as the primary logger at init time,
  487. // we need to remove it in case the user doesn't configure to use it after the
  488. // logging service is initalized.
  489. hasConsole := false
  490. // Iterate over [log.*] sections to initialize individual logger.
  491. LogModes = strings.Split(File.Section("log").Key("MODE").MustString("console"), ",")
  492. LogConfigs = make([]interface{}, len(LogModes))
  493. levelMappings := map[string]log.Level{
  494. "trace": log.LevelTrace,
  495. "info": log.LevelInfo,
  496. "warn": log.LevelWarn,
  497. "error": log.LevelError,
  498. "fatal": log.LevelFatal,
  499. }
  500. type config struct {
  501. Buffer int64
  502. Config interface{}
  503. }
  504. for i, mode := range LogModes {
  505. mode = strings.ToLower(strings.TrimSpace(mode))
  506. secName := "log." + mode
  507. sec, err := File.GetSection(secName)
  508. if err != nil {
  509. log.Fatal("Missing configuration section [%s] for %q logger", secName, mode)
  510. return
  511. }
  512. level := levelMappings[sec.Key("LEVEL").MustString("trace")]
  513. buffer := sec.Key("BUFFER_LEN").MustInt64(100)
  514. c := new(config)
  515. switch mode {
  516. case log.DefaultConsoleName:
  517. hasConsole = true
  518. c = &config{
  519. Buffer: buffer,
  520. Config: log.ConsoleConfig{
  521. Level: level,
  522. },
  523. }
  524. err = log.NewConsole(c.Buffer, c.Config)
  525. case log.DefaultFileName:
  526. logPath := filepath.Join(LogRootPath, "gogs.log")
  527. logDir := filepath.Dir(logPath)
  528. err = os.MkdirAll(logDir, os.ModePerm)
  529. if err != nil {
  530. log.Fatal("Failed to create log directory %q: %v", logDir, err)
  531. return
  532. }
  533. c = &config{
  534. Buffer: buffer,
  535. Config: log.FileConfig{
  536. Level: level,
  537. Filename: logPath,
  538. FileRotationConfig: log.FileRotationConfig{
  539. Rotate: sec.Key("LOG_ROTATE").MustBool(true),
  540. Daily: sec.Key("DAILY_ROTATE").MustBool(true),
  541. MaxSize: 1 << uint(sec.Key("MAX_SIZE_SHIFT").MustInt(28)),
  542. MaxLines: sec.Key("MAX_LINES").MustInt64(1000000),
  543. MaxDays: sec.Key("MAX_DAYS").MustInt64(7),
  544. },
  545. },
  546. }
  547. err = log.NewFile(c.Buffer, c.Config)
  548. case log.DefaultSlackName:
  549. c = &config{
  550. Buffer: buffer,
  551. Config: log.SlackConfig{
  552. Level: level,
  553. URL: sec.Key("URL").String(),
  554. },
  555. }
  556. err = log.NewSlack(c.Buffer, c.Config)
  557. case log.DefaultDiscordName:
  558. c = &config{
  559. Buffer: buffer,
  560. Config: log.DiscordConfig{
  561. Level: level,
  562. URL: sec.Key("URL").String(),
  563. Username: sec.Key("USERNAME").String(),
  564. },
  565. }
  566. default:
  567. continue
  568. }
  569. if err != nil {
  570. log.Fatal("Failed to init %s logger: %v", mode, err)
  571. return
  572. }
  573. LogConfigs[i] = c
  574. log.Trace("Log mode: %s (%s)", strings.Title(mode), strings.Title(strings.ToLower(level.String())))
  575. }
  576. if !hasConsole {
  577. log.Remove(log.DefaultConsoleName)
  578. }
  579. }
  580. var Service struct {
  581. ActiveCodeLives int
  582. ResetPwdCodeLives int
  583. RegisterEmailConfirm bool
  584. DisableRegistration bool
  585. ShowRegistrationButton bool
  586. RequireSignInView bool
  587. EnableNotifyMail bool
  588. EnableReverseProxyAuth bool
  589. EnableReverseProxyAutoRegister bool
  590. EnableCaptcha bool
  591. }
  592. func newService() {
  593. sec := File.Section("service")
  594. Service.ActiveCodeLives = sec.Key("ACTIVE_CODE_LIVE_MINUTES").MustInt(180)
  595. Service.ResetPwdCodeLives = sec.Key("RESET_PASSWD_CODE_LIVE_MINUTES").MustInt(180)
  596. Service.DisableRegistration = sec.Key("DISABLE_REGISTRATION").MustBool()
  597. Service.ShowRegistrationButton = sec.Key("SHOW_REGISTRATION_BUTTON").MustBool(!Service.DisableRegistration)
  598. Service.RequireSignInView = sec.Key("REQUIRE_SIGNIN_VIEW").MustBool()
  599. Service.EnableReverseProxyAuth = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION").MustBool()
  600. Service.EnableReverseProxyAutoRegister = sec.Key("ENABLE_REVERSE_PROXY_AUTO_REGISTRATION").MustBool()
  601. Service.EnableCaptcha = sec.Key("ENABLE_CAPTCHA").MustBool()
  602. }
  603. func newCacheService() {
  604. CacheAdapter = File.Section("cache").Key("ADAPTER").In("memory", []string{"memory", "redis", "memcache"})
  605. switch CacheAdapter {
  606. case "memory":
  607. CacheInterval = File.Section("cache").Key("INTERVAL").MustInt(60)
  608. case "redis", "memcache":
  609. CacheConn = strings.Trim(File.Section("cache").Key("HOST").String(), "\" ")
  610. default:
  611. log.Fatal("Unrecognized cache adapter %q", CacheAdapter)
  612. return
  613. }
  614. log.Trace("Cache service is enabled")
  615. }
  616. func newSessionService() {
  617. SessionConfig.Provider = File.Section("session").Key("PROVIDER").In("memory",
  618. []string{"memory", "file", "redis", "mysql"})
  619. SessionConfig.ProviderConfig = strings.Trim(File.Section("session").Key("PROVIDER_CONFIG").String(), "\" ")
  620. SessionConfig.CookieName = File.Section("session").Key("COOKIE_NAME").MustString("i_like_gogs")
  621. SessionConfig.CookiePath = Server.Subpath
  622. SessionConfig.Secure = File.Section("session").Key("COOKIE_SECURE").MustBool()
  623. SessionConfig.Gclifetime = File.Section("session").Key("GC_INTERVAL_TIME").MustInt64(3600)
  624. SessionConfig.Maxlifetime = File.Section("session").Key("SESSION_LIFE_TIME").MustInt64(86400)
  625. CSRFCookieName = File.Section("session").Key("CSRF_COOKIE_NAME").MustString("_csrf")
  626. log.Trace("Session service is enabled")
  627. }
  628. // Mailer represents mail service.
  629. type Mailer struct {
  630. QueueLength int
  631. SubjectPrefix string
  632. Host string
  633. From string
  634. FromEmail string
  635. User, Passwd string
  636. DisableHelo bool
  637. HeloHostname string
  638. SkipVerify bool
  639. UseCertificate bool
  640. CertFile, KeyFile string
  641. UsePlainText bool
  642. AddPlainTextAlt bool
  643. }
  644. var (
  645. MailService *Mailer
  646. )
  647. // newMailService initializes mail service options from configuration.
  648. // No non-error log will be printed in hook mode.
  649. func newMailService() {
  650. sec := File.Section("mailer")
  651. if !sec.Key("ENABLED").MustBool() {
  652. return
  653. }
  654. MailService = &Mailer{
  655. QueueLength: sec.Key("SEND_BUFFER_LEN").MustInt(100),
  656. SubjectPrefix: sec.Key("SUBJECT_PREFIX").MustString("[" + App.BrandName + "] "),
  657. Host: sec.Key("HOST").String(),
  658. User: sec.Key("USER").String(),
  659. Passwd: sec.Key("PASSWD").String(),
  660. DisableHelo: sec.Key("DISABLE_HELO").MustBool(),
  661. HeloHostname: sec.Key("HELO_HOSTNAME").String(),
  662. SkipVerify: sec.Key("SKIP_VERIFY").MustBool(),
  663. UseCertificate: sec.Key("USE_CERTIFICATE").MustBool(),
  664. CertFile: sec.Key("CERT_FILE").String(),
  665. KeyFile: sec.Key("KEY_FILE").String(),
  666. UsePlainText: sec.Key("USE_PLAIN_TEXT").MustBool(),
  667. AddPlainTextAlt: sec.Key("ADD_PLAIN_TEXT_ALT").MustBool(),
  668. }
  669. MailService.From = sec.Key("FROM").MustString(MailService.User)
  670. if len(MailService.From) > 0 {
  671. parsed, err := mail.ParseAddress(MailService.From)
  672. if err != nil {
  673. log.Fatal("Failed to parse value %q for '[mailer] FROM': %v", MailService.From, err)
  674. return
  675. }
  676. MailService.FromEmail = parsed.Address
  677. }
  678. if HookMode {
  679. return
  680. }
  681. log.Trace("Mail service is enabled")
  682. }
  683. func newRegisterMailService() {
  684. if !File.Section("service").Key("REGISTER_EMAIL_CONFIRM").MustBool() {
  685. return
  686. } else if MailService == nil {
  687. log.Warn("Email confirmation is not enabled due to the mail service is not available")
  688. return
  689. }
  690. Service.RegisterEmailConfirm = true
  691. log.Trace("Email confirmation is enabled")
  692. }
  693. // newNotifyMailService initializes notification email service options from configuration.
  694. // No non-error log will be printed in hook mode.
  695. func newNotifyMailService() {
  696. if !File.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() {
  697. return
  698. } else if MailService == nil {
  699. log.Warn("Email notification is not enabled due to the mail service is not available")
  700. return
  701. }
  702. Service.EnableNotifyMail = true
  703. if HookMode {
  704. return
  705. }
  706. log.Trace("Email notification is enabled")
  707. }
  708. func NewService() {
  709. newService()
  710. }
  711. func NewServices() {
  712. newService()
  713. newCacheService()
  714. newSessionService()
  715. newMailService()
  716. newRegisterMailService()
  717. newNotifyMailService()
  718. }
  719. // HookMode indicates whether program starts as Git server-side hook callback.
  720. var HookMode bool
  721. // NewPostReceiveHookServices initializes all services that are needed by
  722. // Git server-side post-receive hook callback.
  723. func NewPostReceiveHookServices() {
  724. HookMode = true
  725. newService()
  726. newMailService()
  727. newNotifyMailService()
  728. }