You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

setting.go 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754
  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 setting
  5. import (
  6. "fmt"
  7. "net/url"
  8. "os"
  9. "os/exec"
  10. "path"
  11. "path/filepath"
  12. "runtime"
  13. "strings"
  14. "time"
  15. "github.com/Unknwon/com"
  16. _ "github.com/go-macaron/cache/memcache"
  17. _ "github.com/go-macaron/cache/redis"
  18. "github.com/go-macaron/session"
  19. _ "github.com/go-macaron/session/redis"
  20. "github.com/strk/go-libravatar"
  21. "gopkg.in/ini.v1"
  22. "github.com/gogits/gogs/modules/bindata"
  23. "github.com/gogits/gogs/modules/log"
  24. "github.com/gogits/gogs/modules/user"
  25. )
  26. type Scheme string
  27. const (
  28. HTTP Scheme = "http"
  29. HTTPS Scheme = "https"
  30. FCGI Scheme = "fcgi"
  31. )
  32. type LandingPage string
  33. const (
  34. LANDING_PAGE_HOME LandingPage = "/"
  35. LANDING_PAGE_EXPLORE LandingPage = "/explore"
  36. )
  37. var (
  38. // Build information
  39. BuildTime string
  40. BuildGitHash string
  41. // App settings
  42. AppVer string
  43. AppName string
  44. AppUrl string
  45. AppSubUrl string
  46. AppSubUrlDepth int // Number of slashes
  47. AppPath string
  48. AppDataPath string
  49. // Server settings
  50. Protocol Scheme
  51. Domain string
  52. HttpAddr, HttpPort string
  53. LocalURL string
  54. OfflineMode bool
  55. DisableRouterLog bool
  56. CertFile, KeyFile string
  57. StaticRootPath string
  58. EnableGzip bool
  59. LandingPageUrl LandingPage
  60. SSH struct {
  61. Disabled bool `ini:"DISABLE_SSH"`
  62. StartBuiltinServer bool `ini:"START_SSH_SERVER"`
  63. Domain string `ini:"SSH_DOMAIN"`
  64. Port int `ini:"SSH_PORT"`
  65. ListenPort int `ini:"SSH_LISTEN_PORT"`
  66. RootPath string `ini:"SSH_ROOT_PATH"`
  67. KeyTestPath string `ini:"SSH_KEY_TEST_PATH"`
  68. KeygenPath string `ini:"SSH_KEYGEN_PATH"`
  69. MinimumKeySizeCheck bool `ini:"-"`
  70. MinimumKeySizes map[string]int `ini:"-"`
  71. }
  72. // Security settings
  73. InstallLock bool
  74. SecretKey string
  75. LogInRememberDays int
  76. CookieUserName string
  77. CookieRememberName string
  78. ReverseProxyAuthUser string
  79. // Database settings
  80. UseSQLite3 bool
  81. UseMySQL bool
  82. UsePostgreSQL bool
  83. UseTiDB bool
  84. // Webhook settings
  85. Webhook struct {
  86. QueueLength int
  87. DeliverTimeout int
  88. SkipTLSVerify bool
  89. Types []string
  90. PagingNum int
  91. }
  92. // Repository settings
  93. Repository struct {
  94. AnsiCharset string
  95. ForcePrivate bool
  96. MaxCreationLimit int
  97. PullRequestQueueLength int
  98. }
  99. RepoRootPath string
  100. ScriptType string
  101. // UI settings
  102. UI struct {
  103. ExplorePagingNum int
  104. IssuePagingNum int
  105. FeedMaxCommitNum int
  106. ThemeColorMetaTag string
  107. MaxDisplayFileSize int64
  108. Admin struct {
  109. UserPagingNum int
  110. RepoPagingNum int
  111. NoticePagingNum int
  112. OrgPagingNum int
  113. } `ini:"ui.admin"`
  114. User struct {
  115. RepoPagingNum int
  116. } `ini:"ui.user"`
  117. }
  118. // Markdown sttings
  119. Markdown struct {
  120. EnableHardLineBreak bool
  121. CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"`
  122. }
  123. // Picture settings
  124. AvatarUploadPath string
  125. GravatarSource string
  126. DisableGravatar bool
  127. EnableFederatedAvatar bool
  128. LibravatarService *libravatar.Libravatar
  129. // Log settings
  130. LogRootPath string
  131. LogModes []string
  132. LogConfigs []string
  133. // Attachment settings
  134. AttachmentPath string
  135. AttachmentAllowedTypes string
  136. AttachmentMaxSize int64
  137. AttachmentMaxFiles int
  138. AttachmentEnabled bool
  139. // Time settings
  140. TimeFormat string
  141. // Cache settings
  142. CacheAdapter string
  143. CacheInternal int
  144. CacheConn string
  145. // Session settings
  146. SessionConfig session.Options
  147. CSRFCookieName = "_csrf"
  148. // Cron tasks
  149. Cron struct {
  150. UpdateMirror struct {
  151. Enabled bool
  152. RunAtStart bool
  153. Schedule string
  154. } `ini:"cron.update_mirrors"`
  155. RepoHealthCheck struct {
  156. Enabled bool
  157. RunAtStart bool
  158. Schedule string
  159. Timeout time.Duration
  160. Args []string `delim:" "`
  161. } `ini:"cron.repo_health_check"`
  162. CheckRepoStats struct {
  163. Enabled bool
  164. RunAtStart bool
  165. Schedule string
  166. } `ini:"cron.check_repo_stats"`
  167. }
  168. // Git settings
  169. Git struct {
  170. DisableDiffHighlight bool
  171. MaxGitDiffLines int
  172. MaxGitDiffLineCharacters int
  173. MaxGitDiffFiles int
  174. GCArgs []string `delim:" "`
  175. Timeout struct {
  176. Migrate int
  177. Mirror int
  178. Clone int
  179. Pull int
  180. GC int `ini:"GC"`
  181. } `ini:"git.timeout"`
  182. }
  183. Mirror struct {
  184. DefaultInterval int
  185. }
  186. // API settings
  187. API struct {
  188. MaxResponseItems int
  189. }
  190. // I18n settings
  191. Langs, Names []string
  192. dateLangs map[string]string
  193. // Highlight settings are loaded in modules/template/hightlight.go
  194. // Other settings
  195. ShowFooterBranding bool
  196. ShowFooterVersion bool
  197. SupportMiniWinService bool
  198. // Global setting objects
  199. Cfg *ini.File
  200. CustomPath string // Custom directory path
  201. CustomConf string
  202. ProdMode bool
  203. RunUser string
  204. IsWindows bool
  205. HasRobotsTxt bool
  206. )
  207. func DateLang(lang string) string {
  208. name, ok := dateLangs[lang]
  209. if ok {
  210. return name
  211. }
  212. return "en"
  213. }
  214. // execPath returns the executable path.
  215. func execPath() (string, error) {
  216. file, err := exec.LookPath(os.Args[0])
  217. if err != nil {
  218. return "", err
  219. }
  220. return filepath.Abs(file)
  221. }
  222. func init() {
  223. IsWindows = runtime.GOOS == "windows"
  224. log.NewLogger(0, "console", `{"level": 0}`)
  225. var err error
  226. if AppPath, err = execPath(); err != nil {
  227. log.Fatal(4, "fail to get app path: %v\n", err)
  228. }
  229. // Note: we don't use path.Dir here because it does not handle case
  230. // which path starts with two "/" in Windows: "//psf/Home/..."
  231. AppPath = strings.Replace(AppPath, "\\", "/", -1)
  232. }
  233. // WorkDir returns absolute path of work directory.
  234. func WorkDir() (string, error) {
  235. wd := os.Getenv("GOGS_WORK_DIR")
  236. if len(wd) > 0 {
  237. return wd, nil
  238. }
  239. i := strings.LastIndex(AppPath, "/")
  240. if i == -1 {
  241. return AppPath, nil
  242. }
  243. return AppPath[:i], nil
  244. }
  245. func forcePathSeparator(path string) {
  246. if strings.Contains(path, "\\") {
  247. log.Fatal(4, "Do not use '\\' or '\\\\' in paths, instead, please use '/' in all places")
  248. }
  249. }
  250. // IsRunUserMatchCurrentUser returns false if configured run user does not match
  251. // actual user that runs the app. The first return value is the actual user name.
  252. // This check is ignored under Windows since SSH remote login is not the main
  253. // method to login on Windows.
  254. func IsRunUserMatchCurrentUser(runUser string) (string, bool) {
  255. if IsWindows {
  256. return "", true
  257. }
  258. currentUser := user.CurrentUsername()
  259. return currentUser, runUser == currentUser
  260. }
  261. // NewContext initializes configuration context.
  262. // NOTE: do not print any log except error.
  263. func NewContext() {
  264. workDir, err := WorkDir()
  265. if err != nil {
  266. log.Fatal(4, "Fail to get work directory: %v", err)
  267. }
  268. Cfg, err = ini.Load(bindata.MustAsset("conf/app.ini"))
  269. if err != nil {
  270. log.Fatal(4, "Fail to parse 'conf/app.ini': %v", err)
  271. }
  272. CustomPath = os.Getenv("GOGS_CUSTOM")
  273. if len(CustomPath) == 0 {
  274. CustomPath = workDir + "/custom"
  275. }
  276. if len(CustomConf) == 0 {
  277. CustomConf = CustomPath + "/conf/app.ini"
  278. }
  279. if com.IsFile(CustomConf) {
  280. if err = Cfg.Append(CustomConf); err != nil {
  281. log.Fatal(4, "Fail to load custom conf '%s': %v", CustomConf, err)
  282. }
  283. } else {
  284. log.Warn("Custom config '%s' not found, ignore this if you're running first time", CustomConf)
  285. }
  286. Cfg.NameMapper = ini.AllCapsUnderscore
  287. homeDir, err := com.HomeDir()
  288. if err != nil {
  289. log.Fatal(4, "Fail to get home directory: %v", err)
  290. }
  291. homeDir = strings.Replace(homeDir, "\\", "/", -1)
  292. LogRootPath = Cfg.Section("log").Key("ROOT_PATH").MustString(path.Join(workDir, "log"))
  293. forcePathSeparator(LogRootPath)
  294. sec := Cfg.Section("server")
  295. AppName = Cfg.Section("").Key("APP_NAME").MustString("Gogs: Go Git Service")
  296. AppUrl = sec.Key("ROOT_URL").MustString("http://localhost:3000/")
  297. if AppUrl[len(AppUrl)-1] != '/' {
  298. AppUrl += "/"
  299. }
  300. // Check if has app suburl.
  301. url, err := url.Parse(AppUrl)
  302. if err != nil {
  303. log.Fatal(4, "Invalid ROOT_URL '%s': %s", AppUrl, err)
  304. }
  305. // Suburl should start with '/' and end without '/', such as '/{subpath}'.
  306. // This value is empty if site does not have sub-url.
  307. AppSubUrl = strings.TrimSuffix(url.Path, "/")
  308. AppSubUrlDepth = strings.Count(AppSubUrl, "/")
  309. Protocol = HTTP
  310. if sec.Key("PROTOCOL").String() == "https" {
  311. Protocol = HTTPS
  312. CertFile = sec.Key("CERT_FILE").String()
  313. KeyFile = sec.Key("KEY_FILE").String()
  314. } else if sec.Key("PROTOCOL").String() == "fcgi" {
  315. Protocol = FCGI
  316. }
  317. Domain = sec.Key("DOMAIN").MustString("localhost")
  318. HttpAddr = sec.Key("HTTP_ADDR").MustString("0.0.0.0")
  319. HttpPort = sec.Key("HTTP_PORT").MustString("3000")
  320. LocalURL = sec.Key("LOCAL_ROOT_URL").MustString(string(Protocol) + "://localhost:" + HttpPort + "/")
  321. OfflineMode = sec.Key("OFFLINE_MODE").MustBool()
  322. DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool()
  323. StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(workDir)
  324. AppDataPath = sec.Key("APP_DATA_PATH").MustString("data")
  325. EnableGzip = sec.Key("ENABLE_GZIP").MustBool()
  326. switch sec.Key("LANDING_PAGE").MustString("home") {
  327. case "explore":
  328. LandingPageUrl = LANDING_PAGE_EXPLORE
  329. default:
  330. LandingPageUrl = LANDING_PAGE_HOME
  331. }
  332. SSH.RootPath = path.Join(homeDir, ".ssh")
  333. SSH.KeyTestPath = os.TempDir()
  334. if err = Cfg.Section("server").MapTo(&SSH); err != nil {
  335. log.Fatal(4, "Fail to map SSH settings: %v", err)
  336. }
  337. // When disable SSH, start builtin server value is ignored.
  338. if SSH.Disabled {
  339. SSH.StartBuiltinServer = false
  340. }
  341. if !SSH.Disabled && !SSH.StartBuiltinServer {
  342. if err := os.MkdirAll(SSH.RootPath, 0700); err != nil {
  343. log.Fatal(4, "Fail to create '%s': %v", SSH.RootPath, err)
  344. } else if err = os.MkdirAll(SSH.KeyTestPath, 0644); err != nil {
  345. log.Fatal(4, "Fail to create '%s': %v", SSH.KeyTestPath, err)
  346. }
  347. }
  348. SSH.MinimumKeySizeCheck = sec.Key("MINIMUM_KEY_SIZE_CHECK").MustBool()
  349. SSH.MinimumKeySizes = map[string]int{}
  350. minimumKeySizes := Cfg.Section("ssh.minimum_key_sizes").Keys()
  351. for _, key := range minimumKeySizes {
  352. if key.MustInt() != -1 {
  353. SSH.MinimumKeySizes[strings.ToLower(key.Name())] = key.MustInt()
  354. }
  355. }
  356. sec = Cfg.Section("security")
  357. InstallLock = sec.Key("INSTALL_LOCK").MustBool()
  358. SecretKey = sec.Key("SECRET_KEY").String()
  359. LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt()
  360. CookieUserName = sec.Key("COOKIE_USERNAME").String()
  361. CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").String()
  362. ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER")
  363. sec = Cfg.Section("attachment")
  364. AttachmentPath = sec.Key("PATH").MustString(path.Join(AppDataPath, "attachments"))
  365. if !filepath.IsAbs(AttachmentPath) {
  366. AttachmentPath = path.Join(workDir, AttachmentPath)
  367. }
  368. AttachmentAllowedTypes = strings.Replace(sec.Key("ALLOWED_TYPES").MustString("image/jpeg,image/png"), "|", ",", -1)
  369. AttachmentMaxSize = sec.Key("MAX_SIZE").MustInt64(4)
  370. AttachmentMaxFiles = sec.Key("MAX_FILES").MustInt(5)
  371. AttachmentEnabled = sec.Key("ENABLE").MustBool(true)
  372. TimeFormat = map[string]string{
  373. "ANSIC": time.ANSIC,
  374. "UnixDate": time.UnixDate,
  375. "RubyDate": time.RubyDate,
  376. "RFC822": time.RFC822,
  377. "RFC822Z": time.RFC822Z,
  378. "RFC850": time.RFC850,
  379. "RFC1123": time.RFC1123,
  380. "RFC1123Z": time.RFC1123Z,
  381. "RFC3339": time.RFC3339,
  382. "RFC3339Nano": time.RFC3339Nano,
  383. "Kitchen": time.Kitchen,
  384. "Stamp": time.Stamp,
  385. "StampMilli": time.StampMilli,
  386. "StampMicro": time.StampMicro,
  387. "StampNano": time.StampNano,
  388. }[Cfg.Section("time").Key("FORMAT").MustString("RFC1123")]
  389. RunUser = Cfg.Section("").Key("RUN_USER").String()
  390. // Does not check run user when the install lock is off.
  391. if InstallLock {
  392. currentUser, match := IsRunUserMatchCurrentUser(RunUser)
  393. if !match {
  394. log.Fatal(4, "Expect user '%s' but current user is: %s", RunUser, currentUser)
  395. }
  396. }
  397. // Determine and create root git repository path.
  398. sec = Cfg.Section("repository")
  399. RepoRootPath = sec.Key("ROOT").MustString(path.Join(homeDir, "gogs-repositories"))
  400. forcePathSeparator(RepoRootPath)
  401. if !filepath.IsAbs(RepoRootPath) {
  402. RepoRootPath = path.Join(workDir, RepoRootPath)
  403. } else {
  404. RepoRootPath = path.Clean(RepoRootPath)
  405. }
  406. ScriptType = sec.Key("SCRIPT_TYPE").MustString("bash")
  407. if err = Cfg.Section("repository").MapTo(&Repository); err != nil {
  408. log.Fatal(4, "Fail to map Repository settings: %v", err)
  409. }
  410. sec = Cfg.Section("picture")
  411. AvatarUploadPath = sec.Key("AVATAR_UPLOAD_PATH").MustString(path.Join(AppDataPath, "avatars"))
  412. forcePathSeparator(AvatarUploadPath)
  413. if !filepath.IsAbs(AvatarUploadPath) {
  414. AvatarUploadPath = path.Join(workDir, AvatarUploadPath)
  415. }
  416. switch source := sec.Key("GRAVATAR_SOURCE").MustString("gravatar"); source {
  417. case "duoshuo":
  418. GravatarSource = "http://gravatar.duoshuo.com/avatar/"
  419. case "gravatar":
  420. GravatarSource = "https://secure.gravatar.com/avatar/"
  421. default:
  422. GravatarSource = source
  423. }
  424. DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool()
  425. EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool()
  426. if OfflineMode {
  427. DisableGravatar = true
  428. EnableFederatedAvatar = false
  429. }
  430. if DisableGravatar {
  431. EnableFederatedAvatar = false
  432. }
  433. if EnableFederatedAvatar {
  434. LibravatarService = libravatar.New()
  435. parts := strings.Split(GravatarSource, "/")
  436. if len(parts) >= 3 {
  437. if parts[0] == "https:" {
  438. LibravatarService.SetUseHTTPS(true)
  439. LibravatarService.SetSecureFallbackHost(parts[2])
  440. } else {
  441. LibravatarService.SetUseHTTPS(false)
  442. LibravatarService.SetFallbackHost(parts[2])
  443. }
  444. }
  445. }
  446. if err = Cfg.Section("ui").MapTo(&UI); err != nil {
  447. log.Fatal(4, "Fail to map UI settings: %v", err)
  448. } else if err = Cfg.Section("markdown").MapTo(&Markdown); err != nil {
  449. log.Fatal(4, "Fail to map Markdown settings: %v", err)
  450. } else if err = Cfg.Section("cron").MapTo(&Cron); err != nil {
  451. log.Fatal(4, "Fail to map Cron settings: %v", err)
  452. } else if err = Cfg.Section("git").MapTo(&Git); err != nil {
  453. log.Fatal(4, "Fail to map Git settings: %v", err)
  454. } else if err = Cfg.Section("api").MapTo(&API); err != nil {
  455. log.Fatal(4, "Fail to map API settings: %v", err)
  456. } else if err = Cfg.Section("mirror").MapTo(&Mirror); err != nil {
  457. log.Fatal(4, "Fail to map API settings: %v", err)
  458. }
  459. if Mirror.DefaultInterval <= 0 {
  460. Mirror.DefaultInterval = 24
  461. }
  462. Langs = Cfg.Section("i18n").Key("LANGS").Strings(",")
  463. Names = Cfg.Section("i18n").Key("NAMES").Strings(",")
  464. dateLangs = Cfg.Section("i18n.datelang").KeysHash()
  465. ShowFooterBranding = Cfg.Section("other").Key("SHOW_FOOTER_BRANDING").MustBool()
  466. ShowFooterVersion = Cfg.Section("other").Key("SHOW_FOOTER_VERSION").MustBool()
  467. HasRobotsTxt = com.IsFile(path.Join(CustomPath, "robots.txt"))
  468. }
  469. var Service struct {
  470. ActiveCodeLives int
  471. ResetPwdCodeLives int
  472. RegisterEmailConfirm bool
  473. DisableRegistration bool
  474. ShowRegistrationButton bool
  475. RequireSignInView bool
  476. EnableNotifyMail bool
  477. EnableReverseProxyAuth bool
  478. EnableReverseProxyAutoRegister bool
  479. EnableCaptcha bool
  480. }
  481. func newService() {
  482. sec := Cfg.Section("service")
  483. Service.ActiveCodeLives = sec.Key("ACTIVE_CODE_LIVE_MINUTES").MustInt(180)
  484. Service.ResetPwdCodeLives = sec.Key("RESET_PASSWD_CODE_LIVE_MINUTES").MustInt(180)
  485. Service.DisableRegistration = sec.Key("DISABLE_REGISTRATION").MustBool()
  486. Service.ShowRegistrationButton = sec.Key("SHOW_REGISTRATION_BUTTON").MustBool(!Service.DisableRegistration)
  487. Service.RequireSignInView = sec.Key("REQUIRE_SIGNIN_VIEW").MustBool()
  488. Service.EnableReverseProxyAuth = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION").MustBool()
  489. Service.EnableReverseProxyAutoRegister = sec.Key("ENABLE_REVERSE_PROXY_AUTO_REGISTRATION").MustBool()
  490. Service.EnableCaptcha = sec.Key("ENABLE_CAPTCHA").MustBool()
  491. }
  492. var logLevels = map[string]string{
  493. "Trace": "0",
  494. "Debug": "1",
  495. "Info": "2",
  496. "Warn": "3",
  497. "Error": "4",
  498. "Critical": "5",
  499. }
  500. func newLogService() {
  501. log.Info("%s %s", AppName, AppVer)
  502. if len(BuildTime) > 0 {
  503. log.Info("Build Time: %s", BuildTime)
  504. log.Info("Build Git Hash: %s", BuildGitHash)
  505. }
  506. // Get and check log mode.
  507. LogModes = strings.Split(Cfg.Section("log").Key("MODE").MustString("console"), ",")
  508. LogConfigs = make([]string, len(LogModes))
  509. for i, mode := range LogModes {
  510. mode = strings.TrimSpace(mode)
  511. sec, err := Cfg.GetSection("log." + mode)
  512. if err != nil {
  513. log.Fatal(4, "Unknown log mode: %s", mode)
  514. }
  515. validLevels := []string{"Trace", "Debug", "Info", "Warn", "Error", "Critical"}
  516. // Log level.
  517. levelName := Cfg.Section("log."+mode).Key("LEVEL").In(
  518. Cfg.Section("log").Key("LEVEL").In("Trace", validLevels),
  519. validLevels)
  520. level, ok := logLevels[levelName]
  521. if !ok {
  522. log.Fatal(4, "Unknown log level: %s", levelName)
  523. }
  524. // Generate log configuration.
  525. switch mode {
  526. case "console":
  527. LogConfigs[i] = fmt.Sprintf(`{"level":%s}`, level)
  528. case "file":
  529. logPath := sec.Key("FILE_NAME").MustString(path.Join(LogRootPath, "gogs.log"))
  530. if err = os.MkdirAll(path.Dir(logPath), os.ModePerm); err != nil {
  531. panic(err.Error())
  532. }
  533. LogConfigs[i] = fmt.Sprintf(
  534. `{"level":%s,"filename":"%s","rotate":%v,"maxlines":%d,"maxsize":%d,"daily":%v,"maxdays":%d}`, level,
  535. logPath,
  536. sec.Key("LOG_ROTATE").MustBool(true),
  537. sec.Key("MAX_LINES").MustInt(1000000),
  538. 1<<uint(sec.Key("MAX_SIZE_SHIFT").MustInt(28)),
  539. sec.Key("DAILY_ROTATE").MustBool(true),
  540. sec.Key("MAX_DAYS").MustInt(7))
  541. case "conn":
  542. LogConfigs[i] = fmt.Sprintf(`{"level":%s,"reconnectOnMsg":%v,"reconnect":%v,"net":"%s","addr":"%s"}`, level,
  543. sec.Key("RECONNECT_ON_MSG").MustBool(),
  544. sec.Key("RECONNECT").MustBool(),
  545. sec.Key("PROTOCOL").In("tcp", []string{"tcp", "unix", "udp"}),
  546. sec.Key("ADDR").MustString(":7020"))
  547. case "smtp":
  548. LogConfigs[i] = fmt.Sprintf(`{"level":%s,"username":"%s","password":"%s","host":"%s","sendTos":"%s","subject":"%s"}`, level,
  549. sec.Key("USER").MustString("example@example.com"),
  550. sec.Key("PASSWD").MustString("******"),
  551. sec.Key("HOST").MustString("127.0.0.1:25"),
  552. sec.Key("RECEIVERS").MustString("[]"),
  553. sec.Key("SUBJECT").MustString("Diagnostic message from serve"))
  554. case "database":
  555. LogConfigs[i] = fmt.Sprintf(`{"level":%s,"driver":"%s","conn":"%s"}`, level,
  556. sec.Key("DRIVER").String(),
  557. sec.Key("CONN").String())
  558. }
  559. log.NewLogger(Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000), mode, LogConfigs[i])
  560. log.Info("Log Mode: %s(%s)", strings.Title(mode), levelName)
  561. }
  562. }
  563. func newCacheService() {
  564. CacheAdapter = Cfg.Section("cache").Key("ADAPTER").In("memory", []string{"memory", "redis", "memcache"})
  565. switch CacheAdapter {
  566. case "memory":
  567. CacheInternal = Cfg.Section("cache").Key("INTERVAL").MustInt(60)
  568. case "redis", "memcache":
  569. CacheConn = strings.Trim(Cfg.Section("cache").Key("HOST").String(), "\" ")
  570. default:
  571. log.Fatal(4, "Unknown cache adapter: %s", CacheAdapter)
  572. }
  573. log.Info("Cache Service Enabled")
  574. }
  575. func newSessionService() {
  576. SessionConfig.Provider = Cfg.Section("session").Key("PROVIDER").In("memory",
  577. []string{"memory", "file", "redis", "mysql"})
  578. SessionConfig.ProviderConfig = strings.Trim(Cfg.Section("session").Key("PROVIDER_CONFIG").String(), "\" ")
  579. SessionConfig.CookieName = Cfg.Section("session").Key("COOKIE_NAME").MustString("i_like_gogits")
  580. SessionConfig.CookiePath = AppSubUrl
  581. SessionConfig.Secure = Cfg.Section("session").Key("COOKIE_SECURE").MustBool()
  582. SessionConfig.Gclifetime = Cfg.Section("session").Key("GC_INTERVAL_TIME").MustInt64(86400)
  583. SessionConfig.Maxlifetime = Cfg.Section("session").Key("SESSION_LIFE_TIME").MustInt64(86400)
  584. log.Info("Session Service Enabled")
  585. }
  586. // Mailer represents mail service.
  587. type Mailer struct {
  588. QueueLength int
  589. Name string
  590. Host string
  591. From string
  592. User, Passwd string
  593. DisableHelo bool
  594. HeloHostname string
  595. SkipVerify bool
  596. UseCertificate bool
  597. CertFile, KeyFile string
  598. EnableHTMLAlternative bool
  599. }
  600. var (
  601. MailService *Mailer
  602. )
  603. func newMailService() {
  604. sec := Cfg.Section("mailer")
  605. // Check mailer setting.
  606. if !sec.Key("ENABLED").MustBool() {
  607. return
  608. }
  609. MailService = &Mailer{
  610. QueueLength: sec.Key("SEND_BUFFER_LEN").MustInt(100),
  611. Name: sec.Key("NAME").MustString(AppName),
  612. Host: sec.Key("HOST").String(),
  613. User: sec.Key("USER").String(),
  614. Passwd: sec.Key("PASSWD").String(),
  615. DisableHelo: sec.Key("DISABLE_HELO").MustBool(),
  616. HeloHostname: sec.Key("HELO_HOSTNAME").String(),
  617. SkipVerify: sec.Key("SKIP_VERIFY").MustBool(),
  618. UseCertificate: sec.Key("USE_CERTIFICATE").MustBool(),
  619. CertFile: sec.Key("CERT_FILE").String(),
  620. KeyFile: sec.Key("KEY_FILE").String(),
  621. EnableHTMLAlternative: sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(),
  622. }
  623. MailService.From = sec.Key("FROM").MustString(MailService.User)
  624. log.Info("Mail Service Enabled")
  625. }
  626. func newRegisterMailService() {
  627. if !Cfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").MustBool() {
  628. return
  629. } else if MailService == nil {
  630. log.Warn("Register Mail Service: Mail Service is not enabled")
  631. return
  632. }
  633. Service.RegisterEmailConfirm = true
  634. log.Info("Register Mail Service Enabled")
  635. }
  636. func newNotifyMailService() {
  637. if !Cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() {
  638. return
  639. } else if MailService == nil {
  640. log.Warn("Notify Mail Service: Mail Service is not enabled")
  641. return
  642. }
  643. Service.EnableNotifyMail = true
  644. log.Info("Notify Mail Service Enabled")
  645. }
  646. func newWebhookService() {
  647. sec := Cfg.Section("webhook")
  648. Webhook.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000)
  649. Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5)
  650. Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool()
  651. Webhook.Types = []string{"gogs", "slack"}
  652. Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10)
  653. }
  654. func NewServices() {
  655. newService()
  656. newLogService()
  657. newCacheService()
  658. newSessionService()
  659. newMailService()
  660. newRegisterMailService()
  661. newNotifyMailService()
  662. newWebhookService()
  663. }