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