Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

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