setting.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  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/Unknwon/goconfig"
  17. "github.com/macaron-contrib/session"
  18. "github.com/gogits/gogs/modules/log"
  19. // "github.com/gogits/gogs-ng/modules/ssh"
  20. )
  21. type Scheme string
  22. const (
  23. HTTP Scheme = "http"
  24. HTTPS Scheme = "https"
  25. FCGI Scheme = "fcgi"
  26. )
  27. var (
  28. // App settings.
  29. AppVer string
  30. AppName string
  31. AppUrl string
  32. AppSubUrl string
  33. // Server settings.
  34. Protocol Scheme
  35. Domain string
  36. HttpAddr, HttpPort string
  37. SshPort int
  38. OfflineMode bool
  39. DisableRouterLog bool
  40. CertFile, KeyFile string
  41. StaticRootPath string
  42. EnableGzip bool
  43. // Security settings.
  44. InstallLock bool
  45. SecretKey string
  46. LogInRememberDays int
  47. CookieUserName string
  48. CookieRememberName string
  49. ReverseProxyAuthUser string
  50. // Webhook settings.
  51. WebhookTaskInterval int
  52. WebhookDeliverTimeout int
  53. // Repository settings.
  54. RepoRootPath string
  55. ScriptType string
  56. // Picture settings.
  57. PictureService string
  58. AvatarUploadPath string
  59. GravatarSource string
  60. DisableGravatar bool
  61. // Log settings.
  62. LogRootPath string
  63. LogModes []string
  64. LogConfigs []string
  65. // Attachment settings.
  66. AttachmentPath string
  67. AttachmentAllowedTypes string
  68. AttachmentMaxSize int64
  69. AttachmentMaxFiles int
  70. AttachmentEnabled bool
  71. // Time settings.
  72. TimeFormat string
  73. // Cache settings.
  74. CacheAdapter string
  75. CacheInternal int
  76. CacheConn string
  77. EnableRedis bool
  78. EnableMemcache bool
  79. // Session settings.
  80. SessionProvider string
  81. SessionConfig *session.Config
  82. // Git settings.
  83. MaxGitDiffLines int
  84. // I18n settings.
  85. Langs, Names []string
  86. // Global setting objects.
  87. Cfg *goconfig.ConfigFile
  88. ConfRootPath string
  89. CustomPath string // Custom directory path.
  90. ProdMode bool
  91. RunUser string
  92. IsWindows bool
  93. HasRobotsTxt bool
  94. )
  95. func init() {
  96. IsWindows = runtime.GOOS == "windows"
  97. log.NewLogger(0, "console", `{"level": 0}`)
  98. }
  99. func ExecPath() (string, error) {
  100. file, err := exec.LookPath(os.Args[0])
  101. if err != nil {
  102. return "", err
  103. }
  104. p, err := filepath.Abs(file)
  105. if err != nil {
  106. return "", err
  107. }
  108. return p, nil
  109. }
  110. // WorkDir returns absolute path of work directory.
  111. func WorkDir() (string, error) {
  112. execPath, err := ExecPath()
  113. return path.Dir(strings.Replace(execPath, "\\", "/", -1)), err
  114. }
  115. // NewConfigContext initializes configuration context.
  116. // NOTE: do not print any log except error.
  117. func NewConfigContext() {
  118. workDir, err := WorkDir()
  119. if err != nil {
  120. log.Fatal(4, "Fail to get work directory: %v", err)
  121. }
  122. ConfRootPath = path.Join(workDir, "conf")
  123. Cfg, err = goconfig.LoadConfigFile(path.Join(workDir, "conf/app.ini"))
  124. if err != nil {
  125. log.Fatal(4, "Fail to parse 'conf/app.ini': %v", err)
  126. }
  127. CustomPath = os.Getenv("GOGS_CUSTOM")
  128. if len(CustomPath) == 0 {
  129. CustomPath = path.Join(workDir, "custom")
  130. }
  131. cfgPath := path.Join(CustomPath, "conf/app.ini")
  132. if com.IsFile(cfgPath) {
  133. if err = Cfg.AppendFiles(cfgPath); err != nil {
  134. log.Fatal(4, "Fail to load custom 'conf/app.ini': %v", err)
  135. }
  136. } else {
  137. log.Warn("No custom 'conf/app.ini' found, please go to '/install'")
  138. }
  139. AppName = Cfg.MustValue("", "APP_NAME", "Gogs: Go Git Service")
  140. AppUrl = Cfg.MustValue("server", "ROOT_URL", "http://localhost:3000/")
  141. if AppUrl[len(AppUrl)-1] != '/' {
  142. AppUrl += "/"
  143. }
  144. // Check if has app suburl.
  145. url, err := url.Parse(AppUrl)
  146. if err != nil {
  147. log.Fatal(4, "Invalid ROOT_URL(%s): %s", AppUrl, err)
  148. }
  149. AppSubUrl = strings.TrimSuffix(url.Path, "/")
  150. Protocol = HTTP
  151. if Cfg.MustValue("server", "PROTOCOL") == "https" {
  152. Protocol = HTTPS
  153. CertFile = Cfg.MustValue("server", "CERT_FILE")
  154. KeyFile = Cfg.MustValue("server", "KEY_FILE")
  155. }
  156. if Cfg.MustValue("server", "PROTOCOL") == "fcgi" {
  157. Protocol = FCGI
  158. }
  159. Domain = Cfg.MustValue("server", "DOMAIN", "localhost")
  160. HttpAddr = Cfg.MustValue("server", "HTTP_ADDR", "0.0.0.0")
  161. HttpPort = Cfg.MustValue("server", "HTTP_PORT", "3000")
  162. SshPort = Cfg.MustInt("server", "SSH_PORT", 22)
  163. OfflineMode = Cfg.MustBool("server", "OFFLINE_MODE")
  164. DisableRouterLog = Cfg.MustBool("server", "DISABLE_ROUTER_LOG")
  165. StaticRootPath = Cfg.MustValue("server", "STATIC_ROOT_PATH", workDir)
  166. LogRootPath = Cfg.MustValue("log", "ROOT_PATH", path.Join(workDir, "log"))
  167. EnableGzip = Cfg.MustBool("server", "ENABLE_GZIP")
  168. InstallLock = Cfg.MustBool("security", "INSTALL_LOCK")
  169. SecretKey = Cfg.MustValue("security", "SECRET_KEY")
  170. LogInRememberDays = Cfg.MustInt("security", "LOGIN_REMEMBER_DAYS")
  171. CookieUserName = Cfg.MustValue("security", "COOKIE_USERNAME")
  172. CookieRememberName = Cfg.MustValue("security", "COOKIE_REMEMBER_NAME")
  173. ReverseProxyAuthUser = Cfg.MustValue("security", "REVERSE_PROXY_AUTHENTICATION_USER", "X-WEBAUTH-USER")
  174. AttachmentPath = Cfg.MustValue("attachment", "PATH", "data/attachments")
  175. AttachmentAllowedTypes = Cfg.MustValue("attachment", "ALLOWED_TYPES", "image/jpeg|image/png")
  176. AttachmentMaxSize = Cfg.MustInt64("attachment", "MAX_SIZE", 32)
  177. AttachmentMaxFiles = Cfg.MustInt("attachment", "MAX_FILES", 10)
  178. AttachmentEnabled = Cfg.MustBool("attachment", "ENABLE", true)
  179. TimeFormat = map[string]string{
  180. "ANSIC": time.ANSIC,
  181. "UnixDate": time.UnixDate,
  182. "RubyDate": time.RubyDate,
  183. "RFC822": time.RFC822,
  184. "RFC822Z": time.RFC822Z,
  185. "RFC850": time.RFC850,
  186. "RFC1123": time.RFC1123,
  187. "RFC1123Z": time.RFC1123Z,
  188. "RFC3339": time.RFC3339,
  189. "RFC3339Nano": time.RFC3339Nano,
  190. "Kitchen": time.Kitchen,
  191. "Stamp": time.Stamp,
  192. "StampMilli": time.StampMilli,
  193. "StampMicro": time.StampMicro,
  194. "StampNano": time.StampNano,
  195. }[Cfg.MustValue("time", "FORMAT", "RFC1123")]
  196. if err = os.MkdirAll(AttachmentPath, os.ModePerm); err != nil {
  197. log.Fatal(4, "Could not create directory %s: %s", AttachmentPath, err)
  198. }
  199. RunUser = Cfg.MustValue("", "RUN_USER")
  200. curUser := os.Getenv("USER")
  201. if len(curUser) == 0 {
  202. curUser = os.Getenv("USERNAME")
  203. }
  204. // Does not check run user when the install lock is off.
  205. if InstallLock && RunUser != curUser {
  206. log.Fatal(4, "Expect user(%s) but current user is: %s", RunUser, curUser)
  207. }
  208. // Determine and create root git reposiroty path.
  209. homeDir, err := com.HomeDir()
  210. if err != nil {
  211. log.Fatal(4, "Fail to get home directory: %v", err)
  212. }
  213. RepoRootPath = Cfg.MustValue("repository", "ROOT", filepath.Join(homeDir, "gogs-repositories"))
  214. if !filepath.IsAbs(RepoRootPath) {
  215. RepoRootPath = filepath.Join(workDir, RepoRootPath)
  216. } else {
  217. RepoRootPath = filepath.Clean(RepoRootPath)
  218. }
  219. if err = os.MkdirAll(RepoRootPath, os.ModePerm); err != nil {
  220. log.Fatal(4, "Fail to create repository root path(%s): %v", RepoRootPath, err)
  221. }
  222. ScriptType = Cfg.MustValue("repository", "SCRIPT_TYPE", "bash")
  223. PictureService = Cfg.MustValueRange("picture", "SERVICE", "server", []string{"server"})
  224. AvatarUploadPath = Cfg.MustValue("picture", "AVATAR_UPLOAD_PATH", "data/avatars")
  225. os.MkdirAll(AvatarUploadPath, os.ModePerm)
  226. switch Cfg.MustValue("picture", "GRAVATAR_SOURCE", "gravatar") {
  227. case "duoshuo":
  228. GravatarSource = "http://gravatar.duoshuo.com/avatar/"
  229. default:
  230. GravatarSource = "//1.gravatar.com/avatar/"
  231. }
  232. DisableGravatar = Cfg.MustBool("picture", "DISABLE_GRAVATAR")
  233. MaxGitDiffLines = Cfg.MustInt("git", "MAX_GITDIFF_LINES", 10000)
  234. Langs = Cfg.MustValueArray("i18n", "LANGS", ",")
  235. Names = Cfg.MustValueArray("i18n", "NAMES", ",")
  236. HasRobotsTxt = com.IsFile(path.Join(CustomPath, "robots.txt"))
  237. }
  238. var Service struct {
  239. RegisterEmailConfirm bool
  240. DisableRegistration bool
  241. RequireSignInView bool
  242. EnableCacheAvatar bool
  243. EnableNotifyMail bool
  244. EnableReverseProxyAuth bool
  245. LdapAuth bool
  246. ActiveCodeLives int
  247. ResetPwdCodeLives int
  248. }
  249. func newService() {
  250. Service.ActiveCodeLives = Cfg.MustInt("service", "ACTIVE_CODE_LIVE_MINUTES", 180)
  251. Service.ResetPwdCodeLives = Cfg.MustInt("service", "RESET_PASSWD_CODE_LIVE_MINUTES", 180)
  252. Service.DisableRegistration = Cfg.MustBool("service", "DISABLE_REGISTRATION")
  253. Service.RequireSignInView = Cfg.MustBool("service", "REQUIRE_SIGNIN_VIEW")
  254. Service.EnableCacheAvatar = Cfg.MustBool("service", "ENABLE_CACHE_AVATAR")
  255. Service.EnableReverseProxyAuth = Cfg.MustBool("service", "ENABLE_REVERSE_PROXY_AUTHENTICATION")
  256. }
  257. var logLevels = map[string]string{
  258. "Trace": "0",
  259. "Debug": "1",
  260. "Info": "2",
  261. "Warn": "3",
  262. "Error": "4",
  263. "Critical": "5",
  264. }
  265. func newLogService() {
  266. log.Info("%s %s", AppName, AppVer)
  267. // Get and check log mode.
  268. LogModes = strings.Split(Cfg.MustValue("log", "MODE", "console"), ",")
  269. LogConfigs = make([]string, len(LogModes))
  270. for i, mode := range LogModes {
  271. mode = strings.TrimSpace(mode)
  272. modeSec := "log." + mode
  273. if _, err := Cfg.GetSection(modeSec); err != nil {
  274. log.Fatal(4, "Unknown log mode: %s", mode)
  275. }
  276. // Log level.
  277. levelName := Cfg.MustValueRange("log."+mode, "LEVEL", "Trace",
  278. []string{"Trace", "Debug", "Info", "Warn", "Error", "Critical"})
  279. level, ok := logLevels[levelName]
  280. if !ok {
  281. log.Fatal(4, "Unknown log level: %s", levelName)
  282. }
  283. // Generate log configuration.
  284. switch mode {
  285. case "console":
  286. LogConfigs[i] = fmt.Sprintf(`{"level":%s}`, level)
  287. case "file":
  288. logPath := Cfg.MustValue(modeSec, "FILE_NAME", path.Join(LogRootPath, "gogs.log"))
  289. os.MkdirAll(path.Dir(logPath), os.ModePerm)
  290. LogConfigs[i] = fmt.Sprintf(
  291. `{"level":%s,"filename":"%s","rotate":%v,"maxlines":%d,"maxsize":%d,"daily":%v,"maxdays":%d}`, level,
  292. logPath,
  293. Cfg.MustBool(modeSec, "LOG_ROTATE", true),
  294. Cfg.MustInt(modeSec, "MAX_LINES", 1000000),
  295. 1<<uint(Cfg.MustInt(modeSec, "MAX_SIZE_SHIFT", 28)),
  296. Cfg.MustBool(modeSec, "DAILY_ROTATE", true),
  297. Cfg.MustInt(modeSec, "MAX_DAYS", 7))
  298. case "conn":
  299. LogConfigs[i] = fmt.Sprintf(`{"level":%s,"reconnectOnMsg":%v,"reconnect":%v,"net":"%s","addr":"%s"}`, level,
  300. Cfg.MustBool(modeSec, "RECONNECT_ON_MSG"),
  301. Cfg.MustBool(modeSec, "RECONNECT"),
  302. Cfg.MustValueRange(modeSec, "PROTOCOL", "tcp", []string{"tcp", "unix", "udp"}),
  303. Cfg.MustValue(modeSec, "ADDR", ":7020"))
  304. case "smtp":
  305. LogConfigs[i] = fmt.Sprintf(`{"level":%s,"username":"%s","password":"%s","host":"%s","sendTos":"%s","subject":"%s"}`, level,
  306. Cfg.MustValue(modeSec, "USER", "example@example.com"),
  307. Cfg.MustValue(modeSec, "PASSWD", "******"),
  308. Cfg.MustValue(modeSec, "HOST", "127.0.0.1:25"),
  309. Cfg.MustValue(modeSec, "RECEIVERS", "[]"),
  310. Cfg.MustValue(modeSec, "SUBJECT", "Diagnostic message from serve"))
  311. case "database":
  312. LogConfigs[i] = fmt.Sprintf(`{"level":%s,"driver":"%s","conn":"%s"}`, level,
  313. Cfg.MustValue(modeSec, "DRIVER"),
  314. Cfg.MustValue(modeSec, "CONN"))
  315. }
  316. log.NewLogger(Cfg.MustInt64("log", "BUFFER_LEN", 10000), mode, LogConfigs[i])
  317. log.Info("Log Mode: %s(%s)", strings.Title(mode), levelName)
  318. }
  319. }
  320. func newCacheService() {
  321. CacheAdapter = Cfg.MustValueRange("cache", "ADAPTER", "memory", []string{"memory", "redis", "memcache"})
  322. if EnableRedis {
  323. log.Info("Redis Enabled")
  324. }
  325. if EnableMemcache {
  326. log.Info("Memcache Enabled")
  327. }
  328. switch CacheAdapter {
  329. case "memory":
  330. CacheInternal = Cfg.MustInt("cache", "INTERVAL", 60)
  331. case "redis", "memcache":
  332. CacheConn = strings.Trim(Cfg.MustValue("cache", "HOST"), "\" ")
  333. default:
  334. log.Fatal(4, "Unknown cache adapter: %s", CacheAdapter)
  335. }
  336. log.Info("Cache Service Enabled")
  337. }
  338. func newSessionService() {
  339. SessionProvider = Cfg.MustValueRange("session", "PROVIDER", "memory",
  340. []string{"memory", "file", "redis", "mysql"})
  341. SessionConfig = new(session.Config)
  342. SessionConfig.ProviderConfig = strings.Trim(Cfg.MustValue("session", "PROVIDER_CONFIG"), "\" ")
  343. SessionConfig.CookieName = Cfg.MustValue("session", "COOKIE_NAME", "i_like_gogits")
  344. SessionConfig.CookiePath = AppSubUrl
  345. SessionConfig.Secure = Cfg.MustBool("session", "COOKIE_SECURE")
  346. SessionConfig.EnableSetCookie = Cfg.MustBool("session", "ENABLE_SET_COOKIE", true)
  347. SessionConfig.Gclifetime = Cfg.MustInt64("session", "GC_INTERVAL_TIME", 86400)
  348. SessionConfig.Maxlifetime = Cfg.MustInt64("session", "SESSION_LIFE_TIME", 86400)
  349. if SessionProvider == "file" {
  350. os.MkdirAll(path.Dir(SessionConfig.ProviderConfig), os.ModePerm)
  351. }
  352. log.Info("Session Service Enabled")
  353. }
  354. // Mailer represents mail service.
  355. type Mailer struct {
  356. Name string
  357. Host string
  358. From string
  359. User, Passwd string
  360. }
  361. type OauthInfo struct {
  362. ClientId, ClientSecret string
  363. Scopes string
  364. AuthUrl, TokenUrl string
  365. }
  366. // Oauther represents oauth service.
  367. type Oauther struct {
  368. GitHub, Google, Tencent,
  369. Twitter, Weibo bool
  370. OauthInfos map[string]*OauthInfo
  371. }
  372. var (
  373. MailService *Mailer
  374. OauthService *Oauther
  375. )
  376. func newMailService() {
  377. // Check mailer setting.
  378. if !Cfg.MustBool("mailer", "ENABLED") {
  379. return
  380. }
  381. MailService = &Mailer{
  382. Name: Cfg.MustValue("mailer", "NAME", AppName),
  383. Host: Cfg.MustValue("mailer", "HOST"),
  384. User: Cfg.MustValue("mailer", "USER"),
  385. Passwd: Cfg.MustValue("mailer", "PASSWD"),
  386. }
  387. MailService.From = Cfg.MustValue("mailer", "FROM", MailService.User)
  388. log.Info("Mail Service Enabled")
  389. }
  390. func newRegisterMailService() {
  391. if !Cfg.MustBool("service", "REGISTER_EMAIL_CONFIRM") {
  392. return
  393. } else if MailService == nil {
  394. log.Warn("Register Mail Service: Mail Service is not enabled")
  395. return
  396. }
  397. Service.RegisterEmailConfirm = true
  398. log.Info("Register Mail Service Enabled")
  399. }
  400. func newNotifyMailService() {
  401. if !Cfg.MustBool("service", "ENABLE_NOTIFY_MAIL") {
  402. return
  403. } else if MailService == nil {
  404. log.Warn("Notify Mail Service: Mail Service is not enabled")
  405. return
  406. }
  407. Service.EnableNotifyMail = true
  408. log.Info("Notify Mail Service Enabled")
  409. }
  410. func newWebhookService() {
  411. WebhookTaskInterval = Cfg.MustInt("webhook", "TASK_INTERVAL", 1)
  412. WebhookDeliverTimeout = Cfg.MustInt("webhook", "DELIVER_TIMEOUT", 5)
  413. }
  414. func NewServices() {
  415. newService()
  416. newLogService()
  417. newCacheService()
  418. newSessionService()
  419. newMailService()
  420. newRegisterMailService()
  421. newNotifyMailService()
  422. newWebhookService()
  423. // ssh.Listen("2022")
  424. }