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

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