conf.go 24 KB

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