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:
parent
2b02343e21
commit
c53ad052d8
86 changed files with 1694 additions and 1464 deletions
|
@ -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 := newDefaultLogOptions()
|
||||||
options.filename = filepath.Join(LogRootPath, "access.log")
|
options.bufferLength = Log.BufferLength
|
||||||
options.flags = "" // For the router we don't want any prefixed flags
|
|
||||||
options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000)
|
|
||||||
generateNamedLogger("access", options)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newRouterLogService() {
|
|
||||||
Cfg.Section("log").Key("ROUTER").MustString("console")
|
|
||||||
// Allow [log] DISABLE_ROUTER_LOG to override [server] DISABLE_ROUTER_LOG
|
|
||||||
DisableRouterLog = Cfg.Section("log").Key("DISABLE_ROUTER_LOG").MustBool(DisableRouterLog)
|
|
||||||
|
|
||||||
if !DisableRouterLog {
|
|
||||||
options := newDefaultLogOptions()
|
|
||||||
options.filename = filepath.Join(LogRootPath, "router.log")
|
|
||||||
options.flags = "date,time" // For the router we don't want any prefixed flags
|
|
||||||
options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000)
|
|
||||||
generateNamedLogger("router", options)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newLogService() {
|
|
||||||
options := newDefaultLogOptions()
|
|
||||||
options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000)
|
|
||||||
EnableSSHLog = Cfg.Section("log").Key("ENABLE_SSH_LOG").MustBool(false)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
loadOpenIDSetting(rootCfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
sec = Cfg.Section("openid")
|
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,6 +15,7 @@ import (
|
||||||
|
|
||||||
// SessionConfig defines Session settings
|
// SessionConfig defines Session settings
|
||||||
var SessionConfig = struct {
|
var SessionConfig = struct {
|
||||||
|
OriginalProvider string
|
||||||
Provider string
|
Provider string
|
||||||
// Provider configuration, it's corresponding to provider.
|
// Provider configuration, it's corresponding to provider.
|
||||||
ProviderConfig string
|
ProviderConfig string
|
||||||
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue