setting.go 15 KB

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