mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00:00 
			
		
		
		
	Refactor the setting to make unit test easier (#22405)
Some bugs caused by less unit tests in fundamental packages. This PR refactor `setting` package so that create a unit test will be easier than before. - All `LoadFromXXX` files has been splited as two functions, one is `InitProviderFromXXX` and `LoadCommonSettings`. The first functions will only include the code to create or new a ini file. The second function will load common settings. - It also renames all functions in setting from `newXXXService` to `loadXXXSetting` or `loadXXXFrom` to make the function name less confusing. - Move `XORMLog` to `SQLLog` because it's a better name for that. Maybe we should finally move these `loadXXXSetting` into the `XXXInit` function? Any idea? --------- Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: delvh <dev.lh@web.de>
This commit is contained in:
		| @@ -57,9 +57,10 @@ func confirm() (bool, error) { | ||||
| } | ||||
|  | ||||
| func initDB(ctx context.Context) error { | ||||
| 	setting.LoadFromExisting() | ||||
| 	setting.InitDBConfig() | ||||
| 	setting.NewXORMLogService(false) | ||||
| 	setting.InitProviderFromExistingFile() | ||||
| 	setting.LoadCommonSettings() | ||||
| 	setting.LoadDBSetting() | ||||
| 	setting.InitSQLLog(false) | ||||
|  | ||||
| 	if setting.Database.Type == "" { | ||||
| 		log.Fatal(`Database settings are missing from the configuration file: %q. | ||||
|   | ||||
| @@ -32,7 +32,7 @@ func runConvert(ctx *cli.Context) error { | ||||
| 	log.Info("AppPath: %s", setting.AppPath) | ||||
| 	log.Info("AppWorkPath: %s", setting.AppWorkPath) | ||||
| 	log.Info("Custom path: %s", setting.CustomPath) | ||||
| 	log.Info("Log path: %s", setting.LogRootPath) | ||||
| 	log.Info("Log path: %s", setting.Log.RootPath) | ||||
| 	log.Info("Configuration file: %s", setting.CustomConf) | ||||
|  | ||||
| 	if !setting.Database.UseMySQL { | ||||
|   | ||||
| @@ -87,14 +87,16 @@ func runRecreateTable(ctx *cli.Context) error { | ||||
| 	golog.SetPrefix("") | ||||
| 	golog.SetOutput(log.NewLoggerAsWriter("INFO", log.GetLogger(log.DEFAULT))) | ||||
|  | ||||
| 	setting.LoadFromExisting() | ||||
| 	setting.InitDBConfig() | ||||
| 	setting.InitProviderFromExistingFile() | ||||
| 	setting.LoadCommonSettings() | ||||
| 	setting.LoadDBSetting() | ||||
|  | ||||
| 	setting.EnableXORMLog = ctx.Bool("debug") | ||||
| 	setting.Log.EnableXORMLog = ctx.Bool("debug") | ||||
| 	setting.Database.LogSQL = ctx.Bool("debug") | ||||
| 	setting.Cfg.Section("log").Key("XORM").SetValue(",") | ||||
| 	// FIXME: don't use CfgProvider directly | ||||
| 	setting.CfgProvider.Section("log").Key("XORM").SetValue(",") | ||||
|  | ||||
| 	setting.NewXORMLogService(!ctx.Bool("debug")) | ||||
| 	setting.InitSQLLog(!ctx.Bool("debug")) | ||||
| 	stdCtx, cancel := installSignals() | ||||
| 	defer cancel() | ||||
|  | ||||
|   | ||||
							
								
								
									
										20
									
								
								cmd/dump.go
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								cmd/dump.go
									
									
									
									
									
								
							| @@ -181,20 +181,22 @@ func runDump(ctx *cli.Context) error { | ||||
| 		} | ||||
| 		fileName += "." + outType | ||||
| 	} | ||||
| 	setting.LoadFromExisting() | ||||
| 	setting.InitProviderFromExistingFile() | ||||
| 	setting.LoadCommonSettings() | ||||
|  | ||||
| 	// make sure we are logging to the console no matter what the configuration tells us do to | ||||
| 	if _, err := setting.Cfg.Section("log").NewKey("MODE", "console"); err != nil { | ||||
| 	// FIXME: don't use CfgProvider directly | ||||
| 	if _, err := setting.CfgProvider.Section("log").NewKey("MODE", "console"); err != nil { | ||||
| 		fatal("Setting logging mode to console failed: %v", err) | ||||
| 	} | ||||
| 	if _, err := setting.Cfg.Section("log.console").NewKey("STDERR", "true"); err != nil { | ||||
| 	if _, err := setting.CfgProvider.Section("log.console").NewKey("STDERR", "true"); err != nil { | ||||
| 		fatal("Setting console logger to stderr failed: %v", err) | ||||
| 	} | ||||
| 	if !setting.InstallLock { | ||||
| 		log.Error("Is '%s' really the right config path?\n", setting.CustomConf) | ||||
| 		return fmt.Errorf("gitea is not initialized") | ||||
| 	} | ||||
| 	setting.NewServices() // cannot access session settings otherwise | ||||
| 	setting.LoadSettings() // cannot access session settings otherwise | ||||
|  | ||||
| 	stdCtx, cancel := installSignals() | ||||
| 	defer cancel() | ||||
| @@ -322,7 +324,7 @@ func runDump(ctx *cli.Context) error { | ||||
| 		log.Info("Packing data directory...%s", setting.AppDataPath) | ||||
|  | ||||
| 		var excludes []string | ||||
| 		if setting.Cfg.Section("session").Key("PROVIDER").Value() == "file" { | ||||
| 		if setting.SessionConfig.OriginalProvider == "file" { | ||||
| 			var opts session.Options | ||||
| 			if err = json.Unmarshal([]byte(setting.SessionConfig.ProviderConfig), &opts); err != nil { | ||||
| 				return err | ||||
| @@ -339,7 +341,7 @@ func runDump(ctx *cli.Context) error { | ||||
| 		excludes = append(excludes, setting.LFS.Path) | ||||
| 		excludes = append(excludes, setting.Attachment.Path) | ||||
| 		excludes = append(excludes, setting.Packages.Path) | ||||
| 		excludes = append(excludes, setting.LogRootPath) | ||||
| 		excludes = append(excludes, setting.Log.RootPath) | ||||
| 		excludes = append(excludes, absFileName) | ||||
| 		if err := addRecursiveExclude(w, "data", setting.AppDataPath, excludes, verbose); err != nil { | ||||
| 			fatal("Failed to include data directory: %v", err) | ||||
| @@ -378,12 +380,12 @@ func runDump(ctx *cli.Context) error { | ||||
| 	if ctx.IsSet("skip-log") && ctx.Bool("skip-log") { | ||||
| 		log.Info("Skip dumping log files") | ||||
| 	} else { | ||||
| 		isExist, err := util.IsExist(setting.LogRootPath) | ||||
| 		isExist, err := util.IsExist(setting.Log.RootPath) | ||||
| 		if err != nil { | ||||
| 			log.Error("Unable to check if %s exists. Error: %v", setting.LogRootPath, err) | ||||
| 			log.Error("Unable to check if %s exists. Error: %v", setting.Log.RootPath, err) | ||||
| 		} | ||||
| 		if isExist { | ||||
| 			if err := addRecursiveExclude(w, "log", setting.LogRootPath, []string{absFileName}, verbose); err != nil { | ||||
| 			if err := addRecursiveExclude(w, "log", setting.Log.RootPath, []string{absFileName}, verbose); err != nil { | ||||
| 				fatal("Failed to include log: %v", err) | ||||
| 			} | ||||
| 		} | ||||
|   | ||||
| @@ -94,7 +94,7 @@ func runDumpRepository(ctx *cli.Context) error { | ||||
| 	log.Info("AppPath: %s", setting.AppPath) | ||||
| 	log.Info("AppWorkPath: %s", setting.AppWorkPath) | ||||
| 	log.Info("Custom path: %s", setting.CustomPath) | ||||
| 	log.Info("Log path: %s", setting.LogRootPath) | ||||
| 	log.Info("Log path: %s", setting.Log.RootPath) | ||||
| 	log.Info("Configuration file: %s", setting.CustomConf) | ||||
|  | ||||
| 	var ( | ||||
|   | ||||
| @@ -112,7 +112,8 @@ func initEmbeddedExtractor(c *cli.Context) error { | ||||
| 	log.DelNamedLogger(log.DEFAULT) | ||||
|  | ||||
| 	// Read configuration file | ||||
| 	setting.LoadAllowEmpty() | ||||
| 	setting.InitProviderAllowEmpty() | ||||
| 	setting.LoadCommonSettings() | ||||
|  | ||||
| 	pats, err := getPatterns(c.Args()) | ||||
| 	if err != nil { | ||||
|   | ||||
| @@ -17,7 +17,8 @@ func runSendMail(c *cli.Context) error { | ||||
| 	ctx, cancel := installSignals() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	setting.LoadFromExisting() | ||||
| 	setting.InitProviderFromExistingFile() | ||||
| 	setting.LoadCommonSettings() | ||||
|  | ||||
| 	if err := argsSet(c, "title"); err != nil { | ||||
| 		return err | ||||
|   | ||||
| @@ -12,7 +12,7 @@ import ( | ||||
|  | ||||
| func init() { | ||||
| 	setting.SetCustomPathAndConf("", "", "") | ||||
| 	setting.LoadForTest() | ||||
| 	setting.InitProviderAndLoadCommonSettingsForTest() | ||||
| } | ||||
|  | ||||
| func TestMain(m *testing.M) { | ||||
|   | ||||
| @@ -33,7 +33,7 @@ func runMigrate(ctx *cli.Context) error { | ||||
| 	log.Info("AppPath: %s", setting.AppPath) | ||||
| 	log.Info("AppWorkPath: %s", setting.AppWorkPath) | ||||
| 	log.Info("Custom path: %s", setting.CustomPath) | ||||
| 	log.Info("Log path: %s", setting.LogRootPath) | ||||
| 	log.Info("Log path: %s", setting.Log.RootPath) | ||||
| 	log.Info("Configuration file: %s", setting.CustomConf) | ||||
|  | ||||
| 	if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil { | ||||
|   | ||||
| @@ -136,7 +136,7 @@ func runMigrateStorage(ctx *cli.Context) error { | ||||
| 	log.Info("AppPath: %s", setting.AppPath) | ||||
| 	log.Info("AppWorkPath: %s", setting.AppWorkPath) | ||||
| 	log.Info("Custom path: %s", setting.CustomPath) | ||||
| 	log.Info("Log path: %s", setting.LogRootPath) | ||||
| 	log.Info("Log path: %s", setting.Log.RootPath) | ||||
| 	log.Info("Configuration file: %s", setting.CustomConf) | ||||
|  | ||||
| 	if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil { | ||||
|   | ||||
| @@ -54,7 +54,8 @@ func runRestoreRepository(c *cli.Context) error { | ||||
| 	ctx, cancel := installSignals() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	setting.LoadFromExisting() | ||||
| 	setting.InitProviderFromExistingFile() | ||||
| 	setting.LoadCommonSettings() | ||||
| 	var units []string | ||||
| 	if s := c.String("units"); s != "" { | ||||
| 		units = strings.Split(s, ",") | ||||
|   | ||||
| @@ -61,7 +61,8 @@ func setup(logPath string, debug bool) { | ||||
| 	} else { | ||||
| 		_ = log.NewLogger(1000, "console", "console", `{"level":"fatal","stacktracelevel":"NONE","stderr":true}`) | ||||
| 	} | ||||
| 	setting.LoadFromExisting() | ||||
| 	setting.InitProviderFromExistingFile() | ||||
| 	setting.LoadCommonSettings() | ||||
| 	if debug { | ||||
| 		setting.RunMode = "dev" | ||||
| 	} | ||||
|   | ||||
| @@ -158,7 +158,8 @@ func runWeb(ctx *cli.Context) error { | ||||
|  | ||||
| 	log.Info("Global init") | ||||
| 	// Perform global initialization | ||||
| 	setting.LoadFromExisting() | ||||
| 	setting.InitProviderFromExistingFile() | ||||
| 	setting.LoadCommonSettings() | ||||
| 	routers.GlobalInitInstalled(graceful.GetManager().HammerContext()) | ||||
|  | ||||
| 	// We check that AppDataPath exists here (it should have been created during installation) | ||||
|   | ||||
| @@ -49,7 +49,8 @@ func runPR() { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	setting.SetCustomPathAndConf("", "", "") | ||||
| 	setting.LoadAllowEmpty() | ||||
| 	setting.InitProviderAllowEmpty() | ||||
| 	setting.LoadCommonSettings() | ||||
|  | ||||
| 	setting.RepoRootPath, err = os.MkdirTemp(os.TempDir(), "repos") | ||||
| 	if err != nil { | ||||
| @@ -82,7 +83,7 @@ func runPR() { | ||||
| 		setting.Database.Path = ":memory:" | ||||
| 		setting.Database.Timeout = 500 | ||||
| 	*/ | ||||
| 	dbCfg := setting.Cfg.Section("database") | ||||
| 	dbCfg := setting.CfgProvider.Section("database") | ||||
| 	dbCfg.NewKey("DB_TYPE", "sqlite3") | ||||
| 	dbCfg.NewKey("PATH", ":memory:") | ||||
|  | ||||
|   | ||||
| @@ -13,7 +13,7 @@ import ( | ||||
|  | ||||
| func init() { | ||||
| 	setting.SetCustomPathAndConf("", "", "") | ||||
| 	setting.LoadForTest() | ||||
| 	setting.InitProviderAndLoadCommonSettingsForTest() | ||||
| } | ||||
|  | ||||
| func TestMain(m *testing.M) { | ||||
|   | ||||
| @@ -13,7 +13,7 @@ import ( | ||||
|  | ||||
| func init() { | ||||
| 	setting.SetCustomPathAndConf("", "", "") | ||||
| 	setting.LoadForTest() | ||||
| 	setting.InitProviderAndLoadCommonSettingsForTest() | ||||
| } | ||||
|  | ||||
| func TestMain(m *testing.M) { | ||||
|   | ||||
| @@ -20,7 +20,7 @@ import ( | ||||
|  | ||||
| func init() { | ||||
| 	setting.SetCustomPathAndConf("", "", "") | ||||
| 	setting.LoadForTest() | ||||
| 	setting.InitProviderAndLoadCommonSettingsForTest() | ||||
| } | ||||
|  | ||||
| func TestFixturesAreConsistent(t *testing.T) { | ||||
|   | ||||
| @@ -20,7 +20,7 @@ import ( | ||||
|  | ||||
| func init() { | ||||
| 	setting.SetCustomPathAndConf("", "", "") | ||||
| 	setting.LoadForTest() | ||||
| 	setting.InitProviderAndLoadCommonSettingsForTest() | ||||
| } | ||||
|  | ||||
| // TestFixturesAreConsistent assert that test fixtures are consistent | ||||
|   | ||||
| @@ -149,13 +149,13 @@ func MainTest(m *testing.M) { | ||||
| 	setting.AppDataPath = tmpDataPath | ||||
|  | ||||
| 	setting.SetCustomPathAndConf("", "", "") | ||||
| 	setting.LoadForTest() | ||||
| 	setting.InitProviderAndLoadCommonSettingsForTest() | ||||
| 	if err = git.InitFull(context.Background()); err != nil { | ||||
| 		fmt.Printf("Unable to InitFull: %v\n", err) | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 	setting.InitDBConfig() | ||||
| 	setting.NewLogServices(true) | ||||
| 	setting.LoadDBSetting() | ||||
| 	setting.InitLogs(true) | ||||
|  | ||||
| 	exitStatus := m.Run() | ||||
|  | ||||
|   | ||||
| @@ -27,7 +27,7 @@ var signedUserNameStringPointerKey interface{} = "signedUserNameStringPointerKey | ||||
| // AccessLogger returns a middleware to log access logger | ||||
| func AccessLogger() func(http.Handler) http.Handler { | ||||
| 	logger := log.GetLogger("access") | ||||
| 	logTemplate, _ := template.New("log").Parse(setting.AccessLogTemplate) | ||||
| 	logTemplate, _ := template.New("log").Parse(setting.Log.AccessLogTemplate) | ||||
| 	return func(next http.Handler) http.Handler { | ||||
| 		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||||
| 			start := time.Now() | ||||
|   | ||||
| @@ -44,10 +44,10 @@ func (w *wrappedLevelLogger) Log(skip int, level log.Level, format string, v ... | ||||
| } | ||||
|  | ||||
| func initDBDisableConsole(ctx context.Context, disableConsole bool) error { | ||||
| 	setting.LoadFromExisting() | ||||
| 	setting.InitDBConfig() | ||||
|  | ||||
| 	setting.NewXORMLogService(disableConsole) | ||||
| 	setting.InitProviderFromExistingFile() | ||||
| 	setting.LoadCommonSettings() | ||||
| 	setting.LoadDBSetting() | ||||
| 	setting.InitSQLLog(disableConsole) | ||||
| 	if err := db.InitEngine(ctx); err != nil { | ||||
| 		return fmt.Errorf("db.InitEngine: %w", err) | ||||
| 	} | ||||
| @@ -71,7 +71,7 @@ func RunChecks(ctx context.Context, logger log.Logger, autofix bool, checks []*C | ||||
| 	for i, check := range checks { | ||||
| 		if !dbIsInit && !check.SkipDatabaseInitialization { | ||||
| 			// Only open database after the most basic configuration check | ||||
| 			setting.EnableXORMLog = false | ||||
| 			setting.Log.EnableXORMLog = false | ||||
| 			if err := initDBDisableConsole(ctx, true); err != nil { | ||||
| 				logger.Error("Error whilst initializing the database: %v", err) | ||||
| 				logger.Error("Check if you are using the right config file. You can use a --config directive to specify one.") | ||||
|   | ||||
| @@ -67,7 +67,8 @@ func checkConfigurationFiles(ctx context.Context, logger log.Logger, autofix boo | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	setting.LoadFromExisting() | ||||
| 	setting.InitProviderFromExistingFile() | ||||
| 	setting.LoadCommonSettings() | ||||
|  | ||||
| 	configurationFiles := []configurationFile{ | ||||
| 		{"Configuration File Path", setting.CustomConf, false, true, false}, | ||||
| @@ -75,7 +76,7 @@ func checkConfigurationFiles(ctx context.Context, logger log.Logger, autofix boo | ||||
| 		{"Data Root Path", setting.AppDataPath, true, true, true}, | ||||
| 		{"Custom File Root Path", setting.CustomPath, true, false, false}, | ||||
| 		{"Work directory", setting.AppWorkPath, true, true, false}, | ||||
| 		{"Log Root Path", setting.LogRootPath, true, true, true}, | ||||
| 		{"Log Root Path", setting.Log.RootPath, true, true, true}, | ||||
| 	} | ||||
|  | ||||
| 	if options.IsDynamic() { | ||||
|   | ||||
| @@ -41,12 +41,8 @@ var ( | ||||
| // NewContext loads custom highlight map from local config | ||||
| func NewContext() { | ||||
| 	once.Do(func() { | ||||
| 		if setting.Cfg != nil { | ||||
| 			keys := setting.Cfg.Section("highlight.mapping").Keys() | ||||
| 			for i := range keys { | ||||
| 				highlightMapping[keys[i].Name()] = keys[i].Value() | ||||
| 			} | ||||
| 		} | ||||
| 		highlightMapping = setting.GetHighlightMapping() | ||||
|  | ||||
| 		// The size 512 is simply a conservative rule of thumb | ||||
| 		c, err := lru.New2Q(512) | ||||
| 		if err != nil { | ||||
|   | ||||
| @@ -27,11 +27,11 @@ func TestMain(m *testing.M) { | ||||
|  | ||||
| func TestBleveSearchIssues(t *testing.T) { | ||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
| 	setting.Cfg = ini.Empty() | ||||
| 	setting.CfgProvider = ini.Empty() | ||||
|  | ||||
| 	tmpIndexerDir := t.TempDir() | ||||
|  | ||||
| 	setting.Cfg.Section("queue.issue_indexer").Key("DATADIR").MustString(path.Join(tmpIndexerDir, "issues.queue")) | ||||
| 	setting.CfgProvider.Section("queue.issue_indexer").Key("DATADIR").MustString(path.Join(tmpIndexerDir, "issues.queue")) | ||||
|  | ||||
| 	oldIssuePath := setting.Indexer.IssuePath | ||||
| 	setting.Indexer.IssuePath = path.Join(tmpIndexerDir, "issues.queue") | ||||
| @@ -40,7 +40,7 @@ func TestBleveSearchIssues(t *testing.T) { | ||||
| 	}() | ||||
|  | ||||
| 	setting.Indexer.IssueType = "bleve" | ||||
| 	setting.NewQueueService() | ||||
| 	setting.LoadQueueSettings() | ||||
| 	InitIssueIndexer(true) | ||||
| 	defer func() { | ||||
| 		indexer := holder.get() | ||||
|   | ||||
| @@ -29,9 +29,9 @@ func TestMain(m *testing.M) { | ||||
|  | ||||
| func TestRepoStatsIndex(t *testing.T) { | ||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
| 	setting.Cfg = ini.Empty() | ||||
| 	setting.CfgProvider = ini.Empty() | ||||
|  | ||||
| 	setting.NewQueueService() | ||||
| 	setting.LoadQueueSettings() | ||||
|  | ||||
| 	err := Init() | ||||
| 	assert.NoError(t, err) | ||||
|   | ||||
| @@ -28,7 +28,8 @@ var localMetas = map[string]string{ | ||||
| } | ||||
|  | ||||
| func TestMain(m *testing.M) { | ||||
| 	setting.LoadAllowEmpty() | ||||
| 	setting.InitProviderAllowEmpty() | ||||
| 	setting.LoadCommonSettings() | ||||
| 	if err := git.InitSimple(context.Background()); err != nil { | ||||
| 		log.Fatal("git init failed, err: %v", err) | ||||
| 	} | ||||
|   | ||||
| @@ -33,7 +33,8 @@ var localMetas = map[string]string{ | ||||
| } | ||||
|  | ||||
| func TestMain(m *testing.M) { | ||||
| 	setting.LoadAllowEmpty() | ||||
| 	setting.InitProviderAllowEmpty() | ||||
| 	setting.LoadCommonSettings() | ||||
| 	if err := git.InitSimple(context.Background()); err != nil { | ||||
| 		log.Fatal("git init failed, err: %v", err) | ||||
| 	} | ||||
|   | ||||
| @@ -19,11 +19,11 @@ var ( | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| func newActions() { | ||||
| 	sec := Cfg.Section("actions") | ||||
| func loadActionsFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("actions") | ||||
| 	if err := sec.MapTo(&Actions); err != nil { | ||||
| 		log.Fatal("Failed to map Actions settings: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	Actions.Storage = getStorage("actions_log", "", nil) | ||||
| 	Actions.Storage = getStorage(rootCfg, "actions_log", "", nil) | ||||
| } | ||||
|   | ||||
							
								
								
									
										16
									
								
								modules/setting/admin.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								modules/setting/admin.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| // Copyright 2023 The Gitea Authors. All rights reserved. | ||||
| // SPDX-License-Identifier: MIT | ||||
|  | ||||
| package setting | ||||
|  | ||||
| // Admin settings | ||||
| var Admin struct { | ||||
| 	DisableRegularOrgCreation bool | ||||
| 	DefaultEmailNotification  string | ||||
| } | ||||
|  | ||||
| func loadAdminFrom(rootCfg ConfigProvider) { | ||||
| 	mustMapSetting(rootCfg, "admin", &Admin) | ||||
| 	sec := rootCfg.Section("admin") | ||||
| 	Admin.DefaultEmailNotification = sec.Key("DEFAULT_EMAIL_NOTIFICATIONS").MustString("enabled") | ||||
| } | ||||
							
								
								
									
										40
									
								
								modules/setting/api.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								modules/setting/api.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| // Copyright 2023 The Gitea Authors. All rights reserved. | ||||
| // SPDX-License-Identifier: MIT | ||||
|  | ||||
| package setting | ||||
|  | ||||
| import ( | ||||
| 	"net/url" | ||||
| 	"path" | ||||
|  | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| ) | ||||
|  | ||||
| // API settings | ||||
| var API = struct { | ||||
| 	EnableSwagger          bool | ||||
| 	SwaggerURL             string | ||||
| 	MaxResponseItems       int | ||||
| 	DefaultPagingNum       int | ||||
| 	DefaultGitTreesPerPage int | ||||
| 	DefaultMaxBlobSize     int64 | ||||
| }{ | ||||
| 	EnableSwagger:          true, | ||||
| 	SwaggerURL:             "", | ||||
| 	MaxResponseItems:       50, | ||||
| 	DefaultPagingNum:       30, | ||||
| 	DefaultGitTreesPerPage: 1000, | ||||
| 	DefaultMaxBlobSize:     10485760, | ||||
| } | ||||
|  | ||||
| func loadAPIFrom(rootCfg ConfigProvider) { | ||||
| 	mustMapSetting(rootCfg, "api", &API) | ||||
|  | ||||
| 	defaultAppURL := string(Protocol) + "://" + Domain + ":" + HTTPPort | ||||
| 	u, err := url.Parse(rootCfg.Section("server").Key("ROOT_URL").MustString(defaultAppURL)) | ||||
| 	if err != nil { | ||||
| 		log.Fatal("Invalid ROOT_URL '%s': %s", AppURL, err) | ||||
| 	} | ||||
| 	u.Path = path.Join(u.Path, "api", "swagger") | ||||
| 	API.SwaggerURL = u.String() | ||||
| } | ||||
| @@ -20,11 +20,11 @@ var Attachment = struct { | ||||
| 	Enabled:      true, | ||||
| } | ||||
|  | ||||
| func newAttachmentService() { | ||||
| 	sec := Cfg.Section("attachment") | ||||
| func loadAttachmentFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("attachment") | ||||
| 	storageType := sec.Key("STORAGE_TYPE").MustString("") | ||||
|  | ||||
| 	Attachment.Storage = getStorage("attachments", storageType, sec) | ||||
| 	Attachment.Storage = getStorage(rootCfg, "attachments", storageType, sec) | ||||
|  | ||||
| 	Attachment.AllowedTypes = sec.Key("ALLOWED_TYPES").MustString(".csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip") | ||||
| 	Attachment.MaxSize = sec.Key("MAX_SIZE").MustInt64(4) | ||||
|   | ||||
| @@ -49,8 +49,8 @@ var CacheService = struct { | ||||
| // MemcacheMaxTTL represents the maximum memcache TTL | ||||
| const MemcacheMaxTTL = 30 * 24 * time.Hour | ||||
|  | ||||
| func newCacheService() { | ||||
| 	sec := Cfg.Section("cache") | ||||
| func loadCacheFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("cache") | ||||
| 	if err := sec.MapTo(&CacheService); err != nil { | ||||
| 		log.Fatal("Failed to map Cache settings: %v", err) | ||||
| 	} | ||||
| @@ -79,7 +79,7 @@ func newCacheService() { | ||||
| 		Service.EnableCaptcha = false | ||||
| 	} | ||||
|  | ||||
| 	sec = Cfg.Section("cache.last_commit") | ||||
| 	sec = rootCfg.Section("cache.last_commit") | ||||
| 	if !CacheService.Enabled { | ||||
| 		CacheService.LastCommit.Enabled = false | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										22
									
								
								modules/setting/camo.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								modules/setting/camo.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| // Copyright 2023 The Gitea Authors. All rights reserved. | ||||
| // SPDX-License-Identifier: MIT | ||||
|  | ||||
| package setting | ||||
|  | ||||
| import "code.gitea.io/gitea/modules/log" | ||||
|  | ||||
| var Camo = struct { | ||||
| 	Enabled   bool | ||||
| 	ServerURL string `ini:"SERVER_URL"` | ||||
| 	HMACKey   string `ini:"HMAC_KEY"` | ||||
| 	Allways   bool | ||||
| }{} | ||||
|  | ||||
| func loadCamoFrom(rootCfg ConfigProvider) { | ||||
| 	mustMapSetting(rootCfg, "camo", &Camo) | ||||
| 	if Camo.Enabled { | ||||
| 		if Camo.ServerURL == "" || Camo.HMACKey == "" { | ||||
| 			log.Fatal(`Camo settings require "SERVER_URL" and HMAC_KEY`) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										39
									
								
								modules/setting/config_provider.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								modules/setting/config_provider.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| // Copyright 2023 The Gitea Authors. All rights reserved. | ||||
| // SPDX-License-Identifier: MIT | ||||
|  | ||||
| package setting | ||||
|  | ||||
| import ( | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
|  | ||||
| 	ini "gopkg.in/ini.v1" | ||||
| ) | ||||
|  | ||||
| // ConfigProvider represents a config provider | ||||
| type ConfigProvider interface { | ||||
| 	Section(section string) *ini.Section | ||||
| 	NewSection(name string) (*ini.Section, error) | ||||
| 	GetSection(name string) (*ini.Section, error) | ||||
| } | ||||
|  | ||||
| // a file is an implementation ConfigProvider and other implementations are possible, i.e. from docker, k8s, … | ||||
| var _ ConfigProvider = &ini.File{} | ||||
|  | ||||
| func mustMapSetting(rootCfg ConfigProvider, sectionName string, setting interface{}) { | ||||
| 	if err := rootCfg.Section(sectionName).MapTo(setting); err != nil { | ||||
| 		log.Fatal("Failed to map %s settings: %v", sectionName, err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func deprecatedSetting(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey string) { | ||||
| 	if rootCfg.Section(oldSection).HasKey(oldKey) { | ||||
| 		log.Error("Deprecated fallback `[%s]` `%s` present. Use `[%s]` `%s` instead. This fallback will be removed in v1.19.0", oldSection, oldKey, newSection, newKey) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini | ||||
| func deprecatedSettingDB(rootCfg ConfigProvider, oldSection, oldKey string) { | ||||
| 	if rootCfg.Section(oldSection).HasKey(oldKey) { | ||||
| 		log.Error("Deprecated `[%s]` `%s` present which has been copied to database table sys_setting", oldSection, oldKey) | ||||
| 	} | ||||
| } | ||||
| @@ -27,12 +27,8 @@ var CORSConfig = struct { | ||||
| 	XFrameOptions: "SAMEORIGIN", | ||||
| } | ||||
|  | ||||
| func newCORSService() { | ||||
| 	sec := Cfg.Section("cors") | ||||
| 	if err := sec.MapTo(&CORSConfig); err != nil { | ||||
| 		log.Fatal("Failed to map cors settings: %v", err) | ||||
| 	} | ||||
|  | ||||
| func loadCorsFrom(rootCfg ConfigProvider) { | ||||
| 	mustMapSetting(rootCfg, "cors", &CORSConfig) | ||||
| 	if CORSConfig.Enabled { | ||||
| 		log.Info("CORS Service Enabled") | ||||
| 	} | ||||
|   | ||||
| @@ -7,7 +7,11 @@ import "reflect" | ||||
|  | ||||
| // GetCronSettings maps the cron subsection to the provided config | ||||
| func GetCronSettings(name string, config interface{}) (interface{}, error) { | ||||
| 	if err := Cfg.Section("cron." + name).MapTo(config); err != nil { | ||||
| 	return getCronSettings(CfgProvider, name, config) | ||||
| } | ||||
|  | ||||
| func getCronSettings(rootCfg ConfigProvider, name string, config interface{}) (interface{}, error) { | ||||
| 	if err := rootCfg.Section("cron." + name).MapTo(config); err != nil { | ||||
| 		return config, err | ||||
| 	} | ||||
|  | ||||
| @@ -18,7 +22,7 @@ func GetCronSettings(name string, config interface{}) (interface{}, error) { | ||||
| 		field := val.Field(i) | ||||
| 		tpField := typ.Field(i) | ||||
| 		if tpField.Type.Kind() == reflect.Struct && tpField.Anonymous { | ||||
| 			if err := Cfg.Section("cron." + name).MapTo(field.Addr().Interface()); err != nil { | ||||
| 			if err := rootCfg.Section("cron." + name).MapTo(field.Addr().Interface()); err != nil { | ||||
| 				return config, err | ||||
| 			} | ||||
| 		} | ||||
|   | ||||
| @@ -10,7 +10,7 @@ import ( | ||||
| 	ini "gopkg.in/ini.v1" | ||||
| ) | ||||
|  | ||||
| func Test_GetCronSettings(t *testing.T) { | ||||
| func Test_getCronSettings(t *testing.T) { | ||||
| 	type BaseStruct struct { | ||||
| 		Base   bool | ||||
| 		Second string | ||||
| @@ -27,7 +27,8 @@ Base = true | ||||
| Second = white rabbit | ||||
| Extend = true | ||||
| ` | ||||
| 	Cfg, _ = ini.Load([]byte(iniStr)) | ||||
| 	cfg, err := ini.Load([]byte(iniStr)) | ||||
| 	assert.NoError(t, err) | ||||
|  | ||||
| 	extended := &Extended{ | ||||
| 		BaseStruct: BaseStruct{ | ||||
| @@ -35,8 +36,7 @@ Extend = true | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	_, err := GetCronSettings("test", extended) | ||||
|  | ||||
| 	_, err = getCronSettings(cfg, "test", extended) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.True(t, extended.Base) | ||||
| 	assert.EqualValues(t, extended.Second, "white rabbit") | ||||
|   | ||||
| @@ -56,9 +56,9 @@ var ( | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| // InitDBConfig loads the database settings | ||||
| func InitDBConfig() { | ||||
| 	sec := Cfg.Section("database") | ||||
| // LoadDBSetting loads the database settings | ||||
| func LoadDBSetting() { | ||||
| 	sec := CfgProvider.Section("database") | ||||
| 	Database.Type = sec.Key("DB_TYPE").String() | ||||
| 	defaultCharset := "utf8" | ||||
| 	Database.UseMySQL = false | ||||
|   | ||||
| @@ -1,39 +0,0 @@ | ||||
| // Copyright 2021 The Gitea Authors. All rights reserved. | ||||
| // SPDX-License-Identifier: MIT | ||||
|  | ||||
| package setting | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| ) | ||||
|  | ||||
| // PrepareAppDataPath creates app data directory if necessary | ||||
| func PrepareAppDataPath() error { | ||||
| 	// FIXME: There are too many calls to MkdirAll in old code. It is incorrect. | ||||
| 	// For example, if someDir=/mnt/vol1/gitea-home/data, if the mount point /mnt/vol1 is not mounted when Gitea runs, | ||||
| 	// then gitea will make new empty directories in /mnt/vol1, all are stored in the root filesystem. | ||||
| 	// The correct behavior should be: creating parent directories is end users' duty. We only create sub-directories in existing parent directories. | ||||
| 	// For quickstart, the parent directories should be created automatically for first startup (eg: a flag or a check of INSTALL_LOCK). | ||||
| 	// Now we can take the first step to do correctly (using Mkdir) in other packages, and prepare the AppDataPath here, then make a refactor in future. | ||||
|  | ||||
| 	st, err := os.Stat(AppDataPath) | ||||
|  | ||||
| 	if os.IsNotExist(err) { | ||||
| 		err = os.MkdirAll(AppDataPath, os.ModePerm) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("unable to create the APP_DATA_PATH directory: %q, Error: %w", AppDataPath, err) | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to use APP_DATA_PATH %q. Error: %w", AppDataPath, err) | ||||
| 	} | ||||
|  | ||||
| 	if !st.IsDir() /* also works for symlink */ { | ||||
| 		return fmt.Errorf("the APP_DATA_PATH %q is not a directory (or symlink to a directory) and can't be used", AppDataPath) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
| @@ -33,8 +33,8 @@ var ( | ||||
| // HttpsigAlgs is a constant slice of httpsig algorithm objects | ||||
| var HttpsigAlgs []httpsig.Algorithm | ||||
|  | ||||
| func newFederationService() { | ||||
| 	if err := Cfg.Section("federation").MapTo(&Federation); err != nil { | ||||
| func loadFederationFrom(rootCfg ConfigProvider) { | ||||
| 	if err := rootCfg.Section("federation").MapTo(&Federation); err != nil { | ||||
| 		log.Fatal("Failed to map Federation settings: %v", err) | ||||
| 	} else if !httpsig.IsSupportedDigestAlgorithm(Federation.DigestAlgorithm) { | ||||
| 		log.Fatal("unsupported digest algorithm: %s", Federation.DigestAlgorithm) | ||||
|   | ||||
| @@ -67,9 +67,8 @@ var Git = struct { | ||||
| 	}, | ||||
| } | ||||
|  | ||||
| func newGit() { | ||||
| 	sec := Cfg.Section("git") | ||||
|  | ||||
| func loadGitFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("git") | ||||
| 	if err := sec.MapTo(&Git); err != nil { | ||||
| 		log.Fatal("Failed to map Git settings: %v", err) | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										17
									
								
								modules/setting/highlight.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								modules/setting/highlight.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| // Copyright 2023 The Gitea Authors. All rights reserved. | ||||
| // SPDX-License-Identifier: MIT | ||||
|  | ||||
| package setting | ||||
|  | ||||
| func GetHighlightMapping() map[string]string { | ||||
| 	highlightMapping := map[string]string{} | ||||
| 	if CfgProvider == nil { | ||||
| 		return highlightMapping | ||||
| 	} | ||||
|  | ||||
| 	keys := CfgProvider.Section("highlight.mapping").Keys() | ||||
| 	for _, key := range keys { | ||||
| 		highlightMapping[key.Name()] = key.Value() | ||||
| 	} | ||||
| 	return highlightMapping | ||||
| } | ||||
| @@ -47,3 +47,20 @@ func defaultI18nNames() (res []string) { | ||||
| 	} | ||||
| 	return res | ||||
| } | ||||
|  | ||||
| var ( | ||||
| 	// I18n settings | ||||
| 	Langs []string | ||||
| 	Names []string | ||||
| ) | ||||
|  | ||||
| func loadI18nFrom(rootCfg ConfigProvider) { | ||||
| 	Langs = rootCfg.Section("i18n").Key("LANGS").Strings(",") | ||||
| 	if len(Langs) == 0 { | ||||
| 		Langs = defaultI18nLangs() | ||||
| 	} | ||||
| 	Names = rootCfg.Section("i18n").Key("NAMES").Strings(",") | ||||
| 	if len(Names) == 0 { | ||||
| 		Names = defaultI18nNames() | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -31,10 +31,8 @@ var IncomingEmail = struct { | ||||
| 	MaximumMessageSize:   10485760, | ||||
| } | ||||
|  | ||||
| func newIncomingEmail() { | ||||
| 	if err := Cfg.Section("email.incoming").MapTo(&IncomingEmail); err != nil { | ||||
| 		log.Fatal("Unable to map [email.incoming] section on to IncomingEmail. Error: %v", err) | ||||
| 	} | ||||
| func loadIncomingEmailFrom(rootCfg ConfigProvider) { | ||||
| 	mustMapSetting(rootCfg, "email.incoming", &IncomingEmail) | ||||
|  | ||||
| 	if !IncomingEmail.Enabled { | ||||
| 		return | ||||
|   | ||||
| @@ -45,8 +45,8 @@ var Indexer = struct { | ||||
| 	ExcludeVendored:    true, | ||||
| } | ||||
|  | ||||
| func newIndexerService() { | ||||
| 	sec := Cfg.Section("indexer") | ||||
| func loadIndexerFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("indexer") | ||||
| 	Indexer.IssueType = sec.Key("ISSUE_INDEXER_TYPE").MustString("bleve") | ||||
| 	Indexer.IssuePath = filepath.ToSlash(sec.Key("ISSUE_INDEXER_PATH").MustString(filepath.ToSlash(filepath.Join(AppDataPath, "indexers/issues.bleve")))) | ||||
| 	if !filepath.IsAbs(Indexer.IssuePath) { | ||||
| @@ -57,11 +57,11 @@ func newIndexerService() { | ||||
|  | ||||
| 	// The following settings are deprecated and can be overridden by settings in [queue] or [queue.issue_indexer] | ||||
| 	// FIXME: DEPRECATED to be removed in v1.18.0 | ||||
| 	deprecatedSetting("indexer", "ISSUE_INDEXER_QUEUE_TYPE", "queue.issue_indexer", "TYPE") | ||||
| 	deprecatedSetting("indexer", "ISSUE_INDEXER_QUEUE_DIR", "queue.issue_indexer", "DATADIR") | ||||
| 	deprecatedSetting("indexer", "ISSUE_INDEXER_QUEUE_CONN_STR", "queue.issue_indexer", "CONN_STR") | ||||
| 	deprecatedSetting("indexer", "ISSUE_INDEXER_QUEUE_BATCH_NUMBER", "queue.issue_indexer", "BATCH_LENGTH") | ||||
| 	deprecatedSetting("indexer", "UPDATE_BUFFER_LEN", "queue.issue_indexer", "LENGTH") | ||||
| 	deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_TYPE", "queue.issue_indexer", "TYPE") | ||||
| 	deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_DIR", "queue.issue_indexer", "DATADIR") | ||||
| 	deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_CONN_STR", "queue.issue_indexer", "CONN_STR") | ||||
| 	deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_BATCH_NUMBER", "queue.issue_indexer", "BATCH_LENGTH") | ||||
| 	deprecatedSetting(rootCfg, "indexer", "UPDATE_BUFFER_LEN", "queue.issue_indexer", "LENGTH") | ||||
|  | ||||
| 	Indexer.RepoIndexerEnabled = sec.Key("REPO_INDEXER_ENABLED").MustBool(false) | ||||
| 	Indexer.RepoType = sec.Key("REPO_INDEXER_TYPE").MustString("bleve") | ||||
|   | ||||
| @@ -25,22 +25,22 @@ var LFS = struct { | ||||
| 	Storage | ||||
| }{} | ||||
|  | ||||
| func newLFSService() { | ||||
| 	sec := Cfg.Section("server") | ||||
| func loadLFSFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("server") | ||||
| 	if err := sec.MapTo(&LFS); err != nil { | ||||
| 		log.Fatal("Failed to map LFS settings: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	lfsSec := Cfg.Section("lfs") | ||||
| 	lfsSec := rootCfg.Section("lfs") | ||||
| 	storageType := lfsSec.Key("STORAGE_TYPE").MustString("") | ||||
|  | ||||
| 	// Specifically default PATH to LFS_CONTENT_PATH | ||||
| 	// FIXME: DEPRECATED to be removed in v1.18.0 | ||||
| 	deprecatedSetting("server", "LFS_CONTENT_PATH", "lfs", "PATH") | ||||
| 	deprecatedSetting(rootCfg, "server", "LFS_CONTENT_PATH", "lfs", "PATH") | ||||
| 	lfsSec.Key("PATH").MustString( | ||||
| 		sec.Key("LFS_CONTENT_PATH").String()) | ||||
|  | ||||
| 	LFS.Storage = getStorage("lfs", storageType, lfsSec) | ||||
| 	LFS.Storage = getStorage(rootCfg, "lfs", storageType, lfsSec) | ||||
|  | ||||
| 	// Rest of LFS service settings | ||||
| 	if LFS.LocksPagingNum == 0 { | ||||
|   | ||||
| @@ -25,6 +25,21 @@ var ( | ||||
| 	logDescriptions = make(map[string]*LogDescription) | ||||
| ) | ||||
|  | ||||
| // Log settings | ||||
| var Log struct { | ||||
| 	Level              log.Level | ||||
| 	StacktraceLogLevel string | ||||
| 	RootPath           string | ||||
| 	EnableSSHLog       bool | ||||
| 	EnableXORMLog      bool | ||||
|  | ||||
| 	DisableRouterLog bool | ||||
|  | ||||
| 	EnableAccessLog   bool | ||||
| 	AccessLogTemplate string | ||||
| 	BufferLength      int64 | ||||
| } | ||||
|  | ||||
| // GetLogDescriptions returns a race safe set of descriptions | ||||
| func GetLogDescriptions() map[string]*LogDescription { | ||||
| 	descriptionLock.RLock() | ||||
| @@ -94,9 +109,9 @@ type defaultLogOptions struct { | ||||
|  | ||||
| func newDefaultLogOptions() defaultLogOptions { | ||||
| 	return defaultLogOptions{ | ||||
| 		levelName:      LogLevel.String(), | ||||
| 		levelName:      Log.Level.String(), | ||||
| 		flags:          "stdflags", | ||||
| 		filename:       filepath.Join(LogRootPath, "gitea.log"), | ||||
| 		filename:       filepath.Join(Log.RootPath, "gitea.log"), | ||||
| 		bufferLength:   10000, | ||||
| 		disableConsole: false, | ||||
| 	} | ||||
| @@ -125,10 +140,33 @@ func getStacktraceLogLevel(section *ini.Section, key, defaultValue string) strin | ||||
| 	return log.FromString(value).String() | ||||
| } | ||||
|  | ||||
| func loadLogFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("log") | ||||
| 	Log.Level = getLogLevel(sec, "LEVEL", log.INFO) | ||||
| 	Log.StacktraceLogLevel = getStacktraceLogLevel(sec, "STACKTRACE_LEVEL", "None") | ||||
| 	Log.RootPath = sec.Key("ROOT_PATH").MustString(path.Join(AppWorkPath, "log")) | ||||
| 	forcePathSeparator(Log.RootPath) | ||||
| 	Log.BufferLength = sec.Key("BUFFER_LEN").MustInt64(10000) | ||||
|  | ||||
| 	Log.EnableSSHLog = sec.Key("ENABLE_SSH_LOG").MustBool(false) | ||||
| 	Log.EnableAccessLog = sec.Key("ENABLE_ACCESS_LOG").MustBool(false) | ||||
| 	Log.AccessLogTemplate = sec.Key("ACCESS_LOG_TEMPLATE").MustString( | ||||
| 		`{{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"`, | ||||
| 	) | ||||
| 	// the `MustString` updates the default value, and `log.ACCESS` is used by `generateNamedLogger("access")` later | ||||
| 	_ = rootCfg.Section("log").Key("ACCESS").MustString("file") | ||||
|  | ||||
| 	sec.Key("ROUTER").MustString("console") | ||||
| 	// Allow [log]  DISABLE_ROUTER_LOG to override [server] DISABLE_ROUTER_LOG | ||||
| 	Log.DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool(Log.DisableRouterLog) | ||||
|  | ||||
| 	Log.EnableXORMLog = rootCfg.Section("log").Key("ENABLE_XORM_LOG").MustBool(true) | ||||
| } | ||||
|  | ||||
| func generateLogConfig(sec *ini.Section, name string, defaults defaultLogOptions) (mode, jsonConfig, levelName string) { | ||||
| 	level := getLogLevel(sec, "LEVEL", LogLevel) | ||||
| 	level := getLogLevel(sec, "LEVEL", Log.Level) | ||||
| 	levelName = level.String() | ||||
| 	stacktraceLevelName := getStacktraceLogLevel(sec, "STACKTRACE_LEVEL", StacktraceLogLevel) | ||||
| 	stacktraceLevelName := getStacktraceLogLevel(sec, "STACKTRACE_LEVEL", Log.StacktraceLogLevel) | ||||
| 	stacktraceLevel := log.FromString(stacktraceLevelName) | ||||
| 	mode = name | ||||
| 	keys := sec.Keys() | ||||
| @@ -144,7 +182,7 @@ func generateLogConfig(sec *ini.Section, name string, defaults defaultLogOptions | ||||
| 			logPath = key.MustString(defaults.filename) | ||||
| 			forcePathSeparator(logPath) | ||||
| 			if !filepath.IsAbs(logPath) { | ||||
| 				logPath = path.Join(LogRootPath, logPath) | ||||
| 				logPath = path.Join(Log.RootPath, logPath) | ||||
| 			} | ||||
| 		case "FLAGS": | ||||
| 			flags = log.FlagsFromString(key.MustString(defaults.flags)) | ||||
| @@ -213,12 +251,12 @@ func generateLogConfig(sec *ini.Section, name string, defaults defaultLogOptions | ||||
| 	return mode, jsonConfig, levelName | ||||
| } | ||||
|  | ||||
| func generateNamedLogger(key string, options defaultLogOptions) *LogDescription { | ||||
| func generateNamedLogger(rootCfg ConfigProvider, key string, options defaultLogOptions) *LogDescription { | ||||
| 	description := LogDescription{ | ||||
| 		Name: key, | ||||
| 	} | ||||
|  | ||||
| 	sections := strings.Split(Cfg.Section("log").Key(strings.ToUpper(key)).MustString(""), ",") | ||||
| 	sections := strings.Split(rootCfg.Section("log").Key(strings.ToUpper(key)).MustString(""), ",") | ||||
|  | ||||
| 	for i := 0; i < len(sections); i++ { | ||||
| 		sections[i] = strings.TrimSpace(sections[i]) | ||||
| @@ -228,9 +266,9 @@ func generateNamedLogger(key string, options defaultLogOptions) *LogDescription | ||||
| 		if len(name) == 0 || (name == "console" && options.disableConsole) { | ||||
| 			continue | ||||
| 		} | ||||
| 		sec, err := Cfg.GetSection("log." + name + "." + key) | ||||
| 		sec, err := rootCfg.GetSection("log." + name + "." + key) | ||||
| 		if err != nil { | ||||
| 			sec, _ = Cfg.NewSection("log." + name + "." + key) | ||||
| 			sec, _ = rootCfg.NewSection("log." + name + "." + key) | ||||
| 		} | ||||
|  | ||||
| 		provider, config, levelName := generateLogConfig(sec, name, options) | ||||
| @@ -253,46 +291,17 @@ func generateNamedLogger(key string, options defaultLogOptions) *LogDescription | ||||
| 	return &description | ||||
| } | ||||
|  | ||||
| func newAccessLogService() { | ||||
| 	EnableAccessLog = Cfg.Section("log").Key("ENABLE_ACCESS_LOG").MustBool(false) | ||||
| 	AccessLogTemplate = Cfg.Section("log").Key("ACCESS_LOG_TEMPLATE").MustString( | ||||
| 		`{{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"`, | ||||
| 	) | ||||
| 	// the `MustString` updates the default value, and `log.ACCESS` is used by `generateNamedLogger("access")` later | ||||
| 	_ = Cfg.Section("log").Key("ACCESS").MustString("file") | ||||
| 	if EnableAccessLog { | ||||
| // initLogFrom initializes logging with settings from configuration provider | ||||
| func initLogFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("log") | ||||
| 	options := newDefaultLogOptions() | ||||
| 		options.filename = filepath.Join(LogRootPath, "access.log") | ||||
| 		options.flags = "" // For the router we don't want any prefixed flags | ||||
| 		options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000) | ||||
| 		generateNamedLogger("access", options) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func newRouterLogService() { | ||||
| 	Cfg.Section("log").Key("ROUTER").MustString("console") | ||||
| 	// Allow [log]  DISABLE_ROUTER_LOG to override [server] DISABLE_ROUTER_LOG | ||||
| 	DisableRouterLog = Cfg.Section("log").Key("DISABLE_ROUTER_LOG").MustBool(DisableRouterLog) | ||||
|  | ||||
| 	if !DisableRouterLog { | ||||
| 		options := newDefaultLogOptions() | ||||
| 		options.filename = filepath.Join(LogRootPath, "router.log") | ||||
| 		options.flags = "date,time" // For the router we don't want any prefixed flags | ||||
| 		options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000) | ||||
| 		generateNamedLogger("router", options) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func newLogService() { | ||||
| 	options := newDefaultLogOptions() | ||||
| 	options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000) | ||||
| 	EnableSSHLog = Cfg.Section("log").Key("ENABLE_SSH_LOG").MustBool(false) | ||||
| 	options.bufferLength = Log.BufferLength | ||||
|  | ||||
| 	description := LogDescription{ | ||||
| 		Name: log.DEFAULT, | ||||
| 	} | ||||
|  | ||||
| 	sections := strings.Split(Cfg.Section("log").Key("MODE").MustString("console"), ",") | ||||
| 	sections := strings.Split(sec.Key("MODE").MustString("console"), ",") | ||||
|  | ||||
| 	useConsole := false | ||||
| 	for _, name := range sections { | ||||
| @@ -304,11 +313,11 @@ func newLogService() { | ||||
| 			useConsole = true | ||||
| 		} | ||||
|  | ||||
| 		sec, err := Cfg.GetSection("log." + name + ".default") | ||||
| 		sec, err := rootCfg.GetSection("log." + name + ".default") | ||||
| 		if err != nil { | ||||
| 			sec, err = Cfg.GetSection("log." + name) | ||||
| 			sec, err = rootCfg.GetSection("log." + name) | ||||
| 			if err != nil { | ||||
| 				sec, _ = Cfg.NewSection("log." + name) | ||||
| 				sec, _ = rootCfg.NewSection("log." + name) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| @@ -340,27 +349,45 @@ func newLogService() { | ||||
| // RestartLogsWithPIDSuffix restarts the logs with a PID suffix on files | ||||
| func RestartLogsWithPIDSuffix() { | ||||
| 	filenameSuffix = fmt.Sprintf(".%d", os.Getpid()) | ||||
| 	NewLogServices(false) | ||||
| 	InitLogs(false) | ||||
| } | ||||
|  | ||||
| // NewLogServices creates all the log services | ||||
| func NewLogServices(disableConsole bool) { | ||||
| 	newLogService() | ||||
| 	newRouterLogService() | ||||
| 	newAccessLogService() | ||||
| 	NewXORMLogService(disableConsole) | ||||
| } | ||||
| // InitLogs creates all the log services | ||||
| func InitLogs(disableConsole bool) { | ||||
| 	initLogFrom(CfgProvider) | ||||
|  | ||||
| // NewXORMLogService initializes xorm logger service | ||||
| func NewXORMLogService(disableConsole bool) { | ||||
| 	EnableXORMLog = Cfg.Section("log").Key("ENABLE_XORM_LOG").MustBool(true) | ||||
| 	if EnableXORMLog { | ||||
| 	if !Log.DisableRouterLog { | ||||
| 		options := newDefaultLogOptions() | ||||
| 		options.filename = filepath.Join(LogRootPath, "xorm.log") | ||||
| 		options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000) | ||||
| 		options.filename = filepath.Join(Log.RootPath, "router.log") | ||||
| 		options.flags = "date,time" // For the router we don't want any prefixed flags | ||||
| 		options.bufferLength = Log.BufferLength | ||||
| 		generateNamedLogger(CfgProvider, "router", options) | ||||
| 	} | ||||
|  | ||||
| 	if Log.EnableAccessLog { | ||||
| 		options := newDefaultLogOptions() | ||||
| 		options.filename = filepath.Join(Log.RootPath, "access.log") | ||||
| 		options.flags = "" // For the router we don't want any prefixed flags | ||||
| 		options.bufferLength = Log.BufferLength | ||||
| 		generateNamedLogger(CfgProvider, "access", options) | ||||
| 	} | ||||
|  | ||||
| 	initSQLLogFrom(CfgProvider, disableConsole) | ||||
| } | ||||
|  | ||||
| // InitSQLLog initializes xorm logger setting | ||||
| func InitSQLLog(disableConsole bool) { | ||||
| 	initSQLLogFrom(CfgProvider, disableConsole) | ||||
| } | ||||
|  | ||||
| func initSQLLogFrom(rootCfg ConfigProvider, disableConsole bool) { | ||||
| 	if Log.EnableXORMLog { | ||||
| 		options := newDefaultLogOptions() | ||||
| 		options.filename = filepath.Join(Log.RootPath, "xorm.log") | ||||
| 		options.bufferLength = Log.BufferLength | ||||
| 		options.disableConsole = disableConsole | ||||
|  | ||||
| 		Cfg.Section("log").Key("XORM").MustString(",") | ||||
| 		generateNamedLogger("xorm", options) | ||||
| 		rootCfg.Section("log").Key("XORM").MustString(",") | ||||
| 		generateNamedLogger(rootCfg, "xorm", options) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -12,7 +12,6 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
|  | ||||
| 	shellquote "github.com/kballard/go-shellquote" | ||||
| 	ini "gopkg.in/ini.v1" | ||||
| ) | ||||
|  | ||||
| // Mailer represents mail service. | ||||
| @@ -50,7 +49,14 @@ type Mailer struct { | ||||
| // MailService the global mailer | ||||
| var MailService *Mailer | ||||
|  | ||||
| func parseMailerConfig(rootCfg *ini.File) { | ||||
| func loadMailsFrom(rootCfg ConfigProvider) { | ||||
| 	loadMailerFrom(rootCfg) | ||||
| 	loadRegisterMailFrom(rootCfg) | ||||
| 	loadNotifyMailFrom(rootCfg) | ||||
| 	loadIncomingEmailFrom(rootCfg) | ||||
| } | ||||
|  | ||||
| func loadMailerFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("mailer") | ||||
| 	// Check mailer setting. | ||||
| 	if !sec.Key("ENABLED").MustBool() { | ||||
| @@ -59,7 +65,7 @@ func parseMailerConfig(rootCfg *ini.File) { | ||||
|  | ||||
| 	// Handle Deprecations and map on to new configuration | ||||
| 	// FIXME: DEPRECATED to be removed in v1.19.0 | ||||
| 	deprecatedSetting("mailer", "MAILER_TYPE", "mailer", "PROTOCOL") | ||||
| 	deprecatedSetting(rootCfg, "mailer", "MAILER_TYPE", "mailer", "PROTOCOL") | ||||
| 	if sec.HasKey("MAILER_TYPE") && !sec.HasKey("PROTOCOL") { | ||||
| 		if sec.Key("MAILER_TYPE").String() == "sendmail" { | ||||
| 			sec.Key("PROTOCOL").MustString("sendmail") | ||||
| @@ -67,7 +73,7 @@ func parseMailerConfig(rootCfg *ini.File) { | ||||
| 	} | ||||
|  | ||||
| 	// FIXME: DEPRECATED to be removed in v1.19.0 | ||||
| 	deprecatedSetting("mailer", "HOST", "mailer", "SMTP_ADDR") | ||||
| 	deprecatedSetting(rootCfg, "mailer", "HOST", "mailer", "SMTP_ADDR") | ||||
| 	if sec.HasKey("HOST") && !sec.HasKey("SMTP_ADDR") { | ||||
| 		givenHost := sec.Key("HOST").String() | ||||
| 		addr, port, err := net.SplitHostPort(givenHost) | ||||
| @@ -84,7 +90,7 @@ func parseMailerConfig(rootCfg *ini.File) { | ||||
| 	} | ||||
|  | ||||
| 	// FIXME: DEPRECATED to be removed in v1.19.0 | ||||
| 	deprecatedSetting("mailer", "IS_TLS_ENABLED", "mailer", "PROTOCOL") | ||||
| 	deprecatedSetting(rootCfg, "mailer", "IS_TLS_ENABLED", "mailer", "PROTOCOL") | ||||
| 	if sec.HasKey("IS_TLS_ENABLED") && !sec.HasKey("PROTOCOL") { | ||||
| 		if sec.Key("IS_TLS_ENABLED").MustBool() { | ||||
| 			sec.Key("PROTOCOL").MustString("smtps") | ||||
| @@ -94,37 +100,37 @@ func parseMailerConfig(rootCfg *ini.File) { | ||||
| 	} | ||||
|  | ||||
| 	// FIXME: DEPRECATED to be removed in v1.19.0 | ||||
| 	deprecatedSetting("mailer", "DISABLE_HELO", "mailer", "ENABLE_HELO") | ||||
| 	deprecatedSetting(rootCfg, "mailer", "DISABLE_HELO", "mailer", "ENABLE_HELO") | ||||
| 	if sec.HasKey("DISABLE_HELO") && !sec.HasKey("ENABLE_HELO") { | ||||
| 		sec.Key("ENABLE_HELO").MustBool(!sec.Key("DISABLE_HELO").MustBool()) | ||||
| 	} | ||||
|  | ||||
| 	// FIXME: DEPRECATED to be removed in v1.19.0 | ||||
| 	deprecatedSetting("mailer", "SKIP_VERIFY", "mailer", "FORCE_TRUST_SERVER_CERT") | ||||
| 	deprecatedSetting(rootCfg, "mailer", "SKIP_VERIFY", "mailer", "FORCE_TRUST_SERVER_CERT") | ||||
| 	if sec.HasKey("SKIP_VERIFY") && !sec.HasKey("FORCE_TRUST_SERVER_CERT") { | ||||
| 		sec.Key("FORCE_TRUST_SERVER_CERT").MustBool(sec.Key("SKIP_VERIFY").MustBool()) | ||||
| 	} | ||||
|  | ||||
| 	// FIXME: DEPRECATED to be removed in v1.19.0 | ||||
| 	deprecatedSetting("mailer", "USE_CERTIFICATE", "mailer", "USE_CLIENT_CERT") | ||||
| 	deprecatedSetting(rootCfg, "mailer", "USE_CERTIFICATE", "mailer", "USE_CLIENT_CERT") | ||||
| 	if sec.HasKey("USE_CERTIFICATE") && !sec.HasKey("USE_CLIENT_CERT") { | ||||
| 		sec.Key("USE_CLIENT_CERT").MustBool(sec.Key("USE_CERTIFICATE").MustBool()) | ||||
| 	} | ||||
|  | ||||
| 	// FIXME: DEPRECATED to be removed in v1.19.0 | ||||
| 	deprecatedSetting("mailer", "CERT_FILE", "mailer", "CLIENT_CERT_FILE") | ||||
| 	deprecatedSetting(rootCfg, "mailer", "CERT_FILE", "mailer", "CLIENT_CERT_FILE") | ||||
| 	if sec.HasKey("CERT_FILE") && !sec.HasKey("CLIENT_CERT_FILE") { | ||||
| 		sec.Key("CERT_FILE").MustString(sec.Key("CERT_FILE").String()) | ||||
| 	} | ||||
|  | ||||
| 	// FIXME: DEPRECATED to be removed in v1.19.0 | ||||
| 	deprecatedSetting("mailer", "KEY_FILE", "mailer", "CLIENT_KEY_FILE") | ||||
| 	deprecatedSetting(rootCfg, "mailer", "KEY_FILE", "mailer", "CLIENT_KEY_FILE") | ||||
| 	if sec.HasKey("KEY_FILE") && !sec.HasKey("CLIENT_KEY_FILE") { | ||||
| 		sec.Key("KEY_FILE").MustString(sec.Key("KEY_FILE").String()) | ||||
| 	} | ||||
|  | ||||
| 	// FIXME: DEPRECATED to be removed in v1.19.0 | ||||
| 	deprecatedSetting("mailer", "ENABLE_HTML_ALTERNATIVE", "mailer", "SEND_AS_PLAIN_TEXT") | ||||
| 	deprecatedSetting(rootCfg, "mailer", "ENABLE_HTML_ALTERNATIVE", "mailer", "SEND_AS_PLAIN_TEXT") | ||||
| 	if sec.HasKey("ENABLE_HTML_ALTERNATIVE") && !sec.HasKey("SEND_AS_PLAIN_TEXT") { | ||||
| 		sec.Key("SEND_AS_PLAIN_TEXT").MustBool(!sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(false)) | ||||
| 	} | ||||
| @@ -237,8 +243,8 @@ func parseMailerConfig(rootCfg *ini.File) { | ||||
| 	log.Info("Mail Service Enabled") | ||||
| } | ||||
|  | ||||
| func newRegisterMailService() { | ||||
| 	if !Cfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").MustBool() { | ||||
| func loadRegisterMailFrom(rootCfg ConfigProvider) { | ||||
| 	if !rootCfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").MustBool() { | ||||
| 		return | ||||
| 	} else if MailService == nil { | ||||
| 		log.Warn("Register Mail Service: Mail Service is not enabled") | ||||
| @@ -248,8 +254,8 @@ func newRegisterMailService() { | ||||
| 	log.Info("Register Mail Service Enabled") | ||||
| } | ||||
|  | ||||
| func newNotifyMailService() { | ||||
| 	if !Cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() { | ||||
| func loadNotifyMailFrom(rootCfg ConfigProvider) { | ||||
| 	if !rootCfg.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() { | ||||
| 		return | ||||
| 	} else if MailService == nil { | ||||
| 		log.Warn("Notify Mail Service: Mail Service is not enabled") | ||||
|   | ||||
| @@ -10,7 +10,7 @@ import ( | ||||
| 	ini "gopkg.in/ini.v1" | ||||
| ) | ||||
|  | ||||
| func TestParseMailerConfig(t *testing.T) { | ||||
| func Test_loadMailerFrom(t *testing.T) { | ||||
| 	iniFile := ini.Empty() | ||||
| 	kases := map[string]*Mailer{ | ||||
| 		"smtp.mydomain.com": { | ||||
| @@ -34,7 +34,7 @@ func TestParseMailerConfig(t *testing.T) { | ||||
| 			sec.NewKey("HOST", host) | ||||
|  | ||||
| 			// Check mailer setting | ||||
| 			parseMailerConfig(iniFile) | ||||
| 			loadMailerFrom(iniFile) | ||||
|  | ||||
| 			assert.EqualValues(t, kase.SMTPAddr, MailService.SMTPAddr) | ||||
| 			assert.EqualValues(t, kase.SMTPPort, MailService.SMTPPort) | ||||
|   | ||||
| @@ -25,6 +25,20 @@ const ( | ||||
| 	RenderContentModeIframe      = "iframe" | ||||
| ) | ||||
|  | ||||
| // Markdown settings | ||||
| var Markdown = struct { | ||||
| 	EnableHardLineBreakInComments  bool | ||||
| 	EnableHardLineBreakInDocuments bool | ||||
| 	CustomURLSchemes               []string `ini:"CUSTOM_URL_SCHEMES"` | ||||
| 	FileExtensions                 []string | ||||
| 	EnableMath                     bool | ||||
| }{ | ||||
| 	EnableHardLineBreakInComments:  true, | ||||
| 	EnableHardLineBreakInDocuments: false, | ||||
| 	FileExtensions:                 strings.Split(".md,.markdown,.mdown,.mkd", ","), | ||||
| 	EnableMath:                     true, | ||||
| } | ||||
|  | ||||
| // MarkupRenderer defines the external parser configured in ini | ||||
| type MarkupRenderer struct { | ||||
| 	Enabled              bool | ||||
| @@ -46,12 +60,14 @@ type MarkupSanitizerRule struct { | ||||
| 	AllowDataURIImages bool | ||||
| } | ||||
|  | ||||
| func newMarkup() { | ||||
| 	MermaidMaxSourceCharacters = Cfg.Section("markup").Key("MERMAID_MAX_SOURCE_CHARACTERS").MustInt(5000) | ||||
| func loadMarkupFrom(rootCfg ConfigProvider) { | ||||
| 	mustMapSetting(rootCfg, "markdown", &Markdown) | ||||
|  | ||||
| 	MermaidMaxSourceCharacters = rootCfg.Section("markup").Key("MERMAID_MAX_SOURCE_CHARACTERS").MustInt(5000) | ||||
| 	ExternalMarkupRenderers = make([]*MarkupRenderer, 0, 10) | ||||
| 	ExternalSanitizerRules = make([]MarkupSanitizerRule, 0, 10) | ||||
|  | ||||
| 	for _, sec := range Cfg.Section("markup").ChildSections() { | ||||
| 	for _, sec := range rootCfg.Section("markup").ChildSections() { | ||||
| 		name := strings.TrimPrefix(sec.Name(), "markup.") | ||||
| 		if name == "" { | ||||
| 			log.Warn("name is empty, markup " + sec.Name() + "ignored") | ||||
|   | ||||
							
								
								
									
										21
									
								
								modules/setting/metrics.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								modules/setting/metrics.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| // Copyright 2023 The Gitea Authors. All rights reserved. | ||||
| // SPDX-License-Identifier: MIT | ||||
|  | ||||
| package setting | ||||
|  | ||||
| // Metrics settings | ||||
| var Metrics = struct { | ||||
| 	Enabled                  bool | ||||
| 	Token                    string | ||||
| 	EnabledIssueByLabel      bool | ||||
| 	EnabledIssueByRepository bool | ||||
| }{ | ||||
| 	Enabled:                  false, | ||||
| 	Token:                    "", | ||||
| 	EnabledIssueByLabel:      false, | ||||
| 	EnabledIssueByRepository: false, | ||||
| } | ||||
|  | ||||
| func loadMetricsFrom(rootCfg ConfigProvider) { | ||||
| 	mustMapSetting(rootCfg, "metrics", &Metrics) | ||||
| } | ||||
| @@ -16,8 +16,8 @@ var Migrations = struct { | ||||
| 	RetryBackoff: 3, | ||||
| } | ||||
|  | ||||
| func newMigrationsService() { | ||||
| 	sec := Cfg.Section("migrations") | ||||
| func loadMigrationsFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("migrations") | ||||
| 	Migrations.MaxAttempts = sec.Key("MAX_ATTEMPTS").MustInt(Migrations.MaxAttempts) | ||||
| 	Migrations.RetryBackoff = sec.Key("RETRY_BACKOFF").MustInt(Migrations.RetryBackoff) | ||||
|  | ||||
|   | ||||
| @@ -14,8 +14,8 @@ var MimeTypeMap = struct { | ||||
| 	Map:     map[string]string{}, | ||||
| } | ||||
|  | ||||
| func newMimeTypeMap() { | ||||
| 	sec := Cfg.Section("repository.mimetype_mapping") | ||||
| func loadMimeTypeMapFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("repository.mimetype_mapping") | ||||
| 	keys := sec.Keys() | ||||
| 	m := make(map[string]string, len(keys)) | ||||
| 	for _, key := range keys { | ||||
|   | ||||
| @@ -24,16 +24,16 @@ var Mirror = struct { | ||||
| 	DefaultInterval: 8 * time.Hour, | ||||
| } | ||||
|  | ||||
| func newMirror() { | ||||
| func loadMirrorFrom(rootCfg ConfigProvider) { | ||||
| 	// Handle old configuration through `[repository]` `DISABLE_MIRRORS` | ||||
| 	// - please note this was badly named and only disabled the creation of new pull mirrors | ||||
| 	// FIXME: DEPRECATED to be removed in v1.18.0 | ||||
| 	deprecatedSetting("repository", "DISABLE_MIRRORS", "mirror", "ENABLED") | ||||
| 	if Cfg.Section("repository").Key("DISABLE_MIRRORS").MustBool(false) { | ||||
| 	deprecatedSetting(rootCfg, "repository", "DISABLE_MIRRORS", "mirror", "ENABLED") | ||||
| 	if rootCfg.Section("repository").Key("DISABLE_MIRRORS").MustBool(false) { | ||||
| 		Mirror.DisableNewPull = true | ||||
| 	} | ||||
|  | ||||
| 	if err := Cfg.Section("mirror").MapTo(&Mirror); err != nil { | ||||
| 	if err := rootCfg.Section("mirror").MapTo(&Mirror); err != nil { | ||||
| 		log.Fatal("Failed to map Mirror settings: %v", err) | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -4,6 +4,9 @@ | ||||
| package setting | ||||
| 
 | ||||
| import ( | ||||
| 	"math" | ||||
| 	"path/filepath" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 
 | ||||
| 	"gopkg.in/ini.v1" | ||||
| @@ -59,8 +62,8 @@ var OAuth2Client struct { | ||||
| 	AccountLinking         OAuth2AccountLinkingType | ||||
| } | ||||
| 
 | ||||
| func newOAuth2Client() { | ||||
| 	sec := Cfg.Section("oauth2_client") | ||||
| func loadOAuth2ClientFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("oauth2_client") | ||||
| 	OAuth2Client.RegisterEmailConfirm = sec.Key("REGISTER_EMAIL_CONFIRM").MustBool(Service.RegisterEmailConfirm) | ||||
| 	OAuth2Client.OpenIDConnectScopes = parseScopes(sec, "OPENID_CONNECT_SCOPES") | ||||
| 	OAuth2Client.EnableAutoRegistration = sec.Key("ENABLE_AUTO_REGISTRATION").MustBool() | ||||
| @@ -87,3 +90,33 @@ func parseScopes(sec *ini.Section, name string) []string { | ||||
| 	} | ||||
| 	return scopes | ||||
| } | ||||
| 
 | ||||
| var OAuth2 = struct { | ||||
| 	Enable                     bool | ||||
| 	AccessTokenExpirationTime  int64 | ||||
| 	RefreshTokenExpirationTime int64 | ||||
| 	InvalidateRefreshTokens    bool | ||||
| 	JWTSigningAlgorithm        string `ini:"JWT_SIGNING_ALGORITHM"` | ||||
| 	JWTSecretBase64            string `ini:"JWT_SECRET"` | ||||
| 	JWTSigningPrivateKeyFile   string `ini:"JWT_SIGNING_PRIVATE_KEY_FILE"` | ||||
| 	MaxTokenLength             int | ||||
| }{ | ||||
| 	Enable:                     true, | ||||
| 	AccessTokenExpirationTime:  3600, | ||||
| 	RefreshTokenExpirationTime: 730, | ||||
| 	InvalidateRefreshTokens:    false, | ||||
| 	JWTSigningAlgorithm:        "RS256", | ||||
| 	JWTSigningPrivateKeyFile:   "jwt/private.pem", | ||||
| 	MaxTokenLength:             math.MaxInt16, | ||||
| } | ||||
| 
 | ||||
| func loadOAuth2From(rootCfg ConfigProvider) { | ||||
| 	if err := rootCfg.Section("oauth2").MapTo(&OAuth2); err != nil { | ||||
| 		log.Fatal("Failed to OAuth2 settings: %v", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if !filepath.IsAbs(OAuth2.JWTSigningPrivateKeyFile) { | ||||
| 		OAuth2.JWTSigningPrivateKeyFile = filepath.Join(AppDataPath, OAuth2.JWTSigningPrivateKeyFile) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										22
									
								
								modules/setting/other.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								modules/setting/other.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| // Copyright 2023 The Gitea Authors. All rights reserved. | ||||
| // SPDX-License-Identifier: MIT | ||||
|  | ||||
| package setting | ||||
|  | ||||
| var ( | ||||
| 	// Other settings | ||||
| 	ShowFooterBranding         bool | ||||
| 	ShowFooterVersion          bool | ||||
| 	ShowFooterTemplateLoadTime bool | ||||
| 	EnableFeed                 bool | ||||
| 	EnableSitemap              bool | ||||
| ) | ||||
|  | ||||
| func loadOtherFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("other") | ||||
| 	ShowFooterBranding = sec.Key("SHOW_FOOTER_BRANDING").MustBool(false) | ||||
| 	ShowFooterVersion = sec.Key("SHOW_FOOTER_VERSION").MustBool(true) | ||||
| 	ShowFooterTemplateLoadTime = sec.Key("SHOW_FOOTER_TEMPLATE_LOAD_TIME").MustBool(true) | ||||
| 	EnableSitemap = sec.Key("ENABLE_SITEMAP").MustBool(true) | ||||
| 	EnableFeed = sec.Key("ENABLE_FEED").MustBool(true) | ||||
| } | ||||
| @@ -46,13 +46,13 @@ var ( | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| func newPackages() { | ||||
| 	sec := Cfg.Section("packages") | ||||
| func loadPackagesFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("packages") | ||||
| 	if err := sec.MapTo(&Packages); err != nil { | ||||
| 		log.Fatal("Failed to map Packages settings: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	Packages.Storage = getStorage("packages", "", nil) | ||||
| 	Packages.Storage = getStorage(rootCfg, "packages", "", nil) | ||||
|  | ||||
| 	appURL, _ := url.Parse(AppURL) | ||||
| 	Packages.RegistryHost = appURL.Host | ||||
|   | ||||
| @@ -32,16 +32,16 @@ var ( | ||||
| 	}{} | ||||
| ) | ||||
|  | ||||
| func newPictureService() { | ||||
| 	sec := Cfg.Section("picture") | ||||
| func loadPictureFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("picture") | ||||
|  | ||||
| 	avatarSec := Cfg.Section("avatar") | ||||
| 	avatarSec := rootCfg.Section("avatar") | ||||
| 	storageType := sec.Key("AVATAR_STORAGE_TYPE").MustString("") | ||||
| 	// Specifically default PATH to AVATAR_UPLOAD_PATH | ||||
| 	avatarSec.Key("PATH").MustString( | ||||
| 		sec.Key("AVATAR_UPLOAD_PATH").String()) | ||||
|  | ||||
| 	Avatar.Storage = getStorage("avatars", storageType, avatarSec) | ||||
| 	Avatar.Storage = getStorage(rootCfg, "avatars", storageType, avatarSec) | ||||
|  | ||||
| 	Avatar.MaxWidth = sec.Key("AVATAR_MAX_WIDTH").MustInt(4096) | ||||
| 	Avatar.MaxHeight = sec.Key("AVATAR_MAX_HEIGHT").MustInt(3072) | ||||
| @@ -60,11 +60,11 @@ func newPictureService() { | ||||
| 	} | ||||
|  | ||||
| 	DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool(GetDefaultDisableGravatar()) | ||||
| 	deprecatedSettingDB("", "DISABLE_GRAVATAR") | ||||
| 	deprecatedSettingDB(rootCfg, "", "DISABLE_GRAVATAR") | ||||
| 	EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(GetDefaultEnableFederatedAvatar(DisableGravatar)) | ||||
| 	deprecatedSettingDB("", "ENABLE_FEDERATED_AVATAR") | ||||
| 	deprecatedSettingDB(rootCfg, "", "ENABLE_FEDERATED_AVATAR") | ||||
|  | ||||
| 	newRepoAvatarService() | ||||
| 	loadRepoAvatarFrom(rootCfg) | ||||
| } | ||||
|  | ||||
| func GetDefaultDisableGravatar() bool { | ||||
| @@ -82,16 +82,16 @@ func GetDefaultEnableFederatedAvatar(disableGravatar bool) bool { | ||||
| 	return v | ||||
| } | ||||
|  | ||||
| func newRepoAvatarService() { | ||||
| 	sec := Cfg.Section("picture") | ||||
| func loadRepoAvatarFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("picture") | ||||
|  | ||||
| 	repoAvatarSec := Cfg.Section("repo-avatar") | ||||
| 	repoAvatarSec := rootCfg.Section("repo-avatar") | ||||
| 	storageType := sec.Key("REPOSITORY_AVATAR_STORAGE_TYPE").MustString("") | ||||
| 	// Specifically default PATH to AVATAR_UPLOAD_PATH | ||||
| 	repoAvatarSec.Key("PATH").MustString( | ||||
| 		sec.Key("REPOSITORY_AVATAR_UPLOAD_PATH").String()) | ||||
|  | ||||
| 	RepoAvatar.Storage = getStorage("repo-avatars", storageType, repoAvatarSec) | ||||
| 	RepoAvatar.Storage = getStorage(rootCfg, "repo-avatars", storageType, repoAvatarSec) | ||||
|  | ||||
| 	RepoAvatar.Fallback = sec.Key("REPOSITORY_AVATAR_FALLBACK").MustString("none") | ||||
| 	RepoAvatar.FallbackImage = sec.Key("REPOSITORY_AVATAR_FALLBACK_IMAGE").MustString("/assets/img/repo_default.png") | ||||
|   | ||||
| @@ -3,8 +3,6 @@ | ||||
|  | ||||
| package setting | ||||
|  | ||||
| import "code.gitea.io/gitea/modules/log" | ||||
|  | ||||
| // Project settings | ||||
| var ( | ||||
| 	Project = struct { | ||||
| @@ -16,8 +14,6 @@ var ( | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| func newProject() { | ||||
| 	if err := Cfg.Section("project").MapTo(&Project); err != nil { | ||||
| 		log.Fatal("Failed to map Project settings: %v", err) | ||||
| 	} | ||||
| func loadProjectFrom(rootCfg ConfigProvider) { | ||||
| 	mustMapSetting(rootCfg, "project", &Project) | ||||
| } | ||||
|   | ||||
| @@ -21,8 +21,8 @@ var Proxy = struct { | ||||
| 	ProxyHosts: []string{}, | ||||
| } | ||||
|  | ||||
| func newProxyService() { | ||||
| 	sec := Cfg.Section("proxy") | ||||
| func loadProxyFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("proxy") | ||||
| 	Proxy.Enabled = sec.Key("PROXY_ENABLED").MustBool(false) | ||||
| 	Proxy.ProxyURL = sec.Key("PROXY_URL").MustString("") | ||||
| 	if Proxy.ProxyURL != "" { | ||||
|   | ||||
| @@ -39,8 +39,12 @@ var Queue = QueueSettings{} | ||||
|  | ||||
| // GetQueueSettings returns the queue settings for the appropriately named queue | ||||
| func GetQueueSettings(name string) QueueSettings { | ||||
| 	return getQueueSettings(CfgProvider, name) | ||||
| } | ||||
|  | ||||
| func getQueueSettings(rootCfg ConfigProvider, name string) QueueSettings { | ||||
| 	q := QueueSettings{} | ||||
| 	sec := Cfg.Section("queue." + name) | ||||
| 	sec := rootCfg.Section("queue." + name) | ||||
| 	q.Name = name | ||||
|  | ||||
| 	// DataDir is not directly inheritable | ||||
| @@ -82,10 +86,14 @@ func GetQueueSettings(name string) QueueSettings { | ||||
| 	return q | ||||
| } | ||||
|  | ||||
| // NewQueueService sets up the default settings for Queues | ||||
| // LoadQueueSettings sets up the default settings for Queues | ||||
| // This is exported for tests to be able to use the queue | ||||
| func NewQueueService() { | ||||
| 	sec := Cfg.Section("queue") | ||||
| func LoadQueueSettings() { | ||||
| 	loadQueueFrom(CfgProvider) | ||||
| } | ||||
|  | ||||
| func loadQueueFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("queue") | ||||
| 	Queue.DataDir = filepath.ToSlash(sec.Key("DATADIR").MustString("queues/")) | ||||
| 	if !filepath.IsAbs(Queue.DataDir) { | ||||
| 		Queue.DataDir = filepath.ToSlash(filepath.Join(AppDataPath, Queue.DataDir)) | ||||
| @@ -108,10 +116,10 @@ func NewQueueService() { | ||||
|  | ||||
| 	// Now handle the old issue_indexer configuration | ||||
| 	// FIXME: DEPRECATED to be removed in v1.18.0 | ||||
| 	section := Cfg.Section("queue.issue_indexer") | ||||
| 	section := rootCfg.Section("queue.issue_indexer") | ||||
| 	directlySet := toDirectlySetKeysSet(section) | ||||
| 	if !directlySet.Contains("TYPE") && defaultType == "" { | ||||
| 		switch typ := Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_TYPE").MustString(""); typ { | ||||
| 		switch typ := rootCfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_TYPE").MustString(""); typ { | ||||
| 		case "levelqueue": | ||||
| 			_, _ = section.NewKey("TYPE", "level") | ||||
| 		case "channel": | ||||
| @@ -125,25 +133,25 @@ func NewQueueService() { | ||||
| 		} | ||||
| 	} | ||||
| 	if !directlySet.Contains("LENGTH") { | ||||
| 		length := Cfg.Section("indexer").Key("UPDATE_BUFFER_LEN").MustInt(0) | ||||
| 		length := rootCfg.Section("indexer").Key("UPDATE_BUFFER_LEN").MustInt(0) | ||||
| 		if length != 0 { | ||||
| 			_, _ = section.NewKey("LENGTH", strconv.Itoa(length)) | ||||
| 		} | ||||
| 	} | ||||
| 	if !directlySet.Contains("BATCH_LENGTH") { | ||||
| 		fallback := Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_BATCH_NUMBER").MustInt(0) | ||||
| 		fallback := rootCfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_BATCH_NUMBER").MustInt(0) | ||||
| 		if fallback != 0 { | ||||
| 			_, _ = section.NewKey("BATCH_LENGTH", strconv.Itoa(fallback)) | ||||
| 		} | ||||
| 	} | ||||
| 	if !directlySet.Contains("DATADIR") { | ||||
| 		queueDir := filepath.ToSlash(Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_DIR").MustString("")) | ||||
| 		queueDir := filepath.ToSlash(rootCfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_DIR").MustString("")) | ||||
| 		if queueDir != "" { | ||||
| 			_, _ = section.NewKey("DATADIR", queueDir) | ||||
| 		} | ||||
| 	} | ||||
| 	if !directlySet.Contains("CONN_STR") { | ||||
| 		connStr := Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_CONN_STR").MustString("") | ||||
| 		connStr := rootCfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_CONN_STR").MustString("") | ||||
| 		if connStr != "" { | ||||
| 			_, _ = section.NewKey("CONN_STR", connStr) | ||||
| 		} | ||||
| @@ -153,31 +161,31 @@ func NewQueueService() { | ||||
| 	// - will need to set default for [queue.*)] LENGTH appropriately though though | ||||
|  | ||||
| 	// Handle the old mailer configuration | ||||
| 	handleOldLengthConfiguration("mailer", "mailer", "SEND_BUFFER_LEN", 100) | ||||
| 	handleOldLengthConfiguration(rootCfg, "mailer", "mailer", "SEND_BUFFER_LEN", 100) | ||||
|  | ||||
| 	// Handle the old test pull requests configuration | ||||
| 	// Please note this will be a unique queue | ||||
| 	handleOldLengthConfiguration("pr_patch_checker", "repository", "PULL_REQUEST_QUEUE_LENGTH", 1000) | ||||
| 	handleOldLengthConfiguration(rootCfg, "pr_patch_checker", "repository", "PULL_REQUEST_QUEUE_LENGTH", 1000) | ||||
|  | ||||
| 	// Handle the old mirror queue configuration | ||||
| 	// Please note this will be a unique queue | ||||
| 	handleOldLengthConfiguration("mirror", "repository", "MIRROR_QUEUE_LENGTH", 1000) | ||||
| 	handleOldLengthConfiguration(rootCfg, "mirror", "repository", "MIRROR_QUEUE_LENGTH", 1000) | ||||
| } | ||||
|  | ||||
| // handleOldLengthConfiguration allows fallback to older configuration. `[queue.name]` `LENGTH` will override this configuration, but | ||||
| // if that is left unset then we should fallback to the older configuration. (Except where the new length woul be <=0) | ||||
| func handleOldLengthConfiguration(queueName, oldSection, oldKey string, defaultValue int) { | ||||
| 	if Cfg.Section(oldSection).HasKey(oldKey) { | ||||
| func handleOldLengthConfiguration(rootCfg ConfigProvider, queueName, oldSection, oldKey string, defaultValue int) { | ||||
| 	if rootCfg.Section(oldSection).HasKey(oldKey) { | ||||
| 		log.Error("Deprecated fallback for %s queue length `[%s]` `%s` present. Use `[queue.%s]` `LENGTH`. This will be removed in v1.18.0", queueName, queueName, oldSection, oldKey) | ||||
| 	} | ||||
| 	value := Cfg.Section(oldSection).Key(oldKey).MustInt(defaultValue) | ||||
| 	value := rootCfg.Section(oldSection).Key(oldKey).MustInt(defaultValue) | ||||
|  | ||||
| 	// Don't override with 0 | ||||
| 	if value <= 0 { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	section := Cfg.Section("queue." + queueName) | ||||
| 	section := rootCfg.Section("queue." + queueName) | ||||
| 	directlySet := toDirectlySetKeysSet(section) | ||||
| 	if !directlySet.Contains("LENGTH") { | ||||
| 		_, _ = section.NewKey("LENGTH", strconv.Itoa(value)) | ||||
|   | ||||
| @@ -270,10 +270,10 @@ var ( | ||||
| 	}{} | ||||
| ) | ||||
|  | ||||
| func newRepository() { | ||||
| func loadRepositoryFrom(rootCfg ConfigProvider) { | ||||
| 	var err error | ||||
| 	// Determine and create root git repository path. | ||||
| 	sec := Cfg.Section("repository") | ||||
| 	sec := rootCfg.Section("repository") | ||||
| 	Repository.DisableHTTPGit = sec.Key("DISABLE_HTTP_GIT").MustBool() | ||||
| 	Repository.UseCompatSSHURI = sec.Key("USE_COMPAT_SSH_URI").MustBool() | ||||
| 	Repository.MaxCreationLimit = sec.Key("MAX_CREATION_LIMIT").MustInt(-1) | ||||
| @@ -295,19 +295,19 @@ func newRepository() { | ||||
| 		log.Warn("SCRIPT_TYPE %q is not on the current PATH. Are you sure that this is the correct SCRIPT_TYPE?", ScriptType) | ||||
| 	} | ||||
|  | ||||
| 	if err = Cfg.Section("repository").MapTo(&Repository); err != nil { | ||||
| 	if err = sec.MapTo(&Repository); err != nil { | ||||
| 		log.Fatal("Failed to map Repository settings: %v", err) | ||||
| 	} else if err = Cfg.Section("repository.editor").MapTo(&Repository.Editor); err != nil { | ||||
| 	} else if err = rootCfg.Section("repository.editor").MapTo(&Repository.Editor); err != nil { | ||||
| 		log.Fatal("Failed to map Repository.Editor settings: %v", err) | ||||
| 	} else if err = Cfg.Section("repository.upload").MapTo(&Repository.Upload); err != nil { | ||||
| 	} else if err = rootCfg.Section("repository.upload").MapTo(&Repository.Upload); err != nil { | ||||
| 		log.Fatal("Failed to map Repository.Upload settings: %v", err) | ||||
| 	} else if err = Cfg.Section("repository.local").MapTo(&Repository.Local); err != nil { | ||||
| 	} else if err = rootCfg.Section("repository.local").MapTo(&Repository.Local); err != nil { | ||||
| 		log.Fatal("Failed to map Repository.Local settings: %v", err) | ||||
| 	} else if err = Cfg.Section("repository.pull-request").MapTo(&Repository.PullRequest); err != nil { | ||||
| 	} else if err = rootCfg.Section("repository.pull-request").MapTo(&Repository.PullRequest); err != nil { | ||||
| 		log.Fatal("Failed to map Repository.PullRequest settings: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	if !Cfg.Section("packages").Key("ENABLED").MustBool(true) { | ||||
| 	if !rootCfg.Section("packages").Key("ENABLED").MustBool(true) { | ||||
| 		Repository.DisabledRepoUnits = append(Repository.DisabledRepoUnits, "repo.packages") | ||||
| 	} | ||||
|  | ||||
| @@ -354,5 +354,5 @@ func newRepository() { | ||||
| 		Repository.Upload.TempPath = path.Join(AppWorkPath, Repository.Upload.TempPath) | ||||
| 	} | ||||
|  | ||||
| 	RepoArchive.Storage = getStorage("repo-archive", "", nil) | ||||
| 	RepoArchive.Storage = getStorage(rootCfg, "repo-archive", "", nil) | ||||
| } | ||||
|   | ||||
							
								
								
									
										158
									
								
								modules/setting/security.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								modules/setting/security.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,158 @@ | ||||
| // Copyright 2023 The Gitea Authors. All rights reserved. | ||||
| // SPDX-License-Identifier: MIT | ||||
|  | ||||
| package setting | ||||
|  | ||||
| import ( | ||||
| 	"net/url" | ||||
| 	"os" | ||||
| 	"strings" | ||||
|  | ||||
| 	"code.gitea.io/gitea/modules/auth/password/hash" | ||||
| 	"code.gitea.io/gitea/modules/generate" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
|  | ||||
| 	ini "gopkg.in/ini.v1" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	// Security settings | ||||
| 	InstallLock                        bool | ||||
| 	SecretKey                          string | ||||
| 	InternalToken                      string // internal access token | ||||
| 	LogInRememberDays                  int | ||||
| 	CookieUserName                     string | ||||
| 	CookieRememberName                 string | ||||
| 	ReverseProxyAuthUser               string | ||||
| 	ReverseProxyAuthEmail              string | ||||
| 	ReverseProxyAuthFullName           string | ||||
| 	ReverseProxyLimit                  int | ||||
| 	ReverseProxyTrustedProxies         []string | ||||
| 	MinPasswordLength                  int | ||||
| 	ImportLocalPaths                   bool | ||||
| 	DisableGitHooks                    bool | ||||
| 	DisableWebhooks                    bool | ||||
| 	OnlyAllowPushIfGiteaEnvironmentSet bool | ||||
| 	PasswordComplexity                 []string | ||||
| 	PasswordHashAlgo                   string | ||||
| 	PasswordCheckPwn                   bool | ||||
| 	SuccessfulTokensCacheSize          int | ||||
| 	CSRFCookieName                     = "_csrf" | ||||
| 	CSRFCookieHTTPOnly                 = true | ||||
| ) | ||||
|  | ||||
| // loadSecret load the secret from ini by uriKey or verbatimKey, only one of them could be set | ||||
| // If the secret is loaded from uriKey (file), the file should be non-empty, to guarantee the behavior stable and clear. | ||||
| func loadSecret(sec *ini.Section, uriKey, verbatimKey string) string { | ||||
| 	// don't allow setting both URI and verbatim string | ||||
| 	uri := sec.Key(uriKey).String() | ||||
| 	verbatim := sec.Key(verbatimKey).String() | ||||
| 	if uri != "" && verbatim != "" { | ||||
| 		log.Fatal("Cannot specify both %s and %s", uriKey, verbatimKey) | ||||
| 	} | ||||
|  | ||||
| 	// if we have no URI, use verbatim | ||||
| 	if uri == "" { | ||||
| 		return verbatim | ||||
| 	} | ||||
|  | ||||
| 	tempURI, err := url.Parse(uri) | ||||
| 	if err != nil { | ||||
| 		log.Fatal("Failed to parse %s (%s): %v", uriKey, uri, err) | ||||
| 	} | ||||
| 	switch tempURI.Scheme { | ||||
| 	case "file": | ||||
| 		buf, err := os.ReadFile(tempURI.RequestURI()) | ||||
| 		if err != nil { | ||||
| 			log.Fatal("Failed to read %s (%s): %v", uriKey, tempURI.RequestURI(), err) | ||||
| 		} | ||||
| 		val := strings.TrimSpace(string(buf)) | ||||
| 		if val == "" { | ||||
| 			// The file shouldn't be empty, otherwise we can not know whether the user has ever set the KEY or KEY_URI | ||||
| 			// For example: if INTERNAL_TOKEN_URI=file:///empty-file, | ||||
| 			// Then if the token is re-generated during installation and saved to INTERNAL_TOKEN | ||||
| 			// Then INTERNAL_TOKEN and INTERNAL_TOKEN_URI both exist, that's a fatal error (they shouldn't) | ||||
| 			log.Fatal("Failed to read %s (%s): the file is empty", uriKey, tempURI.RequestURI()) | ||||
| 		} | ||||
| 		return val | ||||
|  | ||||
| 	// only file URIs are allowed | ||||
| 	default: | ||||
| 		log.Fatal("Unsupported URI-Scheme %q (INTERNAL_TOKEN_URI = %q)", tempURI.Scheme, uri) | ||||
| 		return "" | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // generateSaveInternalToken generates and saves the internal token to app.ini | ||||
| func generateSaveInternalToken() { | ||||
| 	token, err := generate.NewInternalToken() | ||||
| 	if err != nil { | ||||
| 		log.Fatal("Error generate internal token: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	InternalToken = token | ||||
| 	CreateOrAppendToCustomConf("security.INTERNAL_TOKEN", func(cfg *ini.File) { | ||||
| 		cfg.Section("security").Key("INTERNAL_TOKEN").SetValue(token) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func loadSecurityFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("security") | ||||
| 	InstallLock = sec.Key("INSTALL_LOCK").MustBool(false) | ||||
| 	LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7) | ||||
| 	CookieUserName = sec.Key("COOKIE_USERNAME").MustString("gitea_awesome") | ||||
| 	SecretKey = loadSecret(sec, "SECRET_KEY_URI", "SECRET_KEY") | ||||
| 	if SecretKey == "" { | ||||
| 		// FIXME: https://github.com/go-gitea/gitea/issues/16832 | ||||
| 		// Until it supports rotating an existing secret key, we shouldn't move users off of the widely used default value | ||||
| 		SecretKey = "!#@FDEWREWR&*(" //nolint:gosec | ||||
| 	} | ||||
|  | ||||
| 	CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").MustString("gitea_incredible") | ||||
|  | ||||
| 	ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER") | ||||
| 	ReverseProxyAuthEmail = sec.Key("REVERSE_PROXY_AUTHENTICATION_EMAIL").MustString("X-WEBAUTH-EMAIL") | ||||
| 	ReverseProxyAuthFullName = sec.Key("REVERSE_PROXY_AUTHENTICATION_FULL_NAME").MustString("X-WEBAUTH-FULLNAME") | ||||
|  | ||||
| 	ReverseProxyLimit = sec.Key("REVERSE_PROXY_LIMIT").MustInt(1) | ||||
| 	ReverseProxyTrustedProxies = sec.Key("REVERSE_PROXY_TRUSTED_PROXIES").Strings(",") | ||||
| 	if len(ReverseProxyTrustedProxies) == 0 { | ||||
| 		ReverseProxyTrustedProxies = []string{"127.0.0.0/8", "::1/128"} | ||||
| 	} | ||||
|  | ||||
| 	MinPasswordLength = sec.Key("MIN_PASSWORD_LENGTH").MustInt(6) | ||||
| 	ImportLocalPaths = sec.Key("IMPORT_LOCAL_PATHS").MustBool(false) | ||||
| 	DisableGitHooks = sec.Key("DISABLE_GIT_HOOKS").MustBool(true) | ||||
| 	DisableWebhooks = sec.Key("DISABLE_WEBHOOKS").MustBool(false) | ||||
| 	OnlyAllowPushIfGiteaEnvironmentSet = sec.Key("ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET").MustBool(true) | ||||
|  | ||||
| 	// Ensure that the provided default hash algorithm is a valid hash algorithm | ||||
| 	var algorithm *hash.PasswordHashAlgorithm | ||||
| 	PasswordHashAlgo, algorithm = hash.SetDefaultPasswordHashAlgorithm(sec.Key("PASSWORD_HASH_ALGO").MustString("")) | ||||
| 	if algorithm == nil { | ||||
| 		log.Fatal("The provided password hash algorithm was invalid: %s", sec.Key("PASSWORD_HASH_ALGO").MustString("")) | ||||
| 	} | ||||
|  | ||||
| 	CSRFCookieHTTPOnly = sec.Key("CSRF_COOKIE_HTTP_ONLY").MustBool(true) | ||||
| 	PasswordCheckPwn = sec.Key("PASSWORD_CHECK_PWN").MustBool(false) | ||||
| 	SuccessfulTokensCacheSize = sec.Key("SUCCESSFUL_TOKENS_CACHE_SIZE").MustInt(20) | ||||
|  | ||||
| 	InternalToken = loadSecret(sec, "INTERNAL_TOKEN_URI", "INTERNAL_TOKEN") | ||||
| 	if InstallLock && InternalToken == "" { | ||||
| 		// if Gitea has been installed but the InternalToken hasn't been generated (upgrade from an old release), we should generate | ||||
| 		// some users do cluster deployment, they still depend on this auto-generating behavior. | ||||
| 		generateSaveInternalToken() | ||||
| 	} | ||||
|  | ||||
| 	cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",") | ||||
| 	if len(cfgdata) == 0 { | ||||
| 		cfgdata = []string{"off"} | ||||
| 	} | ||||
| 	PasswordComplexity = make([]string, 0, len(cfgdata)) | ||||
| 	for _, name := range cfgdata { | ||||
| 		name := strings.ToLower(strings.Trim(name, `"`)) | ||||
| 		if name != "" { | ||||
| 			PasswordComplexity = append(PasswordComplexity, name) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										356
									
								
								modules/setting/server.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										356
									
								
								modules/setting/server.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,356 @@ | ||||
| // Copyright 2023 The Gitea Authors. All rights reserved. | ||||
| // SPDX-License-Identifier: MIT | ||||
|  | ||||
| package setting | ||||
|  | ||||
| import ( | ||||
| 	"encoding/base64" | ||||
| 	"net" | ||||
| 	"net/url" | ||||
| 	"path" | ||||
| 	"path/filepath" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"code.gitea.io/gitea/modules/json" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| ) | ||||
|  | ||||
| // Scheme describes protocol types | ||||
| type Scheme string | ||||
|  | ||||
| // enumerates all the scheme types | ||||
| const ( | ||||
| 	HTTP     Scheme = "http" | ||||
| 	HTTPS    Scheme = "https" | ||||
| 	FCGI     Scheme = "fcgi" | ||||
| 	FCGIUnix Scheme = "fcgi+unix" | ||||
| 	HTTPUnix Scheme = "http+unix" | ||||
| ) | ||||
|  | ||||
| // LandingPage describes the default page | ||||
| type LandingPage string | ||||
|  | ||||
| // enumerates all the landing page types | ||||
| const ( | ||||
| 	LandingPageHome          LandingPage = "/" | ||||
| 	LandingPageExplore       LandingPage = "/explore" | ||||
| 	LandingPageOrganizations LandingPage = "/explore/organizations" | ||||
| 	LandingPageLogin         LandingPage = "/user/login" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	// AppName is the Application name, used in the page title. | ||||
| 	// It maps to ini:"APP_NAME" | ||||
| 	AppName string | ||||
| 	// AppURL is the Application ROOT_URL. It always has a '/' suffix | ||||
| 	// It maps to ini:"ROOT_URL" | ||||
| 	AppURL string | ||||
| 	// AppSubURL represents the sub-url mounting point for gitea. It is either "" or starts with '/' and ends without '/', such as '/{subpath}'. | ||||
| 	// This value is empty if site does not have sub-url. | ||||
| 	AppSubURL string | ||||
| 	// AppDataPath is the default path for storing data. | ||||
| 	// It maps to ini:"APP_DATA_PATH" in [server] and defaults to AppWorkPath + "/data" | ||||
| 	AppDataPath string | ||||
| 	// LocalURL is the url for locally running applications to contact Gitea. It always has a '/' suffix | ||||
| 	// It maps to ini:"LOCAL_ROOT_URL" in [server] | ||||
| 	LocalURL string | ||||
| 	// AssetVersion holds a opaque value that is used for cache-busting assets | ||||
| 	AssetVersion string | ||||
|  | ||||
| 	// Server settings | ||||
| 	Protocol                   Scheme | ||||
| 	UseProxyProtocol           bool // `ini:"USE_PROXY_PROTOCOL"` | ||||
| 	ProxyProtocolTLSBridging   bool //`ini:"PROXY_PROTOCOL_TLS_BRIDGING"` | ||||
| 	ProxyProtocolHeaderTimeout time.Duration | ||||
| 	ProxyProtocolAcceptUnknown bool | ||||
| 	Domain                     string | ||||
| 	HTTPAddr                   string | ||||
| 	HTTPPort                   string | ||||
| 	LocalUseProxyProtocol      bool | ||||
| 	RedirectOtherPort          bool | ||||
| 	RedirectorUseProxyProtocol bool | ||||
| 	PortToRedirect             string | ||||
| 	OfflineMode                bool | ||||
| 	CertFile                   string | ||||
| 	KeyFile                    string | ||||
| 	StaticRootPath             string | ||||
| 	StaticCacheTime            time.Duration | ||||
| 	EnableGzip                 bool | ||||
| 	LandingPageURL             LandingPage | ||||
| 	LandingPageCustom          string | ||||
| 	UnixSocketPermission       uint32 | ||||
| 	EnablePprof                bool | ||||
| 	PprofDataPath              string | ||||
| 	EnableAcme                 bool | ||||
| 	AcmeTOS                    bool | ||||
| 	AcmeLiveDirectory          string | ||||
| 	AcmeEmail                  string | ||||
| 	AcmeURL                    string | ||||
| 	AcmeCARoot                 string | ||||
| 	SSLMinimumVersion          string | ||||
| 	SSLMaximumVersion          string | ||||
| 	SSLCurvePreferences        []string | ||||
| 	SSLCipherSuites            []string | ||||
| 	GracefulRestartable        bool | ||||
| 	GracefulHammerTime         time.Duration | ||||
| 	StartupTimeout             time.Duration | ||||
| 	PerWriteTimeout            = 30 * time.Second | ||||
| 	PerWritePerKbTimeout       = 10 * time.Second | ||||
| 	StaticURLPrefix            string | ||||
| 	AbsoluteAssetURL           string | ||||
|  | ||||
| 	HasRobotsTxt bool | ||||
| 	ManifestData string | ||||
| ) | ||||
|  | ||||
| // MakeManifestData generates web app manifest JSON | ||||
| func MakeManifestData(appName, appURL, absoluteAssetURL string) []byte { | ||||
| 	type manifestIcon struct { | ||||
| 		Src   string `json:"src"` | ||||
| 		Type  string `json:"type"` | ||||
| 		Sizes string `json:"sizes"` | ||||
| 	} | ||||
|  | ||||
| 	type manifestJSON struct { | ||||
| 		Name      string         `json:"name"` | ||||
| 		ShortName string         `json:"short_name"` | ||||
| 		StartURL  string         `json:"start_url"` | ||||
| 		Icons     []manifestIcon `json:"icons"` | ||||
| 	} | ||||
|  | ||||
| 	bytes, err := json.Marshal(&manifestJSON{ | ||||
| 		Name:      appName, | ||||
| 		ShortName: appName, | ||||
| 		StartURL:  appURL, | ||||
| 		Icons: []manifestIcon{ | ||||
| 			{ | ||||
| 				Src:   absoluteAssetURL + "/assets/img/logo.png", | ||||
| 				Type:  "image/png", | ||||
| 				Sizes: "512x512", | ||||
| 			}, | ||||
| 			{ | ||||
| 				Src:   absoluteAssetURL + "/assets/img/logo.svg", | ||||
| 				Type:  "image/svg+xml", | ||||
| 				Sizes: "512x512", | ||||
| 			}, | ||||
| 		}, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		log.Error("unable to marshal manifest JSON. Error: %v", err) | ||||
| 		return make([]byte, 0) | ||||
| 	} | ||||
|  | ||||
| 	return bytes | ||||
| } | ||||
|  | ||||
| // MakeAbsoluteAssetURL returns the absolute asset url prefix without a trailing slash | ||||
| func MakeAbsoluteAssetURL(appURL, staticURLPrefix string) string { | ||||
| 	parsedPrefix, err := url.Parse(strings.TrimSuffix(staticURLPrefix, "/")) | ||||
| 	if err != nil { | ||||
| 		log.Fatal("Unable to parse STATIC_URL_PREFIX: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	if err == nil && parsedPrefix.Hostname() == "" { | ||||
| 		if staticURLPrefix == "" { | ||||
| 			return strings.TrimSuffix(appURL, "/") | ||||
| 		} | ||||
|  | ||||
| 		// StaticURLPrefix is just a path | ||||
| 		return util.URLJoin(appURL, strings.TrimSuffix(staticURLPrefix, "/")) | ||||
| 	} | ||||
|  | ||||
| 	return strings.TrimSuffix(staticURLPrefix, "/") | ||||
| } | ||||
|  | ||||
| func loadServerFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("server") | ||||
| 	AppName = rootCfg.Section("").Key("APP_NAME").MustString("Gitea: Git with a cup of tea") | ||||
|  | ||||
| 	Domain = sec.Key("DOMAIN").MustString("localhost") | ||||
| 	HTTPAddr = sec.Key("HTTP_ADDR").MustString("0.0.0.0") | ||||
| 	HTTPPort = sec.Key("HTTP_PORT").MustString("3000") | ||||
|  | ||||
| 	Protocol = HTTP | ||||
| 	protocolCfg := sec.Key("PROTOCOL").String() | ||||
| 	switch protocolCfg { | ||||
| 	case "https": | ||||
| 		Protocol = HTTPS | ||||
| 		// FIXME: DEPRECATED to be removed in v1.18.0 | ||||
| 		if sec.HasKey("ENABLE_ACME") { | ||||
| 			EnableAcme = sec.Key("ENABLE_ACME").MustBool(false) | ||||
| 		} else { | ||||
| 			deprecatedSetting(rootCfg, "server", "ENABLE_LETSENCRYPT", "server", "ENABLE_ACME") | ||||
| 			EnableAcme = sec.Key("ENABLE_LETSENCRYPT").MustBool(false) | ||||
| 		} | ||||
| 		if EnableAcme { | ||||
| 			AcmeURL = sec.Key("ACME_URL").MustString("") | ||||
| 			AcmeCARoot = sec.Key("ACME_CA_ROOT").MustString("") | ||||
| 			// FIXME: DEPRECATED to be removed in v1.18.0 | ||||
| 			if sec.HasKey("ACME_ACCEPTTOS") { | ||||
| 				AcmeTOS = sec.Key("ACME_ACCEPTTOS").MustBool(false) | ||||
| 			} else { | ||||
| 				deprecatedSetting(rootCfg, "server", "LETSENCRYPT_ACCEPTTOS", "server", "ACME_ACCEPTTOS") | ||||
| 				AcmeTOS = sec.Key("LETSENCRYPT_ACCEPTTOS").MustBool(false) | ||||
| 			} | ||||
| 			if !AcmeTOS { | ||||
| 				log.Fatal("ACME TOS is not accepted (ACME_ACCEPTTOS).") | ||||
| 			} | ||||
| 			// FIXME: DEPRECATED to be removed in v1.18.0 | ||||
| 			if sec.HasKey("ACME_DIRECTORY") { | ||||
| 				AcmeLiveDirectory = sec.Key("ACME_DIRECTORY").MustString("https") | ||||
| 			} else { | ||||
| 				deprecatedSetting(rootCfg, "server", "LETSENCRYPT_DIRECTORY", "server", "ACME_DIRECTORY") | ||||
| 				AcmeLiveDirectory = sec.Key("LETSENCRYPT_DIRECTORY").MustString("https") | ||||
| 			} | ||||
| 			// FIXME: DEPRECATED to be removed in v1.18.0 | ||||
| 			if sec.HasKey("ACME_EMAIL") { | ||||
| 				AcmeEmail = sec.Key("ACME_EMAIL").MustString("") | ||||
| 			} else { | ||||
| 				deprecatedSetting(rootCfg, "server", "LETSENCRYPT_EMAIL", "server", "ACME_EMAIL") | ||||
| 				AcmeEmail = sec.Key("LETSENCRYPT_EMAIL").MustString("") | ||||
| 			} | ||||
| 		} else { | ||||
| 			CertFile = sec.Key("CERT_FILE").String() | ||||
| 			KeyFile = sec.Key("KEY_FILE").String() | ||||
| 			if len(CertFile) > 0 && !filepath.IsAbs(CertFile) { | ||||
| 				CertFile = filepath.Join(CustomPath, CertFile) | ||||
| 			} | ||||
| 			if len(KeyFile) > 0 && !filepath.IsAbs(KeyFile) { | ||||
| 				KeyFile = filepath.Join(CustomPath, KeyFile) | ||||
| 			} | ||||
| 		} | ||||
| 		SSLMinimumVersion = sec.Key("SSL_MIN_VERSION").MustString("") | ||||
| 		SSLMaximumVersion = sec.Key("SSL_MAX_VERSION").MustString("") | ||||
| 		SSLCurvePreferences = sec.Key("SSL_CURVE_PREFERENCES").Strings(",") | ||||
| 		SSLCipherSuites = sec.Key("SSL_CIPHER_SUITES").Strings(",") | ||||
| 	case "fcgi": | ||||
| 		Protocol = FCGI | ||||
| 	case "fcgi+unix", "unix", "http+unix": | ||||
| 		switch protocolCfg { | ||||
| 		case "fcgi+unix": | ||||
| 			Protocol = FCGIUnix | ||||
| 		case "unix": | ||||
| 			log.Warn("unix PROTOCOL value is deprecated, please use http+unix") | ||||
| 			fallthrough | ||||
| 		case "http+unix": | ||||
| 			Protocol = HTTPUnix | ||||
| 		} | ||||
| 		UnixSocketPermissionRaw := sec.Key("UNIX_SOCKET_PERMISSION").MustString("666") | ||||
| 		UnixSocketPermissionParsed, err := strconv.ParseUint(UnixSocketPermissionRaw, 8, 32) | ||||
| 		if err != nil || UnixSocketPermissionParsed > 0o777 { | ||||
| 			log.Fatal("Failed to parse unixSocketPermission: %s", UnixSocketPermissionRaw) | ||||
| 		} | ||||
|  | ||||
| 		UnixSocketPermission = uint32(UnixSocketPermissionParsed) | ||||
| 		if !filepath.IsAbs(HTTPAddr) { | ||||
| 			HTTPAddr = filepath.Join(AppWorkPath, HTTPAddr) | ||||
| 		} | ||||
| 	} | ||||
| 	UseProxyProtocol = sec.Key("USE_PROXY_PROTOCOL").MustBool(false) | ||||
| 	ProxyProtocolTLSBridging = sec.Key("PROXY_PROTOCOL_TLS_BRIDGING").MustBool(false) | ||||
| 	ProxyProtocolHeaderTimeout = sec.Key("PROXY_PROTOCOL_HEADER_TIMEOUT").MustDuration(5 * time.Second) | ||||
| 	ProxyProtocolAcceptUnknown = sec.Key("PROXY_PROTOCOL_ACCEPT_UNKNOWN").MustBool(false) | ||||
| 	GracefulRestartable = sec.Key("ALLOW_GRACEFUL_RESTARTS").MustBool(true) | ||||
| 	GracefulHammerTime = sec.Key("GRACEFUL_HAMMER_TIME").MustDuration(60 * time.Second) | ||||
| 	StartupTimeout = sec.Key("STARTUP_TIMEOUT").MustDuration(0 * time.Second) | ||||
| 	PerWriteTimeout = sec.Key("PER_WRITE_TIMEOUT").MustDuration(PerWriteTimeout) | ||||
| 	PerWritePerKbTimeout = sec.Key("PER_WRITE_PER_KB_TIMEOUT").MustDuration(PerWritePerKbTimeout) | ||||
|  | ||||
| 	defaultAppURL := string(Protocol) + "://" + Domain + ":" + HTTPPort | ||||
| 	AppURL = sec.Key("ROOT_URL").MustString(defaultAppURL) | ||||
|  | ||||
| 	// Check validity of AppURL | ||||
| 	appURL, err := url.Parse(AppURL) | ||||
| 	if err != nil { | ||||
| 		log.Fatal("Invalid ROOT_URL '%s': %s", AppURL, err) | ||||
| 	} | ||||
| 	// Remove default ports from AppURL. | ||||
| 	// (scheme-based URL normalization, RFC 3986 section 6.2.3) | ||||
| 	if (appURL.Scheme == string(HTTP) && appURL.Port() == "80") || (appURL.Scheme == string(HTTPS) && appURL.Port() == "443") { | ||||
| 		appURL.Host = appURL.Hostname() | ||||
| 	} | ||||
| 	// This should be TrimRight to ensure that there is only a single '/' at the end of AppURL. | ||||
| 	AppURL = strings.TrimRight(appURL.String(), "/") + "/" | ||||
|  | ||||
| 	// Suburl should start with '/' and end without '/', such as '/{subpath}'. | ||||
| 	// This value is empty if site does not have sub-url. | ||||
| 	AppSubURL = strings.TrimSuffix(appURL.Path, "/") | ||||
| 	StaticURLPrefix = strings.TrimSuffix(sec.Key("STATIC_URL_PREFIX").MustString(AppSubURL), "/") | ||||
|  | ||||
| 	// Check if Domain differs from AppURL domain than update it to AppURL's domain | ||||
| 	urlHostname := appURL.Hostname() | ||||
| 	if urlHostname != Domain && net.ParseIP(urlHostname) == nil && urlHostname != "" { | ||||
| 		Domain = urlHostname | ||||
| 	} | ||||
|  | ||||
| 	AbsoluteAssetURL = MakeAbsoluteAssetURL(AppURL, StaticURLPrefix) | ||||
| 	AssetVersion = strings.ReplaceAll(AppVer, "+", "~") // make sure the version string is clear (no real escaping is needed) | ||||
|  | ||||
| 	manifestBytes := MakeManifestData(AppName, AppURL, AbsoluteAssetURL) | ||||
| 	ManifestData = `application/json;base64,` + base64.StdEncoding.EncodeToString(manifestBytes) | ||||
|  | ||||
| 	var defaultLocalURL string | ||||
| 	switch Protocol { | ||||
| 	case HTTPUnix: | ||||
| 		defaultLocalURL = "http://unix/" | ||||
| 	case FCGI: | ||||
| 		defaultLocalURL = AppURL | ||||
| 	case FCGIUnix: | ||||
| 		defaultLocalURL = AppURL | ||||
| 	default: | ||||
| 		defaultLocalURL = string(Protocol) + "://" | ||||
| 		if HTTPAddr == "0.0.0.0" { | ||||
| 			defaultLocalURL += net.JoinHostPort("localhost", HTTPPort) + "/" | ||||
| 		} else { | ||||
| 			defaultLocalURL += net.JoinHostPort(HTTPAddr, HTTPPort) + "/" | ||||
| 		} | ||||
| 	} | ||||
| 	LocalURL = sec.Key("LOCAL_ROOT_URL").MustString(defaultLocalURL) | ||||
| 	LocalURL = strings.TrimRight(LocalURL, "/") + "/" | ||||
| 	LocalUseProxyProtocol = sec.Key("LOCAL_USE_PROXY_PROTOCOL").MustBool(UseProxyProtocol) | ||||
| 	RedirectOtherPort = sec.Key("REDIRECT_OTHER_PORT").MustBool(false) | ||||
| 	PortToRedirect = sec.Key("PORT_TO_REDIRECT").MustString("80") | ||||
| 	RedirectorUseProxyProtocol = sec.Key("REDIRECTOR_USE_PROXY_PROTOCOL").MustBool(UseProxyProtocol) | ||||
| 	OfflineMode = sec.Key("OFFLINE_MODE").MustBool() | ||||
| 	Log.DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool() | ||||
| 	if len(StaticRootPath) == 0 { | ||||
| 		StaticRootPath = AppWorkPath | ||||
| 	} | ||||
| 	StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(StaticRootPath) | ||||
| 	StaticCacheTime = sec.Key("STATIC_CACHE_TIME").MustDuration(6 * time.Hour) | ||||
| 	AppDataPath = sec.Key("APP_DATA_PATH").MustString(path.Join(AppWorkPath, "data")) | ||||
| 	if !filepath.IsAbs(AppDataPath) { | ||||
| 		log.Info("The provided APP_DATA_PATH: %s is not absolute - it will be made absolute against the work path: %s", AppDataPath, AppWorkPath) | ||||
| 		AppDataPath = filepath.ToSlash(filepath.Join(AppWorkPath, AppDataPath)) | ||||
| 	} | ||||
|  | ||||
| 	EnableGzip = sec.Key("ENABLE_GZIP").MustBool() | ||||
| 	EnablePprof = sec.Key("ENABLE_PPROF").MustBool(false) | ||||
| 	PprofDataPath = sec.Key("PPROF_DATA_PATH").MustString(path.Join(AppWorkPath, "data/tmp/pprof")) | ||||
| 	if !filepath.IsAbs(PprofDataPath) { | ||||
| 		PprofDataPath = filepath.Join(AppWorkPath, PprofDataPath) | ||||
| 	} | ||||
|  | ||||
| 	landingPage := sec.Key("LANDING_PAGE").MustString("home") | ||||
| 	switch landingPage { | ||||
| 	case "explore": | ||||
| 		LandingPageURL = LandingPageExplore | ||||
| 	case "organizations": | ||||
| 		LandingPageURL = LandingPageOrganizations | ||||
| 	case "login": | ||||
| 		LandingPageURL = LandingPageLogin | ||||
| 	case "": | ||||
| 	case "home": | ||||
| 		LandingPageURL = LandingPageHome | ||||
| 	default: | ||||
| 		LandingPageURL = LandingPage(landingPage) | ||||
| 	} | ||||
|  | ||||
| 	HasRobotsTxt, err = util.IsFile(path.Join(CustomPath, "robots.txt")) | ||||
| 	if err != nil { | ||||
| 		log.Error("Unable to check if %s is a file. Error: %v", path.Join(CustomPath, "robots.txt"), err) | ||||
| 	} | ||||
| } | ||||
| @@ -12,6 +12,15 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/structs" | ||||
| ) | ||||
|  | ||||
| // enumerates all the types of captchas | ||||
| const ( | ||||
| 	ImageCaptcha = "image" | ||||
| 	ReCaptcha    = "recaptcha" | ||||
| 	HCaptcha     = "hcaptcha" | ||||
| 	MCaptcha     = "mcaptcha" | ||||
| 	CfTurnstile  = "cfturnstile" | ||||
| ) | ||||
|  | ||||
| // Service settings | ||||
| var Service = struct { | ||||
| 	DefaultUserVisibility                   string | ||||
| @@ -105,8 +114,8 @@ func (a AllowedVisibility) ToVisibleTypeSlice() (result []structs.VisibleType) { | ||||
| 	return result | ||||
| } | ||||
|  | ||||
| func newService() { | ||||
| 	sec := Cfg.Section("service") | ||||
| func loadServiceFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("service") | ||||
| 	Service.ActiveCodeLives = sec.Key("ACTIVE_CODE_LIVE_MINUTES").MustInt(180) | ||||
| 	Service.ResetPwdCodeLives = sec.Key("RESET_PASSWD_CODE_LIVE_MINUTES").MustInt(180) | ||||
| 	Service.DisableRegistration = sec.Key("DISABLE_REGISTRATION").MustBool() | ||||
| @@ -184,11 +193,13 @@ func newService() { | ||||
| 	} | ||||
| 	Service.ValidSiteURLSchemes = schemes | ||||
|  | ||||
| 	if err := Cfg.Section("service.explore").MapTo(&Service.Explore); err != nil { | ||||
| 		log.Fatal("Failed to map service.explore settings: %v", err) | ||||
| 	mustMapSetting(rootCfg, "service.explore", &Service.Explore) | ||||
|  | ||||
| 	loadOpenIDSetting(rootCfg) | ||||
| } | ||||
|  | ||||
| 	sec = Cfg.Section("openid") | ||||
| func loadOpenIDSetting(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("openid") | ||||
| 	Service.EnableOpenIDSignIn = sec.Key("ENABLE_OPENID_SIGNIN").MustBool(!InstallLock) | ||||
| 	Service.EnableOpenIDSignUp = sec.Key("ENABLE_OPENID_SIGNUP").MustBool(!Service.DisableRegistration && Service.EnableOpenIDSignIn) | ||||
| 	pats := sec.Key("WHITELISTED_URIS").Strings(" ") | ||||
|   | ||||
| @@ -15,6 +15,7 @@ import ( | ||||
|  | ||||
| // SessionConfig defines Session settings | ||||
| var SessionConfig = struct { | ||||
| 	OriginalProvider string | ||||
| 	Provider         string | ||||
| 	// Provider configuration, it's corresponding to provider. | ||||
| 	ProviderConfig string | ||||
| @@ -39,8 +40,8 @@ var SessionConfig = struct { | ||||
| 	SameSite:    http.SameSiteLaxMode, | ||||
| } | ||||
|  | ||||
| func newSessionService() { | ||||
| 	sec := Cfg.Section("session") | ||||
| func loadSessionFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("session") | ||||
| 	SessionConfig.Provider = sec.Key("PROVIDER").In("memory", | ||||
| 		[]string{"memory", "file", "redis", "mysql", "postgres", "couchbase", "memcache", "db"}) | ||||
| 	SessionConfig.ProviderConfig = strings.Trim(sec.Key("PROVIDER_CONFIG").MustString(path.Join(AppDataPath, "sessions")), "\" ") | ||||
| @@ -67,6 +68,7 @@ func newSessionService() { | ||||
| 		log.Fatal("Can't shadow session config: %v", err) | ||||
| 	} | ||||
| 	SessionConfig.ProviderConfig = string(shadowConfig) | ||||
| 	SessionConfig.OriginalProvider = SessionConfig.Provider | ||||
| 	SessionConfig.Provider = "VirtualSession" | ||||
|  | ||||
| 	log.Info("Session Service Enabled") | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										197
									
								
								modules/setting/ssh.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								modules/setting/ssh.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,197 @@ | ||||
| // Copyright 2023 The Gitea Authors. All rights reserved. | ||||
| // SPDX-License-Identifier: MIT | ||||
|  | ||||
| package setting | ||||
|  | ||||
| import ( | ||||
| 	"os" | ||||
| 	"path" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"text/template" | ||||
| 	"time" | ||||
|  | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
|  | ||||
| 	gossh "golang.org/x/crypto/ssh" | ||||
| ) | ||||
|  | ||||
| var SSH = struct { | ||||
| 	Disabled                              bool               `ini:"DISABLE_SSH"` | ||||
| 	StartBuiltinServer                    bool               `ini:"START_SSH_SERVER"` | ||||
| 	BuiltinServerUser                     string             `ini:"BUILTIN_SSH_SERVER_USER"` | ||||
| 	UseProxyProtocol                      bool               `ini:"SSH_SERVER_USE_PROXY_PROTOCOL"` | ||||
| 	Domain                                string             `ini:"SSH_DOMAIN"` | ||||
| 	Port                                  int                `ini:"SSH_PORT"` | ||||
| 	User                                  string             `ini:"SSH_USER"` | ||||
| 	ListenHost                            string             `ini:"SSH_LISTEN_HOST"` | ||||
| 	ListenPort                            int                `ini:"SSH_LISTEN_PORT"` | ||||
| 	RootPath                              string             `ini:"SSH_ROOT_PATH"` | ||||
| 	ServerCiphers                         []string           `ini:"SSH_SERVER_CIPHERS"` | ||||
| 	ServerKeyExchanges                    []string           `ini:"SSH_SERVER_KEY_EXCHANGES"` | ||||
| 	ServerMACs                            []string           `ini:"SSH_SERVER_MACS"` | ||||
| 	ServerHostKeys                        []string           `ini:"SSH_SERVER_HOST_KEYS"` | ||||
| 	KeyTestPath                           string             `ini:"SSH_KEY_TEST_PATH"` | ||||
| 	KeygenPath                            string             `ini:"SSH_KEYGEN_PATH"` | ||||
| 	AuthorizedKeysBackup                  bool               `ini:"SSH_AUTHORIZED_KEYS_BACKUP"` | ||||
| 	AuthorizedPrincipalsBackup            bool               `ini:"SSH_AUTHORIZED_PRINCIPALS_BACKUP"` | ||||
| 	AuthorizedKeysCommandTemplate         string             `ini:"SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE"` | ||||
| 	AuthorizedKeysCommandTemplateTemplate *template.Template `ini:"-"` | ||||
| 	MinimumKeySizeCheck                   bool               `ini:"-"` | ||||
| 	MinimumKeySizes                       map[string]int     `ini:"-"` | ||||
| 	CreateAuthorizedKeysFile              bool               `ini:"SSH_CREATE_AUTHORIZED_KEYS_FILE"` | ||||
| 	CreateAuthorizedPrincipalsFile        bool               `ini:"SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE"` | ||||
| 	ExposeAnonymous                       bool               `ini:"SSH_EXPOSE_ANONYMOUS"` | ||||
| 	AuthorizedPrincipalsAllow             []string           `ini:"SSH_AUTHORIZED_PRINCIPALS_ALLOW"` | ||||
| 	AuthorizedPrincipalsEnabled           bool               `ini:"-"` | ||||
| 	TrustedUserCAKeys                     []string           `ini:"SSH_TRUSTED_USER_CA_KEYS"` | ||||
| 	TrustedUserCAKeysFile                 string             `ini:"SSH_TRUSTED_USER_CA_KEYS_FILENAME"` | ||||
| 	TrustedUserCAKeysParsed               []gossh.PublicKey  `ini:"-"` | ||||
| 	PerWriteTimeout                       time.Duration      `ini:"SSH_PER_WRITE_TIMEOUT"` | ||||
| 	PerWritePerKbTimeout                  time.Duration      `ini:"SSH_PER_WRITE_PER_KB_TIMEOUT"` | ||||
| }{ | ||||
| 	Disabled:                      false, | ||||
| 	StartBuiltinServer:            false, | ||||
| 	Domain:                        "", | ||||
| 	Port:                          22, | ||||
| 	ServerCiphers:                 []string{"chacha20-poly1305@openssh.com", "aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "aes256-gcm@openssh.com"}, | ||||
| 	ServerKeyExchanges:            []string{"curve25519-sha256", "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "diffie-hellman-group14-sha256", "diffie-hellman-group14-sha1"}, | ||||
| 	ServerMACs:                    []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1"}, | ||||
| 	KeygenPath:                    "ssh-keygen", | ||||
| 	MinimumKeySizeCheck:           true, | ||||
| 	MinimumKeySizes:               map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 2047}, | ||||
| 	ServerHostKeys:                []string{"ssh/gitea.rsa", "ssh/gogs.rsa"}, | ||||
| 	AuthorizedKeysCommandTemplate: "{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}", | ||||
| 	PerWriteTimeout:               PerWriteTimeout, | ||||
| 	PerWritePerKbTimeout:          PerWritePerKbTimeout, | ||||
| } | ||||
|  | ||||
| func parseAuthorizedPrincipalsAllow(values []string) ([]string, bool) { | ||||
| 	anything := false | ||||
| 	email := false | ||||
| 	username := false | ||||
| 	for _, value := range values { | ||||
| 		v := strings.ToLower(strings.TrimSpace(value)) | ||||
| 		switch v { | ||||
| 		case "off": | ||||
| 			return []string{"off"}, false | ||||
| 		case "email": | ||||
| 			email = true | ||||
| 		case "username": | ||||
| 			username = true | ||||
| 		case "anything": | ||||
| 			anything = true | ||||
| 		} | ||||
| 	} | ||||
| 	if anything { | ||||
| 		return []string{"anything"}, true | ||||
| 	} | ||||
|  | ||||
| 	authorizedPrincipalsAllow := []string{} | ||||
| 	if username { | ||||
| 		authorizedPrincipalsAllow = append(authorizedPrincipalsAllow, "username") | ||||
| 	} | ||||
| 	if email { | ||||
| 		authorizedPrincipalsAllow = append(authorizedPrincipalsAllow, "email") | ||||
| 	} | ||||
|  | ||||
| 	return authorizedPrincipalsAllow, true | ||||
| } | ||||
|  | ||||
| func loadSSHFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("server") | ||||
| 	if len(SSH.Domain) == 0 { | ||||
| 		SSH.Domain = Domain | ||||
| 	} | ||||
|  | ||||
| 	homeDir, err := util.HomeDir() | ||||
| 	if err != nil { | ||||
| 		log.Fatal("Failed to get home directory: %v", err) | ||||
| 	} | ||||
| 	homeDir = strings.ReplaceAll(homeDir, "\\", "/") | ||||
|  | ||||
| 	SSH.RootPath = path.Join(homeDir, ".ssh") | ||||
| 	serverCiphers := sec.Key("SSH_SERVER_CIPHERS").Strings(",") | ||||
| 	if len(serverCiphers) > 0 { | ||||
| 		SSH.ServerCiphers = serverCiphers | ||||
| 	} | ||||
| 	serverKeyExchanges := sec.Key("SSH_SERVER_KEY_EXCHANGES").Strings(",") | ||||
| 	if len(serverKeyExchanges) > 0 { | ||||
| 		SSH.ServerKeyExchanges = serverKeyExchanges | ||||
| 	} | ||||
| 	serverMACs := sec.Key("SSH_SERVER_MACS").Strings(",") | ||||
| 	if len(serverMACs) > 0 { | ||||
| 		SSH.ServerMACs = serverMACs | ||||
| 	} | ||||
| 	SSH.KeyTestPath = os.TempDir() | ||||
| 	if err = sec.MapTo(&SSH); err != nil { | ||||
| 		log.Fatal("Failed to map SSH settings: %v", err) | ||||
| 	} | ||||
| 	for i, key := range SSH.ServerHostKeys { | ||||
| 		if !filepath.IsAbs(key) { | ||||
| 			SSH.ServerHostKeys[i] = filepath.Join(AppDataPath, key) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	SSH.KeygenPath = sec.Key("SSH_KEYGEN_PATH").MustString("ssh-keygen") | ||||
| 	SSH.Port = sec.Key("SSH_PORT").MustInt(22) | ||||
| 	SSH.ListenPort = sec.Key("SSH_LISTEN_PORT").MustInt(SSH.Port) | ||||
| 	SSH.UseProxyProtocol = sec.Key("SSH_SERVER_USE_PROXY_PROTOCOL").MustBool(false) | ||||
|  | ||||
| 	// When disable SSH, start builtin server value is ignored. | ||||
| 	if SSH.Disabled { | ||||
| 		SSH.StartBuiltinServer = false | ||||
| 	} | ||||
|  | ||||
| 	SSH.TrustedUserCAKeysFile = sec.Key("SSH_TRUSTED_USER_CA_KEYS_FILENAME").MustString(filepath.Join(SSH.RootPath, "gitea-trusted-user-ca-keys.pem")) | ||||
|  | ||||
| 	for _, caKey := range SSH.TrustedUserCAKeys { | ||||
| 		pubKey, _, _, _, err := gossh.ParseAuthorizedKey([]byte(caKey)) | ||||
| 		if err != nil { | ||||
| 			log.Fatal("Failed to parse TrustedUserCaKeys: %s %v", caKey, err) | ||||
| 		} | ||||
|  | ||||
| 		SSH.TrustedUserCAKeysParsed = append(SSH.TrustedUserCAKeysParsed, pubKey) | ||||
| 	} | ||||
| 	if len(SSH.TrustedUserCAKeys) > 0 { | ||||
| 		// Set the default as email,username otherwise we can leave it empty | ||||
| 		sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").MustString("username,email") | ||||
| 	} else { | ||||
| 		sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").MustString("off") | ||||
| 	} | ||||
|  | ||||
| 	SSH.AuthorizedPrincipalsAllow, SSH.AuthorizedPrincipalsEnabled = parseAuthorizedPrincipalsAllow(sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").Strings(",")) | ||||
|  | ||||
| 	SSH.MinimumKeySizeCheck = sec.Key("MINIMUM_KEY_SIZE_CHECK").MustBool(SSH.MinimumKeySizeCheck) | ||||
| 	minimumKeySizes := rootCfg.Section("ssh.minimum_key_sizes").Keys() | ||||
| 	for _, key := range minimumKeySizes { | ||||
| 		if key.MustInt() != -1 { | ||||
| 			SSH.MinimumKeySizes[strings.ToLower(key.Name())] = key.MustInt() | ||||
| 		} else { | ||||
| 			delete(SSH.MinimumKeySizes, strings.ToLower(key.Name())) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	SSH.AuthorizedKeysBackup = sec.Key("SSH_AUTHORIZED_KEYS_BACKUP").MustBool(true) | ||||
| 	SSH.CreateAuthorizedKeysFile = sec.Key("SSH_CREATE_AUTHORIZED_KEYS_FILE").MustBool(true) | ||||
|  | ||||
| 	SSH.AuthorizedPrincipalsBackup = false | ||||
| 	SSH.CreateAuthorizedPrincipalsFile = false | ||||
| 	if SSH.AuthorizedPrincipalsEnabled { | ||||
| 		SSH.AuthorizedPrincipalsBackup = sec.Key("SSH_AUTHORIZED_PRINCIPALS_BACKUP").MustBool(true) | ||||
| 		SSH.CreateAuthorizedPrincipalsFile = sec.Key("SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE").MustBool(true) | ||||
| 	} | ||||
|  | ||||
| 	SSH.ExposeAnonymous = sec.Key("SSH_EXPOSE_ANONYMOUS").MustBool(false) | ||||
| 	SSH.AuthorizedKeysCommandTemplate = sec.Key("SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE").MustString(SSH.AuthorizedKeysCommandTemplate) | ||||
|  | ||||
| 	SSH.AuthorizedKeysCommandTemplateTemplate = template.Must(template.New("").Parse(SSH.AuthorizedKeysCommandTemplate)) | ||||
|  | ||||
| 	SSH.PerWriteTimeout = sec.Key("SSH_PER_WRITE_TIMEOUT").MustDuration(PerWriteTimeout) | ||||
| 	SSH.PerWritePerKbTimeout = sec.Key("SSH_PER_WRITE_PER_KB_TIMEOUT").MustDuration(PerWritePerKbTimeout) | ||||
|  | ||||
| 	// ensure parseRunModeSetting has been executed before this | ||||
| 	SSH.BuiltinServerUser = rootCfg.Section("server").Key("BUILTIN_SSH_SERVER_USER").MustString(RunUser) | ||||
| 	SSH.User = rootCfg.Section("server").Key("SSH_USER").MustString(SSH.BuiltinServerUser) | ||||
| } | ||||
| @@ -30,9 +30,9 @@ func (s *Storage) MapTo(v interface{}) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func getStorage(name, typ string, targetSec *ini.Section) Storage { | ||||
| func getStorage(rootCfg ConfigProvider, name, typ string, targetSec *ini.Section) Storage { | ||||
| 	const sectionName = "storage" | ||||
| 	sec := Cfg.Section(sectionName) | ||||
| 	sec := rootCfg.Section(sectionName) | ||||
|  | ||||
| 	// Global Defaults | ||||
| 	sec.Key("MINIO_ENDPOINT").MustString("localhost:9000") | ||||
| @@ -43,7 +43,7 @@ func getStorage(name, typ string, targetSec *ini.Section) Storage { | ||||
| 	sec.Key("MINIO_USE_SSL").MustBool(false) | ||||
|  | ||||
| 	if targetSec == nil { | ||||
| 		targetSec, _ = Cfg.NewSection(name) | ||||
| 		targetSec, _ = rootCfg.NewSection(name) | ||||
| 	} | ||||
|  | ||||
| 	var storage Storage | ||||
| @@ -51,12 +51,12 @@ func getStorage(name, typ string, targetSec *ini.Section) Storage { | ||||
| 	storage.Type = typ | ||||
|  | ||||
| 	overrides := make([]*ini.Section, 0, 3) | ||||
| 	nameSec, err := Cfg.GetSection(sectionName + "." + name) | ||||
| 	nameSec, err := rootCfg.GetSection(sectionName + "." + name) | ||||
| 	if err == nil { | ||||
| 		overrides = append(overrides, nameSec) | ||||
| 	} | ||||
|  | ||||
| 	typeSec, err := Cfg.GetSection(sectionName + "." + typ) | ||||
| 	typeSec, err := rootCfg.GetSection(sectionName + "." + typ) | ||||
| 	if err == nil { | ||||
| 		overrides = append(overrides, typeSec) | ||||
| 		nextType := typeSec.Key("STORAGE_TYPE").String() | ||||
|   | ||||
| @@ -20,11 +20,12 @@ MINIO_BUCKET = gitea-attachment | ||||
| STORAGE_TYPE = minio | ||||
| MINIO_ENDPOINT = my_minio:9000 | ||||
| ` | ||||
| 	Cfg, _ = ini.Load([]byte(iniStr)) | ||||
| 	cfg, err := ini.Load([]byte(iniStr)) | ||||
| 	assert.NoError(t, err) | ||||
|  | ||||
| 	sec := Cfg.Section("attachment") | ||||
| 	sec := cfg.Section("attachment") | ||||
| 	storageType := sec.Key("STORAGE_TYPE").MustString("") | ||||
| 	storage := getStorage("attachments", storageType, sec) | ||||
| 	storage := getStorage(cfg, "attachments", storageType, sec) | ||||
|  | ||||
| 	assert.EqualValues(t, "minio", storage.Type) | ||||
| 	assert.EqualValues(t, "my_minio:9000", storage.Section.Key("MINIO_ENDPOINT").String()) | ||||
| @@ -42,11 +43,12 @@ MINIO_BUCKET = gitea-attachment | ||||
| [storage.minio] | ||||
| MINIO_BUCKET = gitea | ||||
| ` | ||||
| 	Cfg, _ = ini.Load([]byte(iniStr)) | ||||
| 	cfg, err := ini.Load([]byte(iniStr)) | ||||
| 	assert.NoError(t, err) | ||||
|  | ||||
| 	sec := Cfg.Section("attachment") | ||||
| 	sec := cfg.Section("attachment") | ||||
| 	storageType := sec.Key("STORAGE_TYPE").MustString("") | ||||
| 	storage := getStorage("attachments", storageType, sec) | ||||
| 	storage := getStorage(cfg, "attachments", storageType, sec) | ||||
|  | ||||
| 	assert.EqualValues(t, "minio", storage.Type) | ||||
| 	assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String()) | ||||
| @@ -63,11 +65,12 @@ MINIO_BUCKET = gitea-minio | ||||
| [storage] | ||||
| MINIO_BUCKET = gitea | ||||
| ` | ||||
| 	Cfg, _ = ini.Load([]byte(iniStr)) | ||||
| 	cfg, err := ini.Load([]byte(iniStr)) | ||||
| 	assert.NoError(t, err) | ||||
|  | ||||
| 	sec := Cfg.Section("attachment") | ||||
| 	sec := cfg.Section("attachment") | ||||
| 	storageType := sec.Key("STORAGE_TYPE").MustString("") | ||||
| 	storage := getStorage("attachments", storageType, sec) | ||||
| 	storage := getStorage(cfg, "attachments", storageType, sec) | ||||
|  | ||||
| 	assert.EqualValues(t, "minio", storage.Type) | ||||
| 	assert.EqualValues(t, "gitea-minio", storage.Section.Key("MINIO_BUCKET").String()) | ||||
| @@ -85,22 +88,24 @@ MINIO_BUCKET = gitea | ||||
| [storage] | ||||
| STORAGE_TYPE = local | ||||
| ` | ||||
| 	Cfg, _ = ini.Load([]byte(iniStr)) | ||||
| 	cfg, err := ini.Load([]byte(iniStr)) | ||||
| 	assert.NoError(t, err) | ||||
|  | ||||
| 	sec := Cfg.Section("attachment") | ||||
| 	sec := cfg.Section("attachment") | ||||
| 	storageType := sec.Key("STORAGE_TYPE").MustString("") | ||||
| 	storage := getStorage("attachments", storageType, sec) | ||||
| 	storage := getStorage(cfg, "attachments", storageType, sec) | ||||
|  | ||||
| 	assert.EqualValues(t, "minio", storage.Type) | ||||
| 	assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String()) | ||||
| } | ||||
|  | ||||
| func Test_getStorageGetDefaults(t *testing.T) { | ||||
| 	Cfg, _ = ini.Load([]byte("")) | ||||
| 	cfg, err := ini.Load([]byte("")) | ||||
| 	assert.NoError(t, err) | ||||
|  | ||||
| 	sec := Cfg.Section("attachment") | ||||
| 	sec := cfg.Section("attachment") | ||||
| 	storageType := sec.Key("STORAGE_TYPE").MustString("") | ||||
| 	storage := getStorage("attachments", storageType, sec) | ||||
| 	storage := getStorage(cfg, "attachments", storageType, sec) | ||||
|  | ||||
| 	assert.EqualValues(t, "gitea", storage.Section.Key("MINIO_BUCKET").String()) | ||||
| } | ||||
| @@ -116,26 +121,27 @@ MINIO_BUCKET = gitea-attachment | ||||
| [storage] | ||||
| MINIO_BUCKET = gitea-storage | ||||
| ` | ||||
| 	Cfg, _ = ini.Load([]byte(iniStr)) | ||||
| 	cfg, err := ini.Load([]byte(iniStr)) | ||||
| 	assert.NoError(t, err) | ||||
|  | ||||
| 	{ | ||||
| 		sec := Cfg.Section("attachment") | ||||
| 		sec := cfg.Section("attachment") | ||||
| 		storageType := sec.Key("STORAGE_TYPE").MustString("") | ||||
| 		storage := getStorage("attachments", storageType, sec) | ||||
| 		storage := getStorage(cfg, "attachments", storageType, sec) | ||||
|  | ||||
| 		assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String()) | ||||
| 	} | ||||
| 	{ | ||||
| 		sec := Cfg.Section("lfs") | ||||
| 		sec := cfg.Section("lfs") | ||||
| 		storageType := sec.Key("STORAGE_TYPE").MustString("") | ||||
| 		storage := getStorage("lfs", storageType, sec) | ||||
| 		storage := getStorage(cfg, "lfs", storageType, sec) | ||||
|  | ||||
| 		assert.EqualValues(t, "gitea-lfs", storage.Section.Key("MINIO_BUCKET").String()) | ||||
| 	} | ||||
| 	{ | ||||
| 		sec := Cfg.Section("avatar") | ||||
| 		sec := cfg.Section("avatar") | ||||
| 		storageType := sec.Key("STORAGE_TYPE").MustString("") | ||||
| 		storage := getStorage("avatars", storageType, sec) | ||||
| 		storage := getStorage(cfg, "avatars", storageType, sec) | ||||
|  | ||||
| 		assert.EqualValues(t, "gitea-storage", storage.Section.Key("MINIO_BUCKET").String()) | ||||
| 	} | ||||
| @@ -149,19 +155,20 @@ STORAGE_TYPE = lfs | ||||
| [storage.lfs] | ||||
| MINIO_BUCKET = gitea-storage | ||||
| ` | ||||
| 	Cfg, _ = ini.Load([]byte(iniStr)) | ||||
| 	cfg, err := ini.Load([]byte(iniStr)) | ||||
| 	assert.NoError(t, err) | ||||
|  | ||||
| 	{ | ||||
| 		sec := Cfg.Section("attachment") | ||||
| 		sec := cfg.Section("attachment") | ||||
| 		storageType := sec.Key("STORAGE_TYPE").MustString("") | ||||
| 		storage := getStorage("attachments", storageType, sec) | ||||
| 		storage := getStorage(cfg, "attachments", storageType, sec) | ||||
|  | ||||
| 		assert.EqualValues(t, "gitea-storage", storage.Section.Key("MINIO_BUCKET").String()) | ||||
| 	} | ||||
| 	{ | ||||
| 		sec := Cfg.Section("lfs") | ||||
| 		sec := cfg.Section("lfs") | ||||
| 		storageType := sec.Key("STORAGE_TYPE").MustString("") | ||||
| 		storage := getStorage("lfs", storageType, sec) | ||||
| 		storage := getStorage(cfg, "lfs", storageType, sec) | ||||
|  | ||||
| 		assert.EqualValues(t, "gitea-storage", storage.Section.Key("MINIO_BUCKET").String()) | ||||
| 	} | ||||
| @@ -172,11 +179,12 @@ func Test_getStorageInheritStorageType(t *testing.T) { | ||||
| [storage] | ||||
| STORAGE_TYPE = minio | ||||
| ` | ||||
| 	Cfg, _ = ini.Load([]byte(iniStr)) | ||||
| 	cfg, err := ini.Load([]byte(iniStr)) | ||||
| 	assert.NoError(t, err) | ||||
|  | ||||
| 	sec := Cfg.Section("attachment") | ||||
| 	sec := cfg.Section("attachment") | ||||
| 	storageType := sec.Key("STORAGE_TYPE").MustString("") | ||||
| 	storage := getStorage("attachments", storageType, sec) | ||||
| 	storage := getStorage(cfg, "attachments", storageType, sec) | ||||
|  | ||||
| 	assert.EqualValues(t, "minio", storage.Type) | ||||
| } | ||||
| @@ -186,11 +194,12 @@ func Test_getStorageInheritNameSectionType(t *testing.T) { | ||||
| [storage.attachments] | ||||
| STORAGE_TYPE = minio | ||||
| ` | ||||
| 	Cfg, _ = ini.Load([]byte(iniStr)) | ||||
| 	cfg, err := ini.Load([]byte(iniStr)) | ||||
| 	assert.NoError(t, err) | ||||
|  | ||||
| 	sec := Cfg.Section("attachment") | ||||
| 	sec := cfg.Section("attachment") | ||||
| 	storageType := sec.Key("STORAGE_TYPE").MustString("") | ||||
| 	storage := getStorage("attachments", storageType, sec) | ||||
| 	storage := getStorage(cfg, "attachments", storageType, sec) | ||||
|  | ||||
| 	assert.EqualValues(t, "minio", storage.Type) | ||||
| } | ||||
|   | ||||
| @@ -5,13 +5,13 @@ package setting | ||||
|  | ||||
| // FIXME: DEPRECATED to be removed in v1.18.0 | ||||
| // - will need to set default for [queue.task] LENGTH to 1000 though | ||||
| func newTaskService() { | ||||
| 	taskSec := Cfg.Section("task") | ||||
| 	queueTaskSec := Cfg.Section("queue.task") | ||||
| func loadTaskFrom(rootCfg ConfigProvider) { | ||||
| 	taskSec := rootCfg.Section("task") | ||||
| 	queueTaskSec := rootCfg.Section("queue.task") | ||||
|  | ||||
| 	deprecatedSetting("task", "QUEUE_TYPE", "queue.task", "TYPE") | ||||
| 	deprecatedSetting("task", "QUEUE_CONN_STR", "queue.task", "CONN_STR") | ||||
| 	deprecatedSetting("task", "QUEUE_LENGTH", "queue.task", "LENGTH") | ||||
| 	deprecatedSetting(rootCfg, "task", "QUEUE_TYPE", "queue.task", "TYPE") | ||||
| 	deprecatedSetting(rootCfg, "task", "QUEUE_CONN_STR", "queue.task", "CONN_STR") | ||||
| 	deprecatedSetting(rootCfg, "task", "QUEUE_LENGTH", "queue.task", "LENGTH") | ||||
|  | ||||
| 	switch taskSec.Key("QUEUE_TYPE").MustString("channel") { | ||||
| 	case "channel": | ||||
|   | ||||
							
								
								
									
										64
									
								
								modules/setting/time.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								modules/setting/time.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| // Copyright 2023 The Gitea Authors. All rights reserved. | ||||
| // SPDX-License-Identifier: MIT | ||||
|  | ||||
| package setting | ||||
|  | ||||
| import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	// Time settings | ||||
| 	TimeFormat string | ||||
| 	// UILocation is the location on the UI, so that we can display the time on UI. | ||||
| 	DefaultUILocation = time.Local | ||||
| ) | ||||
|  | ||||
| func loadTimeFrom(rootCfg ConfigProvider) { | ||||
| 	timeFormatKey := rootCfg.Section("time").Key("FORMAT").MustString("") | ||||
| 	if timeFormatKey != "" { | ||||
| 		TimeFormat = map[string]string{ | ||||
| 			"ANSIC":       time.ANSIC, | ||||
| 			"UnixDate":    time.UnixDate, | ||||
| 			"RubyDate":    time.RubyDate, | ||||
| 			"RFC822":      time.RFC822, | ||||
| 			"RFC822Z":     time.RFC822Z, | ||||
| 			"RFC850":      time.RFC850, | ||||
| 			"RFC1123":     time.RFC1123, | ||||
| 			"RFC1123Z":    time.RFC1123Z, | ||||
| 			"RFC3339":     time.RFC3339, | ||||
| 			"RFC3339Nano": time.RFC3339Nano, | ||||
| 			"Kitchen":     time.Kitchen, | ||||
| 			"Stamp":       time.Stamp, | ||||
| 			"StampMilli":  time.StampMilli, | ||||
| 			"StampMicro":  time.StampMicro, | ||||
| 			"StampNano":   time.StampNano, | ||||
| 		}[timeFormatKey] | ||||
| 		// When the TimeFormatKey does not exist in the previous map e.g.'2006-01-02 15:04:05' | ||||
| 		if len(TimeFormat) == 0 { | ||||
| 			TimeFormat = timeFormatKey | ||||
| 			TestTimeFormat, _ := time.Parse(TimeFormat, TimeFormat) | ||||
| 			if TestTimeFormat.Format(time.RFC3339) != "2006-01-02T15:04:05Z" { | ||||
| 				log.Warn("Provided TimeFormat: %s does not create a fully specified date and time.", TimeFormat) | ||||
| 				log.Warn("In order to display dates and times correctly please check your time format has 2006, 01, 02, 15, 04 and 05") | ||||
| 			} | ||||
| 			log.Trace("Custom TimeFormat: %s", TimeFormat) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	zone := rootCfg.Section("time").Key("DEFAULT_UI_LOCATION").String() | ||||
| 	if zone != "" { | ||||
| 		var err error | ||||
| 		DefaultUILocation, err = time.LoadLocation(zone) | ||||
| 		if err != nil { | ||||
| 			log.Fatal("Load time zone failed: %v", err) | ||||
| 		} else { | ||||
| 			log.Info("Default UI Location is %v", zone) | ||||
| 		} | ||||
| 	} | ||||
| 	if DefaultUILocation == nil { | ||||
| 		DefaultUILocation = time.Local | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										152
									
								
								modules/setting/ui.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								modules/setting/ui.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | ||||
| // Copyright 2023 The Gitea Authors. All rights reserved. | ||||
| // SPDX-License-Identifier: MIT | ||||
|  | ||||
| package setting | ||||
|  | ||||
| import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"code.gitea.io/gitea/modules/container" | ||||
| ) | ||||
|  | ||||
| // UI settings | ||||
| var UI = struct { | ||||
| 	ExplorePagingNum      int | ||||
| 	SitemapPagingNum      int | ||||
| 	IssuePagingNum        int | ||||
| 	RepoSearchPagingNum   int | ||||
| 	MembersPagingNum      int | ||||
| 	FeedMaxCommitNum      int | ||||
| 	FeedPagingNum         int | ||||
| 	PackagesPagingNum     int | ||||
| 	GraphMaxCommitNum     int | ||||
| 	CodeCommentLines      int | ||||
| 	ReactionMaxUserNum    int | ||||
| 	ThemeColorMetaTag     string | ||||
| 	MaxDisplayFileSize    int64 | ||||
| 	ShowUserEmail         bool | ||||
| 	DefaultShowFullName   bool | ||||
| 	DefaultTheme          string | ||||
| 	Themes                []string | ||||
| 	Reactions             []string | ||||
| 	ReactionsLookup       container.Set[string] `ini:"-"` | ||||
| 	CustomEmojis          []string | ||||
| 	CustomEmojisMap       map[string]string `ini:"-"` | ||||
| 	SearchRepoDescription bool | ||||
| 	UseServiceWorker      bool | ||||
| 	OnlyShowRelevantRepos bool | ||||
|  | ||||
| 	Notification struct { | ||||
| 		MinTimeout            time.Duration | ||||
| 		TimeoutStep           time.Duration | ||||
| 		MaxTimeout            time.Duration | ||||
| 		EventSourceUpdateTime time.Duration | ||||
| 	} `ini:"ui.notification"` | ||||
|  | ||||
| 	SVG struct { | ||||
| 		Enabled bool `ini:"ENABLE_RENDER"` | ||||
| 	} `ini:"ui.svg"` | ||||
|  | ||||
| 	CSV struct { | ||||
| 		MaxFileSize int64 | ||||
| 	} `ini:"ui.csv"` | ||||
|  | ||||
| 	Admin struct { | ||||
| 		UserPagingNum   int | ||||
| 		RepoPagingNum   int | ||||
| 		NoticePagingNum int | ||||
| 		OrgPagingNum    int | ||||
| 	} `ini:"ui.admin"` | ||||
| 	User struct { | ||||
| 		RepoPagingNum int | ||||
| 	} `ini:"ui.user"` | ||||
| 	Meta struct { | ||||
| 		Author      string | ||||
| 		Description string | ||||
| 		Keywords    string | ||||
| 	} `ini:"ui.meta"` | ||||
| }{ | ||||
| 	ExplorePagingNum:    20, | ||||
| 	SitemapPagingNum:    20, | ||||
| 	IssuePagingNum:      20, | ||||
| 	RepoSearchPagingNum: 20, | ||||
| 	MembersPagingNum:    20, | ||||
| 	FeedMaxCommitNum:    5, | ||||
| 	FeedPagingNum:       20, | ||||
| 	PackagesPagingNum:   20, | ||||
| 	GraphMaxCommitNum:   100, | ||||
| 	CodeCommentLines:    4, | ||||
| 	ReactionMaxUserNum:  10, | ||||
| 	ThemeColorMetaTag:   `#6cc644`, | ||||
| 	MaxDisplayFileSize:  8388608, | ||||
| 	DefaultTheme:        `auto`, | ||||
| 	Themes:              []string{`auto`, `gitea`, `arc-green`}, | ||||
| 	Reactions:           []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`}, | ||||
| 	CustomEmojis:        []string{`git`, `gitea`, `codeberg`, `gitlab`, `github`, `gogs`}, | ||||
| 	CustomEmojisMap:     map[string]string{"git": ":git:", "gitea": ":gitea:", "codeberg": ":codeberg:", "gitlab": ":gitlab:", "github": ":github:", "gogs": ":gogs:"}, | ||||
| 	Notification: struct { | ||||
| 		MinTimeout            time.Duration | ||||
| 		TimeoutStep           time.Duration | ||||
| 		MaxTimeout            time.Duration | ||||
| 		EventSourceUpdateTime time.Duration | ||||
| 	}{ | ||||
| 		MinTimeout:            10 * time.Second, | ||||
| 		TimeoutStep:           10 * time.Second, | ||||
| 		MaxTimeout:            60 * time.Second, | ||||
| 		EventSourceUpdateTime: 10 * time.Second, | ||||
| 	}, | ||||
| 	SVG: struct { | ||||
| 		Enabled bool `ini:"ENABLE_RENDER"` | ||||
| 	}{ | ||||
| 		Enabled: true, | ||||
| 	}, | ||||
| 	CSV: struct { | ||||
| 		MaxFileSize int64 | ||||
| 	}{ | ||||
| 		MaxFileSize: 524288, | ||||
| 	}, | ||||
| 	Admin: struct { | ||||
| 		UserPagingNum   int | ||||
| 		RepoPagingNum   int | ||||
| 		NoticePagingNum int | ||||
| 		OrgPagingNum    int | ||||
| 	}{ | ||||
| 		UserPagingNum:   50, | ||||
| 		RepoPagingNum:   50, | ||||
| 		NoticePagingNum: 25, | ||||
| 		OrgPagingNum:    50, | ||||
| 	}, | ||||
| 	User: struct { | ||||
| 		RepoPagingNum int | ||||
| 	}{ | ||||
| 		RepoPagingNum: 15, | ||||
| 	}, | ||||
| 	Meta: struct { | ||||
| 		Author      string | ||||
| 		Description string | ||||
| 		Keywords    string | ||||
| 	}{ | ||||
| 		Author:      "Gitea - Git with a cup of tea", | ||||
| 		Description: "Gitea (Git with a cup of tea) is a painless self-hosted Git service written in Go", | ||||
| 		Keywords:    "go,git,self-hosted,gitea", | ||||
| 	}, | ||||
| } | ||||
|  | ||||
| func loadUIFrom(rootCfg ConfigProvider) { | ||||
| 	mustMapSetting(rootCfg, "ui", &UI) | ||||
| 	sec := rootCfg.Section("ui") | ||||
| 	UI.ShowUserEmail = sec.Key("SHOW_USER_EMAIL").MustBool(true) | ||||
| 	UI.DefaultShowFullName = sec.Key("DEFAULT_SHOW_FULL_NAME").MustBool(false) | ||||
| 	UI.SearchRepoDescription = sec.Key("SEARCH_REPO_DESCRIPTION").MustBool(true) | ||||
| 	UI.UseServiceWorker = sec.Key("USE_SERVICE_WORKER").MustBool(false) | ||||
| 	UI.OnlyShowRelevantRepos = sec.Key("ONLY_SHOW_RELEVANT_REPOS").MustBool(false) | ||||
|  | ||||
| 	UI.ReactionsLookup = make(container.Set[string]) | ||||
| 	for _, reaction := range UI.Reactions { | ||||
| 		UI.ReactionsLookup.Add(reaction) | ||||
| 	} | ||||
| 	UI.CustomEmojisMap = make(map[string]string) | ||||
| 	for _, emoji := range UI.CustomEmojis { | ||||
| 		UI.CustomEmojisMap[emoji] = ":" + emoji + ":" | ||||
| 	} | ||||
| } | ||||
| @@ -29,8 +29,8 @@ var Webhook = struct { | ||||
| 	ProxyHosts:     []string{}, | ||||
| } | ||||
|  | ||||
| func newWebhookService() { | ||||
| 	sec := Cfg.Section("webhook") | ||||
| func loadWebhookFrom(rootCfg ConfigProvider) { | ||||
| 	sec := rootCfg.Section("webhook") | ||||
| 	Webhook.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000) | ||||
| 	Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5) | ||||
| 	Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool() | ||||
|   | ||||
| @@ -13,8 +13,8 @@ import ( | ||||
| ) | ||||
|  | ||||
| func TestMain(m *testing.M) { | ||||
| 	setting.LoadForTest() | ||||
| 	setting.NewQueueService() | ||||
| 	setting.InitProviderAndLoadCommonSettingsForTest() | ||||
| 	setting.LoadQueueSettings() | ||||
| 	unittest.MainTest(m, &unittest.TestOptions{ | ||||
| 		GiteaRootPath: filepath.Join("..", "..", "..", ".."), | ||||
| 		SetUp:         webhook_service.Init, | ||||
|   | ||||
| @@ -50,11 +50,11 @@ func Middlewares() []func(http.Handler) http.Handler { | ||||
|  | ||||
| 	handlers = append(handlers, middleware.StripSlashes) | ||||
|  | ||||
| 	if !setting.DisableRouterLog { | ||||
| 	if !setting.Log.DisableRouterLog { | ||||
| 		handlers = append(handlers, routing.NewLoggerHandler()) | ||||
| 	} | ||||
|  | ||||
| 	if setting.EnableAccessLog { | ||||
| 	if setting.Log.EnableAccessLog { | ||||
| 		handlers = append(handlers, context.AccessLogger()) | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -73,7 +73,7 @@ func mustInitCtx(ctx context.Context, fn func(ctx context.Context) error) { | ||||
|  | ||||
| // InitGitServices init new services for git, this is also called in `contrib/pr/checkout.go` | ||||
| func InitGitServices() { | ||||
| 	setting.NewServices() | ||||
| 	setting.LoadSettings() | ||||
| 	mustInit(storage.Init) | ||||
| 	mustInit(repo_service.Init) | ||||
| } | ||||
| @@ -119,7 +119,7 @@ func GlobalInitInstalled(ctx context.Context) { | ||||
| 	log.Info("AppPath: %s", setting.AppPath) | ||||
| 	log.Info("AppWorkPath: %s", setting.AppWorkPath) | ||||
| 	log.Info("Custom path: %s", setting.CustomPath) | ||||
| 	log.Info("Log path: %s", setting.LogRootPath) | ||||
| 	log.Info("Log path: %s", setting.Log.RootPath) | ||||
| 	log.Info("Configuration file: %s", setting.CustomConf) | ||||
| 	log.Info("Run Mode: %s", util.ToTitleCase(setting.RunMode)) | ||||
| 	log.Info("Gitea v%s%s", setting.AppVer, setting.AppBuiltWith) | ||||
| @@ -127,7 +127,7 @@ func GlobalInitInstalled(ctx context.Context) { | ||||
| 	// Setup i18n | ||||
| 	translation.InitLocales(ctx) | ||||
|  | ||||
| 	setting.NewServices() | ||||
| 	setting.LoadSettings() | ||||
| 	mustInit(storage.Init) | ||||
|  | ||||
| 	mailer.NewContext(ctx) | ||||
|   | ||||
| @@ -134,7 +134,7 @@ func Install(ctx *context.Context) { | ||||
| 	form.SSHPort = setting.SSH.Port | ||||
| 	form.HTTPPort = setting.HTTPPort | ||||
| 	form.AppURL = setting.AppURL | ||||
| 	form.LogRootPath = setting.LogRootPath | ||||
| 	form.LogRootPath = setting.Log.RootPath | ||||
|  | ||||
| 	// E-mail service settings | ||||
| 	if setting.MailService != nil { | ||||
| @@ -467,7 +467,7 @@ func SubmitInstall(ctx *context.Context) { | ||||
| 	cfg.Section("session").Key("PROVIDER").SetValue("file") | ||||
|  | ||||
| 	cfg.Section("log").Key("MODE").SetValue("console") | ||||
| 	cfg.Section("log").Key("LEVEL").SetValue(setting.LogLevel.String()) | ||||
| 	cfg.Section("log").Key("LEVEL").SetValue(setting.Log.Level.String()) | ||||
| 	cfg.Section("log").Key("ROOT_PATH").SetValue(form.LogRootPath) | ||||
| 	cfg.Section("log").Key("ROUTER").SetValue("console") | ||||
|  | ||||
|   | ||||
| @@ -15,20 +15,21 @@ import ( | ||||
|  | ||||
| // PreloadSettings preloads the configuration to check if we need to run install | ||||
| func PreloadSettings(ctx context.Context) bool { | ||||
| 	setting.LoadAllowEmpty() | ||||
| 	setting.InitProviderAllowEmpty() | ||||
| 	setting.LoadCommonSettings() | ||||
| 	if !setting.InstallLock { | ||||
| 		log.Info("AppPath: %s", setting.AppPath) | ||||
| 		log.Info("AppWorkPath: %s", setting.AppWorkPath) | ||||
| 		log.Info("Custom path: %s", setting.CustomPath) | ||||
| 		log.Info("Log path: %s", setting.LogRootPath) | ||||
| 		log.Info("Log path: %s", setting.Log.RootPath) | ||||
| 		log.Info("Configuration file: %s", setting.CustomConf) | ||||
| 		log.Info("Prepare to run install page") | ||||
| 		translation.InitLocales(ctx) | ||||
| 		if setting.EnableSQLite3 { | ||||
| 			log.Info("SQLite3 is supported") | ||||
| 		} | ||||
| 		setting.InitDBConfig() | ||||
| 		setting.NewServicesForInstall() | ||||
|  | ||||
| 		setting.LoadSettingsForInstall() | ||||
| 		svg.Init() | ||||
| 	} | ||||
|  | ||||
| @@ -37,8 +38,9 @@ func PreloadSettings(ctx context.Context) bool { | ||||
|  | ||||
| // reloadSettings reloads the existing settings and starts up the database | ||||
| func reloadSettings(ctx context.Context) { | ||||
| 	setting.LoadFromExisting() | ||||
| 	setting.InitDBConfig() | ||||
| 	setting.InitProviderFromExistingFile() | ||||
| 	setting.LoadCommonSettings() | ||||
| 	setting.LoadDBSetting() | ||||
| 	if setting.InstallLock { | ||||
| 		if err := common.InitDBEngine(ctx); err == nil { | ||||
| 			log.Info("ORM engine initialization successful!") | ||||
|   | ||||
| @@ -116,11 +116,11 @@ func AddLogger(ctx *context.PrivateContext) { | ||||
| 	} | ||||
|  | ||||
| 	if _, ok := opts.Config["level"]; !ok { | ||||
| 		opts.Config["level"] = setting.LogLevel | ||||
| 		opts.Config["level"] = setting.Log.Level | ||||
| 	} | ||||
|  | ||||
| 	if _, ok := opts.Config["stacktraceLevel"]; !ok { | ||||
| 		opts.Config["stacktraceLevel"] = setting.StacktraceLogLevel | ||||
| 		opts.Config["stacktraceLevel"] = setting.Log.StacktraceLogLevel | ||||
| 	} | ||||
|  | ||||
| 	if opts.Mode == "file" { | ||||
| @@ -135,7 +135,7 @@ func AddLogger(ctx *context.PrivateContext) { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	bufferLen := setting.Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000) | ||||
| 	bufferLen := setting.Log.BufferLength | ||||
| 	byteConfig, err := json.Marshal(opts.Config) | ||||
| 	if err != nil { | ||||
| 		log.Error("Failed to marshal log configuration: %v %v", opts.Config, err) | ||||
|   | ||||
| @@ -15,7 +15,7 @@ import ( | ||||
|  | ||||
| // SSHLog hook to response ssh log | ||||
| func SSHLog(ctx *context.PrivateContext) { | ||||
| 	if !setting.EnableSSHLog { | ||||
| 	if !setting.Log.EnableSSHLog { | ||||
| 		ctx.Status(http.StatusOK) | ||||
| 		return | ||||
| 	} | ||||
|   | ||||
| @@ -117,7 +117,7 @@ func Config(ctx *context.Context) { | ||||
| 	ctx.Data["AppUrl"] = setting.AppURL | ||||
| 	ctx.Data["Domain"] = setting.Domain | ||||
| 	ctx.Data["OfflineMode"] = setting.OfflineMode | ||||
| 	ctx.Data["DisableRouterLog"] = setting.DisableRouterLog | ||||
| 	ctx.Data["DisableRouterLog"] = setting.Log.DisableRouterLog | ||||
| 	ctx.Data["RunUser"] = setting.RunUser | ||||
| 	ctx.Data["RunMode"] = util.ToTitleCase(setting.RunMode) | ||||
| 	ctx.Data["GitVersion"] = git.VersionInfo() | ||||
| @@ -125,7 +125,7 @@ func Config(ctx *context.Context) { | ||||
| 	ctx.Data["RepoRootPath"] = setting.RepoRootPath | ||||
| 	ctx.Data["CustomRootPath"] = setting.CustomPath | ||||
| 	ctx.Data["StaticRootPath"] = setting.StaticRootPath | ||||
| 	ctx.Data["LogRootPath"] = setting.LogRootPath | ||||
| 	ctx.Data["LogRootPath"] = setting.Log.RootPath | ||||
| 	ctx.Data["ScriptType"] = setting.ScriptType | ||||
| 	ctx.Data["ReverseProxyAuthUser"] = setting.ReverseProxyAuthUser | ||||
| 	ctx.Data["ReverseProxyAuthEmail"] = setting.ReverseProxyAuthEmail | ||||
| @@ -183,10 +183,10 @@ func Config(ctx *context.Context) { | ||||
|  | ||||
| 	ctx.Data["EnvVars"] = envVars | ||||
| 	ctx.Data["Loggers"] = setting.GetLogDescriptions() | ||||
| 	ctx.Data["EnableAccessLog"] = setting.EnableAccessLog | ||||
| 	ctx.Data["AccessLogTemplate"] = setting.AccessLogTemplate | ||||
| 	ctx.Data["DisableRouterLog"] = setting.DisableRouterLog | ||||
| 	ctx.Data["EnableXORMLog"] = setting.EnableXORMLog | ||||
| 	ctx.Data["EnableAccessLog"] = setting.Log.EnableAccessLog | ||||
| 	ctx.Data["AccessLogTemplate"] = setting.Log.AccessLogTemplate | ||||
| 	ctx.Data["DisableRouterLog"] = setting.Log.DisableRouterLog | ||||
| 	ctx.Data["EnableXORMLog"] = setting.Log.EnableXORMLog | ||||
| 	ctx.Data["LogSQL"] = setting.Database.LogSQL | ||||
|  | ||||
| 	ctx.HTML(http.StatusOK, tplConfig) | ||||
|   | ||||
| @@ -15,8 +15,8 @@ import ( | ||||
| ) | ||||
|  | ||||
| func TestMain(m *testing.M) { | ||||
| 	setting.LoadForTest() | ||||
| 	setting.NewQueueService() | ||||
| 	setting.InitProviderAndLoadCommonSettingsForTest() | ||||
| 	setting.LoadQueueSettings() | ||||
|  | ||||
| 	// for tests, allow only loopback IPs | ||||
| 	setting.Webhook.AllowedHostList = hostmatcher.MatchBuiltinLoopback | ||||
|   | ||||
| @@ -87,8 +87,8 @@ func TestMain(m *testing.M) { | ||||
| 	c = routers.NormalRoutes(context.TODO()) | ||||
|  | ||||
| 	// integration test settings... | ||||
| 	if setting.Cfg != nil { | ||||
| 		testingCfg := setting.Cfg.Section("integration-tests") | ||||
| 	if setting.CfgProvider != nil { | ||||
| 		testingCfg := setting.CfgProvider.Section("integration-tests") | ||||
| 		tests.SlowTest = testingCfg.Key("SLOW_TEST").MustDuration(tests.SlowTest) | ||||
| 		tests.SlowFlush = testingCfg.Key("SLOW_FLUSH").MustDuration(tests.SlowFlush) | ||||
| 	} | ||||
|   | ||||
| @@ -57,7 +57,7 @@ func initMigrationTest(t *testing.T) func() { | ||||
| 		setting.CustomConf = giteaConf | ||||
| 	} | ||||
|  | ||||
| 	setting.LoadForTest() | ||||
| 	setting.InitProviderAndLoadCommonSettingsForTest() | ||||
|  | ||||
| 	assert.True(t, len(setting.RepoRootPath) != 0) | ||||
| 	assert.NoError(t, util.RemoveAll(setting.RepoRootPath)) | ||||
| @@ -83,8 +83,8 @@ func initMigrationTest(t *testing.T) func() { | ||||
| 	} | ||||
|  | ||||
| 	assert.NoError(t, git.InitFull(context.Background())) | ||||
| 	setting.InitDBConfig() | ||||
| 	setting.NewLogServices(true) | ||||
| 	setting.LoadDBSetting() | ||||
| 	setting.InitLogs(true) | ||||
| 	return deferFn | ||||
| } | ||||
|  | ||||
| @@ -292,7 +292,7 @@ func doMigrationTest(t *testing.T, version string) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	setting.NewXORMLogService(false) | ||||
| 	setting.InitSQLLog(false) | ||||
|  | ||||
| 	err := db.InitEngineWithMigration(context.Background(), wrappedMigrate) | ||||
| 	assert.NoError(t, err) | ||||
|   | ||||
| @@ -57,7 +57,7 @@ func InitTest(requireGitea bool) { | ||||
| 	} | ||||
|  | ||||
| 	setting.SetCustomPathAndConf("", "", "") | ||||
| 	setting.LoadForTest() | ||||
| 	setting.InitProviderAndLoadCommonSettingsForTest() | ||||
| 	setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master" | ||||
| 	_ = util.RemoveAll(repo_module.LocalCopyPath()) | ||||
|  | ||||
| @@ -65,7 +65,7 @@ func InitTest(requireGitea bool) { | ||||
| 		log.Fatal("git.InitOnceWithSync: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	setting.InitDBConfig() | ||||
| 	setting.LoadDBSetting() | ||||
| 	if err := storage.Init(); err != nil { | ||||
| 		fmt.Printf("Init storage failed: %v", err) | ||||
| 		os.Exit(1) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Lunny Xiao
					Lunny Xiao