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 34KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2017 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package setting
  6. import (
  7. "encoding/base64"
  8. "fmt"
  9. "io"
  10. "io/ioutil"
  11. "net"
  12. "net/url"
  13. "os"
  14. "os/exec"
  15. "path"
  16. "path/filepath"
  17. "runtime"
  18. "strconv"
  19. "strings"
  20. "time"
  21. "code.gitea.io/gitea/modules/generate"
  22. "code.gitea.io/gitea/modules/git"
  23. "code.gitea.io/gitea/modules/log"
  24. _ "code.gitea.io/gitea/modules/minwinsvc" // import minwinsvc for windows services
  25. "code.gitea.io/gitea/modules/user"
  26. shellquote "github.com/kballard/go-shellquote"
  27. version "github.com/mcuadros/go-version"
  28. "github.com/unknwon/cae/zip"
  29. "github.com/unknwon/com"
  30. ini "gopkg.in/ini.v1"
  31. "strk.kbt.io/projects/go/libravatar"
  32. )
  33. // Scheme describes protocol types
  34. type Scheme string
  35. // enumerates all the scheme types
  36. const (
  37. HTTP Scheme = "http"
  38. HTTPS Scheme = "https"
  39. FCGI Scheme = "fcgi"
  40. UnixSocket Scheme = "unix"
  41. )
  42. // LandingPage describes the default page
  43. type LandingPage string
  44. // enumerates all the landing page types
  45. const (
  46. LandingPageHome LandingPage = "/"
  47. LandingPageExplore LandingPage = "/explore"
  48. LandingPageOrganizations LandingPage = "/explore/organizations"
  49. )
  50. // enumerates all the types of captchas
  51. const (
  52. ImageCaptcha = "image"
  53. ReCaptcha = "recaptcha"
  54. )
  55. // settings
  56. var (
  57. // AppVer settings
  58. AppVer string
  59. AppBuiltWith string
  60. AppName string
  61. AppURL string
  62. AppSubURL string
  63. AppSubURLDepth int // Number of slashes
  64. AppPath string
  65. AppDataPath string
  66. AppWorkPath string
  67. // Server settings
  68. Protocol Scheme
  69. Domain string
  70. HTTPAddr string
  71. HTTPPort string
  72. LocalURL string
  73. RedirectOtherPort bool
  74. PortToRedirect string
  75. OfflineMode bool
  76. CertFile string
  77. KeyFile string
  78. StaticRootPath string
  79. StaticCacheTime time.Duration
  80. EnableGzip bool
  81. LandingPageURL LandingPage
  82. UnixSocketPermission uint32
  83. EnablePprof bool
  84. PprofDataPath string
  85. EnableLetsEncrypt bool
  86. LetsEncryptTOS bool
  87. LetsEncryptDirectory string
  88. LetsEncryptEmail string
  89. GracefulRestartable bool
  90. GracefulHammerTime time.Duration
  91. StaticURLPrefix string
  92. SSH = struct {
  93. Disabled bool `ini:"DISABLE_SSH"`
  94. StartBuiltinServer bool `ini:"START_SSH_SERVER"`
  95. BuiltinServerUser string `ini:"BUILTIN_SSH_SERVER_USER"`
  96. Domain string `ini:"SSH_DOMAIN"`
  97. Port int `ini:"SSH_PORT"`
  98. ListenHost string `ini:"SSH_LISTEN_HOST"`
  99. ListenPort int `ini:"SSH_LISTEN_PORT"`
  100. RootPath string `ini:"SSH_ROOT_PATH"`
  101. ServerCiphers []string `ini:"SSH_SERVER_CIPHERS"`
  102. ServerKeyExchanges []string `ini:"SSH_SERVER_KEY_EXCHANGES"`
  103. ServerMACs []string `ini:"SSH_SERVER_MACS"`
  104. KeyTestPath string `ini:"SSH_KEY_TEST_PATH"`
  105. KeygenPath string `ini:"SSH_KEYGEN_PATH"`
  106. AuthorizedKeysBackup bool `ini:"SSH_AUTHORIZED_KEYS_BACKUP"`
  107. MinimumKeySizeCheck bool `ini:"-"`
  108. MinimumKeySizes map[string]int `ini:"-"`
  109. CreateAuthorizedKeysFile bool `ini:"SSH_CREATE_AUTHORIZED_KEYS_FILE"`
  110. ExposeAnonymous bool `ini:"SSH_EXPOSE_ANONYMOUS"`
  111. }{
  112. Disabled: false,
  113. StartBuiltinServer: false,
  114. Domain: "",
  115. Port: 22,
  116. ServerCiphers: []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "arcfour256", "arcfour128"},
  117. ServerKeyExchanges: []string{"diffie-hellman-group1-sha1", "diffie-hellman-group14-sha1", "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "curve25519-sha256@libssh.org"},
  118. ServerMACs: []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1", "hmac-sha1-96"},
  119. KeygenPath: "ssh-keygen",
  120. }
  121. LFS struct {
  122. StartServer bool `ini:"LFS_START_SERVER"`
  123. ContentPath string `ini:"LFS_CONTENT_PATH"`
  124. JWTSecretBase64 string `ini:"LFS_JWT_SECRET"`
  125. JWTSecretBytes []byte `ini:"-"`
  126. HTTPAuthExpiry time.Duration `ini:"LFS_HTTP_AUTH_EXPIRY"`
  127. }
  128. // Security settings
  129. InstallLock bool
  130. SecretKey string
  131. LogInRememberDays int
  132. CookieUserName string
  133. CookieRememberName string
  134. ReverseProxyAuthUser string
  135. ReverseProxyAuthEmail string
  136. MinPasswordLength int
  137. ImportLocalPaths bool
  138. DisableGitHooks bool
  139. OnlyAllowPushIfGiteaEnvironmentSet bool
  140. PasswordComplexity []string
  141. PasswordHashAlgo string
  142. // UI settings
  143. UI = struct {
  144. ExplorePagingNum int
  145. IssuePagingNum int
  146. RepoSearchPagingNum int
  147. FeedMaxCommitNum int
  148. GraphMaxCommitNum int
  149. CodeCommentLines int
  150. ReactionMaxUserNum int
  151. ThemeColorMetaTag string
  152. MaxDisplayFileSize int64
  153. ShowUserEmail bool
  154. DefaultShowFullName bool
  155. DefaultTheme string
  156. Themes []string
  157. SearchRepoDescription bool
  158. Admin struct {
  159. UserPagingNum int
  160. RepoPagingNum int
  161. NoticePagingNum int
  162. OrgPagingNum int
  163. } `ini:"ui.admin"`
  164. User struct {
  165. RepoPagingNum int
  166. } `ini:"ui.user"`
  167. Meta struct {
  168. Author string
  169. Description string
  170. Keywords string
  171. } `ini:"ui.meta"`
  172. }{
  173. ExplorePagingNum: 20,
  174. IssuePagingNum: 10,
  175. RepoSearchPagingNum: 10,
  176. FeedMaxCommitNum: 5,
  177. GraphMaxCommitNum: 100,
  178. CodeCommentLines: 4,
  179. ReactionMaxUserNum: 10,
  180. ThemeColorMetaTag: `#6cc644`,
  181. MaxDisplayFileSize: 8388608,
  182. DefaultTheme: `gitea`,
  183. Themes: []string{`gitea`, `arc-green`},
  184. Admin: struct {
  185. UserPagingNum int
  186. RepoPagingNum int
  187. NoticePagingNum int
  188. OrgPagingNum int
  189. }{
  190. UserPagingNum: 50,
  191. RepoPagingNum: 50,
  192. NoticePagingNum: 25,
  193. OrgPagingNum: 50,
  194. },
  195. User: struct {
  196. RepoPagingNum int
  197. }{
  198. RepoPagingNum: 15,
  199. },
  200. Meta: struct {
  201. Author string
  202. Description string
  203. Keywords string
  204. }{
  205. Author: "Gitea - Git with a cup of tea",
  206. Description: "Gitea (Git with a cup of tea) is a painless self-hosted Git service written in Go",
  207. Keywords: "go,git,self-hosted,gitea",
  208. },
  209. }
  210. // Markdown settings
  211. Markdown = struct {
  212. EnableHardLineBreak bool
  213. CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"`
  214. FileExtensions []string
  215. }{
  216. EnableHardLineBreak: false,
  217. FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd", ","),
  218. }
  219. // Admin settings
  220. Admin struct {
  221. DisableRegularOrgCreation bool
  222. DefaultEmailNotification string
  223. }
  224. // Picture settings
  225. AvatarUploadPath string
  226. AvatarMaxWidth int
  227. AvatarMaxHeight int
  228. GravatarSource string
  229. GravatarSourceURL *url.URL
  230. DisableGravatar bool
  231. EnableFederatedAvatar bool
  232. LibravatarService *libravatar.Libravatar
  233. AvatarMaxFileSize int64
  234. RepositoryAvatarUploadPath string
  235. RepositoryAvatarFallback string
  236. RepositoryAvatarFallbackImage string
  237. // Log settings
  238. LogLevel string
  239. StacktraceLogLevel string
  240. LogRootPath string
  241. LogDescriptions = make(map[string]*LogDescription)
  242. RedirectMacaronLog bool
  243. DisableRouterLog bool
  244. RouterLogLevel log.Level
  245. RouterLogMode string
  246. EnableAccessLog bool
  247. AccessLogTemplate string
  248. EnableXORMLog bool
  249. // Attachment settings
  250. AttachmentPath string
  251. AttachmentAllowedTypes string
  252. AttachmentMaxSize int64
  253. AttachmentMaxFiles int
  254. AttachmentEnabled bool
  255. // Time settings
  256. TimeFormat string
  257. // UILocation is the location on the UI, so that we can display the time on UI.
  258. DefaultUILocation = time.Local
  259. CSRFCookieName = "_csrf"
  260. CSRFCookieHTTPOnly = true
  261. // Mirror settings
  262. Mirror struct {
  263. DefaultInterval time.Duration
  264. MinInterval time.Duration
  265. }
  266. // API settings
  267. API = struct {
  268. EnableSwagger bool
  269. SwaggerURL string
  270. MaxResponseItems int
  271. DefaultPagingNum int
  272. DefaultGitTreesPerPage int
  273. DefaultMaxBlobSize int64
  274. }{
  275. EnableSwagger: true,
  276. SwaggerURL: "",
  277. MaxResponseItems: 50,
  278. DefaultPagingNum: 30,
  279. DefaultGitTreesPerPage: 1000,
  280. DefaultMaxBlobSize: 10485760,
  281. }
  282. OAuth2 = struct {
  283. Enable bool
  284. AccessTokenExpirationTime int64
  285. RefreshTokenExpirationTime int64
  286. InvalidateRefreshTokens bool
  287. JWTSecretBytes []byte `ini:"-"`
  288. JWTSecretBase64 string `ini:"JWT_SECRET"`
  289. }{
  290. Enable: true,
  291. AccessTokenExpirationTime: 3600,
  292. RefreshTokenExpirationTime: 730,
  293. InvalidateRefreshTokens: false,
  294. }
  295. U2F = struct {
  296. AppID string
  297. TrustedFacets []string
  298. }{}
  299. // Metrics settings
  300. Metrics = struct {
  301. Enabled bool
  302. Token string
  303. }{
  304. Enabled: false,
  305. Token: "",
  306. }
  307. // I18n settings
  308. Langs []string
  309. Names []string
  310. dateLangs map[string]string
  311. // Highlight settings are loaded in modules/template/highlight.go
  312. // Other settings
  313. ShowFooterBranding bool
  314. ShowFooterVersion bool
  315. ShowFooterTemplateLoadTime bool
  316. // Global setting objects
  317. Cfg *ini.File
  318. CustomPath string // Custom directory path
  319. CustomConf string
  320. CustomPID string
  321. ProdMode bool
  322. RunUser string
  323. IsWindows bool
  324. HasRobotsTxt bool
  325. InternalToken string // internal access token
  326. // UILocation is the location on the UI, so that we can display the time on UI.
  327. // Currently only show the default time.Local, it could be added to app.ini after UI is ready
  328. UILocation = time.Local
  329. )
  330. // DateLang transforms standard language locale name to corresponding value in datetime plugin.
  331. func DateLang(lang string) string {
  332. name, ok := dateLangs[lang]
  333. if ok {
  334. return name
  335. }
  336. return "en"
  337. }
  338. func getAppPath() (string, error) {
  339. var appPath string
  340. var err error
  341. if IsWindows && filepath.IsAbs(os.Args[0]) {
  342. appPath = filepath.Clean(os.Args[0])
  343. } else {
  344. appPath, err = exec.LookPath(os.Args[0])
  345. }
  346. if err != nil {
  347. return "", err
  348. }
  349. appPath, err = filepath.Abs(appPath)
  350. if err != nil {
  351. return "", err
  352. }
  353. // Note: we don't use path.Dir here because it does not handle case
  354. // which path starts with two "/" in Windows: "//psf/Home/..."
  355. return strings.Replace(appPath, "\\", "/", -1), err
  356. }
  357. func getWorkPath(appPath string) string {
  358. workPath := AppWorkPath
  359. if giteaWorkPath, ok := os.LookupEnv("GITEA_WORK_DIR"); ok {
  360. workPath = giteaWorkPath
  361. }
  362. if len(workPath) == 0 {
  363. i := strings.LastIndex(appPath, "/")
  364. if i == -1 {
  365. workPath = appPath
  366. } else {
  367. workPath = appPath[:i]
  368. }
  369. }
  370. return strings.Replace(workPath, "\\", "/", -1)
  371. }
  372. func init() {
  373. IsWindows = runtime.GOOS == "windows"
  374. // We can rely on log.CanColorStdout being set properly because modules/log/console_windows.go comes before modules/setting/setting.go lexicographically
  375. log.NewLogger(0, "console", "console", fmt.Sprintf(`{"level": "trace", "colorize": %t, "stacktraceLevel": "none"}`, log.CanColorStdout))
  376. var err error
  377. if AppPath, err = getAppPath(); err != nil {
  378. log.Fatal("Failed to get app path: %v", err)
  379. }
  380. AppWorkPath = getWorkPath(AppPath)
  381. }
  382. func forcePathSeparator(path string) {
  383. if strings.Contains(path, "\\") {
  384. log.Fatal("Do not use '\\' or '\\\\' in paths, instead, please use '/' in all places")
  385. }
  386. }
  387. // IsRunUserMatchCurrentUser returns false if configured run user does not match
  388. // actual user that runs the app. The first return value is the actual user name.
  389. // This check is ignored under Windows since SSH remote login is not the main
  390. // method to login on Windows.
  391. func IsRunUserMatchCurrentUser(runUser string) (string, bool) {
  392. if IsWindows || SSH.StartBuiltinServer {
  393. return "", true
  394. }
  395. currentUser := user.CurrentUsername()
  396. return currentUser, runUser == currentUser
  397. }
  398. func createPIDFile(pidPath string) {
  399. currentPid := os.Getpid()
  400. if err := os.MkdirAll(filepath.Dir(pidPath), os.ModePerm); err != nil {
  401. log.Fatal("Failed to create PID folder: %v", err)
  402. }
  403. file, err := os.Create(pidPath)
  404. if err != nil {
  405. log.Fatal("Failed to create PID file: %v", err)
  406. }
  407. defer file.Close()
  408. if _, err := file.WriteString(strconv.FormatInt(int64(currentPid), 10)); err != nil {
  409. log.Fatal("Failed to write PID information: %v", err)
  410. }
  411. }
  412. // CheckLFSVersion will check lfs version, if not satisfied, then disable it.
  413. func CheckLFSVersion() {
  414. if LFS.StartServer {
  415. //Disable LFS client hooks if installed for the current OS user
  416. //Needs at least git v2.1.2
  417. binVersion, err := git.BinVersion()
  418. if err != nil {
  419. log.Fatal("Error retrieving git version: %v", err)
  420. }
  421. if !version.Compare(binVersion, "2.1.2", ">=") {
  422. LFS.StartServer = false
  423. log.Error("LFS server support needs at least Git v2.1.2")
  424. } else {
  425. git.GlobalCommandArgs = append(git.GlobalCommandArgs, "-c", "filter.lfs.required=",
  426. "-c", "filter.lfs.smudge=", "-c", "filter.lfs.clean=")
  427. }
  428. }
  429. }
  430. // SetCustomPathAndConf will set CustomPath and CustomConf with reference to the
  431. // GITEA_CUSTOM environment variable and with provided overrides before stepping
  432. // back to the default
  433. func SetCustomPathAndConf(providedCustom, providedConf, providedWorkPath string) {
  434. if len(providedWorkPath) != 0 {
  435. AppWorkPath = filepath.ToSlash(providedWorkPath)
  436. }
  437. if giteaCustom, ok := os.LookupEnv("GITEA_CUSTOM"); ok {
  438. CustomPath = giteaCustom
  439. }
  440. if len(providedCustom) != 0 {
  441. CustomPath = providedCustom
  442. }
  443. if len(CustomPath) == 0 {
  444. CustomPath = path.Join(AppWorkPath, "custom")
  445. } else if !filepath.IsAbs(CustomPath) {
  446. CustomPath = path.Join(AppWorkPath, CustomPath)
  447. }
  448. if len(providedConf) != 0 {
  449. CustomConf = providedConf
  450. }
  451. if len(CustomConf) == 0 {
  452. CustomConf = path.Join(CustomPath, "conf/app.ini")
  453. } else if !filepath.IsAbs(CustomConf) {
  454. CustomConf = path.Join(CustomPath, CustomConf)
  455. }
  456. }
  457. // NewContext initializes configuration context.
  458. // NOTE: do not print any log except error.
  459. func NewContext() {
  460. Cfg = ini.Empty()
  461. if len(CustomPID) > 0 {
  462. createPIDFile(CustomPID)
  463. }
  464. if com.IsFile(CustomConf) {
  465. if err := Cfg.Append(CustomConf); err != nil {
  466. log.Fatal("Failed to load custom conf '%s': %v", CustomConf, err)
  467. }
  468. } else {
  469. log.Warn("Custom config '%s' not found, ignore this if you're running first time", CustomConf)
  470. }
  471. Cfg.NameMapper = ini.SnackCase
  472. homeDir, err := com.HomeDir()
  473. if err != nil {
  474. log.Fatal("Failed to get home directory: %v", err)
  475. }
  476. homeDir = strings.Replace(homeDir, "\\", "/", -1)
  477. LogLevel = getLogLevel(Cfg.Section("log"), "LEVEL", "Info")
  478. StacktraceLogLevel = getStacktraceLogLevel(Cfg.Section("log"), "STACKTRACE_LEVEL", "None")
  479. LogRootPath = Cfg.Section("log").Key("ROOT_PATH").MustString(path.Join(AppWorkPath, "log"))
  480. forcePathSeparator(LogRootPath)
  481. RedirectMacaronLog = Cfg.Section("log").Key("REDIRECT_MACARON_LOG").MustBool(false)
  482. RouterLogLevel = log.FromString(Cfg.Section("log").Key("ROUTER_LOG_LEVEL").MustString("Info"))
  483. sec := Cfg.Section("server")
  484. AppName = Cfg.Section("").Key("APP_NAME").MustString("Gitea: Git with a cup of tea")
  485. Protocol = HTTP
  486. switch sec.Key("PROTOCOL").String() {
  487. case "https":
  488. Protocol = HTTPS
  489. CertFile = sec.Key("CERT_FILE").String()
  490. KeyFile = sec.Key("KEY_FILE").String()
  491. case "fcgi":
  492. Protocol = FCGI
  493. case "unix":
  494. Protocol = UnixSocket
  495. UnixSocketPermissionRaw := sec.Key("UNIX_SOCKET_PERMISSION").MustString("666")
  496. UnixSocketPermissionParsed, err := strconv.ParseUint(UnixSocketPermissionRaw, 8, 32)
  497. if err != nil || UnixSocketPermissionParsed > 0777 {
  498. log.Fatal("Failed to parse unixSocketPermission: %s", UnixSocketPermissionRaw)
  499. }
  500. UnixSocketPermission = uint32(UnixSocketPermissionParsed)
  501. }
  502. EnableLetsEncrypt = sec.Key("ENABLE_LETSENCRYPT").MustBool(false)
  503. LetsEncryptTOS = sec.Key("LETSENCRYPT_ACCEPTTOS").MustBool(false)
  504. if !LetsEncryptTOS && EnableLetsEncrypt {
  505. log.Warn("Failed to enable Let's Encrypt due to Let's Encrypt TOS not being accepted")
  506. EnableLetsEncrypt = false
  507. }
  508. LetsEncryptDirectory = sec.Key("LETSENCRYPT_DIRECTORY").MustString("https")
  509. LetsEncryptEmail = sec.Key("LETSENCRYPT_EMAIL").MustString("")
  510. Domain = sec.Key("DOMAIN").MustString("localhost")
  511. HTTPAddr = sec.Key("HTTP_ADDR").MustString("0.0.0.0")
  512. HTTPPort = sec.Key("HTTP_PORT").MustString("3000")
  513. GracefulRestartable = sec.Key("ALLOW_GRACEFUL_RESTARTS").MustBool(true)
  514. GracefulHammerTime = sec.Key("GRACEFUL_HAMMER_TIME").MustDuration(60 * time.Second)
  515. defaultAppURL := string(Protocol) + "://" + Domain
  516. if (Protocol == HTTP && HTTPPort != "80") || (Protocol == HTTPS && HTTPPort != "443") {
  517. defaultAppURL += ":" + HTTPPort
  518. }
  519. AppURL = sec.Key("ROOT_URL").MustString(defaultAppURL)
  520. AppURL = strings.TrimSuffix(AppURL, "/") + "/"
  521. // Check if has app suburl.
  522. appURL, err := url.Parse(AppURL)
  523. if err != nil {
  524. log.Fatal("Invalid ROOT_URL '%s': %s", AppURL, err)
  525. }
  526. // Suburl should start with '/' and end without '/', such as '/{subpath}'.
  527. // This value is empty if site does not have sub-url.
  528. AppSubURL = strings.TrimSuffix(appURL.Path, "/")
  529. StaticURLPrefix = strings.TrimSuffix(sec.Key("STATIC_URL_PREFIX").MustString(AppSubURL), "/")
  530. AppSubURLDepth = strings.Count(AppSubURL, "/")
  531. // Check if Domain differs from AppURL domain than update it to AppURL's domain
  532. // TODO: Can be replaced with url.Hostname() when minimal GoLang version is 1.8
  533. urlHostname := strings.SplitN(appURL.Host, ":", 2)[0]
  534. if urlHostname != Domain && net.ParseIP(urlHostname) == nil {
  535. Domain = urlHostname
  536. }
  537. var defaultLocalURL string
  538. switch Protocol {
  539. case UnixSocket:
  540. defaultLocalURL = "http://unix/"
  541. case FCGI:
  542. defaultLocalURL = AppURL
  543. default:
  544. defaultLocalURL = string(Protocol) + "://"
  545. if HTTPAddr == "0.0.0.0" {
  546. defaultLocalURL += "localhost"
  547. } else {
  548. defaultLocalURL += HTTPAddr
  549. }
  550. defaultLocalURL += ":" + HTTPPort + "/"
  551. }
  552. LocalURL = sec.Key("LOCAL_ROOT_URL").MustString(defaultLocalURL)
  553. RedirectOtherPort = sec.Key("REDIRECT_OTHER_PORT").MustBool(false)
  554. PortToRedirect = sec.Key("PORT_TO_REDIRECT").MustString("80")
  555. OfflineMode = sec.Key("OFFLINE_MODE").MustBool()
  556. DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool()
  557. StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(AppWorkPath)
  558. StaticCacheTime = sec.Key("STATIC_CACHE_TIME").MustDuration(6 * time.Hour)
  559. AppDataPath = sec.Key("APP_DATA_PATH").MustString(path.Join(AppWorkPath, "data"))
  560. EnableGzip = sec.Key("ENABLE_GZIP").MustBool()
  561. EnablePprof = sec.Key("ENABLE_PPROF").MustBool(false)
  562. PprofDataPath = sec.Key("PPROF_DATA_PATH").MustString(path.Join(AppWorkPath, "data/tmp/pprof"))
  563. if !filepath.IsAbs(PprofDataPath) {
  564. PprofDataPath = filepath.Join(AppWorkPath, PprofDataPath)
  565. }
  566. switch sec.Key("LANDING_PAGE").MustString("home") {
  567. case "explore":
  568. LandingPageURL = LandingPageExplore
  569. case "organizations":
  570. LandingPageURL = LandingPageOrganizations
  571. default:
  572. LandingPageURL = LandingPageHome
  573. }
  574. if len(SSH.Domain) == 0 {
  575. SSH.Domain = Domain
  576. }
  577. SSH.RootPath = path.Join(homeDir, ".ssh")
  578. serverCiphers := sec.Key("SSH_SERVER_CIPHERS").Strings(",")
  579. if len(serverCiphers) > 0 {
  580. SSH.ServerCiphers = serverCiphers
  581. }
  582. serverKeyExchanges := sec.Key("SSH_SERVER_KEY_EXCHANGES").Strings(",")
  583. if len(serverKeyExchanges) > 0 {
  584. SSH.ServerKeyExchanges = serverKeyExchanges
  585. }
  586. serverMACs := sec.Key("SSH_SERVER_MACS").Strings(",")
  587. if len(serverMACs) > 0 {
  588. SSH.ServerMACs = serverMACs
  589. }
  590. SSH.KeyTestPath = os.TempDir()
  591. if err = Cfg.Section("server").MapTo(&SSH); err != nil {
  592. log.Fatal("Failed to map SSH settings: %v", err)
  593. }
  594. SSH.KeygenPath = sec.Key("SSH_KEYGEN_PATH").MustString("ssh-keygen")
  595. SSH.Port = sec.Key("SSH_PORT").MustInt(22)
  596. SSH.ListenPort = sec.Key("SSH_LISTEN_PORT").MustInt(SSH.Port)
  597. // When disable SSH, start builtin server value is ignored.
  598. if SSH.Disabled {
  599. SSH.StartBuiltinServer = false
  600. }
  601. if !SSH.Disabled && !SSH.StartBuiltinServer {
  602. if err := os.MkdirAll(SSH.RootPath, 0700); err != nil {
  603. log.Fatal("Failed to create '%s': %v", SSH.RootPath, err)
  604. } else if err = os.MkdirAll(SSH.KeyTestPath, 0644); err != nil {
  605. log.Fatal("Failed to create '%s': %v", SSH.KeyTestPath, err)
  606. }
  607. }
  608. SSH.MinimumKeySizeCheck = sec.Key("MINIMUM_KEY_SIZE_CHECK").MustBool()
  609. SSH.MinimumKeySizes = map[string]int{}
  610. minimumKeySizes := Cfg.Section("ssh.minimum_key_sizes").Keys()
  611. for _, key := range minimumKeySizes {
  612. if key.MustInt() != -1 {
  613. SSH.MinimumKeySizes[strings.ToLower(key.Name())] = key.MustInt()
  614. }
  615. }
  616. SSH.AuthorizedKeysBackup = sec.Key("SSH_AUTHORIZED_KEYS_BACKUP").MustBool(true)
  617. SSH.CreateAuthorizedKeysFile = sec.Key("SSH_CREATE_AUTHORIZED_KEYS_FILE").MustBool(true)
  618. SSH.ExposeAnonymous = sec.Key("SSH_EXPOSE_ANONYMOUS").MustBool(false)
  619. sec = Cfg.Section("server")
  620. if err = sec.MapTo(&LFS); err != nil {
  621. log.Fatal("Failed to map LFS settings: %v", err)
  622. }
  623. LFS.ContentPath = sec.Key("LFS_CONTENT_PATH").MustString(filepath.Join(AppDataPath, "lfs"))
  624. if !filepath.IsAbs(LFS.ContentPath) {
  625. LFS.ContentPath = filepath.Join(AppWorkPath, LFS.ContentPath)
  626. }
  627. LFS.HTTPAuthExpiry = sec.Key("LFS_HTTP_AUTH_EXPIRY").MustDuration(20 * time.Minute)
  628. if LFS.StartServer {
  629. if err := os.MkdirAll(LFS.ContentPath, 0700); err != nil {
  630. log.Fatal("Failed to create '%s': %v", LFS.ContentPath, err)
  631. }
  632. LFS.JWTSecretBytes = make([]byte, 32)
  633. n, err := base64.RawURLEncoding.Decode(LFS.JWTSecretBytes, []byte(LFS.JWTSecretBase64))
  634. if err != nil || n != 32 {
  635. LFS.JWTSecretBase64, err = generate.NewJwtSecret()
  636. if err != nil {
  637. log.Fatal("Error generating JWT Secret for custom config: %v", err)
  638. return
  639. }
  640. // Save secret
  641. cfg := ini.Empty()
  642. if com.IsFile(CustomConf) {
  643. // Keeps custom settings if there is already something.
  644. if err := cfg.Append(CustomConf); err != nil {
  645. log.Error("Failed to load custom conf '%s': %v", CustomConf, err)
  646. }
  647. }
  648. cfg.Section("server").Key("LFS_JWT_SECRET").SetValue(LFS.JWTSecretBase64)
  649. if err := os.MkdirAll(filepath.Dir(CustomConf), os.ModePerm); err != nil {
  650. log.Fatal("Failed to create '%s': %v", CustomConf, err)
  651. }
  652. if err := cfg.SaveTo(CustomConf); err != nil {
  653. log.Fatal("Error saving generated JWT Secret to custom config: %v", err)
  654. return
  655. }
  656. }
  657. }
  658. if err = Cfg.Section("oauth2").MapTo(&OAuth2); err != nil {
  659. log.Fatal("Failed to OAuth2 settings: %v", err)
  660. return
  661. }
  662. if OAuth2.Enable {
  663. OAuth2.JWTSecretBytes = make([]byte, 32)
  664. n, err := base64.RawURLEncoding.Decode(OAuth2.JWTSecretBytes, []byte(OAuth2.JWTSecretBase64))
  665. if err != nil || n != 32 {
  666. OAuth2.JWTSecretBase64, err = generate.NewJwtSecret()
  667. if err != nil {
  668. log.Fatal("error generating JWT secret: %v", err)
  669. return
  670. }
  671. cfg := ini.Empty()
  672. if com.IsFile(CustomConf) {
  673. if err := cfg.Append(CustomConf); err != nil {
  674. log.Error("failed to load custom conf %s: %v", CustomConf, err)
  675. return
  676. }
  677. }
  678. cfg.Section("oauth2").Key("JWT_SECRET").SetValue(OAuth2.JWTSecretBase64)
  679. if err := os.MkdirAll(filepath.Dir(CustomConf), os.ModePerm); err != nil {
  680. log.Fatal("failed to create '%s': %v", CustomConf, err)
  681. return
  682. }
  683. if err := cfg.SaveTo(CustomConf); err != nil {
  684. log.Fatal("error saving generating JWT secret to custom config: %v", err)
  685. return
  686. }
  687. }
  688. }
  689. sec = Cfg.Section("admin")
  690. Admin.DefaultEmailNotification = sec.Key("DEFAULT_EMAIL_NOTIFICATIONS").MustString("enabled")
  691. sec = Cfg.Section("security")
  692. InstallLock = sec.Key("INSTALL_LOCK").MustBool(false)
  693. SecretKey = sec.Key("SECRET_KEY").MustString("!#@FDEWREWR&*(")
  694. LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7)
  695. CookieUserName = sec.Key("COOKIE_USERNAME").MustString("gitea_awesome")
  696. CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").MustString("gitea_incredible")
  697. ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER")
  698. ReverseProxyAuthEmail = sec.Key("REVERSE_PROXY_AUTHENTICATION_EMAIL").MustString("X-WEBAUTH-EMAIL")
  699. MinPasswordLength = sec.Key("MIN_PASSWORD_LENGTH").MustInt(6)
  700. ImportLocalPaths = sec.Key("IMPORT_LOCAL_PATHS").MustBool(false)
  701. DisableGitHooks = sec.Key("DISABLE_GIT_HOOKS").MustBool(false)
  702. OnlyAllowPushIfGiteaEnvironmentSet = sec.Key("ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET").MustBool(true)
  703. PasswordHashAlgo = sec.Key("PASSWORD_HASH_ALGO").MustString("pbkdf2")
  704. CSRFCookieHTTPOnly = sec.Key("CSRF_COOKIE_HTTP_ONLY").MustBool(true)
  705. InternalToken = loadInternalToken(sec)
  706. cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",")
  707. PasswordComplexity = make([]string, 0, len(cfgdata))
  708. for _, name := range cfgdata {
  709. name := strings.ToLower(strings.Trim(name, `"`))
  710. if name != "" {
  711. PasswordComplexity = append(PasswordComplexity, name)
  712. }
  713. }
  714. sec = Cfg.Section("attachment")
  715. AttachmentPath = sec.Key("PATH").MustString(path.Join(AppDataPath, "attachments"))
  716. if !filepath.IsAbs(AttachmentPath) {
  717. AttachmentPath = path.Join(AppWorkPath, AttachmentPath)
  718. }
  719. AttachmentAllowedTypes = strings.Replace(sec.Key("ALLOWED_TYPES").MustString("image/jpeg,image/png,application/zip,application/gzip"), "|", ",", -1)
  720. AttachmentMaxSize = sec.Key("MAX_SIZE").MustInt64(4)
  721. AttachmentMaxFiles = sec.Key("MAX_FILES").MustInt(5)
  722. AttachmentEnabled = sec.Key("ENABLED").MustBool(true)
  723. timeFormatKey := Cfg.Section("time").Key("FORMAT").MustString("")
  724. if timeFormatKey != "" {
  725. TimeFormat = map[string]string{
  726. "ANSIC": time.ANSIC,
  727. "UnixDate": time.UnixDate,
  728. "RubyDate": time.RubyDate,
  729. "RFC822": time.RFC822,
  730. "RFC822Z": time.RFC822Z,
  731. "RFC850": time.RFC850,
  732. "RFC1123": time.RFC1123,
  733. "RFC1123Z": time.RFC1123Z,
  734. "RFC3339": time.RFC3339,
  735. "RFC3339Nano": time.RFC3339Nano,
  736. "Kitchen": time.Kitchen,
  737. "Stamp": time.Stamp,
  738. "StampMilli": time.StampMilli,
  739. "StampMicro": time.StampMicro,
  740. "StampNano": time.StampNano,
  741. }[timeFormatKey]
  742. // When the TimeFormatKey does not exist in the previous map e.g.'2006-01-02 15:04:05'
  743. if len(TimeFormat) == 0 {
  744. TimeFormat = timeFormatKey
  745. TestTimeFormat, _ := time.Parse(TimeFormat, TimeFormat)
  746. if TestTimeFormat.Format(time.RFC3339) != "2006-01-02T15:04:05Z" {
  747. log.Fatal("Can't create time properly, please check your time format has 2006, 01, 02, 15, 04 and 05")
  748. }
  749. log.Trace("Custom TimeFormat: %s", TimeFormat)
  750. }
  751. }
  752. zone := Cfg.Section("time").Key("DEFAULT_UI_LOCATION").String()
  753. if zone != "" {
  754. DefaultUILocation, err = time.LoadLocation(zone)
  755. if err != nil {
  756. log.Fatal("Load time zone failed: %v", err)
  757. } else {
  758. log.Info("Default UI Location is %v", zone)
  759. }
  760. }
  761. if DefaultUILocation == nil {
  762. DefaultUILocation = time.Local
  763. }
  764. RunUser = Cfg.Section("").Key("RUN_USER").MustString(user.CurrentUsername())
  765. // Does not check run user when the install lock is off.
  766. if InstallLock {
  767. currentUser, match := IsRunUserMatchCurrentUser(RunUser)
  768. if !match {
  769. log.Fatal("Expect user '%s' but current user is: %s", RunUser, currentUser)
  770. }
  771. }
  772. SSH.BuiltinServerUser = Cfg.Section("server").Key("BUILTIN_SSH_SERVER_USER").MustString(RunUser)
  773. newRepository()
  774. sec = Cfg.Section("picture")
  775. AvatarUploadPath = sec.Key("AVATAR_UPLOAD_PATH").MustString(path.Join(AppDataPath, "avatars"))
  776. forcePathSeparator(AvatarUploadPath)
  777. if !filepath.IsAbs(AvatarUploadPath) {
  778. AvatarUploadPath = path.Join(AppWorkPath, AvatarUploadPath)
  779. }
  780. RepositoryAvatarUploadPath = sec.Key("REPOSITORY_AVATAR_UPLOAD_PATH").MustString(path.Join(AppDataPath, "repo-avatars"))
  781. forcePathSeparator(RepositoryAvatarUploadPath)
  782. if !filepath.IsAbs(RepositoryAvatarUploadPath) {
  783. RepositoryAvatarUploadPath = path.Join(AppWorkPath, RepositoryAvatarUploadPath)
  784. }
  785. RepositoryAvatarFallback = sec.Key("REPOSITORY_AVATAR_FALLBACK").MustString("none")
  786. RepositoryAvatarFallbackImage = sec.Key("REPOSITORY_AVATAR_FALLBACK_IMAGE").MustString("/img/repo_default.png")
  787. AvatarMaxWidth = sec.Key("AVATAR_MAX_WIDTH").MustInt(4096)
  788. AvatarMaxHeight = sec.Key("AVATAR_MAX_HEIGHT").MustInt(3072)
  789. AvatarMaxFileSize = sec.Key("AVATAR_MAX_FILE_SIZE").MustInt64(1048576)
  790. switch source := sec.Key("GRAVATAR_SOURCE").MustString("gravatar"); source {
  791. case "duoshuo":
  792. GravatarSource = "http://gravatar.duoshuo.com/avatar/"
  793. case "gravatar":
  794. GravatarSource = "https://secure.gravatar.com/avatar/"
  795. case "libravatar":
  796. GravatarSource = "https://seccdn.libravatar.org/avatar/"
  797. default:
  798. GravatarSource = source
  799. }
  800. DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool()
  801. EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(!InstallLock)
  802. if OfflineMode {
  803. DisableGravatar = true
  804. EnableFederatedAvatar = false
  805. }
  806. if DisableGravatar {
  807. EnableFederatedAvatar = false
  808. }
  809. if EnableFederatedAvatar || !DisableGravatar {
  810. GravatarSourceURL, err = url.Parse(GravatarSource)
  811. if err != nil {
  812. log.Fatal("Failed to parse Gravatar URL(%s): %v",
  813. GravatarSource, err)
  814. }
  815. }
  816. if EnableFederatedAvatar {
  817. LibravatarService = libravatar.New()
  818. if GravatarSourceURL.Scheme == "https" {
  819. LibravatarService.SetUseHTTPS(true)
  820. LibravatarService.SetSecureFallbackHost(GravatarSourceURL.Host)
  821. } else {
  822. LibravatarService.SetUseHTTPS(false)
  823. LibravatarService.SetFallbackHost(GravatarSourceURL.Host)
  824. }
  825. }
  826. if err = Cfg.Section("ui").MapTo(&UI); err != nil {
  827. log.Fatal("Failed to map UI settings: %v", err)
  828. } else if err = Cfg.Section("markdown").MapTo(&Markdown); err != nil {
  829. log.Fatal("Failed to map Markdown settings: %v", err)
  830. } else if err = Cfg.Section("admin").MapTo(&Admin); err != nil {
  831. log.Fatal("Fail to map Admin settings: %v", err)
  832. } else if err = Cfg.Section("api").MapTo(&API); err != nil {
  833. log.Fatal("Failed to map API settings: %v", err)
  834. } else if err = Cfg.Section("metrics").MapTo(&Metrics); err != nil {
  835. log.Fatal("Failed to map Metrics settings: %v", err)
  836. }
  837. u := *appURL
  838. u.Path = path.Join(u.Path, "api", "swagger")
  839. API.SwaggerURL = u.String()
  840. newCron()
  841. newGit()
  842. sec = Cfg.Section("mirror")
  843. Mirror.MinInterval = sec.Key("MIN_INTERVAL").MustDuration(10 * time.Minute)
  844. Mirror.DefaultInterval = sec.Key("DEFAULT_INTERVAL").MustDuration(8 * time.Hour)
  845. if Mirror.MinInterval.Minutes() < 1 {
  846. log.Warn("Mirror.MinInterval is too low")
  847. Mirror.MinInterval = 1 * time.Minute
  848. }
  849. if Mirror.DefaultInterval < Mirror.MinInterval {
  850. log.Warn("Mirror.DefaultInterval is less than Mirror.MinInterval")
  851. Mirror.DefaultInterval = time.Hour * 8
  852. }
  853. Langs = Cfg.Section("i18n").Key("LANGS").Strings(",")
  854. if len(Langs) == 0 {
  855. Langs = []string{
  856. "en-US", "zh-CN", "zh-HK", "zh-TW", "de-DE", "fr-FR", "nl-NL", "lv-LV",
  857. "ru-RU", "uk-UA", "ja-JP", "es-ES", "pt-BR", "pl-PL", "bg-BG", "it-IT",
  858. "fi-FI", "tr-TR", "cs-CZ", "sr-SP", "sv-SE", "ko-KR"}
  859. }
  860. Names = Cfg.Section("i18n").Key("NAMES").Strings(",")
  861. if len(Names) == 0 {
  862. Names = []string{"English", "简体中文", "繁體中文(香港)", "繁體中文(台灣)", "Deutsch",
  863. "français", "Nederlands", "latviešu", "русский", "Українська", "日本語",
  864. "español", "português do Brasil", "polski", "български", "italiano",
  865. "suomi", "Türkçe", "čeština", "српски", "svenska", "한국어"}
  866. }
  867. dateLangs = Cfg.Section("i18n.datelang").KeysHash()
  868. ShowFooterBranding = Cfg.Section("other").Key("SHOW_FOOTER_BRANDING").MustBool(false)
  869. ShowFooterVersion = Cfg.Section("other").Key("SHOW_FOOTER_VERSION").MustBool(true)
  870. ShowFooterTemplateLoadTime = Cfg.Section("other").Key("SHOW_FOOTER_TEMPLATE_LOAD_TIME").MustBool(true)
  871. UI.ShowUserEmail = Cfg.Section("ui").Key("SHOW_USER_EMAIL").MustBool(true)
  872. UI.DefaultShowFullName = Cfg.Section("ui").Key("DEFAULT_SHOW_FULL_NAME").MustBool(false)
  873. UI.SearchRepoDescription = Cfg.Section("ui").Key("SEARCH_REPO_DESCRIPTION").MustBool(true)
  874. HasRobotsTxt = com.IsFile(path.Join(CustomPath, "robots.txt"))
  875. newMarkup()
  876. sec = Cfg.Section("U2F")
  877. U2F.TrustedFacets, _ = shellquote.Split(sec.Key("TRUSTED_FACETS").MustString(strings.TrimRight(AppURL, "/")))
  878. U2F.AppID = sec.Key("APP_ID").MustString(strings.TrimRight(AppURL, "/"))
  879. zip.Verbose = false
  880. }
  881. func loadInternalToken(sec *ini.Section) string {
  882. uri := sec.Key("INTERNAL_TOKEN_URI").String()
  883. if len(uri) == 0 {
  884. return loadOrGenerateInternalToken(sec)
  885. }
  886. tempURI, err := url.Parse(uri)
  887. if err != nil {
  888. log.Fatal("Failed to parse INTERNAL_TOKEN_URI (%s): %v", uri, err)
  889. }
  890. switch tempURI.Scheme {
  891. case "file":
  892. fp, err := os.OpenFile(tempURI.RequestURI(), os.O_RDWR, 0600)
  893. if err != nil {
  894. log.Fatal("Failed to open InternalTokenURI (%s): %v", uri, err)
  895. }
  896. defer fp.Close()
  897. buf, err := ioutil.ReadAll(fp)
  898. if err != nil {
  899. log.Fatal("Failed to read InternalTokenURI (%s): %v", uri, err)
  900. }
  901. // No token in the file, generate one and store it.
  902. if len(buf) == 0 {
  903. token, err := generate.NewInternalToken()
  904. if err != nil {
  905. log.Fatal("Error generate internal token: %v", err)
  906. }
  907. if _, err := io.WriteString(fp, token); err != nil {
  908. log.Fatal("Error writing to InternalTokenURI (%s): %v", uri, err)
  909. }
  910. return token
  911. }
  912. return string(buf)
  913. default:
  914. log.Fatal("Unsupported URI-Scheme %q (INTERNAL_TOKEN_URI = %q)", tempURI.Scheme, uri)
  915. }
  916. return ""
  917. }
  918. func loadOrGenerateInternalToken(sec *ini.Section) string {
  919. var err error
  920. token := sec.Key("INTERNAL_TOKEN").String()
  921. if len(token) == 0 {
  922. token, err = generate.NewInternalToken()
  923. if err != nil {
  924. log.Fatal("Error generate internal token: %v", err)
  925. }
  926. // Save secret
  927. cfgSave := ini.Empty()
  928. if com.IsFile(CustomConf) {
  929. // Keeps custom settings if there is already something.
  930. if err := cfgSave.Append(CustomConf); err != nil {
  931. log.Error("Failed to load custom conf '%s': %v", CustomConf, err)
  932. }
  933. }
  934. cfgSave.Section("security").Key("INTERNAL_TOKEN").SetValue(token)
  935. if err := os.MkdirAll(filepath.Dir(CustomConf), os.ModePerm); err != nil {
  936. log.Fatal("Failed to create '%s': %v", CustomConf, err)
  937. }
  938. if err := cfgSave.SaveTo(CustomConf); err != nil {
  939. log.Fatal("Error saving generated INTERNAL_TOKEN to custom config: %v", err)
  940. }
  941. }
  942. return token
  943. }
  944. // NewServices initializes the services
  945. func NewServices() {
  946. InitDBConfig()
  947. newService()
  948. NewLogServices(false)
  949. newCacheService()
  950. newSessionService()
  951. newCORSService()
  952. newMailService()
  953. newRegisterMailService()
  954. newNotifyMailService()
  955. newWebhookService()
  956. newIndexerService()
  957. newTaskService()
  958. }