conf.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708
  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. "fmt"
  7. "net/mail"
  8. "net/url"
  9. "os"
  10. "path"
  11. "path/filepath"
  12. "strconv"
  13. "strings"
  14. "time"
  15. _ "github.com/go-macaron/cache/memcache"
  16. _ "github.com/go-macaron/cache/redis"
  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. )
  26. func init() {
  27. // Initialize the primary logger until logging service is up.
  28. err := log.NewConsole()
  29. if err != nil {
  30. panic("init console logger: " + err.Error())
  31. }
  32. }
  33. // Asset is a wrapper for getting conf assets.
  34. func Asset(name string) ([]byte, error) {
  35. return conf.Asset(name)
  36. }
  37. // AssetDir is a wrapper for getting conf assets.
  38. func AssetDir(name string) ([]string, error) {
  39. return conf.AssetDir(name)
  40. }
  41. // MustAsset is a wrapper for getting conf assets.
  42. func MustAsset(name string) []byte {
  43. return conf.MustAsset(name)
  44. }
  45. // File is the configuration object.
  46. var File *ini.File
  47. // Init initializes configuration from conf assets and given custom configuration file.
  48. // If `customConf` is empty, it falls back to default location, i.e. "<WORK DIR>/custom".
  49. // It is safe to call this function multiple times with desired `customConf`, but it is
  50. // not concurrent safe.
  51. //
  52. // NOTE: The order of loading configuration sections matters as one may depend on another.
  53. //
  54. // ⚠️ WARNING: Do not print anything in this function other than wanrings.
  55. func Init(customConf string) error {
  56. var err error
  57. File, err = ini.LoadSources(ini.LoadOptions{
  58. IgnoreInlineComment: true,
  59. }, conf.MustAsset("conf/app.ini"))
  60. if err != nil {
  61. return errors.Wrap(err, "parse 'conf/app.ini'")
  62. }
  63. File.NameMapper = ini.SnackCase
  64. if customConf == "" {
  65. customConf = filepath.Join(CustomDir(), "conf", "app.ini")
  66. } else {
  67. customConf, err = filepath.Abs(customConf)
  68. if err != nil {
  69. return errors.Wrap(err, "get absolute path")
  70. }
  71. }
  72. CustomConf = customConf
  73. if osutil.IsFile(customConf) {
  74. if err = File.Append(customConf); err != nil {
  75. return errors.Wrapf(err, "append %q", customConf)
  76. }
  77. } else {
  78. log.Warn("Custom config %q not found. Ignore this warning if you're running for the first time", customConf)
  79. }
  80. if err = File.Section(ini.DefaultSection).MapTo(&App); err != nil {
  81. return errors.Wrap(err, "mapping default section")
  82. }
  83. // ***************************
  84. // ----- Server settings -----
  85. // ***************************
  86. if err = File.Section("server").MapTo(&Server); err != nil {
  87. return errors.Wrap(err, "mapping [server] section")
  88. }
  89. Server.AppDataPath = ensureAbs(Server.AppDataPath)
  90. if !strings.HasSuffix(Server.ExternalURL, "/") {
  91. Server.ExternalURL += "/"
  92. }
  93. Server.URL, err = url.Parse(Server.ExternalURL)
  94. if err != nil {
  95. return errors.Wrapf(err, "parse '[server] EXTERNAL_URL' %q", err)
  96. }
  97. // Subpath should start with '/' and end without '/', i.e. '/{subpath}'.
  98. Server.Subpath = strings.TrimRight(Server.URL.Path, "/")
  99. Server.SubpathDepth = strings.Count(Server.Subpath, "/")
  100. unixSocketMode, err := strconv.ParseUint(Server.UnixSocketPermission, 8, 32)
  101. if err != nil {
  102. return errors.Wrapf(err, "parse '[server] UNIX_SOCKET_PERMISSION' %q", Server.UnixSocketPermission)
  103. }
  104. if unixSocketMode > 0777 {
  105. unixSocketMode = 0666
  106. }
  107. Server.UnixSocketMode = os.FileMode(unixSocketMode)
  108. // ************************
  109. // ----- SSH settings -----
  110. // ************************
  111. SSH.RootPath = filepath.Join(HomeDir(), ".ssh")
  112. SSH.KeyTestPath = os.TempDir()
  113. if err = File.Section("server").MapTo(&SSH); err != nil {
  114. return errors.Wrap(err, "mapping SSH settings from [server] section")
  115. }
  116. SSH.RootPath = ensureAbs(SSH.RootPath)
  117. SSH.KeyTestPath = ensureAbs(SSH.KeyTestPath)
  118. if !SSH.Disabled {
  119. if !SSH.StartBuiltinServer {
  120. if err := os.MkdirAll(SSH.RootPath, 0700); err != nil {
  121. return errors.Wrap(err, "create SSH root directory")
  122. } else if err = os.MkdirAll(SSH.KeyTestPath, 0644); err != nil {
  123. return errors.Wrap(err, "create SSH key test directory")
  124. }
  125. } else {
  126. SSH.RewriteAuthorizedKeysAtStart = false
  127. }
  128. // Check if server is eligible for minimum key size check when user choose to enable.
  129. // Windows server and OpenSSH version lower than 5.1 are forced to be disabled because
  130. // the "ssh-keygen" in Windows does not print key type.
  131. // See https://github.com/gogs/gogs/issues/4507.
  132. if SSH.MinimumKeySizeCheck {
  133. sshVersion, err := openSSHVersion()
  134. if err != nil {
  135. return errors.Wrap(err, "get OpenSSH version")
  136. }
  137. if IsWindowsRuntime() || version.Compare(sshVersion, "5.1", "<") {
  138. log.Warn(`SSH minimum key size check is forced to be disabled because server is not eligible:
  139. 1. Windows server
  140. 2. OpenSSH version is lower than 5.1`)
  141. } else {
  142. SSH.MinimumKeySizes = map[string]int{}
  143. for _, key := range File.Section("ssh.minimum_key_sizes").Keys() {
  144. if key.MustInt() != -1 {
  145. SSH.MinimumKeySizes[strings.ToLower(key.Name())] = key.MustInt()
  146. }
  147. }
  148. }
  149. }
  150. }
  151. // *******************************
  152. // ----- Repository settings -----
  153. // *******************************
  154. Repository.Root = filepath.Join(HomeDir(), "gogs-repositories")
  155. if err = File.Section("repository").MapTo(&Repository); err != nil {
  156. return errors.Wrap(err, "mapping [repository] section")
  157. }
  158. Repository.Root = ensureAbs(Repository.Root)
  159. Repository.Upload.TempPath = ensureAbs(Repository.Upload.TempPath)
  160. // *******************************
  161. // ----- Database settings -----
  162. // *******************************
  163. if err = File.Section("database").MapTo(&Database); err != nil {
  164. return errors.Wrap(err, "mapping [database] section")
  165. }
  166. Database.Path = ensureAbs(Database.Path)
  167. // *******************************
  168. // ----- Security settings -----
  169. // *******************************
  170. if err = File.Section("security").MapTo(&Security); err != nil {
  171. return errors.Wrap(err, "mapping [security] section")
  172. }
  173. // Check run user when the install is locked.
  174. if Security.InstallLock {
  175. currentUser, match := CheckRunUser(App.RunUser)
  176. if !match {
  177. return fmt.Errorf("user configured to run Gogs is %q, but the current user is %q", App.RunUser, currentUser)
  178. }
  179. }
  180. // **************************
  181. // ----- Email settings -----
  182. // **************************
  183. if err = File.Section("email").MapTo(&Email); err != nil {
  184. return errors.Wrap(err, "mapping [email] section")
  185. }
  186. // LEGACY [0.13]: In case there are values with old section name.
  187. if err = File.Section("mailer").MapTo(&Email); err != nil {
  188. return errors.Wrap(err, "mapping [mailer] section")
  189. }
  190. if Email.Enabled {
  191. if Email.From == "" {
  192. Email.From = Email.User
  193. }
  194. parsed, err := mail.ParseAddress(Email.From)
  195. if err != nil {
  196. return errors.Wrapf(err, "parse mail address %q", Email.From)
  197. }
  198. Email.FromEmail = parsed.Address
  199. }
  200. // ***********************************
  201. // ----- Authentication settings -----
  202. // ***********************************
  203. if err = File.Section("auth").MapTo(&Auth); err != nil {
  204. return errors.Wrap(err, "mapping [auth] section")
  205. }
  206. // LEGACY [0.13]: In case there are values with old section name.
  207. if err = File.Section("service").MapTo(&Auth); err != nil {
  208. return errors.Wrap(err, "mapping [service] section")
  209. }
  210. // ***********************************
  211. // ----- User settings -----
  212. // ***********************************
  213. if err = File.Section("user").MapTo(&User); err != nil {
  214. return errors.Wrap(err, "mapping [user] section")
  215. }
  216. // ***********************************
  217. // ----- Session settings -----
  218. // ***********************************
  219. if err = File.Section("session").MapTo(&Session); err != nil {
  220. return errors.Wrap(err, "mapping [session] section")
  221. }
  222. handleDeprecated()
  223. // TODO
  224. sec := File.Section("attachment")
  225. AttachmentPath = sec.Key("PATH").MustString(filepath.Join(Server.AppDataPath, "attachments"))
  226. if !filepath.IsAbs(AttachmentPath) {
  227. AttachmentPath = path.Join(workDir, AttachmentPath)
  228. }
  229. AttachmentAllowedTypes = strings.Replace(sec.Key("ALLOWED_TYPES").MustString("image/jpeg,image/png"), "|", ",", -1)
  230. AttachmentMaxSize = sec.Key("MAX_SIZE").MustInt64(4)
  231. AttachmentMaxFiles = sec.Key("MAX_FILES").MustInt(5)
  232. AttachmentEnabled = sec.Key("ENABLED").MustBool(true)
  233. TimeFormat = map[string]string{
  234. "ANSIC": time.ANSIC,
  235. "UnixDate": time.UnixDate,
  236. "RubyDate": time.RubyDate,
  237. "RFC822": time.RFC822,
  238. "RFC822Z": time.RFC822Z,
  239. "RFC850": time.RFC850,
  240. "RFC1123": time.RFC1123,
  241. "RFC1123Z": time.RFC1123Z,
  242. "RFC3339": time.RFC3339,
  243. "RFC3339Nano": time.RFC3339Nano,
  244. "Kitchen": time.Kitchen,
  245. "Stamp": time.Stamp,
  246. "StampMilli": time.StampMilli,
  247. "StampMicro": time.StampMicro,
  248. "StampNano": time.StampNano,
  249. }[File.Section("time").Key("FORMAT").MustString("RFC1123")]
  250. sec = File.Section("picture")
  251. AvatarUploadPath = sec.Key("AVATAR_UPLOAD_PATH").MustString(filepath.Join(Server.AppDataPath, "avatars"))
  252. if !filepath.IsAbs(AvatarUploadPath) {
  253. AvatarUploadPath = path.Join(workDir, AvatarUploadPath)
  254. }
  255. RepositoryAvatarUploadPath = sec.Key("REPOSITORY_AVATAR_UPLOAD_PATH").MustString(filepath.Join(Server.AppDataPath, "repo-avatars"))
  256. if !filepath.IsAbs(RepositoryAvatarUploadPath) {
  257. RepositoryAvatarUploadPath = path.Join(workDir, RepositoryAvatarUploadPath)
  258. }
  259. switch source := sec.Key("GRAVATAR_SOURCE").MustString("gravatar"); source {
  260. case "duoshuo":
  261. GravatarSource = "http://gravatar.duoshuo.com/avatar/"
  262. case "gravatar":
  263. GravatarSource = "https://secure.gravatar.com/avatar/"
  264. case "libravatar":
  265. GravatarSource = "https://seccdn.libravatar.org/avatar/"
  266. default:
  267. GravatarSource = source
  268. }
  269. DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool()
  270. EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(true)
  271. if Server.OfflineMode {
  272. DisableGravatar = true
  273. EnableFederatedAvatar = false
  274. }
  275. if DisableGravatar {
  276. EnableFederatedAvatar = false
  277. }
  278. if EnableFederatedAvatar {
  279. LibravatarService = libravatar.New()
  280. parts := strings.Split(GravatarSource, "/")
  281. if len(parts) >= 3 {
  282. if parts[0] == "https:" {
  283. LibravatarService.SetUseHTTPS(true)
  284. LibravatarService.SetSecureFallbackHost(parts[2])
  285. } else {
  286. LibravatarService.SetUseHTTPS(false)
  287. LibravatarService.SetFallbackHost(parts[2])
  288. }
  289. }
  290. }
  291. if err = File.Section("http").MapTo(&HTTP); err != nil {
  292. log.Fatal("Failed to map HTTP settings: %v", err)
  293. } else if err = File.Section("webhook").MapTo(&Webhook); err != nil {
  294. log.Fatal("Failed to map Webhook settings: %v", err)
  295. } else if err = File.Section("release.attachment").MapTo(&Release.Attachment); err != nil {
  296. log.Fatal("Failed to map Release.Attachment settings: %v", err)
  297. } else if err = File.Section("markdown").MapTo(&Markdown); err != nil {
  298. log.Fatal("Failed to map Markdown settings: %v", err)
  299. } else if err = File.Section("smartypants").MapTo(&Smartypants); err != nil {
  300. log.Fatal("Failed to map Smartypants settings: %v", err)
  301. } else if err = File.Section("admin").MapTo(&Admin); err != nil {
  302. log.Fatal("Failed to map Admin settings: %v", err)
  303. } else if err = File.Section("cron").MapTo(&Cron); err != nil {
  304. log.Fatal("Failed to map Cron settings: %v", err)
  305. } else if err = File.Section("git").MapTo(&Git); err != nil {
  306. log.Fatal("Failed to map Git settings: %v", err)
  307. } else if err = File.Section("mirror").MapTo(&Mirror); err != nil {
  308. log.Fatal("Failed to map Mirror settings: %v", err)
  309. } else if err = File.Section("api").MapTo(&API); err != nil {
  310. log.Fatal("Failed to map API settings: %v", err)
  311. } else if err = File.Section("ui").MapTo(&UI); err != nil {
  312. log.Fatal("Failed to map UI settings: %v", err)
  313. } else if err = File.Section("prometheus").MapTo(&Prometheus); err != nil {
  314. log.Fatal("Failed to map Prometheus settings: %v", err)
  315. }
  316. if Mirror.DefaultInterval <= 0 {
  317. Mirror.DefaultInterval = 24
  318. }
  319. Langs = File.Section("i18n").Key("LANGS").Strings(",")
  320. Names = File.Section("i18n").Key("NAMES").Strings(",")
  321. dateLangs = File.Section("i18n.datelang").KeysHash()
  322. ShowFooterBranding = File.Section("other").Key("SHOW_FOOTER_BRANDING").MustBool()
  323. ShowFooterTemplateLoadTime = File.Section("other").Key("SHOW_FOOTER_TEMPLATE_LOAD_TIME").MustBool()
  324. HasRobotsTxt = osutil.IsFile(path.Join(CustomDir(), "robots.txt"))
  325. return nil
  326. }
  327. // MustInit panics if configuration initialization failed.
  328. func MustInit(customConf string) {
  329. err := Init(customConf)
  330. if err != nil {
  331. panic(err)
  332. }
  333. }
  334. // TODO
  335. var (
  336. HTTP struct {
  337. AccessControlAllowOrigin string
  338. }
  339. // Database settings
  340. UseSQLite3 bool
  341. UseMySQL bool
  342. UsePostgreSQL bool
  343. UseMSSQL bool
  344. // Webhook settings
  345. Webhook struct {
  346. Types []string
  347. QueueLength int
  348. DeliverTimeout int
  349. SkipTLSVerify bool `ini:"SKIP_TLS_VERIFY"`
  350. PagingNum int
  351. }
  352. // Release settigns
  353. Release struct {
  354. Attachment struct {
  355. Enabled bool
  356. TempPath string
  357. AllowedTypes []string `delim:"|"`
  358. MaxSize int64
  359. MaxFiles int
  360. } `ini:"-"`
  361. }
  362. // Markdown sttings
  363. Markdown struct {
  364. EnableHardLineBreak bool
  365. CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"`
  366. FileExtensions []string
  367. }
  368. // Smartypants settings
  369. Smartypants struct {
  370. Enabled bool
  371. Fractions bool
  372. Dashes bool
  373. LatexDashes bool
  374. AngledQuotes bool
  375. }
  376. // Admin settings
  377. Admin struct {
  378. DisableRegularOrgCreation bool
  379. }
  380. // Picture settings
  381. AvatarUploadPath string
  382. RepositoryAvatarUploadPath string
  383. GravatarSource string
  384. DisableGravatar bool
  385. EnableFederatedAvatar bool
  386. LibravatarService *libravatar.Libravatar
  387. // Log settings
  388. LogRootPath string
  389. LogModes []string
  390. LogConfigs []interface{}
  391. // Attachment settings
  392. AttachmentPath string
  393. AttachmentAllowedTypes string
  394. AttachmentMaxSize int64
  395. AttachmentMaxFiles int
  396. AttachmentEnabled bool
  397. // Time settings
  398. TimeFormat string
  399. // Cache settings
  400. CacheAdapter string
  401. CacheInterval int
  402. CacheConn string
  403. // Cron tasks
  404. Cron struct {
  405. UpdateMirror struct {
  406. Enabled bool
  407. RunAtStart bool
  408. Schedule string
  409. } `ini:"cron.update_mirrors"`
  410. RepoHealthCheck struct {
  411. Enabled bool
  412. RunAtStart bool
  413. Schedule string
  414. Timeout time.Duration
  415. Args []string `delim:" "`
  416. } `ini:"cron.repo_health_check"`
  417. CheckRepoStats struct {
  418. Enabled bool
  419. RunAtStart bool
  420. Schedule string
  421. } `ini:"cron.check_repo_stats"`
  422. RepoArchiveCleanup struct {
  423. Enabled bool
  424. RunAtStart bool
  425. Schedule string
  426. OlderThan time.Duration
  427. } `ini:"cron.repo_archive_cleanup"`
  428. }
  429. // Git settings
  430. Git struct {
  431. Version string `ini:"-"`
  432. DisableDiffHighlight bool
  433. MaxGitDiffLines int
  434. MaxGitDiffLineCharacters int
  435. MaxGitDiffFiles int
  436. GCArgs []string `ini:"GC_ARGS" delim:" "`
  437. Timeout struct {
  438. Migrate int
  439. Mirror int
  440. Clone int
  441. Pull int
  442. GC int `ini:"GC"`
  443. } `ini:"git.timeout"`
  444. }
  445. // Mirror settings
  446. Mirror struct {
  447. DefaultInterval int
  448. }
  449. // API settings
  450. API struct {
  451. MaxResponseItems int
  452. }
  453. // UI settings
  454. UI struct {
  455. ExplorePagingNum int
  456. IssuePagingNum int
  457. FeedMaxCommitNum int
  458. ThemeColorMetaTag string
  459. MaxDisplayFileSize int64
  460. Admin struct {
  461. UserPagingNum int
  462. RepoPagingNum int
  463. NoticePagingNum int
  464. OrgPagingNum int
  465. } `ini:"ui.admin"`
  466. User struct {
  467. RepoPagingNum int
  468. NewsFeedPagingNum int
  469. CommitsPagingNum int
  470. } `ini:"ui.user"`
  471. }
  472. // Prometheus settings
  473. Prometheus struct {
  474. Enabled bool
  475. EnableBasicAuth bool
  476. BasicAuthUsername string
  477. BasicAuthPassword string
  478. }
  479. // I18n settings
  480. Langs []string
  481. Names []string
  482. dateLangs map[string]string
  483. // Highlight settings are loaded in modules/template/hightlight.go
  484. // Other settings
  485. ShowFooterBranding bool
  486. ShowFooterTemplateLoadTime bool
  487. // Global setting objects
  488. HasRobotsTxt bool
  489. )
  490. // DateLang transforms standard language locale name to corresponding value in datetime plugin.
  491. func DateLang(lang string) string {
  492. name, ok := dateLangs[lang]
  493. if ok {
  494. return name
  495. }
  496. return "en"
  497. }
  498. // InitLogging initializes the logging service of the application.
  499. func InitLogging() {
  500. LogRootPath = File.Section("log").Key("ROOT_PATH").MustString(filepath.Join(WorkDir(), "log"))
  501. // Because we always create a console logger as the primary logger at init time,
  502. // we need to remove it in case the user doesn't configure to use it after the
  503. // logging service is initalized.
  504. hasConsole := false
  505. // Iterate over [log.*] sections to initialize individual logger.
  506. LogModes = strings.Split(File.Section("log").Key("MODE").MustString("console"), ",")
  507. LogConfigs = make([]interface{}, len(LogModes))
  508. levelMappings := map[string]log.Level{
  509. "trace": log.LevelTrace,
  510. "info": log.LevelInfo,
  511. "warn": log.LevelWarn,
  512. "error": log.LevelError,
  513. "fatal": log.LevelFatal,
  514. }
  515. type config struct {
  516. Buffer int64
  517. Config interface{}
  518. }
  519. for i, mode := range LogModes {
  520. mode = strings.ToLower(strings.TrimSpace(mode))
  521. secName := "log." + mode
  522. sec, err := File.GetSection(secName)
  523. if err != nil {
  524. log.Fatal("Missing configuration section [%s] for %q logger", secName, mode)
  525. return
  526. }
  527. level := levelMappings[strings.ToLower(sec.Key("LEVEL").MustString("trace"))]
  528. buffer := sec.Key("BUFFER_LEN").MustInt64(100)
  529. c := new(config)
  530. switch mode {
  531. case log.DefaultConsoleName:
  532. hasConsole = true
  533. c = &config{
  534. Buffer: buffer,
  535. Config: log.ConsoleConfig{
  536. Level: level,
  537. },
  538. }
  539. err = log.NewConsole(c.Buffer, c.Config)
  540. case log.DefaultFileName:
  541. logPath := filepath.Join(LogRootPath, "gogs.log")
  542. logDir := filepath.Dir(logPath)
  543. err = os.MkdirAll(logDir, os.ModePerm)
  544. if err != nil {
  545. log.Fatal("Failed to create log directory %q: %v", logDir, err)
  546. return
  547. }
  548. c = &config{
  549. Buffer: buffer,
  550. Config: log.FileConfig{
  551. Level: level,
  552. Filename: logPath,
  553. FileRotationConfig: log.FileRotationConfig{
  554. Rotate: sec.Key("LOG_ROTATE").MustBool(true),
  555. Daily: sec.Key("DAILY_ROTATE").MustBool(true),
  556. MaxSize: 1 << uint(sec.Key("MAX_SIZE_SHIFT").MustInt(28)),
  557. MaxLines: sec.Key("MAX_LINES").MustInt64(1000000),
  558. MaxDays: sec.Key("MAX_DAYS").MustInt64(7),
  559. },
  560. },
  561. }
  562. err = log.NewFile(c.Buffer, c.Config)
  563. case log.DefaultSlackName:
  564. c = &config{
  565. Buffer: buffer,
  566. Config: log.SlackConfig{
  567. Level: level,
  568. URL: sec.Key("URL").String(),
  569. },
  570. }
  571. err = log.NewSlack(c.Buffer, c.Config)
  572. case log.DefaultDiscordName:
  573. c = &config{
  574. Buffer: buffer,
  575. Config: log.DiscordConfig{
  576. Level: level,
  577. URL: sec.Key("URL").String(),
  578. Username: sec.Key("USERNAME").String(),
  579. },
  580. }
  581. default:
  582. continue
  583. }
  584. if err != nil {
  585. log.Fatal("Failed to init %s logger: %v", mode, err)
  586. return
  587. }
  588. LogConfigs[i] = c
  589. log.Trace("Log mode: %s (%s)", strings.Title(mode), strings.Title(strings.ToLower(level.String())))
  590. }
  591. if !hasConsole {
  592. log.Remove(log.DefaultConsoleName)
  593. }
  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 NewServices() {
  609. newCacheService()
  610. }
  611. // HookMode indicates whether program starts as Git server-side hook callback.
  612. // All operations should be done synchronously to prevent program exits before finishing.
  613. var HookMode bool