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 { | func initDB(ctx context.Context) error { | ||||||
| 	setting.LoadFromExisting() | 	setting.InitProviderFromExistingFile() | ||||||
| 	setting.InitDBConfig() | 	setting.LoadCommonSettings() | ||||||
| 	setting.NewXORMLogService(false) | 	setting.LoadDBSetting() | ||||||
|  | 	setting.InitSQLLog(false) | ||||||
|  |  | ||||||
| 	if setting.Database.Type == "" { | 	if setting.Database.Type == "" { | ||||||
| 		log.Fatal(`Database settings are missing from the configuration file: %q. | 		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("AppPath: %s", setting.AppPath) | ||||||
| 	log.Info("AppWorkPath: %s", setting.AppWorkPath) | 	log.Info("AppWorkPath: %s", setting.AppWorkPath) | ||||||
| 	log.Info("Custom path: %s", setting.CustomPath) | 	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("Configuration file: %s", setting.CustomConf) | ||||||
|  |  | ||||||
| 	if !setting.Database.UseMySQL { | 	if !setting.Database.UseMySQL { | ||||||
|   | |||||||
| @@ -87,14 +87,16 @@ func runRecreateTable(ctx *cli.Context) error { | |||||||
| 	golog.SetPrefix("") | 	golog.SetPrefix("") | ||||||
| 	golog.SetOutput(log.NewLoggerAsWriter("INFO", log.GetLogger(log.DEFAULT))) | 	golog.SetOutput(log.NewLoggerAsWriter("INFO", log.GetLogger(log.DEFAULT))) | ||||||
|  |  | ||||||
| 	setting.LoadFromExisting() | 	setting.InitProviderFromExistingFile() | ||||||
| 	setting.InitDBConfig() | 	setting.LoadCommonSettings() | ||||||
|  | 	setting.LoadDBSetting() | ||||||
|  |  | ||||||
| 	setting.EnableXORMLog = ctx.Bool("debug") | 	setting.Log.EnableXORMLog = ctx.Bool("debug") | ||||||
| 	setting.Database.LogSQL = 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() | 	stdCtx, cancel := installSignals() | ||||||
| 	defer cancel() | 	defer cancel() | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								cmd/dump.go
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								cmd/dump.go
									
									
									
									
									
								
							| @@ -181,20 +181,22 @@ func runDump(ctx *cli.Context) error { | |||||||
| 		} | 		} | ||||||
| 		fileName += "." + outType | 		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 | 	// 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) | 		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) | 		fatal("Setting console logger to stderr failed: %v", err) | ||||||
| 	} | 	} | ||||||
| 	if !setting.InstallLock { | 	if !setting.InstallLock { | ||||||
| 		log.Error("Is '%s' really the right config path?\n", setting.CustomConf) | 		log.Error("Is '%s' really the right config path?\n", setting.CustomConf) | ||||||
| 		return fmt.Errorf("gitea is not initialized") | 		return fmt.Errorf("gitea is not initialized") | ||||||
| 	} | 	} | ||||||
| 	setting.NewServices() // cannot access session settings otherwise | 	setting.LoadSettings() // cannot access session settings otherwise | ||||||
|  |  | ||||||
| 	stdCtx, cancel := installSignals() | 	stdCtx, cancel := installSignals() | ||||||
| 	defer cancel() | 	defer cancel() | ||||||
| @@ -322,7 +324,7 @@ func runDump(ctx *cli.Context) error { | |||||||
| 		log.Info("Packing data directory...%s", setting.AppDataPath) | 		log.Info("Packing data directory...%s", setting.AppDataPath) | ||||||
|  |  | ||||||
| 		var excludes []string | 		var excludes []string | ||||||
| 		if setting.Cfg.Section("session").Key("PROVIDER").Value() == "file" { | 		if setting.SessionConfig.OriginalProvider == "file" { | ||||||
| 			var opts session.Options | 			var opts session.Options | ||||||
| 			if err = json.Unmarshal([]byte(setting.SessionConfig.ProviderConfig), &opts); err != nil { | 			if err = json.Unmarshal([]byte(setting.SessionConfig.ProviderConfig), &opts); err != nil { | ||||||
| 				return err | 				return err | ||||||
| @@ -339,7 +341,7 @@ func runDump(ctx *cli.Context) error { | |||||||
| 		excludes = append(excludes, setting.LFS.Path) | 		excludes = append(excludes, setting.LFS.Path) | ||||||
| 		excludes = append(excludes, setting.Attachment.Path) | 		excludes = append(excludes, setting.Attachment.Path) | ||||||
| 		excludes = append(excludes, setting.Packages.Path) | 		excludes = append(excludes, setting.Packages.Path) | ||||||
| 		excludes = append(excludes, setting.LogRootPath) | 		excludes = append(excludes, setting.Log.RootPath) | ||||||
| 		excludes = append(excludes, absFileName) | 		excludes = append(excludes, absFileName) | ||||||
| 		if err := addRecursiveExclude(w, "data", setting.AppDataPath, excludes, verbose); err != nil { | 		if err := addRecursiveExclude(w, "data", setting.AppDataPath, excludes, verbose); err != nil { | ||||||
| 			fatal("Failed to include data directory: %v", err) | 			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") { | 	if ctx.IsSet("skip-log") && ctx.Bool("skip-log") { | ||||||
| 		log.Info("Skip dumping log files") | 		log.Info("Skip dumping log files") | ||||||
| 	} else { | 	} else { | ||||||
| 		isExist, err := util.IsExist(setting.LogRootPath) | 		isExist, err := util.IsExist(setting.Log.RootPath) | ||||||
| 		if err != nil { | 		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 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) | 				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("AppPath: %s", setting.AppPath) | ||||||
| 	log.Info("AppWorkPath: %s", setting.AppWorkPath) | 	log.Info("AppWorkPath: %s", setting.AppWorkPath) | ||||||
| 	log.Info("Custom path: %s", setting.CustomPath) | 	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("Configuration file: %s", setting.CustomConf) | ||||||
|  |  | ||||||
| 	var ( | 	var ( | ||||||
|   | |||||||
| @@ -112,7 +112,8 @@ func initEmbeddedExtractor(c *cli.Context) error { | |||||||
| 	log.DelNamedLogger(log.DEFAULT) | 	log.DelNamedLogger(log.DEFAULT) | ||||||
|  |  | ||||||
| 	// Read configuration file | 	// Read configuration file | ||||||
| 	setting.LoadAllowEmpty() | 	setting.InitProviderAllowEmpty() | ||||||
|  | 	setting.LoadCommonSettings() | ||||||
|  |  | ||||||
| 	pats, err := getPatterns(c.Args()) | 	pats, err := getPatterns(c.Args()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|   | |||||||
| @@ -17,7 +17,8 @@ func runSendMail(c *cli.Context) error { | |||||||
| 	ctx, cancel := installSignals() | 	ctx, cancel := installSignals() | ||||||
| 	defer cancel() | 	defer cancel() | ||||||
|  |  | ||||||
| 	setting.LoadFromExisting() | 	setting.InitProviderFromExistingFile() | ||||||
|  | 	setting.LoadCommonSettings() | ||||||
|  |  | ||||||
| 	if err := argsSet(c, "title"); err != nil { | 	if err := argsSet(c, "title"); err != nil { | ||||||
| 		return err | 		return err | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ import ( | |||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	setting.SetCustomPathAndConf("", "", "") | 	setting.SetCustomPathAndConf("", "", "") | ||||||
| 	setting.LoadForTest() | 	setting.InitProviderAndLoadCommonSettingsForTest() | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestMain(m *testing.M) { | func TestMain(m *testing.M) { | ||||||
|   | |||||||
| @@ -33,7 +33,7 @@ func runMigrate(ctx *cli.Context) error { | |||||||
| 	log.Info("AppPath: %s", setting.AppPath) | 	log.Info("AppPath: %s", setting.AppPath) | ||||||
| 	log.Info("AppWorkPath: %s", setting.AppWorkPath) | 	log.Info("AppWorkPath: %s", setting.AppWorkPath) | ||||||
| 	log.Info("Custom path: %s", setting.CustomPath) | 	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("Configuration file: %s", setting.CustomConf) | ||||||
|  |  | ||||||
| 	if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil { | 	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("AppPath: %s", setting.AppPath) | ||||||
| 	log.Info("AppWorkPath: %s", setting.AppWorkPath) | 	log.Info("AppWorkPath: %s", setting.AppWorkPath) | ||||||
| 	log.Info("Custom path: %s", setting.CustomPath) | 	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("Configuration file: %s", setting.CustomConf) | ||||||
|  |  | ||||||
| 	if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil { | 	if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil { | ||||||
|   | |||||||
| @@ -54,7 +54,8 @@ func runRestoreRepository(c *cli.Context) error { | |||||||
| 	ctx, cancel := installSignals() | 	ctx, cancel := installSignals() | ||||||
| 	defer cancel() | 	defer cancel() | ||||||
|  |  | ||||||
| 	setting.LoadFromExisting() | 	setting.InitProviderFromExistingFile() | ||||||
|  | 	setting.LoadCommonSettings() | ||||||
| 	var units []string | 	var units []string | ||||||
| 	if s := c.String("units"); s != "" { | 	if s := c.String("units"); s != "" { | ||||||
| 		units = strings.Split(s, ",") | 		units = strings.Split(s, ",") | ||||||
|   | |||||||
| @@ -61,7 +61,8 @@ func setup(logPath string, debug bool) { | |||||||
| 	} else { | 	} else { | ||||||
| 		_ = log.NewLogger(1000, "console", "console", `{"level":"fatal","stacktracelevel":"NONE","stderr":true}`) | 		_ = log.NewLogger(1000, "console", "console", `{"level":"fatal","stacktracelevel":"NONE","stderr":true}`) | ||||||
| 	} | 	} | ||||||
| 	setting.LoadFromExisting() | 	setting.InitProviderFromExistingFile() | ||||||
|  | 	setting.LoadCommonSettings() | ||||||
| 	if debug { | 	if debug { | ||||||
| 		setting.RunMode = "dev" | 		setting.RunMode = "dev" | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -158,7 +158,8 @@ func runWeb(ctx *cli.Context) error { | |||||||
|  |  | ||||||
| 	log.Info("Global init") | 	log.Info("Global init") | ||||||
| 	// Perform global initialization | 	// Perform global initialization | ||||||
| 	setting.LoadFromExisting() | 	setting.InitProviderFromExistingFile() | ||||||
|  | 	setting.LoadCommonSettings() | ||||||
| 	routers.GlobalInitInstalled(graceful.GetManager().HammerContext()) | 	routers.GlobalInitInstalled(graceful.GetManager().HammerContext()) | ||||||
|  |  | ||||||
| 	// We check that AppDataPath exists here (it should have been created during installation) | 	// We check that AppDataPath exists here (it should have been created during installation) | ||||||
|   | |||||||
| @@ -49,7 +49,8 @@ func runPR() { | |||||||
| 		log.Fatal(err) | 		log.Fatal(err) | ||||||
| 	} | 	} | ||||||
| 	setting.SetCustomPathAndConf("", "", "") | 	setting.SetCustomPathAndConf("", "", "") | ||||||
| 	setting.LoadAllowEmpty() | 	setting.InitProviderAllowEmpty() | ||||||
|  | 	setting.LoadCommonSettings() | ||||||
|  |  | ||||||
| 	setting.RepoRootPath, err = os.MkdirTemp(os.TempDir(), "repos") | 	setting.RepoRootPath, err = os.MkdirTemp(os.TempDir(), "repos") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -82,7 +83,7 @@ func runPR() { | |||||||
| 		setting.Database.Path = ":memory:" | 		setting.Database.Path = ":memory:" | ||||||
| 		setting.Database.Timeout = 500 | 		setting.Database.Timeout = 500 | ||||||
| 	*/ | 	*/ | ||||||
| 	dbCfg := setting.Cfg.Section("database") | 	dbCfg := setting.CfgProvider.Section("database") | ||||||
| 	dbCfg.NewKey("DB_TYPE", "sqlite3") | 	dbCfg.NewKey("DB_TYPE", "sqlite3") | ||||||
| 	dbCfg.NewKey("PATH", ":memory:") | 	dbCfg.NewKey("PATH", ":memory:") | ||||||
|  |  | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ import ( | |||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	setting.SetCustomPathAndConf("", "", "") | 	setting.SetCustomPathAndConf("", "", "") | ||||||
| 	setting.LoadForTest() | 	setting.InitProviderAndLoadCommonSettingsForTest() | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestMain(m *testing.M) { | func TestMain(m *testing.M) { | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ import ( | |||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	setting.SetCustomPathAndConf("", "", "") | 	setting.SetCustomPathAndConf("", "", "") | ||||||
| 	setting.LoadForTest() | 	setting.InitProviderAndLoadCommonSettingsForTest() | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestMain(m *testing.M) { | func TestMain(m *testing.M) { | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ import ( | |||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	setting.SetCustomPathAndConf("", "", "") | 	setting.SetCustomPathAndConf("", "", "") | ||||||
| 	setting.LoadForTest() | 	setting.InitProviderAndLoadCommonSettingsForTest() | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestFixturesAreConsistent(t *testing.T) { | func TestFixturesAreConsistent(t *testing.T) { | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ import ( | |||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	setting.SetCustomPathAndConf("", "", "") | 	setting.SetCustomPathAndConf("", "", "") | ||||||
| 	setting.LoadForTest() | 	setting.InitProviderAndLoadCommonSettingsForTest() | ||||||
| } | } | ||||||
|  |  | ||||||
| // TestFixturesAreConsistent assert that test fixtures are consistent | // TestFixturesAreConsistent assert that test fixtures are consistent | ||||||
|   | |||||||
| @@ -149,13 +149,13 @@ func MainTest(m *testing.M) { | |||||||
| 	setting.AppDataPath = tmpDataPath | 	setting.AppDataPath = tmpDataPath | ||||||
|  |  | ||||||
| 	setting.SetCustomPathAndConf("", "", "") | 	setting.SetCustomPathAndConf("", "", "") | ||||||
| 	setting.LoadForTest() | 	setting.InitProviderAndLoadCommonSettingsForTest() | ||||||
| 	if err = git.InitFull(context.Background()); err != nil { | 	if err = git.InitFull(context.Background()); err != nil { | ||||||
| 		fmt.Printf("Unable to InitFull: %v\n", err) | 		fmt.Printf("Unable to InitFull: %v\n", err) | ||||||
| 		os.Exit(1) | 		os.Exit(1) | ||||||
| 	} | 	} | ||||||
| 	setting.InitDBConfig() | 	setting.LoadDBSetting() | ||||||
| 	setting.NewLogServices(true) | 	setting.InitLogs(true) | ||||||
|  |  | ||||||
| 	exitStatus := m.Run() | 	exitStatus := m.Run() | ||||||
|  |  | ||||||
|   | |||||||
| @@ -27,7 +27,7 @@ var signedUserNameStringPointerKey interface{} = "signedUserNameStringPointerKey | |||||||
| // AccessLogger returns a middleware to log access logger | // AccessLogger returns a middleware to log access logger | ||||||
| func AccessLogger() func(http.Handler) http.Handler { | func AccessLogger() func(http.Handler) http.Handler { | ||||||
| 	logger := log.GetLogger("access") | 	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 func(next http.Handler) http.Handler { | ||||||
| 		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | 		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||||||
| 			start := time.Now() | 			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 { | func initDBDisableConsole(ctx context.Context, disableConsole bool) error { | ||||||
| 	setting.LoadFromExisting() | 	setting.InitProviderFromExistingFile() | ||||||
| 	setting.InitDBConfig() | 	setting.LoadCommonSettings() | ||||||
|  | 	setting.LoadDBSetting() | ||||||
| 	setting.NewXORMLogService(disableConsole) | 	setting.InitSQLLog(disableConsole) | ||||||
| 	if err := db.InitEngine(ctx); err != nil { | 	if err := db.InitEngine(ctx); err != nil { | ||||||
| 		return fmt.Errorf("db.InitEngine: %w", err) | 		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 { | 	for i, check := range checks { | ||||||
| 		if !dbIsInit && !check.SkipDatabaseInitialization { | 		if !dbIsInit && !check.SkipDatabaseInitialization { | ||||||
| 			// Only open database after the most basic configuration check | 			// Only open database after the most basic configuration check | ||||||
| 			setting.EnableXORMLog = false | 			setting.Log.EnableXORMLog = false | ||||||
| 			if err := initDBDisableConsole(ctx, true); err != nil { | 			if err := initDBDisableConsole(ctx, true); err != nil { | ||||||
| 				logger.Error("Error whilst initializing the database: %v", err) | 				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.") | 				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 | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	setting.LoadFromExisting() | 	setting.InitProviderFromExistingFile() | ||||||
|  | 	setting.LoadCommonSettings() | ||||||
|  |  | ||||||
| 	configurationFiles := []configurationFile{ | 	configurationFiles := []configurationFile{ | ||||||
| 		{"Configuration File Path", setting.CustomConf, false, true, false}, | 		{"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}, | 		{"Data Root Path", setting.AppDataPath, true, true, true}, | ||||||
| 		{"Custom File Root Path", setting.CustomPath, true, false, false}, | 		{"Custom File Root Path", setting.CustomPath, true, false, false}, | ||||||
| 		{"Work directory", setting.AppWorkPath, true, true, 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() { | 	if options.IsDynamic() { | ||||||
|   | |||||||
| @@ -41,12 +41,8 @@ var ( | |||||||
| // NewContext loads custom highlight map from local config | // NewContext loads custom highlight map from local config | ||||||
| func NewContext() { | func NewContext() { | ||||||
| 	once.Do(func() { | 	once.Do(func() { | ||||||
| 		if setting.Cfg != nil { | 		highlightMapping = setting.GetHighlightMapping() | ||||||
| 			keys := setting.Cfg.Section("highlight.mapping").Keys() |  | ||||||
| 			for i := range keys { |  | ||||||
| 				highlightMapping[keys[i].Name()] = keys[i].Value() |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		// The size 512 is simply a conservative rule of thumb | 		// The size 512 is simply a conservative rule of thumb | ||||||
| 		c, err := lru.New2Q(512) | 		c, err := lru.New2Q(512) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|   | |||||||
| @@ -27,11 +27,11 @@ func TestMain(m *testing.M) { | |||||||
|  |  | ||||||
| func TestBleveSearchIssues(t *testing.T) { | func TestBleveSearchIssues(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	setting.Cfg = ini.Empty() | 	setting.CfgProvider = ini.Empty() | ||||||
|  |  | ||||||
| 	tmpIndexerDir := t.TempDir() | 	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 | 	oldIssuePath := setting.Indexer.IssuePath | ||||||
| 	setting.Indexer.IssuePath = path.Join(tmpIndexerDir, "issues.queue") | 	setting.Indexer.IssuePath = path.Join(tmpIndexerDir, "issues.queue") | ||||||
| @@ -40,7 +40,7 @@ func TestBleveSearchIssues(t *testing.T) { | |||||||
| 	}() | 	}() | ||||||
|  |  | ||||||
| 	setting.Indexer.IssueType = "bleve" | 	setting.Indexer.IssueType = "bleve" | ||||||
| 	setting.NewQueueService() | 	setting.LoadQueueSettings() | ||||||
| 	InitIssueIndexer(true) | 	InitIssueIndexer(true) | ||||||
| 	defer func() { | 	defer func() { | ||||||
| 		indexer := holder.get() | 		indexer := holder.get() | ||||||
|   | |||||||
| @@ -29,9 +29,9 @@ func TestMain(m *testing.M) { | |||||||
|  |  | ||||||
| func TestRepoStatsIndex(t *testing.T) { | func TestRepoStatsIndex(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	setting.Cfg = ini.Empty() | 	setting.CfgProvider = ini.Empty() | ||||||
|  |  | ||||||
| 	setting.NewQueueService() | 	setting.LoadQueueSettings() | ||||||
|  |  | ||||||
| 	err := Init() | 	err := Init() | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
|   | |||||||
| @@ -28,7 +28,8 @@ var localMetas = map[string]string{ | |||||||
| } | } | ||||||
|  |  | ||||||
| func TestMain(m *testing.M) { | func TestMain(m *testing.M) { | ||||||
| 	setting.LoadAllowEmpty() | 	setting.InitProviderAllowEmpty() | ||||||
|  | 	setting.LoadCommonSettings() | ||||||
| 	if err := git.InitSimple(context.Background()); err != nil { | 	if err := git.InitSimple(context.Background()); err != nil { | ||||||
| 		log.Fatal("git init failed, err: %v", err) | 		log.Fatal("git init failed, err: %v", err) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -33,7 +33,8 @@ var localMetas = map[string]string{ | |||||||
| } | } | ||||||
|  |  | ||||||
| func TestMain(m *testing.M) { | func TestMain(m *testing.M) { | ||||||
| 	setting.LoadAllowEmpty() | 	setting.InitProviderAllowEmpty() | ||||||
|  | 	setting.LoadCommonSettings() | ||||||
| 	if err := git.InitSimple(context.Background()); err != nil { | 	if err := git.InitSimple(context.Background()); err != nil { | ||||||
| 		log.Fatal("git init failed, err: %v", err) | 		log.Fatal("git init failed, err: %v", err) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -19,11 +19,11 @@ var ( | |||||||
| 	} | 	} | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func newActions() { | func loadActionsFrom(rootCfg ConfigProvider) { | ||||||
| 	sec := Cfg.Section("actions") | 	sec := rootCfg.Section("actions") | ||||||
| 	if err := sec.MapTo(&Actions); err != nil { | 	if err := sec.MapTo(&Actions); err != nil { | ||||||
| 		log.Fatal("Failed to map Actions settings: %v", err) | 		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, | 	Enabled:      true, | ||||||
| } | } | ||||||
|  |  | ||||||
| func newAttachmentService() { | func loadAttachmentFrom(rootCfg ConfigProvider) { | ||||||
| 	sec := Cfg.Section("attachment") | 	sec := rootCfg.Section("attachment") | ||||||
| 	storageType := sec.Key("STORAGE_TYPE").MustString("") | 	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.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) | 	Attachment.MaxSize = sec.Key("MAX_SIZE").MustInt64(4) | ||||||
|   | |||||||
| @@ -49,8 +49,8 @@ var CacheService = struct { | |||||||
| // MemcacheMaxTTL represents the maximum memcache TTL | // MemcacheMaxTTL represents the maximum memcache TTL | ||||||
| const MemcacheMaxTTL = 30 * 24 * time.Hour | const MemcacheMaxTTL = 30 * 24 * time.Hour | ||||||
|  |  | ||||||
| func newCacheService() { | func loadCacheFrom(rootCfg ConfigProvider) { | ||||||
| 	sec := Cfg.Section("cache") | 	sec := rootCfg.Section("cache") | ||||||
| 	if err := sec.MapTo(&CacheService); err != nil { | 	if err := sec.MapTo(&CacheService); err != nil { | ||||||
| 		log.Fatal("Failed to map Cache settings: %v", err) | 		log.Fatal("Failed to map Cache settings: %v", err) | ||||||
| 	} | 	} | ||||||
| @@ -79,7 +79,7 @@ func newCacheService() { | |||||||
| 		Service.EnableCaptcha = false | 		Service.EnableCaptcha = false | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	sec = Cfg.Section("cache.last_commit") | 	sec = rootCfg.Section("cache.last_commit") | ||||||
| 	if !CacheService.Enabled { | 	if !CacheService.Enabled { | ||||||
| 		CacheService.LastCommit.Enabled = false | 		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", | 	XFrameOptions: "SAMEORIGIN", | ||||||
| } | } | ||||||
|  |  | ||||||
| func newCORSService() { | func loadCorsFrom(rootCfg ConfigProvider) { | ||||||
| 	sec := Cfg.Section("cors") | 	mustMapSetting(rootCfg, "cors", &CORSConfig) | ||||||
| 	if err := sec.MapTo(&CORSConfig); err != nil { |  | ||||||
| 		log.Fatal("Failed to map cors settings: %v", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if CORSConfig.Enabled { | 	if CORSConfig.Enabled { | ||||||
| 		log.Info("CORS Service Enabled") | 		log.Info("CORS Service Enabled") | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -7,7 +7,11 @@ import "reflect" | |||||||
|  |  | ||||||
| // GetCronSettings maps the cron subsection to the provided config | // GetCronSettings maps the cron subsection to the provided config | ||||||
| func GetCronSettings(name string, config interface{}) (interface{}, error) { | 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 | 		return config, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -18,7 +22,7 @@ func GetCronSettings(name string, config interface{}) (interface{}, error) { | |||||||
| 		field := val.Field(i) | 		field := val.Field(i) | ||||||
| 		tpField := typ.Field(i) | 		tpField := typ.Field(i) | ||||||
| 		if tpField.Type.Kind() == reflect.Struct && tpField.Anonymous { | 		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 | 				return config, err | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ import ( | |||||||
| 	ini "gopkg.in/ini.v1" | 	ini "gopkg.in/ini.v1" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func Test_GetCronSettings(t *testing.T) { | func Test_getCronSettings(t *testing.T) { | ||||||
| 	type BaseStruct struct { | 	type BaseStruct struct { | ||||||
| 		Base   bool | 		Base   bool | ||||||
| 		Second string | 		Second string | ||||||
| @@ -27,7 +27,8 @@ Base = true | |||||||
| Second = white rabbit | Second = white rabbit | ||||||
| Extend = true | Extend = true | ||||||
| ` | ` | ||||||
| 	Cfg, _ = ini.Load([]byte(iniStr)) | 	cfg, err := ini.Load([]byte(iniStr)) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
| 	extended := &Extended{ | 	extended := &Extended{ | ||||||
| 		BaseStruct: BaseStruct{ | 		BaseStruct: BaseStruct{ | ||||||
| @@ -35,8 +36,7 @@ Extend = true | |||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	_, err := GetCronSettings("test", extended) | 	_, err = getCronSettings(cfg, "test", extended) | ||||||
|  |  | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.True(t, extended.Base) | 	assert.True(t, extended.Base) | ||||||
| 	assert.EqualValues(t, extended.Second, "white rabbit") | 	assert.EqualValues(t, extended.Second, "white rabbit") | ||||||
|   | |||||||
| @@ -56,9 +56,9 @@ var ( | |||||||
| 	} | 	} | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // InitDBConfig loads the database settings | // LoadDBSetting loads the database settings | ||||||
| func InitDBConfig() { | func LoadDBSetting() { | ||||||
| 	sec := Cfg.Section("database") | 	sec := CfgProvider.Section("database") | ||||||
| 	Database.Type = sec.Key("DB_TYPE").String() | 	Database.Type = sec.Key("DB_TYPE").String() | ||||||
| 	defaultCharset := "utf8" | 	defaultCharset := "utf8" | ||||||
| 	Database.UseMySQL = false | 	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 | // HttpsigAlgs is a constant slice of httpsig algorithm objects | ||||||
| var HttpsigAlgs []httpsig.Algorithm | var HttpsigAlgs []httpsig.Algorithm | ||||||
|  |  | ||||||
| func newFederationService() { | func loadFederationFrom(rootCfg ConfigProvider) { | ||||||
| 	if err := Cfg.Section("federation").MapTo(&Federation); err != nil { | 	if err := rootCfg.Section("federation").MapTo(&Federation); err != nil { | ||||||
| 		log.Fatal("Failed to map Federation settings: %v", err) | 		log.Fatal("Failed to map Federation settings: %v", err) | ||||||
| 	} else if !httpsig.IsSupportedDigestAlgorithm(Federation.DigestAlgorithm) { | 	} else if !httpsig.IsSupportedDigestAlgorithm(Federation.DigestAlgorithm) { | ||||||
| 		log.Fatal("unsupported digest algorithm: %s", Federation.DigestAlgorithm) | 		log.Fatal("unsupported digest algorithm: %s", Federation.DigestAlgorithm) | ||||||
|   | |||||||
| @@ -67,9 +67,8 @@ var Git = struct { | |||||||
| 	}, | 	}, | ||||||
| } | } | ||||||
|  |  | ||||||
| func newGit() { | func loadGitFrom(rootCfg ConfigProvider) { | ||||||
| 	sec := Cfg.Section("git") | 	sec := rootCfg.Section("git") | ||||||
|  |  | ||||||
| 	if err := sec.MapTo(&Git); err != nil { | 	if err := sec.MapTo(&Git); err != nil { | ||||||
| 		log.Fatal("Failed to map Git settings: %v", err) | 		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 | 	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, | 	MaximumMessageSize:   10485760, | ||||||
| } | } | ||||||
|  |  | ||||||
| func newIncomingEmail() { | func loadIncomingEmailFrom(rootCfg ConfigProvider) { | ||||||
| 	if err := Cfg.Section("email.incoming").MapTo(&IncomingEmail); err != nil { | 	mustMapSetting(rootCfg, "email.incoming", &IncomingEmail) | ||||||
| 		log.Fatal("Unable to map [email.incoming] section on to IncomingEmail. Error: %v", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if !IncomingEmail.Enabled { | 	if !IncomingEmail.Enabled { | ||||||
| 		return | 		return | ||||||
|   | |||||||
| @@ -45,8 +45,8 @@ var Indexer = struct { | |||||||
| 	ExcludeVendored:    true, | 	ExcludeVendored:    true, | ||||||
| } | } | ||||||
|  |  | ||||||
| func newIndexerService() { | func loadIndexerFrom(rootCfg ConfigProvider) { | ||||||
| 	sec := Cfg.Section("indexer") | 	sec := rootCfg.Section("indexer") | ||||||
| 	Indexer.IssueType = sec.Key("ISSUE_INDEXER_TYPE").MustString("bleve") | 	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")))) | 	Indexer.IssuePath = filepath.ToSlash(sec.Key("ISSUE_INDEXER_PATH").MustString(filepath.ToSlash(filepath.Join(AppDataPath, "indexers/issues.bleve")))) | ||||||
| 	if !filepath.IsAbs(Indexer.IssuePath) { | 	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] | 	// 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 | 	// FIXME: DEPRECATED to be removed in v1.18.0 | ||||||
| 	deprecatedSetting("indexer", "ISSUE_INDEXER_QUEUE_TYPE", "queue.issue_indexer", "TYPE") | 	deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_TYPE", "queue.issue_indexer", "TYPE") | ||||||
| 	deprecatedSetting("indexer", "ISSUE_INDEXER_QUEUE_DIR", "queue.issue_indexer", "DATADIR") | 	deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_DIR", "queue.issue_indexer", "DATADIR") | ||||||
| 	deprecatedSetting("indexer", "ISSUE_INDEXER_QUEUE_CONN_STR", "queue.issue_indexer", "CONN_STR") | 	deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_CONN_STR", "queue.issue_indexer", "CONN_STR") | ||||||
| 	deprecatedSetting("indexer", "ISSUE_INDEXER_QUEUE_BATCH_NUMBER", "queue.issue_indexer", "BATCH_LENGTH") | 	deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_BATCH_NUMBER", "queue.issue_indexer", "BATCH_LENGTH") | ||||||
| 	deprecatedSetting("indexer", "UPDATE_BUFFER_LEN", "queue.issue_indexer", "LENGTH") | 	deprecatedSetting(rootCfg, "indexer", "UPDATE_BUFFER_LEN", "queue.issue_indexer", "LENGTH") | ||||||
|  |  | ||||||
| 	Indexer.RepoIndexerEnabled = sec.Key("REPO_INDEXER_ENABLED").MustBool(false) | 	Indexer.RepoIndexerEnabled = sec.Key("REPO_INDEXER_ENABLED").MustBool(false) | ||||||
| 	Indexer.RepoType = sec.Key("REPO_INDEXER_TYPE").MustString("bleve") | 	Indexer.RepoType = sec.Key("REPO_INDEXER_TYPE").MustString("bleve") | ||||||
|   | |||||||
| @@ -25,22 +25,22 @@ var LFS = struct { | |||||||
| 	Storage | 	Storage | ||||||
| }{} | }{} | ||||||
|  |  | ||||||
| func newLFSService() { | func loadLFSFrom(rootCfg ConfigProvider) { | ||||||
| 	sec := Cfg.Section("server") | 	sec := rootCfg.Section("server") | ||||||
| 	if err := sec.MapTo(&LFS); err != nil { | 	if err := sec.MapTo(&LFS); err != nil { | ||||||
| 		log.Fatal("Failed to map LFS settings: %v", err) | 		log.Fatal("Failed to map LFS settings: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	lfsSec := Cfg.Section("lfs") | 	lfsSec := rootCfg.Section("lfs") | ||||||
| 	storageType := lfsSec.Key("STORAGE_TYPE").MustString("") | 	storageType := lfsSec.Key("STORAGE_TYPE").MustString("") | ||||||
|  |  | ||||||
| 	// Specifically default PATH to LFS_CONTENT_PATH | 	// Specifically default PATH to LFS_CONTENT_PATH | ||||||
| 	// FIXME: DEPRECATED to be removed in v1.18.0 | 	// 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( | 	lfsSec.Key("PATH").MustString( | ||||||
| 		sec.Key("LFS_CONTENT_PATH").String()) | 		sec.Key("LFS_CONTENT_PATH").String()) | ||||||
|  |  | ||||||
| 	LFS.Storage = getStorage("lfs", storageType, lfsSec) | 	LFS.Storage = getStorage(rootCfg, "lfs", storageType, lfsSec) | ||||||
|  |  | ||||||
| 	// Rest of LFS service settings | 	// Rest of LFS service settings | ||||||
| 	if LFS.LocksPagingNum == 0 { | 	if LFS.LocksPagingNum == 0 { | ||||||
|   | |||||||
| @@ -25,6 +25,21 @@ var ( | |||||||
| 	logDescriptions = make(map[string]*LogDescription) | 	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 | // GetLogDescriptions returns a race safe set of descriptions | ||||||
| func GetLogDescriptions() map[string]*LogDescription { | func GetLogDescriptions() map[string]*LogDescription { | ||||||
| 	descriptionLock.RLock() | 	descriptionLock.RLock() | ||||||
| @@ -94,9 +109,9 @@ type defaultLogOptions struct { | |||||||
|  |  | ||||||
| func newDefaultLogOptions() defaultLogOptions { | func newDefaultLogOptions() defaultLogOptions { | ||||||
| 	return defaultLogOptions{ | 	return defaultLogOptions{ | ||||||
| 		levelName:      LogLevel.String(), | 		levelName:      Log.Level.String(), | ||||||
| 		flags:          "stdflags", | 		flags:          "stdflags", | ||||||
| 		filename:       filepath.Join(LogRootPath, "gitea.log"), | 		filename:       filepath.Join(Log.RootPath, "gitea.log"), | ||||||
| 		bufferLength:   10000, | 		bufferLength:   10000, | ||||||
| 		disableConsole: false, | 		disableConsole: false, | ||||||
| 	} | 	} | ||||||
| @@ -125,10 +140,33 @@ func getStacktraceLogLevel(section *ini.Section, key, defaultValue string) strin | |||||||
| 	return log.FromString(value).String() | 	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) { | 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() | 	levelName = level.String() | ||||||
| 	stacktraceLevelName := getStacktraceLogLevel(sec, "STACKTRACE_LEVEL", StacktraceLogLevel) | 	stacktraceLevelName := getStacktraceLogLevel(sec, "STACKTRACE_LEVEL", Log.StacktraceLogLevel) | ||||||
| 	stacktraceLevel := log.FromString(stacktraceLevelName) | 	stacktraceLevel := log.FromString(stacktraceLevelName) | ||||||
| 	mode = name | 	mode = name | ||||||
| 	keys := sec.Keys() | 	keys := sec.Keys() | ||||||
| @@ -144,7 +182,7 @@ func generateLogConfig(sec *ini.Section, name string, defaults defaultLogOptions | |||||||
| 			logPath = key.MustString(defaults.filename) | 			logPath = key.MustString(defaults.filename) | ||||||
| 			forcePathSeparator(logPath) | 			forcePathSeparator(logPath) | ||||||
| 			if !filepath.IsAbs(logPath) { | 			if !filepath.IsAbs(logPath) { | ||||||
| 				logPath = path.Join(LogRootPath, logPath) | 				logPath = path.Join(Log.RootPath, logPath) | ||||||
| 			} | 			} | ||||||
| 		case "FLAGS": | 		case "FLAGS": | ||||||
| 			flags = log.FlagsFromString(key.MustString(defaults.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 | 	return mode, jsonConfig, levelName | ||||||
| } | } | ||||||
|  |  | ||||||
| func generateNamedLogger(key string, options defaultLogOptions) *LogDescription { | func generateNamedLogger(rootCfg ConfigProvider, key string, options defaultLogOptions) *LogDescription { | ||||||
| 	description := LogDescription{ | 	description := LogDescription{ | ||||||
| 		Name: key, | 		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++ { | 	for i := 0; i < len(sections); i++ { | ||||||
| 		sections[i] = strings.TrimSpace(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) { | 		if len(name) == 0 || (name == "console" && options.disableConsole) { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 		sec, err := Cfg.GetSection("log." + name + "." + key) | 		sec, err := rootCfg.GetSection("log." + name + "." + key) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			sec, _ = Cfg.NewSection("log." + name + "." + key) | 			sec, _ = rootCfg.NewSection("log." + name + "." + key) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		provider, config, levelName := generateLogConfig(sec, name, options) | 		provider, config, levelName := generateLogConfig(sec, name, options) | ||||||
| @@ -253,46 +291,17 @@ func generateNamedLogger(key string, options defaultLogOptions) *LogDescription | |||||||
| 	return &description | 	return &description | ||||||
| } | } | ||||||
|  |  | ||||||
| func newAccessLogService() { | // initLogFrom initializes logging with settings from configuration provider | ||||||
| 	EnableAccessLog = Cfg.Section("log").Key("ENABLE_ACCESS_LOG").MustBool(false) | func initLogFrom(rootCfg ConfigProvider) { | ||||||
| 	AccessLogTemplate = Cfg.Section("log").Key("ACCESS_LOG_TEMPLATE").MustString( | 	sec := rootCfg.Section("log") | ||||||
| 		`{{.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 { |  | ||||||
| 		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 := newDefaultLogOptions() | ||||||
| 	options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000) | 	options.bufferLength = Log.BufferLength | ||||||
| 	EnableSSHLog = Cfg.Section("log").Key("ENABLE_SSH_LOG").MustBool(false) |  | ||||||
|  |  | ||||||
| 	description := LogDescription{ | 	description := LogDescription{ | ||||||
| 		Name: log.DEFAULT, | 		Name: log.DEFAULT, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	sections := strings.Split(Cfg.Section("log").Key("MODE").MustString("console"), ",") | 	sections := strings.Split(sec.Key("MODE").MustString("console"), ",") | ||||||
|  |  | ||||||
| 	useConsole := false | 	useConsole := false | ||||||
| 	for _, name := range sections { | 	for _, name := range sections { | ||||||
| @@ -304,11 +313,11 @@ func newLogService() { | |||||||
| 			useConsole = true | 			useConsole = true | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		sec, err := Cfg.GetSection("log." + name + ".default") | 		sec, err := rootCfg.GetSection("log." + name + ".default") | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			sec, err = Cfg.GetSection("log." + name) | 			sec, err = rootCfg.GetSection("log." + name) | ||||||
| 			if err != nil { | 			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 | // RestartLogsWithPIDSuffix restarts the logs with a PID suffix on files | ||||||
| func RestartLogsWithPIDSuffix() { | func RestartLogsWithPIDSuffix() { | ||||||
| 	filenameSuffix = fmt.Sprintf(".%d", os.Getpid()) | 	filenameSuffix = fmt.Sprintf(".%d", os.Getpid()) | ||||||
| 	NewLogServices(false) | 	InitLogs(false) | ||||||
| } | } | ||||||
|  |  | ||||||
| // NewLogServices creates all the log services | // InitLogs creates all the log services | ||||||
| func NewLogServices(disableConsole bool) { | func InitLogs(disableConsole bool) { | ||||||
| 	newLogService() | 	initLogFrom(CfgProvider) | ||||||
| 	newRouterLogService() |  | ||||||
| 	newAccessLogService() |  | ||||||
| 	NewXORMLogService(disableConsole) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // NewXORMLogService initializes xorm logger service | 	if !Log.DisableRouterLog { | ||||||
| func NewXORMLogService(disableConsole bool) { |  | ||||||
| 	EnableXORMLog = Cfg.Section("log").Key("ENABLE_XORM_LOG").MustBool(true) |  | ||||||
| 	if EnableXORMLog { |  | ||||||
| 		options := newDefaultLogOptions() | 		options := newDefaultLogOptions() | ||||||
| 		options.filename = filepath.Join(LogRootPath, "xorm.log") | 		options.filename = filepath.Join(Log.RootPath, "router.log") | ||||||
| 		options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000) | 		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 | 		options.disableConsole = disableConsole | ||||||
|  |  | ||||||
| 		Cfg.Section("log").Key("XORM").MustString(",") | 		rootCfg.Section("log").Key("XORM").MustString(",") | ||||||
| 		generateNamedLogger("xorm", options) | 		generateNamedLogger(rootCfg, "xorm", options) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -12,7 +12,6 @@ import ( | |||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
|  |  | ||||||
| 	shellquote "github.com/kballard/go-shellquote" | 	shellquote "github.com/kballard/go-shellquote" | ||||||
| 	ini "gopkg.in/ini.v1" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Mailer represents mail service. | // Mailer represents mail service. | ||||||
| @@ -50,7 +49,14 @@ type Mailer struct { | |||||||
| // MailService the global mailer | // MailService the global mailer | ||||||
| var MailService *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") | 	sec := rootCfg.Section("mailer") | ||||||
| 	// Check mailer setting. | 	// Check mailer setting. | ||||||
| 	if !sec.Key("ENABLED").MustBool() { | 	if !sec.Key("ENABLED").MustBool() { | ||||||
| @@ -59,7 +65,7 @@ func parseMailerConfig(rootCfg *ini.File) { | |||||||
|  |  | ||||||
| 	// Handle Deprecations and map on to new configuration | 	// Handle Deprecations and map on to new configuration | ||||||
| 	// FIXME: DEPRECATED to be removed in v1.19.0 | 	// 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.HasKey("MAILER_TYPE") && !sec.HasKey("PROTOCOL") { | ||||||
| 		if sec.Key("MAILER_TYPE").String() == "sendmail" { | 		if sec.Key("MAILER_TYPE").String() == "sendmail" { | ||||||
| 			sec.Key("PROTOCOL").MustString("sendmail") | 			sec.Key("PROTOCOL").MustString("sendmail") | ||||||
| @@ -67,7 +73,7 @@ func parseMailerConfig(rootCfg *ini.File) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// FIXME: DEPRECATED to be removed in v1.19.0 | 	// 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") { | 	if sec.HasKey("HOST") && !sec.HasKey("SMTP_ADDR") { | ||||||
| 		givenHost := sec.Key("HOST").String() | 		givenHost := sec.Key("HOST").String() | ||||||
| 		addr, port, err := net.SplitHostPort(givenHost) | 		addr, port, err := net.SplitHostPort(givenHost) | ||||||
| @@ -84,7 +90,7 @@ func parseMailerConfig(rootCfg *ini.File) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// FIXME: DEPRECATED to be removed in v1.19.0 | 	// 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.HasKey("IS_TLS_ENABLED") && !sec.HasKey("PROTOCOL") { | ||||||
| 		if sec.Key("IS_TLS_ENABLED").MustBool() { | 		if sec.Key("IS_TLS_ENABLED").MustBool() { | ||||||
| 			sec.Key("PROTOCOL").MustString("smtps") | 			sec.Key("PROTOCOL").MustString("smtps") | ||||||
| @@ -94,37 +100,37 @@ func parseMailerConfig(rootCfg *ini.File) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// FIXME: DEPRECATED to be removed in v1.19.0 | 	// 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") { | 	if sec.HasKey("DISABLE_HELO") && !sec.HasKey("ENABLE_HELO") { | ||||||
| 		sec.Key("ENABLE_HELO").MustBool(!sec.Key("DISABLE_HELO").MustBool()) | 		sec.Key("ENABLE_HELO").MustBool(!sec.Key("DISABLE_HELO").MustBool()) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// FIXME: DEPRECATED to be removed in v1.19.0 | 	// 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") { | 	if sec.HasKey("SKIP_VERIFY") && !sec.HasKey("FORCE_TRUST_SERVER_CERT") { | ||||||
| 		sec.Key("FORCE_TRUST_SERVER_CERT").MustBool(sec.Key("SKIP_VERIFY").MustBool()) | 		sec.Key("FORCE_TRUST_SERVER_CERT").MustBool(sec.Key("SKIP_VERIFY").MustBool()) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// FIXME: DEPRECATED to be removed in v1.19.0 | 	// 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") { | 	if sec.HasKey("USE_CERTIFICATE") && !sec.HasKey("USE_CLIENT_CERT") { | ||||||
| 		sec.Key("USE_CLIENT_CERT").MustBool(sec.Key("USE_CERTIFICATE").MustBool()) | 		sec.Key("USE_CLIENT_CERT").MustBool(sec.Key("USE_CERTIFICATE").MustBool()) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// FIXME: DEPRECATED to be removed in v1.19.0 | 	// 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") { | 	if sec.HasKey("CERT_FILE") && !sec.HasKey("CLIENT_CERT_FILE") { | ||||||
| 		sec.Key("CERT_FILE").MustString(sec.Key("CERT_FILE").String()) | 		sec.Key("CERT_FILE").MustString(sec.Key("CERT_FILE").String()) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// FIXME: DEPRECATED to be removed in v1.19.0 | 	// 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") { | 	if sec.HasKey("KEY_FILE") && !sec.HasKey("CLIENT_KEY_FILE") { | ||||||
| 		sec.Key("KEY_FILE").MustString(sec.Key("KEY_FILE").String()) | 		sec.Key("KEY_FILE").MustString(sec.Key("KEY_FILE").String()) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// FIXME: DEPRECATED to be removed in v1.19.0 | 	// 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") { | 	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)) | 		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") | 	log.Info("Mail Service Enabled") | ||||||
| } | } | ||||||
|  |  | ||||||
| func newRegisterMailService() { | func loadRegisterMailFrom(rootCfg ConfigProvider) { | ||||||
| 	if !Cfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").MustBool() { | 	if !rootCfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").MustBool() { | ||||||
| 		return | 		return | ||||||
| 	} else if MailService == nil { | 	} else if MailService == nil { | ||||||
| 		log.Warn("Register Mail Service: Mail Service is not enabled") | 		log.Warn("Register Mail Service: Mail Service is not enabled") | ||||||
| @@ -248,8 +254,8 @@ func newRegisterMailService() { | |||||||
| 	log.Info("Register Mail Service Enabled") | 	log.Info("Register Mail Service Enabled") | ||||||
| } | } | ||||||
|  |  | ||||||
| func newNotifyMailService() { | func loadNotifyMailFrom(rootCfg ConfigProvider) { | ||||||
| 	if !Cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() { | 	if !rootCfg.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() { | ||||||
| 		return | 		return | ||||||
| 	} else if MailService == nil { | 	} else if MailService == nil { | ||||||
| 		log.Warn("Notify Mail Service: Mail Service is not enabled") | 		log.Warn("Notify Mail Service: Mail Service is not enabled") | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ import ( | |||||||
| 	ini "gopkg.in/ini.v1" | 	ini "gopkg.in/ini.v1" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func TestParseMailerConfig(t *testing.T) { | func Test_loadMailerFrom(t *testing.T) { | ||||||
| 	iniFile := ini.Empty() | 	iniFile := ini.Empty() | ||||||
| 	kases := map[string]*Mailer{ | 	kases := map[string]*Mailer{ | ||||||
| 		"smtp.mydomain.com": { | 		"smtp.mydomain.com": { | ||||||
| @@ -34,7 +34,7 @@ func TestParseMailerConfig(t *testing.T) { | |||||||
| 			sec.NewKey("HOST", host) | 			sec.NewKey("HOST", host) | ||||||
|  |  | ||||||
| 			// Check mailer setting | 			// Check mailer setting | ||||||
| 			parseMailerConfig(iniFile) | 			loadMailerFrom(iniFile) | ||||||
|  |  | ||||||
| 			assert.EqualValues(t, kase.SMTPAddr, MailService.SMTPAddr) | 			assert.EqualValues(t, kase.SMTPAddr, MailService.SMTPAddr) | ||||||
| 			assert.EqualValues(t, kase.SMTPPort, MailService.SMTPPort) | 			assert.EqualValues(t, kase.SMTPPort, MailService.SMTPPort) | ||||||
|   | |||||||
| @@ -25,6 +25,20 @@ const ( | |||||||
| 	RenderContentModeIframe      = "iframe" | 	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 | // MarkupRenderer defines the external parser configured in ini | ||||||
| type MarkupRenderer struct { | type MarkupRenderer struct { | ||||||
| 	Enabled              bool | 	Enabled              bool | ||||||
| @@ -46,12 +60,14 @@ type MarkupSanitizerRule struct { | |||||||
| 	AllowDataURIImages bool | 	AllowDataURIImages bool | ||||||
| } | } | ||||||
|  |  | ||||||
| func newMarkup() { | func loadMarkupFrom(rootCfg ConfigProvider) { | ||||||
| 	MermaidMaxSourceCharacters = Cfg.Section("markup").Key("MERMAID_MAX_SOURCE_CHARACTERS").MustInt(5000) | 	mustMapSetting(rootCfg, "markdown", &Markdown) | ||||||
|  |  | ||||||
|  | 	MermaidMaxSourceCharacters = rootCfg.Section("markup").Key("MERMAID_MAX_SOURCE_CHARACTERS").MustInt(5000) | ||||||
| 	ExternalMarkupRenderers = make([]*MarkupRenderer, 0, 10) | 	ExternalMarkupRenderers = make([]*MarkupRenderer, 0, 10) | ||||||
| 	ExternalSanitizerRules = make([]MarkupSanitizerRule, 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.") | 		name := strings.TrimPrefix(sec.Name(), "markup.") | ||||||
| 		if name == "" { | 		if name == "" { | ||||||
| 			log.Warn("name is empty, markup " + sec.Name() + "ignored") | 			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, | 	RetryBackoff: 3, | ||||||
| } | } | ||||||
|  |  | ||||||
| func newMigrationsService() { | func loadMigrationsFrom(rootCfg ConfigProvider) { | ||||||
| 	sec := Cfg.Section("migrations") | 	sec := rootCfg.Section("migrations") | ||||||
| 	Migrations.MaxAttempts = sec.Key("MAX_ATTEMPTS").MustInt(Migrations.MaxAttempts) | 	Migrations.MaxAttempts = sec.Key("MAX_ATTEMPTS").MustInt(Migrations.MaxAttempts) | ||||||
| 	Migrations.RetryBackoff = sec.Key("RETRY_BACKOFF").MustInt(Migrations.RetryBackoff) | 	Migrations.RetryBackoff = sec.Key("RETRY_BACKOFF").MustInt(Migrations.RetryBackoff) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,8 +14,8 @@ var MimeTypeMap = struct { | |||||||
| 	Map:     map[string]string{}, | 	Map:     map[string]string{}, | ||||||
| } | } | ||||||
|  |  | ||||||
| func newMimeTypeMap() { | func loadMimeTypeMapFrom(rootCfg ConfigProvider) { | ||||||
| 	sec := Cfg.Section("repository.mimetype_mapping") | 	sec := rootCfg.Section("repository.mimetype_mapping") | ||||||
| 	keys := sec.Keys() | 	keys := sec.Keys() | ||||||
| 	m := make(map[string]string, len(keys)) | 	m := make(map[string]string, len(keys)) | ||||||
| 	for _, key := range keys { | 	for _, key := range keys { | ||||||
|   | |||||||
| @@ -24,16 +24,16 @@ var Mirror = struct { | |||||||
| 	DefaultInterval: 8 * time.Hour, | 	DefaultInterval: 8 * time.Hour, | ||||||
| } | } | ||||||
|  |  | ||||||
| func newMirror() { | func loadMirrorFrom(rootCfg ConfigProvider) { | ||||||
| 	// Handle old configuration through `[repository]` `DISABLE_MIRRORS` | 	// Handle old configuration through `[repository]` `DISABLE_MIRRORS` | ||||||
| 	// - please note this was badly named and only disabled the creation of new pull 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 | 	// FIXME: DEPRECATED to be removed in v1.18.0 | ||||||
| 	deprecatedSetting("repository", "DISABLE_MIRRORS", "mirror", "ENABLED") | 	deprecatedSetting(rootCfg, "repository", "DISABLE_MIRRORS", "mirror", "ENABLED") | ||||||
| 	if Cfg.Section("repository").Key("DISABLE_MIRRORS").MustBool(false) { | 	if rootCfg.Section("repository").Key("DISABLE_MIRRORS").MustBool(false) { | ||||||
| 		Mirror.DisableNewPull = true | 		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) | 		log.Fatal("Failed to map Mirror settings: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,6 +4,9 @@ | |||||||
| package setting | package setting | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"math" | ||||||
|  | 	"path/filepath" | ||||||
|  | 
 | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 
 | 
 | ||||||
| 	"gopkg.in/ini.v1" | 	"gopkg.in/ini.v1" | ||||||
| @@ -59,8 +62,8 @@ var OAuth2Client struct { | |||||||
| 	AccountLinking         OAuth2AccountLinkingType | 	AccountLinking         OAuth2AccountLinkingType | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func newOAuth2Client() { | func loadOAuth2ClientFrom(rootCfg ConfigProvider) { | ||||||
| 	sec := Cfg.Section("oauth2_client") | 	sec := rootCfg.Section("oauth2_client") | ||||||
| 	OAuth2Client.RegisterEmailConfirm = sec.Key("REGISTER_EMAIL_CONFIRM").MustBool(Service.RegisterEmailConfirm) | 	OAuth2Client.RegisterEmailConfirm = sec.Key("REGISTER_EMAIL_CONFIRM").MustBool(Service.RegisterEmailConfirm) | ||||||
| 	OAuth2Client.OpenIDConnectScopes = parseScopes(sec, "OPENID_CONNECT_SCOPES") | 	OAuth2Client.OpenIDConnectScopes = parseScopes(sec, "OPENID_CONNECT_SCOPES") | ||||||
| 	OAuth2Client.EnableAutoRegistration = sec.Key("ENABLE_AUTO_REGISTRATION").MustBool() | 	OAuth2Client.EnableAutoRegistration = sec.Key("ENABLE_AUTO_REGISTRATION").MustBool() | ||||||
| @@ -87,3 +90,33 @@ func parseScopes(sec *ini.Section, name string) []string { | |||||||
| 	} | 	} | ||||||
| 	return scopes | 	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() { | func loadPackagesFrom(rootCfg ConfigProvider) { | ||||||
| 	sec := Cfg.Section("packages") | 	sec := rootCfg.Section("packages") | ||||||
| 	if err := sec.MapTo(&Packages); err != nil { | 	if err := sec.MapTo(&Packages); err != nil { | ||||||
| 		log.Fatal("Failed to map Packages settings: %v", err) | 		log.Fatal("Failed to map Packages settings: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	Packages.Storage = getStorage("packages", "", nil) | 	Packages.Storage = getStorage(rootCfg, "packages", "", nil) | ||||||
|  |  | ||||||
| 	appURL, _ := url.Parse(AppURL) | 	appURL, _ := url.Parse(AppURL) | ||||||
| 	Packages.RegistryHost = appURL.Host | 	Packages.RegistryHost = appURL.Host | ||||||
|   | |||||||
| @@ -32,16 +32,16 @@ var ( | |||||||
| 	}{} | 	}{} | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func newPictureService() { | func loadPictureFrom(rootCfg ConfigProvider) { | ||||||
| 	sec := Cfg.Section("picture") | 	sec := rootCfg.Section("picture") | ||||||
|  |  | ||||||
| 	avatarSec := Cfg.Section("avatar") | 	avatarSec := rootCfg.Section("avatar") | ||||||
| 	storageType := sec.Key("AVATAR_STORAGE_TYPE").MustString("") | 	storageType := sec.Key("AVATAR_STORAGE_TYPE").MustString("") | ||||||
| 	// Specifically default PATH to AVATAR_UPLOAD_PATH | 	// Specifically default PATH to AVATAR_UPLOAD_PATH | ||||||
| 	avatarSec.Key("PATH").MustString( | 	avatarSec.Key("PATH").MustString( | ||||||
| 		sec.Key("AVATAR_UPLOAD_PATH").String()) | 		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.MaxWidth = sec.Key("AVATAR_MAX_WIDTH").MustInt(4096) | ||||||
| 	Avatar.MaxHeight = sec.Key("AVATAR_MAX_HEIGHT").MustInt(3072) | 	Avatar.MaxHeight = sec.Key("AVATAR_MAX_HEIGHT").MustInt(3072) | ||||||
| @@ -60,11 +60,11 @@ func newPictureService() { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool(GetDefaultDisableGravatar()) | 	DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool(GetDefaultDisableGravatar()) | ||||||
| 	deprecatedSettingDB("", "DISABLE_GRAVATAR") | 	deprecatedSettingDB(rootCfg, "", "DISABLE_GRAVATAR") | ||||||
| 	EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(GetDefaultEnableFederatedAvatar(DisableGravatar)) | 	EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(GetDefaultEnableFederatedAvatar(DisableGravatar)) | ||||||
| 	deprecatedSettingDB("", "ENABLE_FEDERATED_AVATAR") | 	deprecatedSettingDB(rootCfg, "", "ENABLE_FEDERATED_AVATAR") | ||||||
|  |  | ||||||
| 	newRepoAvatarService() | 	loadRepoAvatarFrom(rootCfg) | ||||||
| } | } | ||||||
|  |  | ||||||
| func GetDefaultDisableGravatar() bool { | func GetDefaultDisableGravatar() bool { | ||||||
| @@ -82,16 +82,16 @@ func GetDefaultEnableFederatedAvatar(disableGravatar bool) bool { | |||||||
| 	return v | 	return v | ||||||
| } | } | ||||||
|  |  | ||||||
| func newRepoAvatarService() { | func loadRepoAvatarFrom(rootCfg ConfigProvider) { | ||||||
| 	sec := Cfg.Section("picture") | 	sec := rootCfg.Section("picture") | ||||||
|  |  | ||||||
| 	repoAvatarSec := Cfg.Section("repo-avatar") | 	repoAvatarSec := rootCfg.Section("repo-avatar") | ||||||
| 	storageType := sec.Key("REPOSITORY_AVATAR_STORAGE_TYPE").MustString("") | 	storageType := sec.Key("REPOSITORY_AVATAR_STORAGE_TYPE").MustString("") | ||||||
| 	// Specifically default PATH to AVATAR_UPLOAD_PATH | 	// Specifically default PATH to AVATAR_UPLOAD_PATH | ||||||
| 	repoAvatarSec.Key("PATH").MustString( | 	repoAvatarSec.Key("PATH").MustString( | ||||||
| 		sec.Key("REPOSITORY_AVATAR_UPLOAD_PATH").String()) | 		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.Fallback = sec.Key("REPOSITORY_AVATAR_FALLBACK").MustString("none") | ||||||
| 	RepoAvatar.FallbackImage = sec.Key("REPOSITORY_AVATAR_FALLBACK_IMAGE").MustString("/assets/img/repo_default.png") | 	RepoAvatar.FallbackImage = sec.Key("REPOSITORY_AVATAR_FALLBACK_IMAGE").MustString("/assets/img/repo_default.png") | ||||||
|   | |||||||
| @@ -3,8 +3,6 @@ | |||||||
|  |  | ||||||
| package setting | package setting | ||||||
|  |  | ||||||
| import "code.gitea.io/gitea/modules/log" |  | ||||||
|  |  | ||||||
| // Project settings | // Project settings | ||||||
| var ( | var ( | ||||||
| 	Project = struct { | 	Project = struct { | ||||||
| @@ -16,8 +14,6 @@ var ( | |||||||
| 	} | 	} | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func newProject() { | func loadProjectFrom(rootCfg ConfigProvider) { | ||||||
| 	if err := Cfg.Section("project").MapTo(&Project); err != nil { | 	mustMapSetting(rootCfg, "project", &Project) | ||||||
| 		log.Fatal("Failed to map Project settings: %v", err) |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -21,8 +21,8 @@ var Proxy = struct { | |||||||
| 	ProxyHosts: []string{}, | 	ProxyHosts: []string{}, | ||||||
| } | } | ||||||
|  |  | ||||||
| func newProxyService() { | func loadProxyFrom(rootCfg ConfigProvider) { | ||||||
| 	sec := Cfg.Section("proxy") | 	sec := rootCfg.Section("proxy") | ||||||
| 	Proxy.Enabled = sec.Key("PROXY_ENABLED").MustBool(false) | 	Proxy.Enabled = sec.Key("PROXY_ENABLED").MustBool(false) | ||||||
| 	Proxy.ProxyURL = sec.Key("PROXY_URL").MustString("") | 	Proxy.ProxyURL = sec.Key("PROXY_URL").MustString("") | ||||||
| 	if Proxy.ProxyURL != "" { | 	if Proxy.ProxyURL != "" { | ||||||
|   | |||||||
| @@ -39,8 +39,12 @@ var Queue = QueueSettings{} | |||||||
|  |  | ||||||
| // GetQueueSettings returns the queue settings for the appropriately named queue | // GetQueueSettings returns the queue settings for the appropriately named queue | ||||||
| func GetQueueSettings(name string) QueueSettings { | func GetQueueSettings(name string) QueueSettings { | ||||||
|  | 	return getQueueSettings(CfgProvider, name) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getQueueSettings(rootCfg ConfigProvider, name string) QueueSettings { | ||||||
| 	q := QueueSettings{} | 	q := QueueSettings{} | ||||||
| 	sec := Cfg.Section("queue." + name) | 	sec := rootCfg.Section("queue." + name) | ||||||
| 	q.Name = name | 	q.Name = name | ||||||
|  |  | ||||||
| 	// DataDir is not directly inheritable | 	// DataDir is not directly inheritable | ||||||
| @@ -82,10 +86,14 @@ func GetQueueSettings(name string) QueueSettings { | |||||||
| 	return q | 	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 | // This is exported for tests to be able to use the queue | ||||||
| func NewQueueService() { | func LoadQueueSettings() { | ||||||
| 	sec := Cfg.Section("queue") | 	loadQueueFrom(CfgProvider) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func loadQueueFrom(rootCfg ConfigProvider) { | ||||||
|  | 	sec := rootCfg.Section("queue") | ||||||
| 	Queue.DataDir = filepath.ToSlash(sec.Key("DATADIR").MustString("queues/")) | 	Queue.DataDir = filepath.ToSlash(sec.Key("DATADIR").MustString("queues/")) | ||||||
| 	if !filepath.IsAbs(Queue.DataDir) { | 	if !filepath.IsAbs(Queue.DataDir) { | ||||||
| 		Queue.DataDir = filepath.ToSlash(filepath.Join(AppDataPath, Queue.DataDir)) | 		Queue.DataDir = filepath.ToSlash(filepath.Join(AppDataPath, Queue.DataDir)) | ||||||
| @@ -108,10 +116,10 @@ func NewQueueService() { | |||||||
|  |  | ||||||
| 	// Now handle the old issue_indexer configuration | 	// Now handle the old issue_indexer configuration | ||||||
| 	// FIXME: DEPRECATED to be removed in v1.18.0 | 	// FIXME: DEPRECATED to be removed in v1.18.0 | ||||||
| 	section := Cfg.Section("queue.issue_indexer") | 	section := rootCfg.Section("queue.issue_indexer") | ||||||
| 	directlySet := toDirectlySetKeysSet(section) | 	directlySet := toDirectlySetKeysSet(section) | ||||||
| 	if !directlySet.Contains("TYPE") && defaultType == "" { | 	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": | 		case "levelqueue": | ||||||
| 			_, _ = section.NewKey("TYPE", "level") | 			_, _ = section.NewKey("TYPE", "level") | ||||||
| 		case "channel": | 		case "channel": | ||||||
| @@ -125,25 +133,25 @@ func NewQueueService() { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if !directlySet.Contains("LENGTH") { | 	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 { | 		if length != 0 { | ||||||
| 			_, _ = section.NewKey("LENGTH", strconv.Itoa(length)) | 			_, _ = section.NewKey("LENGTH", strconv.Itoa(length)) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if !directlySet.Contains("BATCH_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 { | 		if fallback != 0 { | ||||||
| 			_, _ = section.NewKey("BATCH_LENGTH", strconv.Itoa(fallback)) | 			_, _ = section.NewKey("BATCH_LENGTH", strconv.Itoa(fallback)) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if !directlySet.Contains("DATADIR") { | 	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 != "" { | 		if queueDir != "" { | ||||||
| 			_, _ = section.NewKey("DATADIR", queueDir) | 			_, _ = section.NewKey("DATADIR", queueDir) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if !directlySet.Contains("CONN_STR") { | 	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 != "" { | 		if connStr != "" { | ||||||
| 			_, _ = section.NewKey("CONN_STR", connStr) | 			_, _ = section.NewKey("CONN_STR", connStr) | ||||||
| 		} | 		} | ||||||
| @@ -153,31 +161,31 @@ func NewQueueService() { | |||||||
| 	// - will need to set default for [queue.*)] LENGTH appropriately though though | 	// - will need to set default for [queue.*)] LENGTH appropriately though though | ||||||
|  |  | ||||||
| 	// Handle the old mailer configuration | 	// 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 | 	// Handle the old test pull requests configuration | ||||||
| 	// Please note this will be a unique queue | 	// 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 | 	// Handle the old mirror queue configuration | ||||||
| 	// Please note this will be a unique queue | 	// 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 | // 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) | // 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) { | func handleOldLengthConfiguration(rootCfg ConfigProvider, queueName, oldSection, oldKey string, defaultValue int) { | ||||||
| 	if Cfg.Section(oldSection).HasKey(oldKey) { | 	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) | 		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 | 	// Don't override with 0 | ||||||
| 	if value <= 0 { | 	if value <= 0 { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	section := Cfg.Section("queue." + queueName) | 	section := rootCfg.Section("queue." + queueName) | ||||||
| 	directlySet := toDirectlySetKeysSet(section) | 	directlySet := toDirectlySetKeysSet(section) | ||||||
| 	if !directlySet.Contains("LENGTH") { | 	if !directlySet.Contains("LENGTH") { | ||||||
| 		_, _ = section.NewKey("LENGTH", strconv.Itoa(value)) | 		_, _ = section.NewKey("LENGTH", strconv.Itoa(value)) | ||||||
|   | |||||||
| @@ -270,10 +270,10 @@ var ( | |||||||
| 	}{} | 	}{} | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func newRepository() { | func loadRepositoryFrom(rootCfg ConfigProvider) { | ||||||
| 	var err error | 	var err error | ||||||
| 	// Determine and create root git repository path. | 	// Determine and create root git repository path. | ||||||
| 	sec := Cfg.Section("repository") | 	sec := rootCfg.Section("repository") | ||||||
| 	Repository.DisableHTTPGit = sec.Key("DISABLE_HTTP_GIT").MustBool() | 	Repository.DisableHTTPGit = sec.Key("DISABLE_HTTP_GIT").MustBool() | ||||||
| 	Repository.UseCompatSSHURI = sec.Key("USE_COMPAT_SSH_URI").MustBool() | 	Repository.UseCompatSSHURI = sec.Key("USE_COMPAT_SSH_URI").MustBool() | ||||||
| 	Repository.MaxCreationLimit = sec.Key("MAX_CREATION_LIMIT").MustInt(-1) | 	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) | 		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) | 		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) | 		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) | 		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) | 		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) | 		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") | 		Repository.DisabledRepoUnits = append(Repository.DisabledRepoUnits, "repo.packages") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -354,5 +354,5 @@ func newRepository() { | |||||||
| 		Repository.Upload.TempPath = path.Join(AppWorkPath, Repository.Upload.TempPath) | 		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" | 	"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 | // Service settings | ||||||
| var Service = struct { | var Service = struct { | ||||||
| 	DefaultUserVisibility                   string | 	DefaultUserVisibility                   string | ||||||
| @@ -105,8 +114,8 @@ func (a AllowedVisibility) ToVisibleTypeSlice() (result []structs.VisibleType) { | |||||||
| 	return result | 	return result | ||||||
| } | } | ||||||
|  |  | ||||||
| func newService() { | func loadServiceFrom(rootCfg ConfigProvider) { | ||||||
| 	sec := Cfg.Section("service") | 	sec := rootCfg.Section("service") | ||||||
| 	Service.ActiveCodeLives = sec.Key("ACTIVE_CODE_LIVE_MINUTES").MustInt(180) | 	Service.ActiveCodeLives = sec.Key("ACTIVE_CODE_LIVE_MINUTES").MustInt(180) | ||||||
| 	Service.ResetPwdCodeLives = sec.Key("RESET_PASSWD_CODE_LIVE_MINUTES").MustInt(180) | 	Service.ResetPwdCodeLives = sec.Key("RESET_PASSWD_CODE_LIVE_MINUTES").MustInt(180) | ||||||
| 	Service.DisableRegistration = sec.Key("DISABLE_REGISTRATION").MustBool() | 	Service.DisableRegistration = sec.Key("DISABLE_REGISTRATION").MustBool() | ||||||
| @@ -184,11 +193,13 @@ func newService() { | |||||||
| 	} | 	} | ||||||
| 	Service.ValidSiteURLSchemes = schemes | 	Service.ValidSiteURLSchemes = schemes | ||||||
|  |  | ||||||
| 	if err := Cfg.Section("service.explore").MapTo(&Service.Explore); err != nil { | 	mustMapSetting(rootCfg, "service.explore", &Service.Explore) | ||||||
| 		log.Fatal("Failed to map service.explore settings: %v", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	sec = Cfg.Section("openid") | 	loadOpenIDSetting(rootCfg) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func loadOpenIDSetting(rootCfg ConfigProvider) { | ||||||
|  | 	sec := rootCfg.Section("openid") | ||||||
| 	Service.EnableOpenIDSignIn = sec.Key("ENABLE_OPENID_SIGNIN").MustBool(!InstallLock) | 	Service.EnableOpenIDSignIn = sec.Key("ENABLE_OPENID_SIGNIN").MustBool(!InstallLock) | ||||||
| 	Service.EnableOpenIDSignUp = sec.Key("ENABLE_OPENID_SIGNUP").MustBool(!Service.DisableRegistration && Service.EnableOpenIDSignIn) | 	Service.EnableOpenIDSignUp = sec.Key("ENABLE_OPENID_SIGNUP").MustBool(!Service.DisableRegistration && Service.EnableOpenIDSignIn) | ||||||
| 	pats := sec.Key("WHITELISTED_URIS").Strings(" ") | 	pats := sec.Key("WHITELISTED_URIS").Strings(" ") | ||||||
|   | |||||||
| @@ -15,7 +15,8 @@ import ( | |||||||
|  |  | ||||||
| // SessionConfig defines Session settings | // SessionConfig defines Session settings | ||||||
| var SessionConfig = struct { | var SessionConfig = struct { | ||||||
| 	Provider string | 	OriginalProvider string | ||||||
|  | 	Provider         string | ||||||
| 	// Provider configuration, it's corresponding to provider. | 	// Provider configuration, it's corresponding to provider. | ||||||
| 	ProviderConfig string | 	ProviderConfig string | ||||||
| 	// Cookie name to save session ID. Default is "MacaronSession". | 	// Cookie name to save session ID. Default is "MacaronSession". | ||||||
| @@ -39,8 +40,8 @@ var SessionConfig = struct { | |||||||
| 	SameSite:    http.SameSiteLaxMode, | 	SameSite:    http.SameSiteLaxMode, | ||||||
| } | } | ||||||
|  |  | ||||||
| func newSessionService() { | func loadSessionFrom(rootCfg ConfigProvider) { | ||||||
| 	sec := Cfg.Section("session") | 	sec := rootCfg.Section("session") | ||||||
| 	SessionConfig.Provider = sec.Key("PROVIDER").In("memory", | 	SessionConfig.Provider = sec.Key("PROVIDER").In("memory", | ||||||
| 		[]string{"memory", "file", "redis", "mysql", "postgres", "couchbase", "memcache", "db"}) | 		[]string{"memory", "file", "redis", "mysql", "postgres", "couchbase", "memcache", "db"}) | ||||||
| 	SessionConfig.ProviderConfig = strings.Trim(sec.Key("PROVIDER_CONFIG").MustString(path.Join(AppDataPath, "sessions")), "\" ") | 	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) | 		log.Fatal("Can't shadow session config: %v", err) | ||||||
| 	} | 	} | ||||||
| 	SessionConfig.ProviderConfig = string(shadowConfig) | 	SessionConfig.ProviderConfig = string(shadowConfig) | ||||||
|  | 	SessionConfig.OriginalProvider = SessionConfig.Provider | ||||||
| 	SessionConfig.Provider = "VirtualSession" | 	SessionConfig.Provider = "VirtualSession" | ||||||
|  |  | ||||||
| 	log.Info("Session Service Enabled") | 	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 | 	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" | 	const sectionName = "storage" | ||||||
| 	sec := Cfg.Section(sectionName) | 	sec := rootCfg.Section(sectionName) | ||||||
|  |  | ||||||
| 	// Global Defaults | 	// Global Defaults | ||||||
| 	sec.Key("MINIO_ENDPOINT").MustString("localhost:9000") | 	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) | 	sec.Key("MINIO_USE_SSL").MustBool(false) | ||||||
|  |  | ||||||
| 	if targetSec == nil { | 	if targetSec == nil { | ||||||
| 		targetSec, _ = Cfg.NewSection(name) | 		targetSec, _ = rootCfg.NewSection(name) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var storage Storage | 	var storage Storage | ||||||
| @@ -51,12 +51,12 @@ func getStorage(name, typ string, targetSec *ini.Section) Storage { | |||||||
| 	storage.Type = typ | 	storage.Type = typ | ||||||
|  |  | ||||||
| 	overrides := make([]*ini.Section, 0, 3) | 	overrides := make([]*ini.Section, 0, 3) | ||||||
| 	nameSec, err := Cfg.GetSection(sectionName + "." + name) | 	nameSec, err := rootCfg.GetSection(sectionName + "." + name) | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		overrides = append(overrides, nameSec) | 		overrides = append(overrides, nameSec) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	typeSec, err := Cfg.GetSection(sectionName + "." + typ) | 	typeSec, err := rootCfg.GetSection(sectionName + "." + typ) | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		overrides = append(overrides, typeSec) | 		overrides = append(overrides, typeSec) | ||||||
| 		nextType := typeSec.Key("STORAGE_TYPE").String() | 		nextType := typeSec.Key("STORAGE_TYPE").String() | ||||||
|   | |||||||
| @@ -20,11 +20,12 @@ MINIO_BUCKET = gitea-attachment | |||||||
| STORAGE_TYPE = minio | STORAGE_TYPE = minio | ||||||
| MINIO_ENDPOINT = my_minio:9000 | 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("") | 	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, "minio", storage.Type) | ||||||
| 	assert.EqualValues(t, "my_minio:9000", storage.Section.Key("MINIO_ENDPOINT").String()) | 	assert.EqualValues(t, "my_minio:9000", storage.Section.Key("MINIO_ENDPOINT").String()) | ||||||
| @@ -42,11 +43,12 @@ MINIO_BUCKET = gitea-attachment | |||||||
| [storage.minio] | [storage.minio] | ||||||
| MINIO_BUCKET = gitea | 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("") | 	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, "minio", storage.Type) | ||||||
| 	assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String()) | 	assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String()) | ||||||
| @@ -63,11 +65,12 @@ MINIO_BUCKET = gitea-minio | |||||||
| [storage] | [storage] | ||||||
| MINIO_BUCKET = gitea | 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("") | 	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, "minio", storage.Type) | ||||||
| 	assert.EqualValues(t, "gitea-minio", storage.Section.Key("MINIO_BUCKET").String()) | 	assert.EqualValues(t, "gitea-minio", storage.Section.Key("MINIO_BUCKET").String()) | ||||||
| @@ -85,22 +88,24 @@ MINIO_BUCKET = gitea | |||||||
| [storage] | [storage] | ||||||
| STORAGE_TYPE = local | 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("") | 	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, "minio", storage.Type) | ||||||
| 	assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String()) | 	assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String()) | ||||||
| } | } | ||||||
|  |  | ||||||
| func Test_getStorageGetDefaults(t *testing.T) { | 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("") | 	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()) | 	assert.EqualValues(t, "gitea", storage.Section.Key("MINIO_BUCKET").String()) | ||||||
| } | } | ||||||
| @@ -116,26 +121,27 @@ MINIO_BUCKET = gitea-attachment | |||||||
| [storage] | [storage] | ||||||
| MINIO_BUCKET = gitea-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("") | 		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()) | 		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("") | 		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()) | 		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("") | 		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()) | 		assert.EqualValues(t, "gitea-storage", storage.Section.Key("MINIO_BUCKET").String()) | ||||||
| 	} | 	} | ||||||
| @@ -149,19 +155,20 @@ STORAGE_TYPE = lfs | |||||||
| [storage.lfs] | [storage.lfs] | ||||||
| MINIO_BUCKET = gitea-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("") | 		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()) | 		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("") | 		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()) | 		assert.EqualValues(t, "gitea-storage", storage.Section.Key("MINIO_BUCKET").String()) | ||||||
| 	} | 	} | ||||||
| @@ -172,11 +179,12 @@ func Test_getStorageInheritStorageType(t *testing.T) { | |||||||
| [storage] | [storage] | ||||||
| STORAGE_TYPE = minio | 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("") | 	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, "minio", storage.Type) | ||||||
| } | } | ||||||
| @@ -186,11 +194,12 @@ func Test_getStorageInheritNameSectionType(t *testing.T) { | |||||||
| [storage.attachments] | [storage.attachments] | ||||||
| STORAGE_TYPE = minio | 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("") | 	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, "minio", storage.Type) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -5,13 +5,13 @@ package setting | |||||||
|  |  | ||||||
| // FIXME: DEPRECATED to be removed in v1.18.0 | // FIXME: DEPRECATED to be removed in v1.18.0 | ||||||
| // - will need to set default for [queue.task] LENGTH to 1000 though | // - will need to set default for [queue.task] LENGTH to 1000 though | ||||||
| func newTaskService() { | func loadTaskFrom(rootCfg ConfigProvider) { | ||||||
| 	taskSec := Cfg.Section("task") | 	taskSec := rootCfg.Section("task") | ||||||
| 	queueTaskSec := Cfg.Section("queue.task") | 	queueTaskSec := rootCfg.Section("queue.task") | ||||||
|  |  | ||||||
| 	deprecatedSetting("task", "QUEUE_TYPE", "queue.task", "TYPE") | 	deprecatedSetting(rootCfg, "task", "QUEUE_TYPE", "queue.task", "TYPE") | ||||||
| 	deprecatedSetting("task", "QUEUE_CONN_STR", "queue.task", "CONN_STR") | 	deprecatedSetting(rootCfg, "task", "QUEUE_CONN_STR", "queue.task", "CONN_STR") | ||||||
| 	deprecatedSetting("task", "QUEUE_LENGTH", "queue.task", "LENGTH") | 	deprecatedSetting(rootCfg, "task", "QUEUE_LENGTH", "queue.task", "LENGTH") | ||||||
|  |  | ||||||
| 	switch taskSec.Key("QUEUE_TYPE").MustString("channel") { | 	switch taskSec.Key("QUEUE_TYPE").MustString("channel") { | ||||||
| 	case "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{}, | 	ProxyHosts:     []string{}, | ||||||
| } | } | ||||||
|  |  | ||||||
| func newWebhookService() { | func loadWebhookFrom(rootCfg ConfigProvider) { | ||||||
| 	sec := Cfg.Section("webhook") | 	sec := rootCfg.Section("webhook") | ||||||
| 	Webhook.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000) | 	Webhook.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000) | ||||||
| 	Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5) | 	Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5) | ||||||
| 	Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool() | 	Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool() | ||||||
|   | |||||||
| @@ -13,8 +13,8 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| func TestMain(m *testing.M) { | func TestMain(m *testing.M) { | ||||||
| 	setting.LoadForTest() | 	setting.InitProviderAndLoadCommonSettingsForTest() | ||||||
| 	setting.NewQueueService() | 	setting.LoadQueueSettings() | ||||||
| 	unittest.MainTest(m, &unittest.TestOptions{ | 	unittest.MainTest(m, &unittest.TestOptions{ | ||||||
| 		GiteaRootPath: filepath.Join("..", "..", "..", ".."), | 		GiteaRootPath: filepath.Join("..", "..", "..", ".."), | ||||||
| 		SetUp:         webhook_service.Init, | 		SetUp:         webhook_service.Init, | ||||||
|   | |||||||
| @@ -50,11 +50,11 @@ func Middlewares() []func(http.Handler) http.Handler { | |||||||
|  |  | ||||||
| 	handlers = append(handlers, middleware.StripSlashes) | 	handlers = append(handlers, middleware.StripSlashes) | ||||||
|  |  | ||||||
| 	if !setting.DisableRouterLog { | 	if !setting.Log.DisableRouterLog { | ||||||
| 		handlers = append(handlers, routing.NewLoggerHandler()) | 		handlers = append(handlers, routing.NewLoggerHandler()) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if setting.EnableAccessLog { | 	if setting.Log.EnableAccessLog { | ||||||
| 		handlers = append(handlers, context.AccessLogger()) | 		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` | // InitGitServices init new services for git, this is also called in `contrib/pr/checkout.go` | ||||||
| func InitGitServices() { | func InitGitServices() { | ||||||
| 	setting.NewServices() | 	setting.LoadSettings() | ||||||
| 	mustInit(storage.Init) | 	mustInit(storage.Init) | ||||||
| 	mustInit(repo_service.Init) | 	mustInit(repo_service.Init) | ||||||
| } | } | ||||||
| @@ -119,7 +119,7 @@ func GlobalInitInstalled(ctx context.Context) { | |||||||
| 	log.Info("AppPath: %s", setting.AppPath) | 	log.Info("AppPath: %s", setting.AppPath) | ||||||
| 	log.Info("AppWorkPath: %s", setting.AppWorkPath) | 	log.Info("AppWorkPath: %s", setting.AppWorkPath) | ||||||
| 	log.Info("Custom path: %s", setting.CustomPath) | 	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("Configuration file: %s", setting.CustomConf) | ||||||
| 	log.Info("Run Mode: %s", util.ToTitleCase(setting.RunMode)) | 	log.Info("Run Mode: %s", util.ToTitleCase(setting.RunMode)) | ||||||
| 	log.Info("Gitea v%s%s", setting.AppVer, setting.AppBuiltWith) | 	log.Info("Gitea v%s%s", setting.AppVer, setting.AppBuiltWith) | ||||||
| @@ -127,7 +127,7 @@ func GlobalInitInstalled(ctx context.Context) { | |||||||
| 	// Setup i18n | 	// Setup i18n | ||||||
| 	translation.InitLocales(ctx) | 	translation.InitLocales(ctx) | ||||||
|  |  | ||||||
| 	setting.NewServices() | 	setting.LoadSettings() | ||||||
| 	mustInit(storage.Init) | 	mustInit(storage.Init) | ||||||
|  |  | ||||||
| 	mailer.NewContext(ctx) | 	mailer.NewContext(ctx) | ||||||
|   | |||||||
| @@ -134,7 +134,7 @@ func Install(ctx *context.Context) { | |||||||
| 	form.SSHPort = setting.SSH.Port | 	form.SSHPort = setting.SSH.Port | ||||||
| 	form.HTTPPort = setting.HTTPPort | 	form.HTTPPort = setting.HTTPPort | ||||||
| 	form.AppURL = setting.AppURL | 	form.AppURL = setting.AppURL | ||||||
| 	form.LogRootPath = setting.LogRootPath | 	form.LogRootPath = setting.Log.RootPath | ||||||
|  |  | ||||||
| 	// E-mail service settings | 	// E-mail service settings | ||||||
| 	if setting.MailService != nil { | 	if setting.MailService != nil { | ||||||
| @@ -467,7 +467,7 @@ func SubmitInstall(ctx *context.Context) { | |||||||
| 	cfg.Section("session").Key("PROVIDER").SetValue("file") | 	cfg.Section("session").Key("PROVIDER").SetValue("file") | ||||||
|  |  | ||||||
| 	cfg.Section("log").Key("MODE").SetValue("console") | 	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("ROOT_PATH").SetValue(form.LogRootPath) | ||||||
| 	cfg.Section("log").Key("ROUTER").SetValue("console") | 	cfg.Section("log").Key("ROUTER").SetValue("console") | ||||||
|  |  | ||||||
|   | |||||||
| @@ -15,20 +15,21 @@ import ( | |||||||
|  |  | ||||||
| // PreloadSettings preloads the configuration to check if we need to run install | // PreloadSettings preloads the configuration to check if we need to run install | ||||||
| func PreloadSettings(ctx context.Context) bool { | func PreloadSettings(ctx context.Context) bool { | ||||||
| 	setting.LoadAllowEmpty() | 	setting.InitProviderAllowEmpty() | ||||||
|  | 	setting.LoadCommonSettings() | ||||||
| 	if !setting.InstallLock { | 	if !setting.InstallLock { | ||||||
| 		log.Info("AppPath: %s", setting.AppPath) | 		log.Info("AppPath: %s", setting.AppPath) | ||||||
| 		log.Info("AppWorkPath: %s", setting.AppWorkPath) | 		log.Info("AppWorkPath: %s", setting.AppWorkPath) | ||||||
| 		log.Info("Custom path: %s", setting.CustomPath) | 		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("Configuration file: %s", setting.CustomConf) | ||||||
| 		log.Info("Prepare to run install page") | 		log.Info("Prepare to run install page") | ||||||
| 		translation.InitLocales(ctx) | 		translation.InitLocales(ctx) | ||||||
| 		if setting.EnableSQLite3 { | 		if setting.EnableSQLite3 { | ||||||
| 			log.Info("SQLite3 is supported") | 			log.Info("SQLite3 is supported") | ||||||
| 		} | 		} | ||||||
| 		setting.InitDBConfig() |  | ||||||
| 		setting.NewServicesForInstall() | 		setting.LoadSettingsForInstall() | ||||||
| 		svg.Init() | 		svg.Init() | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -37,8 +38,9 @@ func PreloadSettings(ctx context.Context) bool { | |||||||
|  |  | ||||||
| // reloadSettings reloads the existing settings and starts up the database | // reloadSettings reloads the existing settings and starts up the database | ||||||
| func reloadSettings(ctx context.Context) { | func reloadSettings(ctx context.Context) { | ||||||
| 	setting.LoadFromExisting() | 	setting.InitProviderFromExistingFile() | ||||||
| 	setting.InitDBConfig() | 	setting.LoadCommonSettings() | ||||||
|  | 	setting.LoadDBSetting() | ||||||
| 	if setting.InstallLock { | 	if setting.InstallLock { | ||||||
| 		if err := common.InitDBEngine(ctx); err == nil { | 		if err := common.InitDBEngine(ctx); err == nil { | ||||||
| 			log.Info("ORM engine initialization successful!") | 			log.Info("ORM engine initialization successful!") | ||||||
|   | |||||||
| @@ -116,11 +116,11 @@ func AddLogger(ctx *context.PrivateContext) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if _, ok := opts.Config["level"]; !ok { | 	if _, ok := opts.Config["level"]; !ok { | ||||||
| 		opts.Config["level"] = setting.LogLevel | 		opts.Config["level"] = setting.Log.Level | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if _, ok := opts.Config["stacktraceLevel"]; !ok { | 	if _, ok := opts.Config["stacktraceLevel"]; !ok { | ||||||
| 		opts.Config["stacktraceLevel"] = setting.StacktraceLogLevel | 		opts.Config["stacktraceLevel"] = setting.Log.StacktraceLogLevel | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if opts.Mode == "file" { | 	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) | 	byteConfig, err := json.Marshal(opts.Config) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error("Failed to marshal log configuration: %v %v", opts.Config, err) | 		log.Error("Failed to marshal log configuration: %v %v", opts.Config, err) | ||||||
|   | |||||||
| @@ -15,7 +15,7 @@ import ( | |||||||
|  |  | ||||||
| // SSHLog hook to response ssh log | // SSHLog hook to response ssh log | ||||||
| func SSHLog(ctx *context.PrivateContext) { | func SSHLog(ctx *context.PrivateContext) { | ||||||
| 	if !setting.EnableSSHLog { | 	if !setting.Log.EnableSSHLog { | ||||||
| 		ctx.Status(http.StatusOK) | 		ctx.Status(http.StatusOK) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -117,7 +117,7 @@ func Config(ctx *context.Context) { | |||||||
| 	ctx.Data["AppUrl"] = setting.AppURL | 	ctx.Data["AppUrl"] = setting.AppURL | ||||||
| 	ctx.Data["Domain"] = setting.Domain | 	ctx.Data["Domain"] = setting.Domain | ||||||
| 	ctx.Data["OfflineMode"] = setting.OfflineMode | 	ctx.Data["OfflineMode"] = setting.OfflineMode | ||||||
| 	ctx.Data["DisableRouterLog"] = setting.DisableRouterLog | 	ctx.Data["DisableRouterLog"] = setting.Log.DisableRouterLog | ||||||
| 	ctx.Data["RunUser"] = setting.RunUser | 	ctx.Data["RunUser"] = setting.RunUser | ||||||
| 	ctx.Data["RunMode"] = util.ToTitleCase(setting.RunMode) | 	ctx.Data["RunMode"] = util.ToTitleCase(setting.RunMode) | ||||||
| 	ctx.Data["GitVersion"] = git.VersionInfo() | 	ctx.Data["GitVersion"] = git.VersionInfo() | ||||||
| @@ -125,7 +125,7 @@ func Config(ctx *context.Context) { | |||||||
| 	ctx.Data["RepoRootPath"] = setting.RepoRootPath | 	ctx.Data["RepoRootPath"] = setting.RepoRootPath | ||||||
| 	ctx.Data["CustomRootPath"] = setting.CustomPath | 	ctx.Data["CustomRootPath"] = setting.CustomPath | ||||||
| 	ctx.Data["StaticRootPath"] = setting.StaticRootPath | 	ctx.Data["StaticRootPath"] = setting.StaticRootPath | ||||||
| 	ctx.Data["LogRootPath"] = setting.LogRootPath | 	ctx.Data["LogRootPath"] = setting.Log.RootPath | ||||||
| 	ctx.Data["ScriptType"] = setting.ScriptType | 	ctx.Data["ScriptType"] = setting.ScriptType | ||||||
| 	ctx.Data["ReverseProxyAuthUser"] = setting.ReverseProxyAuthUser | 	ctx.Data["ReverseProxyAuthUser"] = setting.ReverseProxyAuthUser | ||||||
| 	ctx.Data["ReverseProxyAuthEmail"] = setting.ReverseProxyAuthEmail | 	ctx.Data["ReverseProxyAuthEmail"] = setting.ReverseProxyAuthEmail | ||||||
| @@ -183,10 +183,10 @@ func Config(ctx *context.Context) { | |||||||
|  |  | ||||||
| 	ctx.Data["EnvVars"] = envVars | 	ctx.Data["EnvVars"] = envVars | ||||||
| 	ctx.Data["Loggers"] = setting.GetLogDescriptions() | 	ctx.Data["Loggers"] = setting.GetLogDescriptions() | ||||||
| 	ctx.Data["EnableAccessLog"] = setting.EnableAccessLog | 	ctx.Data["EnableAccessLog"] = setting.Log.EnableAccessLog | ||||||
| 	ctx.Data["AccessLogTemplate"] = setting.AccessLogTemplate | 	ctx.Data["AccessLogTemplate"] = setting.Log.AccessLogTemplate | ||||||
| 	ctx.Data["DisableRouterLog"] = setting.DisableRouterLog | 	ctx.Data["DisableRouterLog"] = setting.Log.DisableRouterLog | ||||||
| 	ctx.Data["EnableXORMLog"] = setting.EnableXORMLog | 	ctx.Data["EnableXORMLog"] = setting.Log.EnableXORMLog | ||||||
| 	ctx.Data["LogSQL"] = setting.Database.LogSQL | 	ctx.Data["LogSQL"] = setting.Database.LogSQL | ||||||
|  |  | ||||||
| 	ctx.HTML(http.StatusOK, tplConfig) | 	ctx.HTML(http.StatusOK, tplConfig) | ||||||
|   | |||||||
| @@ -15,8 +15,8 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| func TestMain(m *testing.M) { | func TestMain(m *testing.M) { | ||||||
| 	setting.LoadForTest() | 	setting.InitProviderAndLoadCommonSettingsForTest() | ||||||
| 	setting.NewQueueService() | 	setting.LoadQueueSettings() | ||||||
|  |  | ||||||
| 	// for tests, allow only loopback IPs | 	// for tests, allow only loopback IPs | ||||||
| 	setting.Webhook.AllowedHostList = hostmatcher.MatchBuiltinLoopback | 	setting.Webhook.AllowedHostList = hostmatcher.MatchBuiltinLoopback | ||||||
|   | |||||||
| @@ -87,8 +87,8 @@ func TestMain(m *testing.M) { | |||||||
| 	c = routers.NormalRoutes(context.TODO()) | 	c = routers.NormalRoutes(context.TODO()) | ||||||
|  |  | ||||||
| 	// integration test settings... | 	// integration test settings... | ||||||
| 	if setting.Cfg != nil { | 	if setting.CfgProvider != nil { | ||||||
| 		testingCfg := setting.Cfg.Section("integration-tests") | 		testingCfg := setting.CfgProvider.Section("integration-tests") | ||||||
| 		tests.SlowTest = testingCfg.Key("SLOW_TEST").MustDuration(tests.SlowTest) | 		tests.SlowTest = testingCfg.Key("SLOW_TEST").MustDuration(tests.SlowTest) | ||||||
| 		tests.SlowFlush = testingCfg.Key("SLOW_FLUSH").MustDuration(tests.SlowFlush) | 		tests.SlowFlush = testingCfg.Key("SLOW_FLUSH").MustDuration(tests.SlowFlush) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -57,7 +57,7 @@ func initMigrationTest(t *testing.T) func() { | |||||||
| 		setting.CustomConf = giteaConf | 		setting.CustomConf = giteaConf | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	setting.LoadForTest() | 	setting.InitProviderAndLoadCommonSettingsForTest() | ||||||
|  |  | ||||||
| 	assert.True(t, len(setting.RepoRootPath) != 0) | 	assert.True(t, len(setting.RepoRootPath) != 0) | ||||||
| 	assert.NoError(t, util.RemoveAll(setting.RepoRootPath)) | 	assert.NoError(t, util.RemoveAll(setting.RepoRootPath)) | ||||||
| @@ -83,8 +83,8 @@ func initMigrationTest(t *testing.T) func() { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	assert.NoError(t, git.InitFull(context.Background())) | 	assert.NoError(t, git.InitFull(context.Background())) | ||||||
| 	setting.InitDBConfig() | 	setting.LoadDBSetting() | ||||||
| 	setting.NewLogServices(true) | 	setting.InitLogs(true) | ||||||
| 	return deferFn | 	return deferFn | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -292,7 +292,7 @@ func doMigrationTest(t *testing.T, version string) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	setting.NewXORMLogService(false) | 	setting.InitSQLLog(false) | ||||||
|  |  | ||||||
| 	err := db.InitEngineWithMigration(context.Background(), wrappedMigrate) | 	err := db.InitEngineWithMigration(context.Background(), wrappedMigrate) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
|   | |||||||
| @@ -57,7 +57,7 @@ func InitTest(requireGitea bool) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	setting.SetCustomPathAndConf("", "", "") | 	setting.SetCustomPathAndConf("", "", "") | ||||||
| 	setting.LoadForTest() | 	setting.InitProviderAndLoadCommonSettingsForTest() | ||||||
| 	setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master" | 	setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master" | ||||||
| 	_ = util.RemoveAll(repo_module.LocalCopyPath()) | 	_ = util.RemoveAll(repo_module.LocalCopyPath()) | ||||||
|  |  | ||||||
| @@ -65,7 +65,7 @@ func InitTest(requireGitea bool) { | |||||||
| 		log.Fatal("git.InitOnceWithSync: %v", err) | 		log.Fatal("git.InitOnceWithSync: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	setting.InitDBConfig() | 	setting.LoadDBSetting() | ||||||
| 	if err := storage.Init(); err != nil { | 	if err := storage.Init(); err != nil { | ||||||
| 		fmt.Printf("Init storage failed: %v", err) | 		fmt.Printf("Init storage failed: %v", err) | ||||||
| 		os.Exit(1) | 		os.Exit(1) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Lunny Xiao
					Lunny Xiao