setting.go 15 KB

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