Refactor the setting to make unit test easier (#22405)

Some bugs caused by less unit tests in fundamental packages. This PR
refactor `setting` package so that create a unit test will be easier
than before.

- All `LoadFromXXX` files has been splited as two functions, one is
`InitProviderFromXXX` and `LoadCommonSettings`. The first functions will
only include the code to create or new a ini file. The second function
will load common settings.
- It also renames all functions in setting from `newXXXService` to
`loadXXXSetting` or `loadXXXFrom` to make the function name less
confusing.
- Move `XORMLog` to `SQLLog` because it's a better name for that.

Maybe we should finally move these `loadXXXSetting` into the `XXXInit`
function? Any idea?

---------

Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: delvh <dev.lh@web.de>
This commit is contained in:
Lunny Xiao 2023-02-20 00:12:01 +08:00 committed by GitHub
parent 2b02343e21
commit c53ad052d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
86 changed files with 1694 additions and 1464 deletions

View File

@ -57,9 +57,10 @@ func confirm() (bool, error) {
} }
func initDB(ctx context.Context) error { func initDB(ctx context.Context) error {
setting.LoadFromExisting() setting.InitProviderFromExistingFile()
setting.InitDBConfig() setting.LoadCommonSettings()
setting.NewXORMLogService(false) setting.LoadDBSetting()
setting.InitSQLLog(false)
if setting.Database.Type == "" { if setting.Database.Type == "" {
log.Fatal(`Database settings are missing from the configuration file: %q. log.Fatal(`Database settings are missing from the configuration file: %q.

View File

@ -32,7 +32,7 @@ func runConvert(ctx *cli.Context) error {
log.Info("AppPath: %s", setting.AppPath) log.Info("AppPath: %s", setting.AppPath)
log.Info("AppWorkPath: %s", setting.AppWorkPath) log.Info("AppWorkPath: %s", setting.AppWorkPath)
log.Info("Custom path: %s", setting.CustomPath) log.Info("Custom path: %s", setting.CustomPath)
log.Info("Log path: %s", setting.LogRootPath) log.Info("Log path: %s", setting.Log.RootPath)
log.Info("Configuration file: %s", setting.CustomConf) log.Info("Configuration file: %s", setting.CustomConf)
if !setting.Database.UseMySQL { if !setting.Database.UseMySQL {

View File

@ -87,14 +87,16 @@ func runRecreateTable(ctx *cli.Context) error {
golog.SetPrefix("") golog.SetPrefix("")
golog.SetOutput(log.NewLoggerAsWriter("INFO", log.GetLogger(log.DEFAULT))) golog.SetOutput(log.NewLoggerAsWriter("INFO", log.GetLogger(log.DEFAULT)))
setting.LoadFromExisting() setting.InitProviderFromExistingFile()
setting.InitDBConfig() setting.LoadCommonSettings()
setting.LoadDBSetting()
setting.EnableXORMLog = ctx.Bool("debug") setting.Log.EnableXORMLog = ctx.Bool("debug")
setting.Database.LogSQL = ctx.Bool("debug") setting.Database.LogSQL = ctx.Bool("debug")
setting.Cfg.Section("log").Key("XORM").SetValue(",") // FIXME: don't use CfgProvider directly
setting.CfgProvider.Section("log").Key("XORM").SetValue(",")
setting.NewXORMLogService(!ctx.Bool("debug")) setting.InitSQLLog(!ctx.Bool("debug"))
stdCtx, cancel := installSignals() stdCtx, cancel := installSignals()
defer cancel() defer cancel()

View File

@ -181,20 +181,22 @@ func runDump(ctx *cli.Context) error {
} }
fileName += "." + outType fileName += "." + outType
} }
setting.LoadFromExisting() setting.InitProviderFromExistingFile()
setting.LoadCommonSettings()
// make sure we are logging to the console no matter what the configuration tells us do to // make sure we are logging to the console no matter what the configuration tells us do to
if _, err := setting.Cfg.Section("log").NewKey("MODE", "console"); err != nil { // FIXME: don't use CfgProvider directly
if _, err := setting.CfgProvider.Section("log").NewKey("MODE", "console"); err != nil {
fatal("Setting logging mode to console failed: %v", err) fatal("Setting logging mode to console failed: %v", err)
} }
if _, err := setting.Cfg.Section("log.console").NewKey("STDERR", "true"); err != nil { if _, err := setting.CfgProvider.Section("log.console").NewKey("STDERR", "true"); err != nil {
fatal("Setting console logger to stderr failed: %v", err) fatal("Setting console logger to stderr failed: %v", err)
} }
if !setting.InstallLock { if !setting.InstallLock {
log.Error("Is '%s' really the right config path?\n", setting.CustomConf) log.Error("Is '%s' really the right config path?\n", setting.CustomConf)
return fmt.Errorf("gitea is not initialized") return fmt.Errorf("gitea is not initialized")
} }
setting.NewServices() // cannot access session settings otherwise setting.LoadSettings() // cannot access session settings otherwise
stdCtx, cancel := installSignals() stdCtx, cancel := installSignals()
defer cancel() defer cancel()
@ -322,7 +324,7 @@ func runDump(ctx *cli.Context) error {
log.Info("Packing data directory...%s", setting.AppDataPath) log.Info("Packing data directory...%s", setting.AppDataPath)
var excludes []string var excludes []string
if setting.Cfg.Section("session").Key("PROVIDER").Value() == "file" { if setting.SessionConfig.OriginalProvider == "file" {
var opts session.Options var opts session.Options
if err = json.Unmarshal([]byte(setting.SessionConfig.ProviderConfig), &opts); err != nil { if err = json.Unmarshal([]byte(setting.SessionConfig.ProviderConfig), &opts); err != nil {
return err return err
@ -339,7 +341,7 @@ func runDump(ctx *cli.Context) error {
excludes = append(excludes, setting.LFS.Path) excludes = append(excludes, setting.LFS.Path)
excludes = append(excludes, setting.Attachment.Path) excludes = append(excludes, setting.Attachment.Path)
excludes = append(excludes, setting.Packages.Path) excludes = append(excludes, setting.Packages.Path)
excludes = append(excludes, setting.LogRootPath) excludes = append(excludes, setting.Log.RootPath)
excludes = append(excludes, absFileName) excludes = append(excludes, absFileName)
if err := addRecursiveExclude(w, "data", setting.AppDataPath, excludes, verbose); err != nil { if err := addRecursiveExclude(w, "data", setting.AppDataPath, excludes, verbose); err != nil {
fatal("Failed to include data directory: %v", err) fatal("Failed to include data directory: %v", err)
@ -378,12 +380,12 @@ func runDump(ctx *cli.Context) error {
if ctx.IsSet("skip-log") && ctx.Bool("skip-log") { if ctx.IsSet("skip-log") && ctx.Bool("skip-log") {
log.Info("Skip dumping log files") log.Info("Skip dumping log files")
} else { } else {
isExist, err := util.IsExist(setting.LogRootPath) isExist, err := util.IsExist(setting.Log.RootPath)
if err != nil { if err != nil {
log.Error("Unable to check if %s exists. Error: %v", setting.LogRootPath, err) log.Error("Unable to check if %s exists. Error: %v", setting.Log.RootPath, err)
} }
if isExist { if isExist {
if err := addRecursiveExclude(w, "log", setting.LogRootPath, []string{absFileName}, verbose); err != nil { if err := addRecursiveExclude(w, "log", setting.Log.RootPath, []string{absFileName}, verbose); err != nil {
fatal("Failed to include log: %v", err) fatal("Failed to include log: %v", err)
} }
} }

View File

@ -94,7 +94,7 @@ func runDumpRepository(ctx *cli.Context) error {
log.Info("AppPath: %s", setting.AppPath) log.Info("AppPath: %s", setting.AppPath)
log.Info("AppWorkPath: %s", setting.AppWorkPath) log.Info("AppWorkPath: %s", setting.AppWorkPath)
log.Info("Custom path: %s", setting.CustomPath) log.Info("Custom path: %s", setting.CustomPath)
log.Info("Log path: %s", setting.LogRootPath) log.Info("Log path: %s", setting.Log.RootPath)
log.Info("Configuration file: %s", setting.CustomConf) log.Info("Configuration file: %s", setting.CustomConf)
var ( var (

View File

@ -112,7 +112,8 @@ func initEmbeddedExtractor(c *cli.Context) error {
log.DelNamedLogger(log.DEFAULT) log.DelNamedLogger(log.DEFAULT)
// Read configuration file // Read configuration file
setting.LoadAllowEmpty() setting.InitProviderAllowEmpty()
setting.LoadCommonSettings()
pats, err := getPatterns(c.Args()) pats, err := getPatterns(c.Args())
if err != nil { if err != nil {

View File

@ -17,7 +17,8 @@ func runSendMail(c *cli.Context) error {
ctx, cancel := installSignals() ctx, cancel := installSignals()
defer cancel() defer cancel()
setting.LoadFromExisting() setting.InitProviderFromExistingFile()
setting.LoadCommonSettings()
if err := argsSet(c, "title"); err != nil { if err := argsSet(c, "title"); err != nil {
return err return err

View File

@ -12,7 +12,7 @@ import (
func init() { func init() {
setting.SetCustomPathAndConf("", "", "") setting.SetCustomPathAndConf("", "", "")
setting.LoadForTest() setting.InitProviderAndLoadCommonSettingsForTest()
} }
func TestMain(m *testing.M) { func TestMain(m *testing.M) {

View File

@ -33,7 +33,7 @@ func runMigrate(ctx *cli.Context) error {
log.Info("AppPath: %s", setting.AppPath) log.Info("AppPath: %s", setting.AppPath)
log.Info("AppWorkPath: %s", setting.AppWorkPath) log.Info("AppWorkPath: %s", setting.AppWorkPath)
log.Info("Custom path: %s", setting.CustomPath) log.Info("Custom path: %s", setting.CustomPath)
log.Info("Log path: %s", setting.LogRootPath) log.Info("Log path: %s", setting.Log.RootPath)
log.Info("Configuration file: %s", setting.CustomConf) log.Info("Configuration file: %s", setting.CustomConf)
if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil { if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil {

View File

@ -136,7 +136,7 @@ func runMigrateStorage(ctx *cli.Context) error {
log.Info("AppPath: %s", setting.AppPath) log.Info("AppPath: %s", setting.AppPath)
log.Info("AppWorkPath: %s", setting.AppWorkPath) log.Info("AppWorkPath: %s", setting.AppWorkPath)
log.Info("Custom path: %s", setting.CustomPath) log.Info("Custom path: %s", setting.CustomPath)
log.Info("Log path: %s", setting.LogRootPath) log.Info("Log path: %s", setting.Log.RootPath)
log.Info("Configuration file: %s", setting.CustomConf) log.Info("Configuration file: %s", setting.CustomConf)
if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil { if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil {

View File

@ -54,7 +54,8 @@ func runRestoreRepository(c *cli.Context) error {
ctx, cancel := installSignals() ctx, cancel := installSignals()
defer cancel() defer cancel()
setting.LoadFromExisting() setting.InitProviderFromExistingFile()
setting.LoadCommonSettings()
var units []string var units []string
if s := c.String("units"); s != "" { if s := c.String("units"); s != "" {
units = strings.Split(s, ",") units = strings.Split(s, ",")

View File

@ -61,7 +61,8 @@ func setup(logPath string, debug bool) {
} else { } else {
_ = log.NewLogger(1000, "console", "console", `{"level":"fatal","stacktracelevel":"NONE","stderr":true}`) _ = log.NewLogger(1000, "console", "console", `{"level":"fatal","stacktracelevel":"NONE","stderr":true}`)
} }
setting.LoadFromExisting() setting.InitProviderFromExistingFile()
setting.LoadCommonSettings()
if debug { if debug {
setting.RunMode = "dev" setting.RunMode = "dev"
} }

View File

@ -158,7 +158,8 @@ func runWeb(ctx *cli.Context) error {
log.Info("Global init") log.Info("Global init")
// Perform global initialization // Perform global initialization
setting.LoadFromExisting() setting.InitProviderFromExistingFile()
setting.LoadCommonSettings()
routers.GlobalInitInstalled(graceful.GetManager().HammerContext()) routers.GlobalInitInstalled(graceful.GetManager().HammerContext())
// We check that AppDataPath exists here (it should have been created during installation) // We check that AppDataPath exists here (it should have been created during installation)

View File

@ -49,7 +49,8 @@ func runPR() {
log.Fatal(err) log.Fatal(err)
} }
setting.SetCustomPathAndConf("", "", "") setting.SetCustomPathAndConf("", "", "")
setting.LoadAllowEmpty() setting.InitProviderAllowEmpty()
setting.LoadCommonSettings()
setting.RepoRootPath, err = os.MkdirTemp(os.TempDir(), "repos") setting.RepoRootPath, err = os.MkdirTemp(os.TempDir(), "repos")
if err != nil { if err != nil {
@ -82,7 +83,7 @@ func runPR() {
setting.Database.Path = ":memory:" setting.Database.Path = ":memory:"
setting.Database.Timeout = 500 setting.Database.Timeout = 500
*/ */
dbCfg := setting.Cfg.Section("database") dbCfg := setting.CfgProvider.Section("database")
dbCfg.NewKey("DB_TYPE", "sqlite3") dbCfg.NewKey("DB_TYPE", "sqlite3")
dbCfg.NewKey("PATH", ":memory:") dbCfg.NewKey("PATH", ":memory:")

View File

@ -13,7 +13,7 @@ import (
func init() { func init() {
setting.SetCustomPathAndConf("", "", "") setting.SetCustomPathAndConf("", "", "")
setting.LoadForTest() setting.InitProviderAndLoadCommonSettingsForTest()
} }
func TestMain(m *testing.M) { func TestMain(m *testing.M) {

View File

@ -13,7 +13,7 @@ import (
func init() { func init() {
setting.SetCustomPathAndConf("", "", "") setting.SetCustomPathAndConf("", "", "")
setting.LoadForTest() setting.InitProviderAndLoadCommonSettingsForTest()
} }
func TestMain(m *testing.M) { func TestMain(m *testing.M) {

View File

@ -20,7 +20,7 @@ import (
func init() { func init() {
setting.SetCustomPathAndConf("", "", "") setting.SetCustomPathAndConf("", "", "")
setting.LoadForTest() setting.InitProviderAndLoadCommonSettingsForTest()
} }
func TestFixturesAreConsistent(t *testing.T) { func TestFixturesAreConsistent(t *testing.T) {

View File

@ -20,7 +20,7 @@ import (
func init() { func init() {
setting.SetCustomPathAndConf("", "", "") setting.SetCustomPathAndConf("", "", "")
setting.LoadForTest() setting.InitProviderAndLoadCommonSettingsForTest()
} }
// TestFixturesAreConsistent assert that test fixtures are consistent // TestFixturesAreConsistent assert that test fixtures are consistent

View File

@ -149,13 +149,13 @@ func MainTest(m *testing.M) {
setting.AppDataPath = tmpDataPath setting.AppDataPath = tmpDataPath
setting.SetCustomPathAndConf("", "", "") setting.SetCustomPathAndConf("", "", "")
setting.LoadForTest() setting.InitProviderAndLoadCommonSettingsForTest()
if err = git.InitFull(context.Background()); err != nil { if err = git.InitFull(context.Background()); err != nil {
fmt.Printf("Unable to InitFull: %v\n", err) fmt.Printf("Unable to InitFull: %v\n", err)
os.Exit(1) os.Exit(1)
} }
setting.InitDBConfig() setting.LoadDBSetting()
setting.NewLogServices(true) setting.InitLogs(true)
exitStatus := m.Run() exitStatus := m.Run()

View File

@ -27,7 +27,7 @@ var signedUserNameStringPointerKey interface{} = "signedUserNameStringPointerKey
// AccessLogger returns a middleware to log access logger // AccessLogger returns a middleware to log access logger
func AccessLogger() func(http.Handler) http.Handler { func AccessLogger() func(http.Handler) http.Handler {
logger := log.GetLogger("access") logger := log.GetLogger("access")
logTemplate, _ := template.New("log").Parse(setting.AccessLogTemplate) logTemplate, _ := template.New("log").Parse(setting.Log.AccessLogTemplate)
return func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
start := time.Now() start := time.Now()

View File

@ -44,10 +44,10 @@ func (w *wrappedLevelLogger) Log(skip int, level log.Level, format string, v ...
} }
func initDBDisableConsole(ctx context.Context, disableConsole bool) error { func initDBDisableConsole(ctx context.Context, disableConsole bool) error {
setting.LoadFromExisting() setting.InitProviderFromExistingFile()
setting.InitDBConfig() setting.LoadCommonSettings()
setting.LoadDBSetting()
setting.NewXORMLogService(disableConsole) setting.InitSQLLog(disableConsole)
if err := db.InitEngine(ctx); err != nil { if err := db.InitEngine(ctx); err != nil {
return fmt.Errorf("db.InitEngine: %w", err) return fmt.Errorf("db.InitEngine: %w", err)
} }
@ -71,7 +71,7 @@ func RunChecks(ctx context.Context, logger log.Logger, autofix bool, checks []*C
for i, check := range checks { for i, check := range checks {
if !dbIsInit && !check.SkipDatabaseInitialization { if !dbIsInit && !check.SkipDatabaseInitialization {
// Only open database after the most basic configuration check // Only open database after the most basic configuration check
setting.EnableXORMLog = false setting.Log.EnableXORMLog = false
if err := initDBDisableConsole(ctx, true); err != nil { if err := initDBDisableConsole(ctx, true); err != nil {
logger.Error("Error whilst initializing the database: %v", err) logger.Error("Error whilst initializing the database: %v", err)
logger.Error("Check if you are using the right config file. You can use a --config directive to specify one.") logger.Error("Check if you are using the right config file. You can use a --config directive to specify one.")

View File

@ -67,7 +67,8 @@ func checkConfigurationFiles(ctx context.Context, logger log.Logger, autofix boo
return err return err
} }
setting.LoadFromExisting() setting.InitProviderFromExistingFile()
setting.LoadCommonSettings()
configurationFiles := []configurationFile{ configurationFiles := []configurationFile{
{"Configuration File Path", setting.CustomConf, false, true, false}, {"Configuration File Path", setting.CustomConf, false, true, false},
@ -75,7 +76,7 @@ func checkConfigurationFiles(ctx context.Context, logger log.Logger, autofix boo
{"Data Root Path", setting.AppDataPath, true, true, true}, {"Data Root Path", setting.AppDataPath, true, true, true},
{"Custom File Root Path", setting.CustomPath, true, false, false}, {"Custom File Root Path", setting.CustomPath, true, false, false},
{"Work directory", setting.AppWorkPath, true, true, false}, {"Work directory", setting.AppWorkPath, true, true, false},
{"Log Root Path", setting.LogRootPath, true, true, true}, {"Log Root Path", setting.Log.RootPath, true, true, true},
} }
if options.IsDynamic() { if options.IsDynamic() {

View File

@ -41,12 +41,8 @@ var (
// NewContext loads custom highlight map from local config // NewContext loads custom highlight map from local config
func NewContext() { func NewContext() {
once.Do(func() { once.Do(func() {
if setting.Cfg != nil { highlightMapping = setting.GetHighlightMapping()
keys := setting.Cfg.Section("highlight.mapping").Keys()
for i := range keys {
highlightMapping[keys[i].Name()] = keys[i].Value()
}
}
// The size 512 is simply a conservative rule of thumb // The size 512 is simply a conservative rule of thumb
c, err := lru.New2Q(512) c, err := lru.New2Q(512)
if err != nil { if err != nil {

View File

@ -27,11 +27,11 @@ func TestMain(m *testing.M) {
func TestBleveSearchIssues(t *testing.T) { func TestBleveSearchIssues(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
setting.Cfg = ini.Empty() setting.CfgProvider = ini.Empty()
tmpIndexerDir := t.TempDir() tmpIndexerDir := t.TempDir()
setting.Cfg.Section("queue.issue_indexer").Key("DATADIR").MustString(path.Join(tmpIndexerDir, "issues.queue")) setting.CfgProvider.Section("queue.issue_indexer").Key("DATADIR").MustString(path.Join(tmpIndexerDir, "issues.queue"))
oldIssuePath := setting.Indexer.IssuePath oldIssuePath := setting.Indexer.IssuePath
setting.Indexer.IssuePath = path.Join(tmpIndexerDir, "issues.queue") setting.Indexer.IssuePath = path.Join(tmpIndexerDir, "issues.queue")
@ -40,7 +40,7 @@ func TestBleveSearchIssues(t *testing.T) {
}() }()
setting.Indexer.IssueType = "bleve" setting.Indexer.IssueType = "bleve"
setting.NewQueueService() setting.LoadQueueSettings()
InitIssueIndexer(true) InitIssueIndexer(true)
defer func() { defer func() {
indexer := holder.get() indexer := holder.get()

View File

@ -29,9 +29,9 @@ func TestMain(m *testing.M) {
func TestRepoStatsIndex(t *testing.T) { func TestRepoStatsIndex(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
setting.Cfg = ini.Empty() setting.CfgProvider = ini.Empty()
setting.NewQueueService() setting.LoadQueueSettings()
err := Init() err := Init()
assert.NoError(t, err) assert.NoError(t, err)

View File

@ -28,7 +28,8 @@ var localMetas = map[string]string{
} }
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
setting.LoadAllowEmpty() setting.InitProviderAllowEmpty()
setting.LoadCommonSettings()
if err := git.InitSimple(context.Background()); err != nil { if err := git.InitSimple(context.Background()); err != nil {
log.Fatal("git init failed, err: %v", err) log.Fatal("git init failed, err: %v", err)
} }

View File

@ -33,7 +33,8 @@ var localMetas = map[string]string{
} }
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
setting.LoadAllowEmpty() setting.InitProviderAllowEmpty()
setting.LoadCommonSettings()
if err := git.InitSimple(context.Background()); err != nil { if err := git.InitSimple(context.Background()); err != nil {
log.Fatal("git init failed, err: %v", err) log.Fatal("git init failed, err: %v", err)
} }

View File

@ -19,11 +19,11 @@ var (
} }
) )
func newActions() { func loadActionsFrom(rootCfg ConfigProvider) {
sec := Cfg.Section("actions") sec := rootCfg.Section("actions")
if err := sec.MapTo(&Actions); err != nil { if err := sec.MapTo(&Actions); err != nil {
log.Fatal("Failed to map Actions settings: %v", err) log.Fatal("Failed to map Actions settings: %v", err)
} }
Actions.Storage = getStorage("actions_log", "", nil) Actions.Storage = getStorage(rootCfg, "actions_log", "", nil)
} }

16
modules/setting/admin.go Normal file
View File

@ -0,0 +1,16 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
// Admin settings
var Admin struct {
DisableRegularOrgCreation bool
DefaultEmailNotification string
}
func loadAdminFrom(rootCfg ConfigProvider) {
mustMapSetting(rootCfg, "admin", &Admin)
sec := rootCfg.Section("admin")
Admin.DefaultEmailNotification = sec.Key("DEFAULT_EMAIL_NOTIFICATIONS").MustString("enabled")
}

40
modules/setting/api.go Normal file
View File

@ -0,0 +1,40 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
import (
"net/url"
"path"
"code.gitea.io/gitea/modules/log"
)
// API settings
var API = struct {
EnableSwagger bool
SwaggerURL string
MaxResponseItems int
DefaultPagingNum int
DefaultGitTreesPerPage int
DefaultMaxBlobSize int64
}{
EnableSwagger: true,
SwaggerURL: "",
MaxResponseItems: 50,
DefaultPagingNum: 30,
DefaultGitTreesPerPage: 1000,
DefaultMaxBlobSize: 10485760,
}
func loadAPIFrom(rootCfg ConfigProvider) {
mustMapSetting(rootCfg, "api", &API)
defaultAppURL := string(Protocol) + "://" + Domain + ":" + HTTPPort
u, err := url.Parse(rootCfg.Section("server").Key("ROOT_URL").MustString(defaultAppURL))
if err != nil {
log.Fatal("Invalid ROOT_URL '%s': %s", AppURL, err)
}
u.Path = path.Join(u.Path, "api", "swagger")
API.SwaggerURL = u.String()
}

View File

@ -20,11 +20,11 @@ var Attachment = struct {
Enabled: true, Enabled: true,
} }
func newAttachmentService() { func loadAttachmentFrom(rootCfg ConfigProvider) {
sec := Cfg.Section("attachment") sec := rootCfg.Section("attachment")
storageType := sec.Key("STORAGE_TYPE").MustString("") storageType := sec.Key("STORAGE_TYPE").MustString("")
Attachment.Storage = getStorage("attachments", storageType, sec) Attachment.Storage = getStorage(rootCfg, "attachments", storageType, sec)
Attachment.AllowedTypes = sec.Key("ALLOWED_TYPES").MustString(".csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip") Attachment.AllowedTypes = sec.Key("ALLOWED_TYPES").MustString(".csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip")
Attachment.MaxSize = sec.Key("MAX_SIZE").MustInt64(4) Attachment.MaxSize = sec.Key("MAX_SIZE").MustInt64(4)

View File

@ -49,8 +49,8 @@ var CacheService = struct {
// MemcacheMaxTTL represents the maximum memcache TTL // MemcacheMaxTTL represents the maximum memcache TTL
const MemcacheMaxTTL = 30 * 24 * time.Hour const MemcacheMaxTTL = 30 * 24 * time.Hour
func newCacheService() { func loadCacheFrom(rootCfg ConfigProvider) {
sec := Cfg.Section("cache") sec := rootCfg.Section("cache")
if err := sec.MapTo(&CacheService); err != nil { if err := sec.MapTo(&CacheService); err != nil {
log.Fatal("Failed to map Cache settings: %v", err) log.Fatal("Failed to map Cache settings: %v", err)
} }
@ -79,7 +79,7 @@ func newCacheService() {
Service.EnableCaptcha = false Service.EnableCaptcha = false
} }
sec = Cfg.Section("cache.last_commit") sec = rootCfg.Section("cache.last_commit")
if !CacheService.Enabled { if !CacheService.Enabled {
CacheService.LastCommit.Enabled = false CacheService.LastCommit.Enabled = false
} }

22
modules/setting/camo.go Normal file
View File

@ -0,0 +1,22 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
import "code.gitea.io/gitea/modules/log"
var Camo = struct {
Enabled bool
ServerURL string `ini:"SERVER_URL"`
HMACKey string `ini:"HMAC_KEY"`
Allways bool
}{}
func loadCamoFrom(rootCfg ConfigProvider) {
mustMapSetting(rootCfg, "camo", &Camo)
if Camo.Enabled {
if Camo.ServerURL == "" || Camo.HMACKey == "" {
log.Fatal(`Camo settings require "SERVER_URL" and HMAC_KEY`)
}
}
}

View File

@ -0,0 +1,39 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
import (
"code.gitea.io/gitea/modules/log"
ini "gopkg.in/ini.v1"
)
// ConfigProvider represents a config provider
type ConfigProvider interface {
Section(section string) *ini.Section
NewSection(name string) (*ini.Section, error)
GetSection(name string) (*ini.Section, error)
}
// a file is an implementation ConfigProvider and other implementations are possible, i.e. from docker, k8s, …
var _ ConfigProvider = &ini.File{}
func mustMapSetting(rootCfg ConfigProvider, sectionName string, setting interface{}) {
if err := rootCfg.Section(sectionName).MapTo(setting); err != nil {
log.Fatal("Failed to map %s settings: %v", sectionName, err)
}
}
func deprecatedSetting(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey string) {
if rootCfg.Section(oldSection).HasKey(oldKey) {
log.Error("Deprecated fallback `[%s]` `%s` present. Use `[%s]` `%s` instead. This fallback will be removed in v1.19.0", oldSection, oldKey, newSection, newKey)
}
}
// deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini
func deprecatedSettingDB(rootCfg ConfigProvider, oldSection, oldKey string) {
if rootCfg.Section(oldSection).HasKey(oldKey) {
log.Error("Deprecated `[%s]` `%s` present which has been copied to database table sys_setting", oldSection, oldKey)
}
}

View File

@ -27,12 +27,8 @@ var CORSConfig = struct {
XFrameOptions: "SAMEORIGIN", XFrameOptions: "SAMEORIGIN",
} }
func newCORSService() { func loadCorsFrom(rootCfg ConfigProvider) {
sec := Cfg.Section("cors") mustMapSetting(rootCfg, "cors", &CORSConfig)
if err := sec.MapTo(&CORSConfig); err != nil {
log.Fatal("Failed to map cors settings: %v", err)
}
if CORSConfig.Enabled { if CORSConfig.Enabled {
log.Info("CORS Service Enabled") log.Info("CORS Service Enabled")
} }

View File

@ -7,7 +7,11 @@ import "reflect"
// GetCronSettings maps the cron subsection to the provided config // GetCronSettings maps the cron subsection to the provided config
func GetCronSettings(name string, config interface{}) (interface{}, error) { func GetCronSettings(name string, config interface{}) (interface{}, error) {
if err := Cfg.Section("cron." + name).MapTo(config); err != nil { return getCronSettings(CfgProvider, name, config)
}
func getCronSettings(rootCfg ConfigProvider, name string, config interface{}) (interface{}, error) {
if err := rootCfg.Section("cron." + name).MapTo(config); err != nil {
return config, err return config, err
} }
@ -18,7 +22,7 @@ func GetCronSettings(name string, config interface{}) (interface{}, error) {
field := val.Field(i) field := val.Field(i)
tpField := typ.Field(i) tpField := typ.Field(i)
if tpField.Type.Kind() == reflect.Struct && tpField.Anonymous { if tpField.Type.Kind() == reflect.Struct && tpField.Anonymous {
if err := Cfg.Section("cron." + name).MapTo(field.Addr().Interface()); err != nil { if err := rootCfg.Section("cron." + name).MapTo(field.Addr().Interface()); err != nil {
return config, err return config, err
} }
} }

View File

@ -10,7 +10,7 @@ import (
ini "gopkg.in/ini.v1" ini "gopkg.in/ini.v1"
) )
func Test_GetCronSettings(t *testing.T) { func Test_getCronSettings(t *testing.T) {
type BaseStruct struct { type BaseStruct struct {
Base bool Base bool
Second string Second string
@ -27,7 +27,8 @@ Base = true
Second = white rabbit Second = white rabbit
Extend = true Extend = true
` `
Cfg, _ = ini.Load([]byte(iniStr)) cfg, err := ini.Load([]byte(iniStr))
assert.NoError(t, err)
extended := &Extended{ extended := &Extended{
BaseStruct: BaseStruct{ BaseStruct: BaseStruct{
@ -35,8 +36,7 @@ Extend = true
}, },
} }
_, err := GetCronSettings("test", extended) _, err = getCronSettings(cfg, "test", extended)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, extended.Base) assert.True(t, extended.Base)
assert.EqualValues(t, extended.Second, "white rabbit") assert.EqualValues(t, extended.Second, "white rabbit")

View File

@ -56,9 +56,9 @@ var (
} }
) )
// InitDBConfig loads the database settings // LoadDBSetting loads the database settings
func InitDBConfig() { func LoadDBSetting() {
sec := Cfg.Section("database") sec := CfgProvider.Section("database")
Database.Type = sec.Key("DB_TYPE").String() Database.Type = sec.Key("DB_TYPE").String()
defaultCharset := "utf8" defaultCharset := "utf8"
Database.UseMySQL = false Database.UseMySQL = false

View File

@ -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
}

View File

@ -33,8 +33,8 @@ var (
// HttpsigAlgs is a constant slice of httpsig algorithm objects // HttpsigAlgs is a constant slice of httpsig algorithm objects
var HttpsigAlgs []httpsig.Algorithm var HttpsigAlgs []httpsig.Algorithm
func newFederationService() { func loadFederationFrom(rootCfg ConfigProvider) {
if err := Cfg.Section("federation").MapTo(&Federation); err != nil { if err := rootCfg.Section("federation").MapTo(&Federation); err != nil {
log.Fatal("Failed to map Federation settings: %v", err) log.Fatal("Failed to map Federation settings: %v", err)
} else if !httpsig.IsSupportedDigestAlgorithm(Federation.DigestAlgorithm) { } else if !httpsig.IsSupportedDigestAlgorithm(Federation.DigestAlgorithm) {
log.Fatal("unsupported digest algorithm: %s", Federation.DigestAlgorithm) log.Fatal("unsupported digest algorithm: %s", Federation.DigestAlgorithm)

View File

@ -67,9 +67,8 @@ var Git = struct {
}, },
} }
func newGit() { func loadGitFrom(rootCfg ConfigProvider) {
sec := Cfg.Section("git") sec := rootCfg.Section("git")
if err := sec.MapTo(&Git); err != nil { if err := sec.MapTo(&Git); err != nil {
log.Fatal("Failed to map Git settings: %v", err) log.Fatal("Failed to map Git settings: %v", err)
} }

View File

@ -0,0 +1,17 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
func GetHighlightMapping() map[string]string {
highlightMapping := map[string]string{}
if CfgProvider == nil {
return highlightMapping
}
keys := CfgProvider.Section("highlight.mapping").Keys()
for _, key := range keys {
highlightMapping[key.Name()] = key.Value()
}
return highlightMapping
}

View File

@ -47,3 +47,20 @@ func defaultI18nNames() (res []string) {
} }
return res return res
} }
var (
// I18n settings
Langs []string
Names []string
)
func loadI18nFrom(rootCfg ConfigProvider) {
Langs = rootCfg.Section("i18n").Key("LANGS").Strings(",")
if len(Langs) == 0 {
Langs = defaultI18nLangs()
}
Names = rootCfg.Section("i18n").Key("NAMES").Strings(",")
if len(Names) == 0 {
Names = defaultI18nNames()
}
}

View File

@ -31,10 +31,8 @@ var IncomingEmail = struct {
MaximumMessageSize: 10485760, MaximumMessageSize: 10485760,
} }
func newIncomingEmail() { func loadIncomingEmailFrom(rootCfg ConfigProvider) {
if err := Cfg.Section("email.incoming").MapTo(&IncomingEmail); err != nil { mustMapSetting(rootCfg, "email.incoming", &IncomingEmail)
log.Fatal("Unable to map [email.incoming] section on to IncomingEmail. Error: %v", err)
}
if !IncomingEmail.Enabled { if !IncomingEmail.Enabled {
return return

View File

@ -45,8 +45,8 @@ var Indexer = struct {
ExcludeVendored: true, ExcludeVendored: true,
} }
func newIndexerService() { func loadIndexerFrom(rootCfg ConfigProvider) {
sec := Cfg.Section("indexer") sec := rootCfg.Section("indexer")
Indexer.IssueType = sec.Key("ISSUE_INDEXER_TYPE").MustString("bleve") Indexer.IssueType = sec.Key("ISSUE_INDEXER_TYPE").MustString("bleve")
Indexer.IssuePath = filepath.ToSlash(sec.Key("ISSUE_INDEXER_PATH").MustString(filepath.ToSlash(filepath.Join(AppDataPath, "indexers/issues.bleve")))) Indexer.IssuePath = filepath.ToSlash(sec.Key("ISSUE_INDEXER_PATH").MustString(filepath.ToSlash(filepath.Join(AppDataPath, "indexers/issues.bleve"))))
if !filepath.IsAbs(Indexer.IssuePath) { if !filepath.IsAbs(Indexer.IssuePath) {
@ -57,11 +57,11 @@ func newIndexerService() {
// The following settings are deprecated and can be overridden by settings in [queue] or [queue.issue_indexer] // The following settings are deprecated and can be overridden by settings in [queue] or [queue.issue_indexer]
// FIXME: DEPRECATED to be removed in v1.18.0 // FIXME: DEPRECATED to be removed in v1.18.0
deprecatedSetting("indexer", "ISSUE_INDEXER_QUEUE_TYPE", "queue.issue_indexer", "TYPE") deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_TYPE", "queue.issue_indexer", "TYPE")
deprecatedSetting("indexer", "ISSUE_INDEXER_QUEUE_DIR", "queue.issue_indexer", "DATADIR") deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_DIR", "queue.issue_indexer", "DATADIR")
deprecatedSetting("indexer", "ISSUE_INDEXER_QUEUE_CONN_STR", "queue.issue_indexer", "CONN_STR") deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_CONN_STR", "queue.issue_indexer", "CONN_STR")
deprecatedSetting("indexer", "ISSUE_INDEXER_QUEUE_BATCH_NUMBER", "queue.issue_indexer", "BATCH_LENGTH") deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_BATCH_NUMBER", "queue.issue_indexer", "BATCH_LENGTH")
deprecatedSetting("indexer", "UPDATE_BUFFER_LEN", "queue.issue_indexer", "LENGTH") deprecatedSetting(rootCfg, "indexer", "UPDATE_BUFFER_LEN", "queue.issue_indexer", "LENGTH")
Indexer.RepoIndexerEnabled = sec.Key("REPO_INDEXER_ENABLED").MustBool(false) Indexer.RepoIndexerEnabled = sec.Key("REPO_INDEXER_ENABLED").MustBool(false)
Indexer.RepoType = sec.Key("REPO_INDEXER_TYPE").MustString("bleve") Indexer.RepoType = sec.Key("REPO_INDEXER_TYPE").MustString("bleve")

View File

@ -25,22 +25,22 @@ var LFS = struct {
Storage Storage
}{} }{}
func newLFSService() { func loadLFSFrom(rootCfg ConfigProvider) {
sec := Cfg.Section("server") sec := rootCfg.Section("server")
if err := sec.MapTo(&LFS); err != nil { if err := sec.MapTo(&LFS); err != nil {
log.Fatal("Failed to map LFS settings: %v", err) log.Fatal("Failed to map LFS settings: %v", err)
} }
lfsSec := Cfg.Section("lfs") lfsSec := rootCfg.Section("lfs")
storageType := lfsSec.Key("STORAGE_TYPE").MustString("") storageType := lfsSec.Key("STORAGE_TYPE").MustString("")
// Specifically default PATH to LFS_CONTENT_PATH // Specifically default PATH to LFS_CONTENT_PATH
// FIXME: DEPRECATED to be removed in v1.18.0 // FIXME: DEPRECATED to be removed in v1.18.0
deprecatedSetting("server", "LFS_CONTENT_PATH", "lfs", "PATH") deprecatedSetting(rootCfg, "server", "LFS_CONTENT_PATH", "lfs", "PATH")
lfsSec.Key("PATH").MustString( lfsSec.Key("PATH").MustString(
sec.Key("LFS_CONTENT_PATH").String()) sec.Key("LFS_CONTENT_PATH").String())
LFS.Storage = getStorage("lfs", storageType, lfsSec) LFS.Storage = getStorage(rootCfg, "lfs", storageType, lfsSec)
// Rest of LFS service settings // Rest of LFS service settings
if LFS.LocksPagingNum == 0 { if LFS.LocksPagingNum == 0 {

View File

@ -25,6 +25,21 @@ var (
logDescriptions = make(map[string]*LogDescription) logDescriptions = make(map[string]*LogDescription)
) )
// Log settings
var Log struct {
Level log.Level
StacktraceLogLevel string
RootPath string
EnableSSHLog bool
EnableXORMLog bool
DisableRouterLog bool
EnableAccessLog bool
AccessLogTemplate string
BufferLength int64
}
// GetLogDescriptions returns a race safe set of descriptions // GetLogDescriptions returns a race safe set of descriptions
func GetLogDescriptions() map[string]*LogDescription { func GetLogDescriptions() map[string]*LogDescription {
descriptionLock.RLock() descriptionLock.RLock()
@ -94,9 +109,9 @@ type defaultLogOptions struct {
func newDefaultLogOptions() defaultLogOptions { func newDefaultLogOptions() defaultLogOptions {
return defaultLogOptions{ return defaultLogOptions{
levelName: LogLevel.String(), levelName: Log.Level.String(),
flags: "stdflags", flags: "stdflags",
filename: filepath.Join(LogRootPath, "gitea.log"), filename: filepath.Join(Log.RootPath, "gitea.log"),
bufferLength: 10000, bufferLength: 10000,
disableConsole: false, disableConsole: false,
} }
@ -125,10 +140,33 @@ func getStacktraceLogLevel(section *ini.Section, key, defaultValue string) strin
return log.FromString(value).String() return log.FromString(value).String()
} }
func loadLogFrom(rootCfg ConfigProvider) {
sec := rootCfg.Section("log")
Log.Level = getLogLevel(sec, "LEVEL", log.INFO)
Log.StacktraceLogLevel = getStacktraceLogLevel(sec, "STACKTRACE_LEVEL", "None")
Log.RootPath = sec.Key("ROOT_PATH").MustString(path.Join(AppWorkPath, "log"))
forcePathSeparator(Log.RootPath)
Log.BufferLength = sec.Key("BUFFER_LEN").MustInt64(10000)
Log.EnableSSHLog = sec.Key("ENABLE_SSH_LOG").MustBool(false)
Log.EnableAccessLog = sec.Key("ENABLE_ACCESS_LOG").MustBool(false)
Log.AccessLogTemplate = sec.Key("ACCESS_LOG_TEMPLATE").MustString(
`{{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"`,
)
// the `MustString` updates the default value, and `log.ACCESS` is used by `generateNamedLogger("access")` later
_ = rootCfg.Section("log").Key("ACCESS").MustString("file")
sec.Key("ROUTER").MustString("console")
// Allow [log] DISABLE_ROUTER_LOG to override [server] DISABLE_ROUTER_LOG
Log.DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool(Log.DisableRouterLog)
Log.EnableXORMLog = rootCfg.Section("log").Key("ENABLE_XORM_LOG").MustBool(true)
}
func generateLogConfig(sec *ini.Section, name string, defaults defaultLogOptions) (mode, jsonConfig, levelName string) { func generateLogConfig(sec *ini.Section, name string, defaults defaultLogOptions) (mode, jsonConfig, levelName string) {
level := getLogLevel(sec, "LEVEL", LogLevel) level := getLogLevel(sec, "LEVEL", Log.Level)
levelName = level.String() levelName = level.String()
stacktraceLevelName := getStacktraceLogLevel(sec, "STACKTRACE_LEVEL", StacktraceLogLevel) stacktraceLevelName := getStacktraceLogLevel(sec, "STACKTRACE_LEVEL", Log.StacktraceLogLevel)
stacktraceLevel := log.FromString(stacktraceLevelName) stacktraceLevel := log.FromString(stacktraceLevelName)
mode = name mode = name
keys := sec.Keys() keys := sec.Keys()
@ -144,7 +182,7 @@ func generateLogConfig(sec *ini.Section, name string, defaults defaultLogOptions
logPath = key.MustString(defaults.filename) logPath = key.MustString(defaults.filename)
forcePathSeparator(logPath) forcePathSeparator(logPath)
if !filepath.IsAbs(logPath) { if !filepath.IsAbs(logPath) {
logPath = path.Join(LogRootPath, logPath) logPath = path.Join(Log.RootPath, logPath)
} }
case "FLAGS": case "FLAGS":
flags = log.FlagsFromString(key.MustString(defaults.flags)) flags = log.FlagsFromString(key.MustString(defaults.flags))
@ -213,12 +251,12 @@ func generateLogConfig(sec *ini.Section, name string, defaults defaultLogOptions
return mode, jsonConfig, levelName return mode, jsonConfig, levelName
} }
func generateNamedLogger(key string, options defaultLogOptions) *LogDescription { func generateNamedLogger(rootCfg ConfigProvider, key string, options defaultLogOptions) *LogDescription {
description := LogDescription{ description := LogDescription{
Name: key, Name: key,
} }
sections := strings.Split(Cfg.Section("log").Key(strings.ToUpper(key)).MustString(""), ",") sections := strings.Split(rootCfg.Section("log").Key(strings.ToUpper(key)).MustString(""), ",")
for i := 0; i < len(sections); i++ { for i := 0; i < len(sections); i++ {
sections[i] = strings.TrimSpace(sections[i]) sections[i] = strings.TrimSpace(sections[i])
@ -228,9 +266,9 @@ func generateNamedLogger(key string, options defaultLogOptions) *LogDescription
if len(name) == 0 || (name == "console" && options.disableConsole) { if len(name) == 0 || (name == "console" && options.disableConsole) {
continue continue
} }
sec, err := Cfg.GetSection("log." + name + "." + key) sec, err := rootCfg.GetSection("log." + name + "." + key)
if err != nil { if err != nil {
sec, _ = Cfg.NewSection("log." + name + "." + key) sec, _ = rootCfg.NewSection("log." + name + "." + key)
} }
provider, config, levelName := generateLogConfig(sec, name, options) provider, config, levelName := generateLogConfig(sec, name, options)
@ -253,46 +291,17 @@ func generateNamedLogger(key string, options defaultLogOptions) *LogDescription
return &description return &description
} }
func newAccessLogService() { // initLogFrom initializes logging with settings from configuration provider
EnableAccessLog = Cfg.Section("log").Key("ENABLE_ACCESS_LOG").MustBool(false) func initLogFrom(rootCfg ConfigProvider) {
AccessLogTemplate = Cfg.Section("log").Key("ACCESS_LOG_TEMPLATE").MustString( sec := rootCfg.Section("log")
`{{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"`,
)
// the `MustString` updates the default value, and `log.ACCESS` is used by `generateNamedLogger("access")` later
_ = Cfg.Section("log").Key("ACCESS").MustString("file")
if EnableAccessLog {
options := newDefaultLogOptions()
options.filename = filepath.Join(LogRootPath, "access.log")
options.flags = "" // For the router we don't want any prefixed flags
options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000)
generateNamedLogger("access", options)
}
}
func newRouterLogService() {
Cfg.Section("log").Key("ROUTER").MustString("console")
// Allow [log] DISABLE_ROUTER_LOG to override [server] DISABLE_ROUTER_LOG
DisableRouterLog = Cfg.Section("log").Key("DISABLE_ROUTER_LOG").MustBool(DisableRouterLog)
if !DisableRouterLog {
options := newDefaultLogOptions()
options.filename = filepath.Join(LogRootPath, "router.log")
options.flags = "date,time" // For the router we don't want any prefixed flags
options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000)
generateNamedLogger("router", options)
}
}
func newLogService() {
options := newDefaultLogOptions() options := newDefaultLogOptions()
options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000) options.bufferLength = Log.BufferLength
EnableSSHLog = Cfg.Section("log").Key("ENABLE_SSH_LOG").MustBool(false)
description := LogDescription{ description := LogDescription{
Name: log.DEFAULT, Name: log.DEFAULT,
} }
sections := strings.Split(Cfg.Section("log").Key("MODE").MustString("console"), ",") sections := strings.Split(sec.Key("MODE").MustString("console"), ",")
useConsole := false useConsole := false
for _, name := range sections { for _, name := range sections {
@ -304,11 +313,11 @@ func newLogService() {
useConsole = true useConsole = true
} }
sec, err := Cfg.GetSection("log." + name + ".default") sec, err := rootCfg.GetSection("log." + name + ".default")
if err != nil { if err != nil {
sec, err = Cfg.GetSection("log." + name) sec, err = rootCfg.GetSection("log." + name)
if err != nil { if err != nil {
sec, _ = Cfg.NewSection("log." + name) sec, _ = rootCfg.NewSection("log." + name)
} }
} }
@ -340,27 +349,45 @@ func newLogService() {
// RestartLogsWithPIDSuffix restarts the logs with a PID suffix on files // RestartLogsWithPIDSuffix restarts the logs with a PID suffix on files
func RestartLogsWithPIDSuffix() { func RestartLogsWithPIDSuffix() {
filenameSuffix = fmt.Sprintf(".%d", os.Getpid()) filenameSuffix = fmt.Sprintf(".%d", os.Getpid())
NewLogServices(false) InitLogs(false)
} }
// NewLogServices creates all the log services // InitLogs creates all the log services
func NewLogServices(disableConsole bool) { func InitLogs(disableConsole bool) {
newLogService() initLogFrom(CfgProvider)
newRouterLogService()
newAccessLogService()
NewXORMLogService(disableConsole)
}
// NewXORMLogService initializes xorm logger service if !Log.DisableRouterLog {
func NewXORMLogService(disableConsole bool) {
EnableXORMLog = Cfg.Section("log").Key("ENABLE_XORM_LOG").MustBool(true)
if EnableXORMLog {
options := newDefaultLogOptions() options := newDefaultLogOptions()
options.filename = filepath.Join(LogRootPath, "xorm.log") options.filename = filepath.Join(Log.RootPath, "router.log")
options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000) options.flags = "date,time" // For the router we don't want any prefixed flags
options.bufferLength = Log.BufferLength
generateNamedLogger(CfgProvider, "router", options)
}
if Log.EnableAccessLog {
options := newDefaultLogOptions()
options.filename = filepath.Join(Log.RootPath, "access.log")
options.flags = "" // For the router we don't want any prefixed flags
options.bufferLength = Log.BufferLength
generateNamedLogger(CfgProvider, "access", options)
}
initSQLLogFrom(CfgProvider, disableConsole)
}
// InitSQLLog initializes xorm logger setting
func InitSQLLog(disableConsole bool) {
initSQLLogFrom(CfgProvider, disableConsole)
}
func initSQLLogFrom(rootCfg ConfigProvider, disableConsole bool) {
if Log.EnableXORMLog {
options := newDefaultLogOptions()
options.filename = filepath.Join(Log.RootPath, "xorm.log")
options.bufferLength = Log.BufferLength
options.disableConsole = disableConsole options.disableConsole = disableConsole
Cfg.Section("log").Key("XORM").MustString(",") rootCfg.Section("log").Key("XORM").MustString(",")
generateNamedLogger("xorm", options) generateNamedLogger(rootCfg, "xorm", options)
} }
} }

View File

@ -12,7 +12,6 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
shellquote "github.com/kballard/go-shellquote" shellquote "github.com/kballard/go-shellquote"
ini "gopkg.in/ini.v1"
) )
// Mailer represents mail service. // Mailer represents mail service.
@ -50,7 +49,14 @@ type Mailer struct {
// MailService the global mailer // MailService the global mailer
var MailService *Mailer var MailService *Mailer
func parseMailerConfig(rootCfg *ini.File) { func loadMailsFrom(rootCfg ConfigProvider) {
loadMailerFrom(rootCfg)
loadRegisterMailFrom(rootCfg)
loadNotifyMailFrom(rootCfg)
loadIncomingEmailFrom(rootCfg)
}
func loadMailerFrom(rootCfg ConfigProvider) {
sec := rootCfg.Section("mailer") sec := rootCfg.Section("mailer")
// Check mailer setting. // Check mailer setting.
if !sec.Key("ENABLED").MustBool() { if !sec.Key("ENABLED").MustBool() {
@ -59,7 +65,7 @@ func parseMailerConfig(rootCfg *ini.File) {
// Handle Deprecations and map on to new configuration // Handle Deprecations and map on to new configuration
// FIXME: DEPRECATED to be removed in v1.19.0 // FIXME: DEPRECATED to be removed in v1.19.0
deprecatedSetting("mailer", "MAILER_TYPE", "mailer", "PROTOCOL") deprecatedSetting(rootCfg, "mailer", "MAILER_TYPE", "mailer", "PROTOCOL")
if sec.HasKey("MAILER_TYPE") && !sec.HasKey("PROTOCOL") { if sec.HasKey("MAILER_TYPE") && !sec.HasKey("PROTOCOL") {
if sec.Key("MAILER_TYPE").String() == "sendmail" { if sec.Key("MAILER_TYPE").String() == "sendmail" {
sec.Key("PROTOCOL").MustString("sendmail") sec.Key("PROTOCOL").MustString("sendmail")
@ -67,7 +73,7 @@ func parseMailerConfig(rootCfg *ini.File) {
} }
// FIXME: DEPRECATED to be removed in v1.19.0 // FIXME: DEPRECATED to be removed in v1.19.0
deprecatedSetting("mailer", "HOST", "mailer", "SMTP_ADDR") deprecatedSetting(rootCfg, "mailer", "HOST", "mailer", "SMTP_ADDR")
if sec.HasKey("HOST") && !sec.HasKey("SMTP_ADDR") { if sec.HasKey("HOST") && !sec.HasKey("SMTP_ADDR") {
givenHost := sec.Key("HOST").String() givenHost := sec.Key("HOST").String()
addr, port, err := net.SplitHostPort(givenHost) addr, port, err := net.SplitHostPort(givenHost)
@ -84,7 +90,7 @@ func parseMailerConfig(rootCfg *ini.File) {
} }
// FIXME: DEPRECATED to be removed in v1.19.0 // FIXME: DEPRECATED to be removed in v1.19.0
deprecatedSetting("mailer", "IS_TLS_ENABLED", "mailer", "PROTOCOL") deprecatedSetting(rootCfg, "mailer", "IS_TLS_ENABLED", "mailer", "PROTOCOL")
if sec.HasKey("IS_TLS_ENABLED") && !sec.HasKey("PROTOCOL") { if sec.HasKey("IS_TLS_ENABLED") && !sec.HasKey("PROTOCOL") {
if sec.Key("IS_TLS_ENABLED").MustBool() { if sec.Key("IS_TLS_ENABLED").MustBool() {
sec.Key("PROTOCOL").MustString("smtps") sec.Key("PROTOCOL").MustString("smtps")
@ -94,37 +100,37 @@ func parseMailerConfig(rootCfg *ini.File) {
} }
// FIXME: DEPRECATED to be removed in v1.19.0 // FIXME: DEPRECATED to be removed in v1.19.0
deprecatedSetting("mailer", "DISABLE_HELO", "mailer", "ENABLE_HELO") deprecatedSetting(rootCfg, "mailer", "DISABLE_HELO", "mailer", "ENABLE_HELO")
if sec.HasKey("DISABLE_HELO") && !sec.HasKey("ENABLE_HELO") { if sec.HasKey("DISABLE_HELO") && !sec.HasKey("ENABLE_HELO") {
sec.Key("ENABLE_HELO").MustBool(!sec.Key("DISABLE_HELO").MustBool()) sec.Key("ENABLE_HELO").MustBool(!sec.Key("DISABLE_HELO").MustBool())
} }
// FIXME: DEPRECATED to be removed in v1.19.0 // FIXME: DEPRECATED to be removed in v1.19.0
deprecatedSetting("mailer", "SKIP_VERIFY", "mailer", "FORCE_TRUST_SERVER_CERT") deprecatedSetting(rootCfg, "mailer", "SKIP_VERIFY", "mailer", "FORCE_TRUST_SERVER_CERT")
if sec.HasKey("SKIP_VERIFY") && !sec.HasKey("FORCE_TRUST_SERVER_CERT") { if sec.HasKey("SKIP_VERIFY") && !sec.HasKey("FORCE_TRUST_SERVER_CERT") {
sec.Key("FORCE_TRUST_SERVER_CERT").MustBool(sec.Key("SKIP_VERIFY").MustBool()) sec.Key("FORCE_TRUST_SERVER_CERT").MustBool(sec.Key("SKIP_VERIFY").MustBool())
} }
// FIXME: DEPRECATED to be removed in v1.19.0 // FIXME: DEPRECATED to be removed in v1.19.0
deprecatedSetting("mailer", "USE_CERTIFICATE", "mailer", "USE_CLIENT_CERT") deprecatedSetting(rootCfg, "mailer", "USE_CERTIFICATE", "mailer", "USE_CLIENT_CERT")
if sec.HasKey("USE_CERTIFICATE") && !sec.HasKey("USE_CLIENT_CERT") { if sec.HasKey("USE_CERTIFICATE") && !sec.HasKey("USE_CLIENT_CERT") {
sec.Key("USE_CLIENT_CERT").MustBool(sec.Key("USE_CERTIFICATE").MustBool()) sec.Key("USE_CLIENT_CERT").MustBool(sec.Key("USE_CERTIFICATE").MustBool())
} }
// FIXME: DEPRECATED to be removed in v1.19.0 // FIXME: DEPRECATED to be removed in v1.19.0
deprecatedSetting("mailer", "CERT_FILE", "mailer", "CLIENT_CERT_FILE") deprecatedSetting(rootCfg, "mailer", "CERT_FILE", "mailer", "CLIENT_CERT_FILE")
if sec.HasKey("CERT_FILE") && !sec.HasKey("CLIENT_CERT_FILE") { if sec.HasKey("CERT_FILE") && !sec.HasKey("CLIENT_CERT_FILE") {
sec.Key("CERT_FILE").MustString(sec.Key("CERT_FILE").String()) sec.Key("CERT_FILE").MustString(sec.Key("CERT_FILE").String())
} }
// FIXME: DEPRECATED to be removed in v1.19.0 // FIXME: DEPRECATED to be removed in v1.19.0
deprecatedSetting("mailer", "KEY_FILE", "mailer", "CLIENT_KEY_FILE") deprecatedSetting(rootCfg, "mailer", "KEY_FILE", "mailer", "CLIENT_KEY_FILE")
if sec.HasKey("KEY_FILE") && !sec.HasKey("CLIENT_KEY_FILE") { if sec.HasKey("KEY_FILE") && !sec.HasKey("CLIENT_KEY_FILE") {
sec.Key("KEY_FILE").MustString(sec.Key("KEY_FILE").String()) sec.Key("KEY_FILE").MustString(sec.Key("KEY_FILE").String())
} }
// FIXME: DEPRECATED to be removed in v1.19.0 // FIXME: DEPRECATED to be removed in v1.19.0
deprecatedSetting("mailer", "ENABLE_HTML_ALTERNATIVE", "mailer", "SEND_AS_PLAIN_TEXT") deprecatedSetting(rootCfg, "mailer", "ENABLE_HTML_ALTERNATIVE", "mailer", "SEND_AS_PLAIN_TEXT")
if sec.HasKey("ENABLE_HTML_ALTERNATIVE") && !sec.HasKey("SEND_AS_PLAIN_TEXT") { if sec.HasKey("ENABLE_HTML_ALTERNATIVE") && !sec.HasKey("SEND_AS_PLAIN_TEXT") {
sec.Key("SEND_AS_PLAIN_TEXT").MustBool(!sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(false)) sec.Key("SEND_AS_PLAIN_TEXT").MustBool(!sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(false))
} }
@ -237,8 +243,8 @@ func parseMailerConfig(rootCfg *ini.File) {
log.Info("Mail Service Enabled") log.Info("Mail Service Enabled")
} }
func newRegisterMailService() { func loadRegisterMailFrom(rootCfg ConfigProvider) {
if !Cfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").MustBool() { if !rootCfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").MustBool() {
return return
} else if MailService == nil { } else if MailService == nil {
log.Warn("Register Mail Service: Mail Service is not enabled") log.Warn("Register Mail Service: Mail Service is not enabled")
@ -248,8 +254,8 @@ func newRegisterMailService() {
log.Info("Register Mail Service Enabled") log.Info("Register Mail Service Enabled")
} }
func newNotifyMailService() { func loadNotifyMailFrom(rootCfg ConfigProvider) {
if !Cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() { if !rootCfg.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() {
return return
} else if MailService == nil { } else if MailService == nil {
log.Warn("Notify Mail Service: Mail Service is not enabled") log.Warn("Notify Mail Service: Mail Service is not enabled")

View File

@ -10,7 +10,7 @@ import (
ini "gopkg.in/ini.v1" ini "gopkg.in/ini.v1"
) )
func TestParseMailerConfig(t *testing.T) { func Test_loadMailerFrom(t *testing.T) {
iniFile := ini.Empty() iniFile := ini.Empty()
kases := map[string]*Mailer{ kases := map[string]*Mailer{
"smtp.mydomain.com": { "smtp.mydomain.com": {
@ -34,7 +34,7 @@ func TestParseMailerConfig(t *testing.T) {
sec.NewKey("HOST", host) sec.NewKey("HOST", host)
// Check mailer setting // Check mailer setting
parseMailerConfig(iniFile) loadMailerFrom(iniFile)
assert.EqualValues(t, kase.SMTPAddr, MailService.SMTPAddr) assert.EqualValues(t, kase.SMTPAddr, MailService.SMTPAddr)
assert.EqualValues(t, kase.SMTPPort, MailService.SMTPPort) assert.EqualValues(t, kase.SMTPPort, MailService.SMTPPort)

View File

@ -25,6 +25,20 @@ const (
RenderContentModeIframe = "iframe" RenderContentModeIframe = "iframe"
) )
// Markdown settings
var Markdown = struct {
EnableHardLineBreakInComments bool
EnableHardLineBreakInDocuments bool
CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"`
FileExtensions []string
EnableMath bool
}{
EnableHardLineBreakInComments: true,
EnableHardLineBreakInDocuments: false,
FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd", ","),
EnableMath: true,
}
// MarkupRenderer defines the external parser configured in ini // MarkupRenderer defines the external parser configured in ini
type MarkupRenderer struct { type MarkupRenderer struct {
Enabled bool Enabled bool
@ -46,12 +60,14 @@ type MarkupSanitizerRule struct {
AllowDataURIImages bool AllowDataURIImages bool
} }
func newMarkup() { func loadMarkupFrom(rootCfg ConfigProvider) {
MermaidMaxSourceCharacters = Cfg.Section("markup").Key("MERMAID_MAX_SOURCE_CHARACTERS").MustInt(5000) mustMapSetting(rootCfg, "markdown", &Markdown)
MermaidMaxSourceCharacters = rootCfg.Section("markup").Key("MERMAID_MAX_SOURCE_CHARACTERS").MustInt(5000)
ExternalMarkupRenderers = make([]*MarkupRenderer, 0, 10) ExternalMarkupRenderers = make([]*MarkupRenderer, 0, 10)
ExternalSanitizerRules = make([]MarkupSanitizerRule, 0, 10) ExternalSanitizerRules = make([]MarkupSanitizerRule, 0, 10)
for _, sec := range Cfg.Section("markup").ChildSections() { for _, sec := range rootCfg.Section("markup").ChildSections() {
name := strings.TrimPrefix(sec.Name(), "markup.") name := strings.TrimPrefix(sec.Name(), "markup.")
if name == "" { if name == "" {
log.Warn("name is empty, markup " + sec.Name() + "ignored") log.Warn("name is empty, markup " + sec.Name() + "ignored")

View File

@ -0,0 +1,21 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
// Metrics settings
var Metrics = struct {
Enabled bool
Token string
EnabledIssueByLabel bool
EnabledIssueByRepository bool
}{
Enabled: false,
Token: "",
EnabledIssueByLabel: false,
EnabledIssueByRepository: false,
}
func loadMetricsFrom(rootCfg ConfigProvider) {
mustMapSetting(rootCfg, "metrics", &Metrics)
}

View File

@ -16,8 +16,8 @@ var Migrations = struct {
RetryBackoff: 3, RetryBackoff: 3,
} }
func newMigrationsService() { func loadMigrationsFrom(rootCfg ConfigProvider) {
sec := Cfg.Section("migrations") sec := rootCfg.Section("migrations")
Migrations.MaxAttempts = sec.Key("MAX_ATTEMPTS").MustInt(Migrations.MaxAttempts) Migrations.MaxAttempts = sec.Key("MAX_ATTEMPTS").MustInt(Migrations.MaxAttempts)
Migrations.RetryBackoff = sec.Key("RETRY_BACKOFF").MustInt(Migrations.RetryBackoff) Migrations.RetryBackoff = sec.Key("RETRY_BACKOFF").MustInt(Migrations.RetryBackoff)

View File

@ -14,8 +14,8 @@ var MimeTypeMap = struct {
Map: map[string]string{}, Map: map[string]string{},
} }
func newMimeTypeMap() { func loadMimeTypeMapFrom(rootCfg ConfigProvider) {
sec := Cfg.Section("repository.mimetype_mapping") sec := rootCfg.Section("repository.mimetype_mapping")
keys := sec.Keys() keys := sec.Keys()
m := make(map[string]string, len(keys)) m := make(map[string]string, len(keys))
for _, key := range keys { for _, key := range keys {

View File

@ -24,16 +24,16 @@ var Mirror = struct {
DefaultInterval: 8 * time.Hour, DefaultInterval: 8 * time.Hour,
} }
func newMirror() { func loadMirrorFrom(rootCfg ConfigProvider) {
// Handle old configuration through `[repository]` `DISABLE_MIRRORS` // Handle old configuration through `[repository]` `DISABLE_MIRRORS`
// - please note this was badly named and only disabled the creation of new pull mirrors // - please note this was badly named and only disabled the creation of new pull mirrors
// FIXME: DEPRECATED to be removed in v1.18.0 // FIXME: DEPRECATED to be removed in v1.18.0
deprecatedSetting("repository", "DISABLE_MIRRORS", "mirror", "ENABLED") deprecatedSetting(rootCfg, "repository", "DISABLE_MIRRORS", "mirror", "ENABLED")
if Cfg.Section("repository").Key("DISABLE_MIRRORS").MustBool(false) { if rootCfg.Section("repository").Key("DISABLE_MIRRORS").MustBool(false) {
Mirror.DisableNewPull = true Mirror.DisableNewPull = true
} }
if err := Cfg.Section("mirror").MapTo(&Mirror); err != nil { if err := rootCfg.Section("mirror").MapTo(&Mirror); err != nil {
log.Fatal("Failed to map Mirror settings: %v", err) log.Fatal("Failed to map Mirror settings: %v", err)
} }

View File

@ -4,6 +4,9 @@
package setting package setting
import ( import (
"math"
"path/filepath"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
@ -59,8 +62,8 @@ var OAuth2Client struct {
AccountLinking OAuth2AccountLinkingType AccountLinking OAuth2AccountLinkingType
} }
func newOAuth2Client() { func loadOAuth2ClientFrom(rootCfg ConfigProvider) {
sec := Cfg.Section("oauth2_client") sec := rootCfg.Section("oauth2_client")
OAuth2Client.RegisterEmailConfirm = sec.Key("REGISTER_EMAIL_CONFIRM").MustBool(Service.RegisterEmailConfirm) OAuth2Client.RegisterEmailConfirm = sec.Key("REGISTER_EMAIL_CONFIRM").MustBool(Service.RegisterEmailConfirm)
OAuth2Client.OpenIDConnectScopes = parseScopes(sec, "OPENID_CONNECT_SCOPES") OAuth2Client.OpenIDConnectScopes = parseScopes(sec, "OPENID_CONNECT_SCOPES")
OAuth2Client.EnableAutoRegistration = sec.Key("ENABLE_AUTO_REGISTRATION").MustBool() OAuth2Client.EnableAutoRegistration = sec.Key("ENABLE_AUTO_REGISTRATION").MustBool()
@ -87,3 +90,33 @@ func parseScopes(sec *ini.Section, name string) []string {
} }
return scopes return scopes
} }
var OAuth2 = struct {
Enable bool
AccessTokenExpirationTime int64
RefreshTokenExpirationTime int64
InvalidateRefreshTokens bool
JWTSigningAlgorithm string `ini:"JWT_SIGNING_ALGORITHM"`
JWTSecretBase64 string `ini:"JWT_SECRET"`
JWTSigningPrivateKeyFile string `ini:"JWT_SIGNING_PRIVATE_KEY_FILE"`
MaxTokenLength int
}{
Enable: true,
AccessTokenExpirationTime: 3600,
RefreshTokenExpirationTime: 730,
InvalidateRefreshTokens: false,
JWTSigningAlgorithm: "RS256",
JWTSigningPrivateKeyFile: "jwt/private.pem",
MaxTokenLength: math.MaxInt16,
}
func loadOAuth2From(rootCfg ConfigProvider) {
if err := rootCfg.Section("oauth2").MapTo(&OAuth2); err != nil {
log.Fatal("Failed to OAuth2 settings: %v", err)
return
}
if !filepath.IsAbs(OAuth2.JWTSigningPrivateKeyFile) {
OAuth2.JWTSigningPrivateKeyFile = filepath.Join(AppDataPath, OAuth2.JWTSigningPrivateKeyFile)
}
}

22
modules/setting/other.go Normal file
View File

@ -0,0 +1,22 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
var (
// Other settings
ShowFooterBranding bool
ShowFooterVersion bool
ShowFooterTemplateLoadTime bool
EnableFeed bool
EnableSitemap bool
)
func loadOtherFrom(rootCfg ConfigProvider) {
sec := rootCfg.Section("other")
ShowFooterBranding = sec.Key("SHOW_FOOTER_BRANDING").MustBool(false)
ShowFooterVersion = sec.Key("SHOW_FOOTER_VERSION").MustBool(true)
ShowFooterTemplateLoadTime = sec.Key("SHOW_FOOTER_TEMPLATE_LOAD_TIME").MustBool(true)
EnableSitemap = sec.Key("ENABLE_SITEMAP").MustBool(true)
EnableFeed = sec.Key("ENABLE_FEED").MustBool(true)
}

View File

@ -46,13 +46,13 @@ var (
} }
) )
func newPackages() { func loadPackagesFrom(rootCfg ConfigProvider) {
sec := Cfg.Section("packages") sec := rootCfg.Section("packages")
if err := sec.MapTo(&Packages); err != nil { if err := sec.MapTo(&Packages); err != nil {
log.Fatal("Failed to map Packages settings: %v", err) log.Fatal("Failed to map Packages settings: %v", err)
} }
Packages.Storage = getStorage("packages", "", nil) Packages.Storage = getStorage(rootCfg, "packages", "", nil)
appURL, _ := url.Parse(AppURL) appURL, _ := url.Parse(AppURL)
Packages.RegistryHost = appURL.Host Packages.RegistryHost = appURL.Host

View File

@ -32,16 +32,16 @@ var (
}{} }{}
) )
func newPictureService() { func loadPictureFrom(rootCfg ConfigProvider) {
sec := Cfg.Section("picture") sec := rootCfg.Section("picture")
avatarSec := Cfg.Section("avatar") avatarSec := rootCfg.Section("avatar")
storageType := sec.Key("AVATAR_STORAGE_TYPE").MustString("") storageType := sec.Key("AVATAR_STORAGE_TYPE").MustString("")
// Specifically default PATH to AVATAR_UPLOAD_PATH // Specifically default PATH to AVATAR_UPLOAD_PATH
avatarSec.Key("PATH").MustString( avatarSec.Key("PATH").MustString(
sec.Key("AVATAR_UPLOAD_PATH").String()) sec.Key("AVATAR_UPLOAD_PATH").String())
Avatar.Storage = getStorage("avatars", storageType, avatarSec) Avatar.Storage = getStorage(rootCfg, "avatars", storageType, avatarSec)
Avatar.MaxWidth = sec.Key("AVATAR_MAX_WIDTH").MustInt(4096) Avatar.MaxWidth = sec.Key("AVATAR_MAX_WIDTH").MustInt(4096)
Avatar.MaxHeight = sec.Key("AVATAR_MAX_HEIGHT").MustInt(3072) Avatar.MaxHeight = sec.Key("AVATAR_MAX_HEIGHT").MustInt(3072)
@ -60,11 +60,11 @@ func newPictureService() {
} }
DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool(GetDefaultDisableGravatar()) DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool(GetDefaultDisableGravatar())
deprecatedSettingDB("", "DISABLE_GRAVATAR") deprecatedSettingDB(rootCfg, "", "DISABLE_GRAVATAR")
EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(GetDefaultEnableFederatedAvatar(DisableGravatar)) EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(GetDefaultEnableFederatedAvatar(DisableGravatar))
deprecatedSettingDB("", "ENABLE_FEDERATED_AVATAR") deprecatedSettingDB(rootCfg, "", "ENABLE_FEDERATED_AVATAR")
newRepoAvatarService() loadRepoAvatarFrom(rootCfg)
} }
func GetDefaultDisableGravatar() bool { func GetDefaultDisableGravatar() bool {
@ -82,16 +82,16 @@ func GetDefaultEnableFederatedAvatar(disableGravatar bool) bool {
return v return v
} }
func newRepoAvatarService() { func loadRepoAvatarFrom(rootCfg ConfigProvider) {
sec := Cfg.Section("picture") sec := rootCfg.Section("picture")
repoAvatarSec := Cfg.Section("repo-avatar") repoAvatarSec := rootCfg.Section("repo-avatar")
storageType := sec.Key("REPOSITORY_AVATAR_STORAGE_TYPE").MustString("") storageType := sec.Key("REPOSITORY_AVATAR_STORAGE_TYPE").MustString("")
// Specifically default PATH to AVATAR_UPLOAD_PATH // Specifically default PATH to AVATAR_UPLOAD_PATH
repoAvatarSec.Key("PATH").MustString( repoAvatarSec.Key("PATH").MustString(
sec.Key("REPOSITORY_AVATAR_UPLOAD_PATH").String()) sec.Key("REPOSITORY_AVATAR_UPLOAD_PATH").String())
RepoAvatar.Storage = getStorage("repo-avatars", storageType, repoAvatarSec) RepoAvatar.Storage = getStorage(rootCfg, "repo-avatars", storageType, repoAvatarSec)
RepoAvatar.Fallback = sec.Key("REPOSITORY_AVATAR_FALLBACK").MustString("none") RepoAvatar.Fallback = sec.Key("REPOSITORY_AVATAR_FALLBACK").MustString("none")
RepoAvatar.FallbackImage = sec.Key("REPOSITORY_AVATAR_FALLBACK_IMAGE").MustString("/assets/img/repo_default.png") RepoAvatar.FallbackImage = sec.Key("REPOSITORY_AVATAR_FALLBACK_IMAGE").MustString("/assets/img/repo_default.png")

View File

@ -3,8 +3,6 @@
package setting package setting
import "code.gitea.io/gitea/modules/log"
// Project settings // Project settings
var ( var (
Project = struct { Project = struct {
@ -16,8 +14,6 @@ var (
} }
) )
func newProject() { func loadProjectFrom(rootCfg ConfigProvider) {
if err := Cfg.Section("project").MapTo(&Project); err != nil { mustMapSetting(rootCfg, "project", &Project)
log.Fatal("Failed to map Project settings: %v", err)
}
} }

View File

@ -21,8 +21,8 @@ var Proxy = struct {
ProxyHosts: []string{}, ProxyHosts: []string{},
} }
func newProxyService() { func loadProxyFrom(rootCfg ConfigProvider) {
sec := Cfg.Section("proxy") sec := rootCfg.Section("proxy")
Proxy.Enabled = sec.Key("PROXY_ENABLED").MustBool(false) Proxy.Enabled = sec.Key("PROXY_ENABLED").MustBool(false)
Proxy.ProxyURL = sec.Key("PROXY_URL").MustString("") Proxy.ProxyURL = sec.Key("PROXY_URL").MustString("")
if Proxy.ProxyURL != "" { if Proxy.ProxyURL != "" {

View File

@ -39,8 +39,12 @@ var Queue = QueueSettings{}
// GetQueueSettings returns the queue settings for the appropriately named queue // GetQueueSettings returns the queue settings for the appropriately named queue
func GetQueueSettings(name string) QueueSettings { func GetQueueSettings(name string) QueueSettings {
return getQueueSettings(CfgProvider, name)
}
func getQueueSettings(rootCfg ConfigProvider, name string) QueueSettings {
q := QueueSettings{} q := QueueSettings{}
sec := Cfg.Section("queue." + name) sec := rootCfg.Section("queue." + name)
q.Name = name q.Name = name
// DataDir is not directly inheritable // DataDir is not directly inheritable
@ -82,10 +86,14 @@ func GetQueueSettings(name string) QueueSettings {
return q return q
} }
// NewQueueService sets up the default settings for Queues // LoadQueueSettings sets up the default settings for Queues
// This is exported for tests to be able to use the queue // This is exported for tests to be able to use the queue
func NewQueueService() { func LoadQueueSettings() {
sec := Cfg.Section("queue") loadQueueFrom(CfgProvider)
}
func loadQueueFrom(rootCfg ConfigProvider) {
sec := rootCfg.Section("queue")
Queue.DataDir = filepath.ToSlash(sec.Key("DATADIR").MustString("queues/")) Queue.DataDir = filepath.ToSlash(sec.Key("DATADIR").MustString("queues/"))
if !filepath.IsAbs(Queue.DataDir) { if !filepath.IsAbs(Queue.DataDir) {
Queue.DataDir = filepath.ToSlash(filepath.Join(AppDataPath, Queue.DataDir)) Queue.DataDir = filepath.ToSlash(filepath.Join(AppDataPath, Queue.DataDir))
@ -108,10 +116,10 @@ func NewQueueService() {
// Now handle the old issue_indexer configuration // Now handle the old issue_indexer configuration
// FIXME: DEPRECATED to be removed in v1.18.0 // FIXME: DEPRECATED to be removed in v1.18.0
section := Cfg.Section("queue.issue_indexer") section := rootCfg.Section("queue.issue_indexer")
directlySet := toDirectlySetKeysSet(section) directlySet := toDirectlySetKeysSet(section)
if !directlySet.Contains("TYPE") && defaultType == "" { if !directlySet.Contains("TYPE") && defaultType == "" {
switch typ := Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_TYPE").MustString(""); typ { switch typ := rootCfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_TYPE").MustString(""); typ {
case "levelqueue": case "levelqueue":
_, _ = section.NewKey("TYPE", "level") _, _ = section.NewKey("TYPE", "level")
case "channel": case "channel":
@ -125,25 +133,25 @@ func NewQueueService() {
} }
} }
if !directlySet.Contains("LENGTH") { if !directlySet.Contains("LENGTH") {
length := Cfg.Section("indexer").Key("UPDATE_BUFFER_LEN").MustInt(0) length := rootCfg.Section("indexer").Key("UPDATE_BUFFER_LEN").MustInt(0)
if length != 0 { if length != 0 {
_, _ = section.NewKey("LENGTH", strconv.Itoa(length)) _, _ = section.NewKey("LENGTH", strconv.Itoa(length))
} }
} }
if !directlySet.Contains("BATCH_LENGTH") { if !directlySet.Contains("BATCH_LENGTH") {
fallback := Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_BATCH_NUMBER").MustInt(0) fallback := rootCfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_BATCH_NUMBER").MustInt(0)
if fallback != 0 { if fallback != 0 {
_, _ = section.NewKey("BATCH_LENGTH", strconv.Itoa(fallback)) _, _ = section.NewKey("BATCH_LENGTH", strconv.Itoa(fallback))
} }
} }
if !directlySet.Contains("DATADIR") { if !directlySet.Contains("DATADIR") {
queueDir := filepath.ToSlash(Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_DIR").MustString("")) queueDir := filepath.ToSlash(rootCfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_DIR").MustString(""))
if queueDir != "" { if queueDir != "" {
_, _ = section.NewKey("DATADIR", queueDir) _, _ = section.NewKey("DATADIR", queueDir)
} }
} }
if !directlySet.Contains("CONN_STR") { if !directlySet.Contains("CONN_STR") {
connStr := Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_CONN_STR").MustString("") connStr := rootCfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_CONN_STR").MustString("")
if connStr != "" { if connStr != "" {
_, _ = section.NewKey("CONN_STR", connStr) _, _ = section.NewKey("CONN_STR", connStr)
} }
@ -153,31 +161,31 @@ func NewQueueService() {
// - will need to set default for [queue.*)] LENGTH appropriately though though // - will need to set default for [queue.*)] LENGTH appropriately though though
// Handle the old mailer configuration // Handle the old mailer configuration
handleOldLengthConfiguration("mailer", "mailer", "SEND_BUFFER_LEN", 100) handleOldLengthConfiguration(rootCfg, "mailer", "mailer", "SEND_BUFFER_LEN", 100)
// Handle the old test pull requests configuration // Handle the old test pull requests configuration
// Please note this will be a unique queue // Please note this will be a unique queue
handleOldLengthConfiguration("pr_patch_checker", "repository", "PULL_REQUEST_QUEUE_LENGTH", 1000) handleOldLengthConfiguration(rootCfg, "pr_patch_checker", "repository", "PULL_REQUEST_QUEUE_LENGTH", 1000)
// Handle the old mirror queue configuration // Handle the old mirror queue configuration
// Please note this will be a unique queue // Please note this will be a unique queue
handleOldLengthConfiguration("mirror", "repository", "MIRROR_QUEUE_LENGTH", 1000) handleOldLengthConfiguration(rootCfg, "mirror", "repository", "MIRROR_QUEUE_LENGTH", 1000)
} }
// handleOldLengthConfiguration allows fallback to older configuration. `[queue.name]` `LENGTH` will override this configuration, but // handleOldLengthConfiguration allows fallback to older configuration. `[queue.name]` `LENGTH` will override this configuration, but
// if that is left unset then we should fallback to the older configuration. (Except where the new length woul be <=0) // if that is left unset then we should fallback to the older configuration. (Except where the new length woul be <=0)
func handleOldLengthConfiguration(queueName, oldSection, oldKey string, defaultValue int) { func handleOldLengthConfiguration(rootCfg ConfigProvider, queueName, oldSection, oldKey string, defaultValue int) {
if Cfg.Section(oldSection).HasKey(oldKey) { if rootCfg.Section(oldSection).HasKey(oldKey) {
log.Error("Deprecated fallback for %s queue length `[%s]` `%s` present. Use `[queue.%s]` `LENGTH`. This will be removed in v1.18.0", queueName, queueName, oldSection, oldKey) log.Error("Deprecated fallback for %s queue length `[%s]` `%s` present. Use `[queue.%s]` `LENGTH`. This will be removed in v1.18.0", queueName, queueName, oldSection, oldKey)
} }
value := Cfg.Section(oldSection).Key(oldKey).MustInt(defaultValue) value := rootCfg.Section(oldSection).Key(oldKey).MustInt(defaultValue)
// Don't override with 0 // Don't override with 0
if value <= 0 { if value <= 0 {
return return
} }
section := Cfg.Section("queue." + queueName) section := rootCfg.Section("queue." + queueName)
directlySet := toDirectlySetKeysSet(section) directlySet := toDirectlySetKeysSet(section)
if !directlySet.Contains("LENGTH") { if !directlySet.Contains("LENGTH") {
_, _ = section.NewKey("LENGTH", strconv.Itoa(value)) _, _ = section.NewKey("LENGTH", strconv.Itoa(value))

View File

@ -270,10 +270,10 @@ var (
}{} }{}
) )
func newRepository() { func loadRepositoryFrom(rootCfg ConfigProvider) {
var err error var err error
// Determine and create root git repository path. // Determine and create root git repository path.
sec := Cfg.Section("repository") sec := rootCfg.Section("repository")
Repository.DisableHTTPGit = sec.Key("DISABLE_HTTP_GIT").MustBool() Repository.DisableHTTPGit = sec.Key("DISABLE_HTTP_GIT").MustBool()
Repository.UseCompatSSHURI = sec.Key("USE_COMPAT_SSH_URI").MustBool() Repository.UseCompatSSHURI = sec.Key("USE_COMPAT_SSH_URI").MustBool()
Repository.MaxCreationLimit = sec.Key("MAX_CREATION_LIMIT").MustInt(-1) Repository.MaxCreationLimit = sec.Key("MAX_CREATION_LIMIT").MustInt(-1)
@ -295,19 +295,19 @@ func newRepository() {
log.Warn("SCRIPT_TYPE %q is not on the current PATH. Are you sure that this is the correct SCRIPT_TYPE?", ScriptType) log.Warn("SCRIPT_TYPE %q is not on the current PATH. Are you sure that this is the correct SCRIPT_TYPE?", ScriptType)
} }
if err = Cfg.Section("repository").MapTo(&Repository); err != nil { if err = sec.MapTo(&Repository); err != nil {
log.Fatal("Failed to map Repository settings: %v", err) log.Fatal("Failed to map Repository settings: %v", err)
} else if err = Cfg.Section("repository.editor").MapTo(&Repository.Editor); err != nil { } else if err = rootCfg.Section("repository.editor").MapTo(&Repository.Editor); err != nil {
log.Fatal("Failed to map Repository.Editor settings: %v", err) log.Fatal("Failed to map Repository.Editor settings: %v", err)
} else if err = Cfg.Section("repository.upload").MapTo(&Repository.Upload); err != nil { } else if err = rootCfg.Section("repository.upload").MapTo(&Repository.Upload); err != nil {
log.Fatal("Failed to map Repository.Upload settings: %v", err) log.Fatal("Failed to map Repository.Upload settings: %v", err)
} else if err = Cfg.Section("repository.local").MapTo(&Repository.Local); err != nil { } else if err = rootCfg.Section("repository.local").MapTo(&Repository.Local); err != nil {
log.Fatal("Failed to map Repository.Local settings: %v", err) log.Fatal("Failed to map Repository.Local settings: %v", err)
} else if err = Cfg.Section("repository.pull-request").MapTo(&Repository.PullRequest); err != nil { } else if err = rootCfg.Section("repository.pull-request").MapTo(&Repository.PullRequest); err != nil {
log.Fatal("Failed to map Repository.PullRequest settings: %v", err) log.Fatal("Failed to map Repository.PullRequest settings: %v", err)
} }
if !Cfg.Section("packages").Key("ENABLED").MustBool(true) { if !rootCfg.Section("packages").Key("ENABLED").MustBool(true) {
Repository.DisabledRepoUnits = append(Repository.DisabledRepoUnits, "repo.packages") Repository.DisabledRepoUnits = append(Repository.DisabledRepoUnits, "repo.packages")
} }
@ -354,5 +354,5 @@ func newRepository() {
Repository.Upload.TempPath = path.Join(AppWorkPath, Repository.Upload.TempPath) Repository.Upload.TempPath = path.Join(AppWorkPath, Repository.Upload.TempPath)
} }
RepoArchive.Storage = getStorage("repo-archive", "", nil) RepoArchive.Storage = getStorage(rootCfg, "repo-archive", "", nil)
} }

158
modules/setting/security.go Normal file
View File

@ -0,0 +1,158 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
import (
"net/url"
"os"
"strings"
"code.gitea.io/gitea/modules/auth/password/hash"
"code.gitea.io/gitea/modules/generate"
"code.gitea.io/gitea/modules/log"
ini "gopkg.in/ini.v1"
)
var (
// Security settings
InstallLock bool
SecretKey string
InternalToken string // internal access token
LogInRememberDays int
CookieUserName string
CookieRememberName string
ReverseProxyAuthUser string
ReverseProxyAuthEmail string
ReverseProxyAuthFullName string
ReverseProxyLimit int
ReverseProxyTrustedProxies []string
MinPasswordLength int
ImportLocalPaths bool
DisableGitHooks bool
DisableWebhooks bool
OnlyAllowPushIfGiteaEnvironmentSet bool
PasswordComplexity []string
PasswordHashAlgo string
PasswordCheckPwn bool
SuccessfulTokensCacheSize int
CSRFCookieName = "_csrf"
CSRFCookieHTTPOnly = true
)
// loadSecret load the secret from ini by uriKey or verbatimKey, only one of them could be set
// If the secret is loaded from uriKey (file), the file should be non-empty, to guarantee the behavior stable and clear.
func loadSecret(sec *ini.Section, uriKey, verbatimKey string) string {
// don't allow setting both URI and verbatim string
uri := sec.Key(uriKey).String()
verbatim := sec.Key(verbatimKey).String()
if uri != "" && verbatim != "" {
log.Fatal("Cannot specify both %s and %s", uriKey, verbatimKey)
}
// if we have no URI, use verbatim
if uri == "" {
return verbatim
}
tempURI, err := url.Parse(uri)
if err != nil {
log.Fatal("Failed to parse %s (%s): %v", uriKey, uri, err)
}
switch tempURI.Scheme {
case "file":
buf, err := os.ReadFile(tempURI.RequestURI())
if err != nil {
log.Fatal("Failed to read %s (%s): %v", uriKey, tempURI.RequestURI(), err)
}
val := strings.TrimSpace(string(buf))
if val == "" {
// The file shouldn't be empty, otherwise we can not know whether the user has ever set the KEY or KEY_URI
// For example: if INTERNAL_TOKEN_URI=file:///empty-file,
// Then if the token is re-generated during installation and saved to INTERNAL_TOKEN
// Then INTERNAL_TOKEN and INTERNAL_TOKEN_URI both exist, that's a fatal error (they shouldn't)
log.Fatal("Failed to read %s (%s): the file is empty", uriKey, tempURI.RequestURI())
}
return val
// only file URIs are allowed
default:
log.Fatal("Unsupported URI-Scheme %q (INTERNAL_TOKEN_URI = %q)", tempURI.Scheme, uri)
return ""
}
}
// generateSaveInternalToken generates and saves the internal token to app.ini
func generateSaveInternalToken() {
token, err := generate.NewInternalToken()
if err != nil {
log.Fatal("Error generate internal token: %v", err)
}
InternalToken = token
CreateOrAppendToCustomConf("security.INTERNAL_TOKEN", func(cfg *ini.File) {
cfg.Section("security").Key("INTERNAL_TOKEN").SetValue(token)
})
}
func loadSecurityFrom(rootCfg ConfigProvider) {
sec := rootCfg.Section("security")
InstallLock = sec.Key("INSTALL_LOCK").MustBool(false)
LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7)
CookieUserName = sec.Key("COOKIE_USERNAME").MustString("gitea_awesome")
SecretKey = loadSecret(sec, "SECRET_KEY_URI", "SECRET_KEY")
if SecretKey == "" {
// FIXME: https://github.com/go-gitea/gitea/issues/16832
// Until it supports rotating an existing secret key, we shouldn't move users off of the widely used default value
SecretKey = "!#@FDEWREWR&*(" //nolint:gosec
}
CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").MustString("gitea_incredible")
ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER")
ReverseProxyAuthEmail = sec.Key("REVERSE_PROXY_AUTHENTICATION_EMAIL").MustString("X-WEBAUTH-EMAIL")
ReverseProxyAuthFullName = sec.Key("REVERSE_PROXY_AUTHENTICATION_FULL_NAME").MustString("X-WEBAUTH-FULLNAME")
ReverseProxyLimit = sec.Key("REVERSE_PROXY_LIMIT").MustInt(1)
ReverseProxyTrustedProxies = sec.Key("REVERSE_PROXY_TRUSTED_PROXIES").Strings(",")
if len(ReverseProxyTrustedProxies) == 0 {
ReverseProxyTrustedProxies = []string{"127.0.0.0/8", "::1/128"}
}
MinPasswordLength = sec.Key("MIN_PASSWORD_LENGTH").MustInt(6)
ImportLocalPaths = sec.Key("IMPORT_LOCAL_PATHS").MustBool(false)
DisableGitHooks = sec.Key("DISABLE_GIT_HOOKS").MustBool(true)
DisableWebhooks = sec.Key("DISABLE_WEBHOOKS").MustBool(false)
OnlyAllowPushIfGiteaEnvironmentSet = sec.Key("ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET").MustBool(true)
// Ensure that the provided default hash algorithm is a valid hash algorithm
var algorithm *hash.PasswordHashAlgorithm
PasswordHashAlgo, algorithm = hash.SetDefaultPasswordHashAlgorithm(sec.Key("PASSWORD_HASH_ALGO").MustString(""))
if algorithm == nil {
log.Fatal("The provided password hash algorithm was invalid: %s", sec.Key("PASSWORD_HASH_ALGO").MustString(""))
}
CSRFCookieHTTPOnly = sec.Key("CSRF_COOKIE_HTTP_ONLY").MustBool(true)
PasswordCheckPwn = sec.Key("PASSWORD_CHECK_PWN").MustBool(false)
SuccessfulTokensCacheSize = sec.Key("SUCCESSFUL_TOKENS_CACHE_SIZE").MustInt(20)
InternalToken = loadSecret(sec, "INTERNAL_TOKEN_URI", "INTERNAL_TOKEN")
if InstallLock && InternalToken == "" {
// if Gitea has been installed but the InternalToken hasn't been generated (upgrade from an old release), we should generate
// some users do cluster deployment, they still depend on this auto-generating behavior.
generateSaveInternalToken()
}
cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",")
if len(cfgdata) == 0 {
cfgdata = []string{"off"}
}
PasswordComplexity = make([]string, 0, len(cfgdata))
for _, name := range cfgdata {
name := strings.ToLower(strings.Trim(name, `"`))
if name != "" {
PasswordComplexity = append(PasswordComplexity, name)
}
}
}

356
modules/setting/server.go Normal file
View File

@ -0,0 +1,356 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
import (
"encoding/base64"
"net"
"net/url"
"path"
"path/filepath"
"strconv"
"strings"
"time"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
)
// Scheme describes protocol types
type Scheme string
// enumerates all the scheme types
const (
HTTP Scheme = "http"
HTTPS Scheme = "https"
FCGI Scheme = "fcgi"
FCGIUnix Scheme = "fcgi+unix"
HTTPUnix Scheme = "http+unix"
)
// LandingPage describes the default page
type LandingPage string
// enumerates all the landing page types
const (
LandingPageHome LandingPage = "/"
LandingPageExplore LandingPage = "/explore"
LandingPageOrganizations LandingPage = "/explore/organizations"
LandingPageLogin LandingPage = "/user/login"
)
var (
// AppName is the Application name, used in the page title.
// It maps to ini:"APP_NAME"
AppName string
// AppURL is the Application ROOT_URL. It always has a '/' suffix
// It maps to ini:"ROOT_URL"
AppURL string
// AppSubURL represents the sub-url mounting point for gitea. It is either "" or starts with '/' and ends without '/', such as '/{subpath}'.
// This value is empty if site does not have sub-url.
AppSubURL string
// AppDataPath is the default path for storing data.
// It maps to ini:"APP_DATA_PATH" in [server] and defaults to AppWorkPath + "/data"
AppDataPath string
// LocalURL is the url for locally running applications to contact Gitea. It always has a '/' suffix
// It maps to ini:"LOCAL_ROOT_URL" in [server]
LocalURL string
// AssetVersion holds a opaque value that is used for cache-busting assets
AssetVersion string
// Server settings
Protocol Scheme
UseProxyProtocol bool // `ini:"USE_PROXY_PROTOCOL"`
ProxyProtocolTLSBridging bool //`ini:"PROXY_PROTOCOL_TLS_BRIDGING"`
ProxyProtocolHeaderTimeout time.Duration
ProxyProtocolAcceptUnknown bool
Domain string
HTTPAddr string
HTTPPort string
LocalUseProxyProtocol bool
RedirectOtherPort bool
RedirectorUseProxyProtocol bool
PortToRedirect string
OfflineMode bool
CertFile string
KeyFile string
StaticRootPath string
StaticCacheTime time.Duration
EnableGzip bool
LandingPageURL LandingPage
LandingPageCustom string
UnixSocketPermission uint32
EnablePprof bool
PprofDataPath string
EnableAcme bool
AcmeTOS bool
AcmeLiveDirectory string
AcmeEmail string
AcmeURL string
AcmeCARoot string
SSLMinimumVersion string
SSLMaximumVersion string
SSLCurvePreferences []string
SSLCipherSuites []string
GracefulRestartable bool
GracefulHammerTime time.Duration
StartupTimeout time.Duration
PerWriteTimeout = 30 * time.Second
PerWritePerKbTimeout = 10 * time.Second
StaticURLPrefix string
AbsoluteAssetURL string
HasRobotsTxt bool
ManifestData string
)
// MakeManifestData generates web app manifest JSON
func MakeManifestData(appName, appURL, absoluteAssetURL string) []byte {
type manifestIcon struct {
Src string `json:"src"`
Type string `json:"type"`
Sizes string `json:"sizes"`
}
type manifestJSON struct {
Name string `json:"name"`
ShortName string `json:"short_name"`
StartURL string `json:"start_url"`
Icons []manifestIcon `json:"icons"`
}
bytes, err := json.Marshal(&manifestJSON{
Name: appName,
ShortName: appName,
StartURL: appURL,
Icons: []manifestIcon{
{
Src: absoluteAssetURL + "/assets/img/logo.png",
Type: "image/png",
Sizes: "512x512",
},
{
Src: absoluteAssetURL + "/assets/img/logo.svg",
Type: "image/svg+xml",
Sizes: "512x512",
},
},
})
if err != nil {
log.Error("unable to marshal manifest JSON. Error: %v", err)
return make([]byte, 0)
}
return bytes
}
// MakeAbsoluteAssetURL returns the absolute asset url prefix without a trailing slash
func MakeAbsoluteAssetURL(appURL, staticURLPrefix string) string {
parsedPrefix, err := url.Parse(strings.TrimSuffix(staticURLPrefix, "/"))
if err != nil {
log.Fatal("Unable to parse STATIC_URL_PREFIX: %v", err)
}
if err == nil && parsedPrefix.Hostname() == "" {
if staticURLPrefix == "" {
return strings.TrimSuffix(appURL, "/")
}
// StaticURLPrefix is just a path
return util.URLJoin(appURL, strings.TrimSuffix(staticURLPrefix, "/"))
}
return strings.TrimSuffix(staticURLPrefix, "/")
}
func loadServerFrom(rootCfg ConfigProvider) {
sec := rootCfg.Section("server")
AppName = rootCfg.Section("").Key("APP_NAME").MustString("Gitea: Git with a cup of tea")
Domain = sec.Key("DOMAIN").MustString("localhost")
HTTPAddr = sec.Key("HTTP_ADDR").MustString("0.0.0.0")
HTTPPort = sec.Key("HTTP_PORT").MustString("3000")
Protocol = HTTP
protocolCfg := sec.Key("PROTOCOL").String()
switch protocolCfg {
case "https":
Protocol = HTTPS
// FIXME: DEPRECATED to be removed in v1.18.0
if sec.HasKey("ENABLE_ACME") {
EnableAcme = sec.Key("ENABLE_ACME").MustBool(false)
} else {
deprecatedSetting(rootCfg, "server", "ENABLE_LETSENCRYPT", "server", "ENABLE_ACME")
EnableAcme = sec.Key("ENABLE_LETSENCRYPT").MustBool(false)
}
if EnableAcme {
AcmeURL = sec.Key("ACME_URL").MustString("")
AcmeCARoot = sec.Key("ACME_CA_ROOT").MustString("")
// FIXME: DEPRECATED to be removed in v1.18.0
if sec.HasKey("ACME_ACCEPTTOS") {
AcmeTOS = sec.Key("ACME_ACCEPTTOS").MustBool(false)
} else {
deprecatedSetting(rootCfg, "server", "LETSENCRYPT_ACCEPTTOS", "server", "ACME_ACCEPTTOS")
AcmeTOS = sec.Key("LETSENCRYPT_ACCEPTTOS").MustBool(false)
}
if !AcmeTOS {
log.Fatal("ACME TOS is not accepted (ACME_ACCEPTTOS).")
}
// FIXME: DEPRECATED to be removed in v1.18.0
if sec.HasKey("ACME_DIRECTORY") {
AcmeLiveDirectory = sec.Key("ACME_DIRECTORY").MustString("https")
} else {
deprecatedSetting(rootCfg, "server", "LETSENCRYPT_DIRECTORY", "server", "ACME_DIRECTORY")
AcmeLiveDirectory = sec.Key("LETSENCRYPT_DIRECTORY").MustString("https")
}
// FIXME: DEPRECATED to be removed in v1.18.0
if sec.HasKey("ACME_EMAIL") {
AcmeEmail = sec.Key("ACME_EMAIL").MustString("")
} else {
deprecatedSetting(rootCfg, "server", "LETSENCRYPT_EMAIL", "server", "ACME_EMAIL")
AcmeEmail = sec.Key("LETSENCRYPT_EMAIL").MustString("")
}
} else {
CertFile = sec.Key("CERT_FILE").String()
KeyFile = sec.Key("KEY_FILE").String()
if len(CertFile) > 0 && !filepath.IsAbs(CertFile) {
CertFile = filepath.Join(CustomPath, CertFile)
}
if len(KeyFile) > 0 && !filepath.IsAbs(KeyFile) {
KeyFile = filepath.Join(CustomPath, KeyFile)
}
}
SSLMinimumVersion = sec.Key("SSL_MIN_VERSION").MustString("")
SSLMaximumVersion = sec.Key("SSL_MAX_VERSION").MustString("")
SSLCurvePreferences = sec.Key("SSL_CURVE_PREFERENCES").Strings(",")
SSLCipherSuites = sec.Key("SSL_CIPHER_SUITES").Strings(",")
case "fcgi":
Protocol = FCGI
case "fcgi+unix", "unix", "http+unix":
switch protocolCfg {
case "fcgi+unix":
Protocol = FCGIUnix
case "unix":
log.Warn("unix PROTOCOL value is deprecated, please use http+unix")
fallthrough
case "http+unix":
Protocol = HTTPUnix
}
UnixSocketPermissionRaw := sec.Key("UNIX_SOCKET_PERMISSION").MustString("666")
UnixSocketPermissionParsed, err := strconv.ParseUint(UnixSocketPermissionRaw, 8, 32)
if err != nil || UnixSocketPermissionParsed > 0o777 {
log.Fatal("Failed to parse unixSocketPermission: %s", UnixSocketPermissionRaw)
}
UnixSocketPermission = uint32(UnixSocketPermissionParsed)
if !filepath.IsAbs(HTTPAddr) {
HTTPAddr = filepath.Join(AppWorkPath, HTTPAddr)
}
}
UseProxyProtocol = sec.Key("USE_PROXY_PROTOCOL").MustBool(false)
ProxyProtocolTLSBridging = sec.Key("PROXY_PROTOCOL_TLS_BRIDGING").MustBool(false)
ProxyProtocolHeaderTimeout = sec.Key("PROXY_PROTOCOL_HEADER_TIMEOUT").MustDuration(5 * time.Second)
ProxyProtocolAcceptUnknown = sec.Key("PROXY_PROTOCOL_ACCEPT_UNKNOWN").MustBool(false)
GracefulRestartable = sec.Key("ALLOW_GRACEFUL_RESTARTS").MustBool(true)
GracefulHammerTime = sec.Key("GRACEFUL_HAMMER_TIME").MustDuration(60 * time.Second)
StartupTimeout = sec.Key("STARTUP_TIMEOUT").MustDuration(0 * time.Second)
PerWriteTimeout = sec.Key("PER_WRITE_TIMEOUT").MustDuration(PerWriteTimeout)
PerWritePerKbTimeout = sec.Key("PER_WRITE_PER_KB_TIMEOUT").MustDuration(PerWritePerKbTimeout)
defaultAppURL := string(Protocol) + "://" + Domain + ":" + HTTPPort
AppURL = sec.Key("ROOT_URL").MustString(defaultAppURL)
// Check validity of AppURL
appURL, err := url.Parse(AppURL)
if err != nil {
log.Fatal("Invalid ROOT_URL '%s': %s", AppURL, err)
}
// Remove default ports from AppURL.
// (scheme-based URL normalization, RFC 3986 section 6.2.3)
if (appURL.Scheme == string(HTTP) && appURL.Port() == "80") || (appURL.Scheme == string(HTTPS) && appURL.Port() == "443") {
appURL.Host = appURL.Hostname()
}
// This should be TrimRight to ensure that there is only a single '/' at the end of AppURL.
AppURL = strings.TrimRight(appURL.String(), "/") + "/"
// Suburl should start with '/' and end without '/', such as '/{subpath}'.
// This value is empty if site does not have sub-url.
AppSubURL = strings.TrimSuffix(appURL.Path, "/")
StaticURLPrefix = strings.TrimSuffix(sec.Key("STATIC_URL_PREFIX").MustString(AppSubURL), "/")
// Check if Domain differs from AppURL domain than update it to AppURL's domain
urlHostname := appURL.Hostname()
if urlHostname != Domain && net.ParseIP(urlHostname) == nil && urlHostname != "" {
Domain = urlHostname
}
AbsoluteAssetURL = MakeAbsoluteAssetURL(AppURL, StaticURLPrefix)
AssetVersion = strings.ReplaceAll(AppVer, "+", "~") // make sure the version string is clear (no real escaping is needed)
manifestBytes := MakeManifestData(AppName, AppURL, AbsoluteAssetURL)
ManifestData = `application/json;base64,` + base64.StdEncoding.EncodeToString(manifestBytes)
var defaultLocalURL string
switch Protocol {
case HTTPUnix:
defaultLocalURL = "http://unix/"
case FCGI:
defaultLocalURL = AppURL
case FCGIUnix:
defaultLocalURL = AppURL
default:
defaultLocalURL = string(Protocol) + "://"
if HTTPAddr == "0.0.0.0" {
defaultLocalURL += net.JoinHostPort("localhost", HTTPPort) + "/"
} else {
defaultLocalURL += net.JoinHostPort(HTTPAddr, HTTPPort) + "/"
}
}
LocalURL = sec.Key("LOCAL_ROOT_URL").MustString(defaultLocalURL)
LocalURL = strings.TrimRight(LocalURL, "/") + "/"
LocalUseProxyProtocol = sec.Key("LOCAL_USE_PROXY_PROTOCOL").MustBool(UseProxyProtocol)
RedirectOtherPort = sec.Key("REDIRECT_OTHER_PORT").MustBool(false)
PortToRedirect = sec.Key("PORT_TO_REDIRECT").MustString("80")
RedirectorUseProxyProtocol = sec.Key("REDIRECTOR_USE_PROXY_PROTOCOL").MustBool(UseProxyProtocol)
OfflineMode = sec.Key("OFFLINE_MODE").MustBool()
Log.DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool()
if len(StaticRootPath) == 0 {
StaticRootPath = AppWorkPath
}
StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(StaticRootPath)
StaticCacheTime = sec.Key("STATIC_CACHE_TIME").MustDuration(6 * time.Hour)
AppDataPath = sec.Key("APP_DATA_PATH").MustString(path.Join(AppWorkPath, "data"))
if !filepath.IsAbs(AppDataPath) {
log.Info("The provided APP_DATA_PATH: %s is not absolute - it will be made absolute against the work path: %s", AppDataPath, AppWorkPath)
AppDataPath = filepath.ToSlash(filepath.Join(AppWorkPath, AppDataPath))
}
EnableGzip = sec.Key("ENABLE_GZIP").MustBool()
EnablePprof = sec.Key("ENABLE_PPROF").MustBool(false)
PprofDataPath = sec.Key("PPROF_DATA_PATH").MustString(path.Join(AppWorkPath, "data/tmp/pprof"))
if !filepath.IsAbs(PprofDataPath) {
PprofDataPath = filepath.Join(AppWorkPath, PprofDataPath)
}
landingPage := sec.Key("LANDING_PAGE").MustString("home")
switch landingPage {
case "explore":
LandingPageURL = LandingPageExplore
case "organizations":
LandingPageURL = LandingPageOrganizations
case "login":
LandingPageURL = LandingPageLogin
case "":
case "home":
LandingPageURL = LandingPageHome
default:
LandingPageURL = LandingPage(landingPage)
}
HasRobotsTxt, err = util.IsFile(path.Join(CustomPath, "robots.txt"))
if err != nil {
log.Error("Unable to check if %s is a file. Error: %v", path.Join(CustomPath, "robots.txt"), err)
}
}

View File

@ -12,6 +12,15 @@ import (
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
) )
// enumerates all the types of captchas
const (
ImageCaptcha = "image"
ReCaptcha = "recaptcha"
HCaptcha = "hcaptcha"
MCaptcha = "mcaptcha"
CfTurnstile = "cfturnstile"
)
// Service settings // Service settings
var Service = struct { var Service = struct {
DefaultUserVisibility string DefaultUserVisibility string
@ -105,8 +114,8 @@ func (a AllowedVisibility) ToVisibleTypeSlice() (result []structs.VisibleType) {
return result return result
} }
func newService() { func loadServiceFrom(rootCfg ConfigProvider) {
sec := Cfg.Section("service") sec := rootCfg.Section("service")
Service.ActiveCodeLives = sec.Key("ACTIVE_CODE_LIVE_MINUTES").MustInt(180) Service.ActiveCodeLives = sec.Key("ACTIVE_CODE_LIVE_MINUTES").MustInt(180)
Service.ResetPwdCodeLives = sec.Key("RESET_PASSWD_CODE_LIVE_MINUTES").MustInt(180) Service.ResetPwdCodeLives = sec.Key("RESET_PASSWD_CODE_LIVE_MINUTES").MustInt(180)
Service.DisableRegistration = sec.Key("DISABLE_REGISTRATION").MustBool() Service.DisableRegistration = sec.Key("DISABLE_REGISTRATION").MustBool()
@ -184,11 +193,13 @@ func newService() {
} }
Service.ValidSiteURLSchemes = schemes Service.ValidSiteURLSchemes = schemes
if err := Cfg.Section("service.explore").MapTo(&Service.Explore); err != nil { mustMapSetting(rootCfg, "service.explore", &Service.Explore)
log.Fatal("Failed to map service.explore settings: %v", err)
}
sec = Cfg.Section("openid") loadOpenIDSetting(rootCfg)
}
func loadOpenIDSetting(rootCfg ConfigProvider) {
sec := rootCfg.Section("openid")
Service.EnableOpenIDSignIn = sec.Key("ENABLE_OPENID_SIGNIN").MustBool(!InstallLock) Service.EnableOpenIDSignIn = sec.Key("ENABLE_OPENID_SIGNIN").MustBool(!InstallLock)
Service.EnableOpenIDSignUp = sec.Key("ENABLE_OPENID_SIGNUP").MustBool(!Service.DisableRegistration && Service.EnableOpenIDSignIn) Service.EnableOpenIDSignUp = sec.Key("ENABLE_OPENID_SIGNUP").MustBool(!Service.DisableRegistration && Service.EnableOpenIDSignIn)
pats := sec.Key("WHITELISTED_URIS").Strings(" ") pats := sec.Key("WHITELISTED_URIS").Strings(" ")

View File

@ -15,7 +15,8 @@ import (
// SessionConfig defines Session settings // SessionConfig defines Session settings
var SessionConfig = struct { var SessionConfig = struct {
Provider string OriginalProvider string
Provider string
// Provider configuration, it's corresponding to provider. // Provider configuration, it's corresponding to provider.
ProviderConfig string ProviderConfig string
// Cookie name to save session ID. Default is "MacaronSession". // Cookie name to save session ID. Default is "MacaronSession".
@ -39,8 +40,8 @@ var SessionConfig = struct {
SameSite: http.SameSiteLaxMode, SameSite: http.SameSiteLaxMode,
} }
func newSessionService() { func loadSessionFrom(rootCfg ConfigProvider) {
sec := Cfg.Section("session") sec := rootCfg.Section("session")
SessionConfig.Provider = sec.Key("PROVIDER").In("memory", SessionConfig.Provider = sec.Key("PROVIDER").In("memory",
[]string{"memory", "file", "redis", "mysql", "postgres", "couchbase", "memcache", "db"}) []string{"memory", "file", "redis", "mysql", "postgres", "couchbase", "memcache", "db"})
SessionConfig.ProviderConfig = strings.Trim(sec.Key("PROVIDER_CONFIG").MustString(path.Join(AppDataPath, "sessions")), "\" ") SessionConfig.ProviderConfig = strings.Trim(sec.Key("PROVIDER_CONFIG").MustString(path.Join(AppDataPath, "sessions")), "\" ")
@ -67,6 +68,7 @@ func newSessionService() {
log.Fatal("Can't shadow session config: %v", err) log.Fatal("Can't shadow session config: %v", err)
} }
SessionConfig.ProviderConfig = string(shadowConfig) SessionConfig.ProviderConfig = string(shadowConfig)
SessionConfig.OriginalProvider = SessionConfig.Provider
SessionConfig.Provider = "VirtualSession" SessionConfig.Provider = "VirtualSession"
log.Info("Session Service Enabled") log.Info("Session Service Enabled")

File diff suppressed because it is too large Load Diff

197
modules/setting/ssh.go Normal file
View File

@ -0,0 +1,197 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
import (
"os"
"path"
"path/filepath"
"strings"
"text/template"
"time"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
gossh "golang.org/x/crypto/ssh"
)
var SSH = struct {
Disabled bool `ini:"DISABLE_SSH"`
StartBuiltinServer bool `ini:"START_SSH_SERVER"`
BuiltinServerUser string `ini:"BUILTIN_SSH_SERVER_USER"`
UseProxyProtocol bool `ini:"SSH_SERVER_USE_PROXY_PROTOCOL"`
Domain string `ini:"SSH_DOMAIN"`
Port int `ini:"SSH_PORT"`
User string `ini:"SSH_USER"`
ListenHost string `ini:"SSH_LISTEN_HOST"`
ListenPort int `ini:"SSH_LISTEN_PORT"`
RootPath string `ini:"SSH_ROOT_PATH"`
ServerCiphers []string `ini:"SSH_SERVER_CIPHERS"`
ServerKeyExchanges []string `ini:"SSH_SERVER_KEY_EXCHANGES"`
ServerMACs []string `ini:"SSH_SERVER_MACS"`
ServerHostKeys []string `ini:"SSH_SERVER_HOST_KEYS"`
KeyTestPath string `ini:"SSH_KEY_TEST_PATH"`
KeygenPath string `ini:"SSH_KEYGEN_PATH"`
AuthorizedKeysBackup bool `ini:"SSH_AUTHORIZED_KEYS_BACKUP"`
AuthorizedPrincipalsBackup bool `ini:"SSH_AUTHORIZED_PRINCIPALS_BACKUP"`
AuthorizedKeysCommandTemplate string `ini:"SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE"`
AuthorizedKeysCommandTemplateTemplate *template.Template `ini:"-"`
MinimumKeySizeCheck bool `ini:"-"`
MinimumKeySizes map[string]int `ini:"-"`
CreateAuthorizedKeysFile bool `ini:"SSH_CREATE_AUTHORIZED_KEYS_FILE"`
CreateAuthorizedPrincipalsFile bool `ini:"SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE"`
ExposeAnonymous bool `ini:"SSH_EXPOSE_ANONYMOUS"`
AuthorizedPrincipalsAllow []string `ini:"SSH_AUTHORIZED_PRINCIPALS_ALLOW"`
AuthorizedPrincipalsEnabled bool `ini:"-"`
TrustedUserCAKeys []string `ini:"SSH_TRUSTED_USER_CA_KEYS"`
TrustedUserCAKeysFile string `ini:"SSH_TRUSTED_USER_CA_KEYS_FILENAME"`
TrustedUserCAKeysParsed []gossh.PublicKey `ini:"-"`
PerWriteTimeout time.Duration `ini:"SSH_PER_WRITE_TIMEOUT"`
PerWritePerKbTimeout time.Duration `ini:"SSH_PER_WRITE_PER_KB_TIMEOUT"`
}{
Disabled: false,
StartBuiltinServer: false,
Domain: "",
Port: 22,
ServerCiphers: []string{"chacha20-poly1305@openssh.com", "aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "aes256-gcm@openssh.com"},
ServerKeyExchanges: []string{"curve25519-sha256", "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "diffie-hellman-group14-sha256", "diffie-hellman-group14-sha1"},
ServerMACs: []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1"},
KeygenPath: "ssh-keygen",
MinimumKeySizeCheck: true,
MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 2047},
ServerHostKeys: []string{"ssh/gitea.rsa", "ssh/gogs.rsa"},
AuthorizedKeysCommandTemplate: "{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}",
PerWriteTimeout: PerWriteTimeout,
PerWritePerKbTimeout: PerWritePerKbTimeout,
}
func parseAuthorizedPrincipalsAllow(values []string) ([]string, bool) {
anything := false
email := false
username := false
for _, value := range values {
v := strings.ToLower(strings.TrimSpace(value))
switch v {
case "off":
return []string{"off"}, false
case "email":
email = true
case "username":
username = true
case "anything":
anything = true
}
}
if anything {
return []string{"anything"}, true
}
authorizedPrincipalsAllow := []string{}
if username {
authorizedPrincipalsAllow = append(authorizedPrincipalsAllow, "username")
}
if email {
authorizedPrincipalsAllow = append(authorizedPrincipalsAllow, "email")
}
return authorizedPrincipalsAllow, true
}
func loadSSHFrom(rootCfg ConfigProvider) {
sec := rootCfg.Section("server")
if len(SSH.Domain) == 0 {
SSH.Domain = Domain
}
homeDir, err := util.HomeDir()
if err != nil {
log.Fatal("Failed to get home directory: %v", err)
}
homeDir = strings.ReplaceAll(homeDir, "\\", "/")
SSH.RootPath = path.Join(homeDir, ".ssh")
serverCiphers := sec.Key("SSH_SERVER_CIPHERS").Strings(",")
if len(serverCiphers) > 0 {
SSH.ServerCiphers = serverCiphers
}
serverKeyExchanges := sec.Key("SSH_SERVER_KEY_EXCHANGES").Strings(",")
if len(serverKeyExchanges) > 0 {
SSH.ServerKeyExchanges = serverKeyExchanges
}
serverMACs := sec.Key("SSH_SERVER_MACS").Strings(",")
if len(serverMACs) > 0 {
SSH.ServerMACs = serverMACs
}
SSH.KeyTestPath = os.TempDir()
if err = sec.MapTo(&SSH); err != nil {
log.Fatal("Failed to map SSH settings: %v", err)
}
for i, key := range SSH.ServerHostKeys {
if !filepath.IsAbs(key) {
SSH.ServerHostKeys[i] = filepath.Join(AppDataPath, key)
}
}
SSH.KeygenPath = sec.Key("SSH_KEYGEN_PATH").MustString("ssh-keygen")
SSH.Port = sec.Key("SSH_PORT").MustInt(22)
SSH.ListenPort = sec.Key("SSH_LISTEN_PORT").MustInt(SSH.Port)
SSH.UseProxyProtocol = sec.Key("SSH_SERVER_USE_PROXY_PROTOCOL").MustBool(false)
// When disable SSH, start builtin server value is ignored.
if SSH.Disabled {
SSH.StartBuiltinServer = false
}
SSH.TrustedUserCAKeysFile = sec.Key("SSH_TRUSTED_USER_CA_KEYS_FILENAME").MustString(filepath.Join(SSH.RootPath, "gitea-trusted-user-ca-keys.pem"))
for _, caKey := range SSH.TrustedUserCAKeys {
pubKey, _, _, _, err := gossh.ParseAuthorizedKey([]byte(caKey))
if err != nil {
log.Fatal("Failed to parse TrustedUserCaKeys: %s %v", caKey, err)
}
SSH.TrustedUserCAKeysParsed = append(SSH.TrustedUserCAKeysParsed, pubKey)
}
if len(SSH.TrustedUserCAKeys) > 0 {
// Set the default as email,username otherwise we can leave it empty
sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").MustString("username,email")
} else {
sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").MustString("off")
}
SSH.AuthorizedPrincipalsAllow, SSH.AuthorizedPrincipalsEnabled = parseAuthorizedPrincipalsAllow(sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").Strings(","))
SSH.MinimumKeySizeCheck = sec.Key("MINIMUM_KEY_SIZE_CHECK").MustBool(SSH.MinimumKeySizeCheck)
minimumKeySizes := rootCfg.Section("ssh.minimum_key_sizes").Keys()
for _, key := range minimumKeySizes {
if key.MustInt() != -1 {
SSH.MinimumKeySizes[strings.ToLower(key.Name())] = key.MustInt()
} else {
delete(SSH.MinimumKeySizes, strings.ToLower(key.Name()))
}
}
SSH.AuthorizedKeysBackup = sec.Key("SSH_AUTHORIZED_KEYS_BACKUP").MustBool(true)
SSH.CreateAuthorizedKeysFile = sec.Key("SSH_CREATE_AUTHORIZED_KEYS_FILE").MustBool(true)
SSH.AuthorizedPrincipalsBackup = false
SSH.CreateAuthorizedPrincipalsFile = false
if SSH.AuthorizedPrincipalsEnabled {
SSH.AuthorizedPrincipalsBackup = sec.Key("SSH_AUTHORIZED_PRINCIPALS_BACKUP").MustBool(true)
SSH.CreateAuthorizedPrincipalsFile = sec.Key("SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE").MustBool(true)
}
SSH.ExposeAnonymous = sec.Key("SSH_EXPOSE_ANONYMOUS").MustBool(false)
SSH.AuthorizedKeysCommandTemplate = sec.Key("SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE").MustString(SSH.AuthorizedKeysCommandTemplate)
SSH.AuthorizedKeysCommandTemplateTemplate = template.Must(template.New("").Parse(SSH.AuthorizedKeysCommandTemplate))
SSH.PerWriteTimeout = sec.Key("SSH_PER_WRITE_TIMEOUT").MustDuration(PerWriteTimeout)
SSH.PerWritePerKbTimeout = sec.Key("SSH_PER_WRITE_PER_KB_TIMEOUT").MustDuration(PerWritePerKbTimeout)
// ensure parseRunModeSetting has been executed before this
SSH.BuiltinServerUser = rootCfg.Section("server").Key("BUILTIN_SSH_SERVER_USER").MustString(RunUser)
SSH.User = rootCfg.Section("server").Key("SSH_USER").MustString(SSH.BuiltinServerUser)
}

View File

@ -30,9 +30,9 @@ func (s *Storage) MapTo(v interface{}) error {
return nil return nil
} }
func getStorage(name, typ string, targetSec *ini.Section) Storage { func getStorage(rootCfg ConfigProvider, name, typ string, targetSec *ini.Section) Storage {
const sectionName = "storage" const sectionName = "storage"
sec := Cfg.Section(sectionName) sec := rootCfg.Section(sectionName)
// Global Defaults // Global Defaults
sec.Key("MINIO_ENDPOINT").MustString("localhost:9000") sec.Key("MINIO_ENDPOINT").MustString("localhost:9000")
@ -43,7 +43,7 @@ func getStorage(name, typ string, targetSec *ini.Section) Storage {
sec.Key("MINIO_USE_SSL").MustBool(false) sec.Key("MINIO_USE_SSL").MustBool(false)
if targetSec == nil { if targetSec == nil {
targetSec, _ = Cfg.NewSection(name) targetSec, _ = rootCfg.NewSection(name)
} }
var storage Storage var storage Storage
@ -51,12 +51,12 @@ func getStorage(name, typ string, targetSec *ini.Section) Storage {
storage.Type = typ storage.Type = typ
overrides := make([]*ini.Section, 0, 3) overrides := make([]*ini.Section, 0, 3)
nameSec, err := Cfg.GetSection(sectionName + "." + name) nameSec, err := rootCfg.GetSection(sectionName + "." + name)
if err == nil { if err == nil {
overrides = append(overrides, nameSec) overrides = append(overrides, nameSec)
} }
typeSec, err := Cfg.GetSection(sectionName + "." + typ) typeSec, err := rootCfg.GetSection(sectionName + "." + typ)
if err == nil { if err == nil {
overrides = append(overrides, typeSec) overrides = append(overrides, typeSec)
nextType := typeSec.Key("STORAGE_TYPE").String() nextType := typeSec.Key("STORAGE_TYPE").String()

View File

@ -20,11 +20,12 @@ MINIO_BUCKET = gitea-attachment
STORAGE_TYPE = minio STORAGE_TYPE = minio
MINIO_ENDPOINT = my_minio:9000 MINIO_ENDPOINT = my_minio:9000
` `
Cfg, _ = ini.Load([]byte(iniStr)) cfg, err := ini.Load([]byte(iniStr))
assert.NoError(t, err)
sec := Cfg.Section("attachment") sec := cfg.Section("attachment")
storageType := sec.Key("STORAGE_TYPE").MustString("") storageType := sec.Key("STORAGE_TYPE").MustString("")
storage := getStorage("attachments", storageType, sec) storage := getStorage(cfg, "attachments", storageType, sec)
assert.EqualValues(t, "minio", storage.Type) assert.EqualValues(t, "minio", storage.Type)
assert.EqualValues(t, "my_minio:9000", storage.Section.Key("MINIO_ENDPOINT").String()) assert.EqualValues(t, "my_minio:9000", storage.Section.Key("MINIO_ENDPOINT").String())
@ -42,11 +43,12 @@ MINIO_BUCKET = gitea-attachment
[storage.minio] [storage.minio]
MINIO_BUCKET = gitea MINIO_BUCKET = gitea
` `
Cfg, _ = ini.Load([]byte(iniStr)) cfg, err := ini.Load([]byte(iniStr))
assert.NoError(t, err)
sec := Cfg.Section("attachment") sec := cfg.Section("attachment")
storageType := sec.Key("STORAGE_TYPE").MustString("") storageType := sec.Key("STORAGE_TYPE").MustString("")
storage := getStorage("attachments", storageType, sec) storage := getStorage(cfg, "attachments", storageType, sec)
assert.EqualValues(t, "minio", storage.Type) assert.EqualValues(t, "minio", storage.Type)
assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String()) assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String())
@ -63,11 +65,12 @@ MINIO_BUCKET = gitea-minio
[storage] [storage]
MINIO_BUCKET = gitea MINIO_BUCKET = gitea
` `
Cfg, _ = ini.Load([]byte(iniStr)) cfg, err := ini.Load([]byte(iniStr))
assert.NoError(t, err)
sec := Cfg.Section("attachment") sec := cfg.Section("attachment")
storageType := sec.Key("STORAGE_TYPE").MustString("") storageType := sec.Key("STORAGE_TYPE").MustString("")
storage := getStorage("attachments", storageType, sec) storage := getStorage(cfg, "attachments", storageType, sec)
assert.EqualValues(t, "minio", storage.Type) assert.EqualValues(t, "minio", storage.Type)
assert.EqualValues(t, "gitea-minio", storage.Section.Key("MINIO_BUCKET").String()) assert.EqualValues(t, "gitea-minio", storage.Section.Key("MINIO_BUCKET").String())
@ -85,22 +88,24 @@ MINIO_BUCKET = gitea
[storage] [storage]
STORAGE_TYPE = local STORAGE_TYPE = local
` `
Cfg, _ = ini.Load([]byte(iniStr)) cfg, err := ini.Load([]byte(iniStr))
assert.NoError(t, err)
sec := Cfg.Section("attachment") sec := cfg.Section("attachment")
storageType := sec.Key("STORAGE_TYPE").MustString("") storageType := sec.Key("STORAGE_TYPE").MustString("")
storage := getStorage("attachments", storageType, sec) storage := getStorage(cfg, "attachments", storageType, sec)
assert.EqualValues(t, "minio", storage.Type) assert.EqualValues(t, "minio", storage.Type)
assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String()) assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String())
} }
func Test_getStorageGetDefaults(t *testing.T) { func Test_getStorageGetDefaults(t *testing.T) {
Cfg, _ = ini.Load([]byte("")) cfg, err := ini.Load([]byte(""))
assert.NoError(t, err)
sec := Cfg.Section("attachment") sec := cfg.Section("attachment")
storageType := sec.Key("STORAGE_TYPE").MustString("") storageType := sec.Key("STORAGE_TYPE").MustString("")
storage := getStorage("attachments", storageType, sec) storage := getStorage(cfg, "attachments", storageType, sec)
assert.EqualValues(t, "gitea", storage.Section.Key("MINIO_BUCKET").String()) assert.EqualValues(t, "gitea", storage.Section.Key("MINIO_BUCKET").String())
} }
@ -116,26 +121,27 @@ MINIO_BUCKET = gitea-attachment
[storage] [storage]
MINIO_BUCKET = gitea-storage MINIO_BUCKET = gitea-storage
` `
Cfg, _ = ini.Load([]byte(iniStr)) cfg, err := ini.Load([]byte(iniStr))
assert.NoError(t, err)
{ {
sec := Cfg.Section("attachment") sec := cfg.Section("attachment")
storageType := sec.Key("STORAGE_TYPE").MustString("") storageType := sec.Key("STORAGE_TYPE").MustString("")
storage := getStorage("attachments", storageType, sec) storage := getStorage(cfg, "attachments", storageType, sec)
assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String()) assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String())
} }
{ {
sec := Cfg.Section("lfs") sec := cfg.Section("lfs")
storageType := sec.Key("STORAGE_TYPE").MustString("") storageType := sec.Key("STORAGE_TYPE").MustString("")
storage := getStorage("lfs", storageType, sec) storage := getStorage(cfg, "lfs", storageType, sec)
assert.EqualValues(t, "gitea-lfs", storage.Section.Key("MINIO_BUCKET").String()) assert.EqualValues(t, "gitea-lfs", storage.Section.Key("MINIO_BUCKET").String())
} }
{ {
sec := Cfg.Section("avatar") sec := cfg.Section("avatar")
storageType := sec.Key("STORAGE_TYPE").MustString("") storageType := sec.Key("STORAGE_TYPE").MustString("")
storage := getStorage("avatars", storageType, sec) storage := getStorage(cfg, "avatars", storageType, sec)
assert.EqualValues(t, "gitea-storage", storage.Section.Key("MINIO_BUCKET").String()) assert.EqualValues(t, "gitea-storage", storage.Section.Key("MINIO_BUCKET").String())
} }
@ -149,19 +155,20 @@ STORAGE_TYPE = lfs
[storage.lfs] [storage.lfs]
MINIO_BUCKET = gitea-storage MINIO_BUCKET = gitea-storage
` `
Cfg, _ = ini.Load([]byte(iniStr)) cfg, err := ini.Load([]byte(iniStr))
assert.NoError(t, err)
{ {
sec := Cfg.Section("attachment") sec := cfg.Section("attachment")
storageType := sec.Key("STORAGE_TYPE").MustString("") storageType := sec.Key("STORAGE_TYPE").MustString("")
storage := getStorage("attachments", storageType, sec) storage := getStorage(cfg, "attachments", storageType, sec)
assert.EqualValues(t, "gitea-storage", storage.Section.Key("MINIO_BUCKET").String()) assert.EqualValues(t, "gitea-storage", storage.Section.Key("MINIO_BUCKET").String())
} }
{ {
sec := Cfg.Section("lfs") sec := cfg.Section("lfs")
storageType := sec.Key("STORAGE_TYPE").MustString("") storageType := sec.Key("STORAGE_TYPE").MustString("")
storage := getStorage("lfs", storageType, sec) storage := getStorage(cfg, "lfs", storageType, sec)
assert.EqualValues(t, "gitea-storage", storage.Section.Key("MINIO_BUCKET").String()) assert.EqualValues(t, "gitea-storage", storage.Section.Key("MINIO_BUCKET").String())
} }
@ -172,11 +179,12 @@ func Test_getStorageInheritStorageType(t *testing.T) {
[storage] [storage]
STORAGE_TYPE = minio STORAGE_TYPE = minio
` `
Cfg, _ = ini.Load([]byte(iniStr)) cfg, err := ini.Load([]byte(iniStr))
assert.NoError(t, err)
sec := Cfg.Section("attachment") sec := cfg.Section("attachment")
storageType := sec.Key("STORAGE_TYPE").MustString("") storageType := sec.Key("STORAGE_TYPE").MustString("")
storage := getStorage("attachments", storageType, sec) storage := getStorage(cfg, "attachments", storageType, sec)
assert.EqualValues(t, "minio", storage.Type) assert.EqualValues(t, "minio", storage.Type)
} }
@ -186,11 +194,12 @@ func Test_getStorageInheritNameSectionType(t *testing.T) {
[storage.attachments] [storage.attachments]
STORAGE_TYPE = minio STORAGE_TYPE = minio
` `
Cfg, _ = ini.Load([]byte(iniStr)) cfg, err := ini.Load([]byte(iniStr))
assert.NoError(t, err)
sec := Cfg.Section("attachment") sec := cfg.Section("attachment")
storageType := sec.Key("STORAGE_TYPE").MustString("") storageType := sec.Key("STORAGE_TYPE").MustString("")
storage := getStorage("attachments", storageType, sec) storage := getStorage(cfg, "attachments", storageType, sec)
assert.EqualValues(t, "minio", storage.Type) assert.EqualValues(t, "minio", storage.Type)
} }

View File

@ -5,13 +5,13 @@ package setting
// FIXME: DEPRECATED to be removed in v1.18.0 // FIXME: DEPRECATED to be removed in v1.18.0
// - will need to set default for [queue.task] LENGTH to 1000 though // - will need to set default for [queue.task] LENGTH to 1000 though
func newTaskService() { func loadTaskFrom(rootCfg ConfigProvider) {
taskSec := Cfg.Section("task") taskSec := rootCfg.Section("task")
queueTaskSec := Cfg.Section("queue.task") queueTaskSec := rootCfg.Section("queue.task")
deprecatedSetting("task", "QUEUE_TYPE", "queue.task", "TYPE") deprecatedSetting(rootCfg, "task", "QUEUE_TYPE", "queue.task", "TYPE")
deprecatedSetting("task", "QUEUE_CONN_STR", "queue.task", "CONN_STR") deprecatedSetting(rootCfg, "task", "QUEUE_CONN_STR", "queue.task", "CONN_STR")
deprecatedSetting("task", "QUEUE_LENGTH", "queue.task", "LENGTH") deprecatedSetting(rootCfg, "task", "QUEUE_LENGTH", "queue.task", "LENGTH")
switch taskSec.Key("QUEUE_TYPE").MustString("channel") { switch taskSec.Key("QUEUE_TYPE").MustString("channel") {
case "channel": case "channel":

64
modules/setting/time.go Normal file
View File

@ -0,0 +1,64 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
import (
"time"
"code.gitea.io/gitea/modules/log"
)
var (
// Time settings
TimeFormat string
// UILocation is the location on the UI, so that we can display the time on UI.
DefaultUILocation = time.Local
)
func loadTimeFrom(rootCfg ConfigProvider) {
timeFormatKey := rootCfg.Section("time").Key("FORMAT").MustString("")
if timeFormatKey != "" {
TimeFormat = map[string]string{
"ANSIC": time.ANSIC,
"UnixDate": time.UnixDate,
"RubyDate": time.RubyDate,
"RFC822": time.RFC822,
"RFC822Z": time.RFC822Z,
"RFC850": time.RFC850,
"RFC1123": time.RFC1123,
"RFC1123Z": time.RFC1123Z,
"RFC3339": time.RFC3339,
"RFC3339Nano": time.RFC3339Nano,
"Kitchen": time.Kitchen,
"Stamp": time.Stamp,
"StampMilli": time.StampMilli,
"StampMicro": time.StampMicro,
"StampNano": time.StampNano,
}[timeFormatKey]
// When the TimeFormatKey does not exist in the previous map e.g.'2006-01-02 15:04:05'
if len(TimeFormat) == 0 {
TimeFormat = timeFormatKey
TestTimeFormat, _ := time.Parse(TimeFormat, TimeFormat)
if TestTimeFormat.Format(time.RFC3339) != "2006-01-02T15:04:05Z" {
log.Warn("Provided TimeFormat: %s does not create a fully specified date and time.", TimeFormat)
log.Warn("In order to display dates and times correctly please check your time format has 2006, 01, 02, 15, 04 and 05")
}
log.Trace("Custom TimeFormat: %s", TimeFormat)
}
}
zone := rootCfg.Section("time").Key("DEFAULT_UI_LOCATION").String()
if zone != "" {
var err error
DefaultUILocation, err = time.LoadLocation(zone)
if err != nil {
log.Fatal("Load time zone failed: %v", err)
} else {
log.Info("Default UI Location is %v", zone)
}
}
if DefaultUILocation == nil {
DefaultUILocation = time.Local
}
}

152
modules/setting/ui.go Normal file
View File

@ -0,0 +1,152 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
import (
"time"
"code.gitea.io/gitea/modules/container"
)
// UI settings
var UI = struct {
ExplorePagingNum int
SitemapPagingNum int
IssuePagingNum int
RepoSearchPagingNum int
MembersPagingNum int
FeedMaxCommitNum int
FeedPagingNum int
PackagesPagingNum int
GraphMaxCommitNum int
CodeCommentLines int
ReactionMaxUserNum int
ThemeColorMetaTag string
MaxDisplayFileSize int64
ShowUserEmail bool
DefaultShowFullName bool
DefaultTheme string
Themes []string
Reactions []string
ReactionsLookup container.Set[string] `ini:"-"`
CustomEmojis []string
CustomEmojisMap map[string]string `ini:"-"`
SearchRepoDescription bool
UseServiceWorker bool
OnlyShowRelevantRepos bool
Notification struct {
MinTimeout time.Duration
TimeoutStep time.Duration
MaxTimeout time.Duration
EventSourceUpdateTime time.Duration
} `ini:"ui.notification"`
SVG struct {
Enabled bool `ini:"ENABLE_RENDER"`
} `ini:"ui.svg"`
CSV struct {
MaxFileSize int64
} `ini:"ui.csv"`
Admin struct {
UserPagingNum int
RepoPagingNum int
NoticePagingNum int
OrgPagingNum int
} `ini:"ui.admin"`
User struct {
RepoPagingNum int
} `ini:"ui.user"`
Meta struct {
Author string
Description string
Keywords string
} `ini:"ui.meta"`
}{
ExplorePagingNum: 20,
SitemapPagingNum: 20,
IssuePagingNum: 20,
RepoSearchPagingNum: 20,
MembersPagingNum: 20,
FeedMaxCommitNum: 5,
FeedPagingNum: 20,
PackagesPagingNum: 20,
GraphMaxCommitNum: 100,
CodeCommentLines: 4,
ReactionMaxUserNum: 10,
ThemeColorMetaTag: `#6cc644`,
MaxDisplayFileSize: 8388608,
DefaultTheme: `auto`,
Themes: []string{`auto`, `gitea`, `arc-green`},
Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`},
CustomEmojis: []string{`git`, `gitea`, `codeberg`, `gitlab`, `github`, `gogs`},
CustomEmojisMap: map[string]string{"git": ":git:", "gitea": ":gitea:", "codeberg": ":codeberg:", "gitlab": ":gitlab:", "github": ":github:", "gogs": ":gogs:"},
Notification: struct {
MinTimeout time.Duration
TimeoutStep time.Duration
MaxTimeout time.Duration
EventSourceUpdateTime time.Duration
}{
MinTimeout: 10 * time.Second,
TimeoutStep: 10 * time.Second,
MaxTimeout: 60 * time.Second,
EventSourceUpdateTime: 10 * time.Second,
},
SVG: struct {
Enabled bool `ini:"ENABLE_RENDER"`
}{
Enabled: true,
},
CSV: struct {
MaxFileSize int64
}{
MaxFileSize: 524288,
},
Admin: struct {
UserPagingNum int
RepoPagingNum int
NoticePagingNum int
OrgPagingNum int
}{
UserPagingNum: 50,
RepoPagingNum: 50,
NoticePagingNum: 25,
OrgPagingNum: 50,
},
User: struct {
RepoPagingNum int
}{
RepoPagingNum: 15,
},
Meta: struct {
Author string
Description string
Keywords string
}{
Author: "Gitea - Git with a cup of tea",
Description: "Gitea (Git with a cup of tea) is a painless self-hosted Git service written in Go",
Keywords: "go,git,self-hosted,gitea",
},
}
func loadUIFrom(rootCfg ConfigProvider) {
mustMapSetting(rootCfg, "ui", &UI)
sec := rootCfg.Section("ui")
UI.ShowUserEmail = sec.Key("SHOW_USER_EMAIL").MustBool(true)
UI.DefaultShowFullName = sec.Key("DEFAULT_SHOW_FULL_NAME").MustBool(false)
UI.SearchRepoDescription = sec.Key("SEARCH_REPO_DESCRIPTION").MustBool(true)
UI.UseServiceWorker = sec.Key("USE_SERVICE_WORKER").MustBool(false)
UI.OnlyShowRelevantRepos = sec.Key("ONLY_SHOW_RELEVANT_REPOS").MustBool(false)
UI.ReactionsLookup = make(container.Set[string])
for _, reaction := range UI.Reactions {
UI.ReactionsLookup.Add(reaction)
}
UI.CustomEmojisMap = make(map[string]string)
for _, emoji := range UI.CustomEmojis {
UI.CustomEmojisMap[emoji] = ":" + emoji + ":"
}
}

View File

@ -29,8 +29,8 @@ var Webhook = struct {
ProxyHosts: []string{}, ProxyHosts: []string{},
} }
func newWebhookService() { func loadWebhookFrom(rootCfg ConfigProvider) {
sec := Cfg.Section("webhook") sec := rootCfg.Section("webhook")
Webhook.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000) Webhook.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000)
Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5) Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5)
Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool() Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool()

View File

@ -13,8 +13,8 @@ import (
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
setting.LoadForTest() setting.InitProviderAndLoadCommonSettingsForTest()
setting.NewQueueService() setting.LoadQueueSettings()
unittest.MainTest(m, &unittest.TestOptions{ unittest.MainTest(m, &unittest.TestOptions{
GiteaRootPath: filepath.Join("..", "..", "..", ".."), GiteaRootPath: filepath.Join("..", "..", "..", ".."),
SetUp: webhook_service.Init, SetUp: webhook_service.Init,

View File

@ -50,11 +50,11 @@ func Middlewares() []func(http.Handler) http.Handler {
handlers = append(handlers, middleware.StripSlashes) handlers = append(handlers, middleware.StripSlashes)
if !setting.DisableRouterLog { if !setting.Log.DisableRouterLog {
handlers = append(handlers, routing.NewLoggerHandler()) handlers = append(handlers, routing.NewLoggerHandler())
} }
if setting.EnableAccessLog { if setting.Log.EnableAccessLog {
handlers = append(handlers, context.AccessLogger()) handlers = append(handlers, context.AccessLogger())
} }

View File

@ -73,7 +73,7 @@ func mustInitCtx(ctx context.Context, fn func(ctx context.Context) error) {
// InitGitServices init new services for git, this is also called in `contrib/pr/checkout.go` // InitGitServices init new services for git, this is also called in `contrib/pr/checkout.go`
func InitGitServices() { func InitGitServices() {
setting.NewServices() setting.LoadSettings()
mustInit(storage.Init) mustInit(storage.Init)
mustInit(repo_service.Init) mustInit(repo_service.Init)
} }
@ -119,7 +119,7 @@ func GlobalInitInstalled(ctx context.Context) {
log.Info("AppPath: %s", setting.AppPath) log.Info("AppPath: %s", setting.AppPath)
log.Info("AppWorkPath: %s", setting.AppWorkPath) log.Info("AppWorkPath: %s", setting.AppWorkPath)
log.Info("Custom path: %s", setting.CustomPath) log.Info("Custom path: %s", setting.CustomPath)
log.Info("Log path: %s", setting.LogRootPath) log.Info("Log path: %s", setting.Log.RootPath)
log.Info("Configuration file: %s", setting.CustomConf) log.Info("Configuration file: %s", setting.CustomConf)
log.Info("Run Mode: %s", util.ToTitleCase(setting.RunMode)) log.Info("Run Mode: %s", util.ToTitleCase(setting.RunMode))
log.Info("Gitea v%s%s", setting.AppVer, setting.AppBuiltWith) log.Info("Gitea v%s%s", setting.AppVer, setting.AppBuiltWith)
@ -127,7 +127,7 @@ func GlobalInitInstalled(ctx context.Context) {
// Setup i18n // Setup i18n
translation.InitLocales(ctx) translation.InitLocales(ctx)
setting.NewServices() setting.LoadSettings()
mustInit(storage.Init) mustInit(storage.Init)
mailer.NewContext(ctx) mailer.NewContext(ctx)

View File

@ -134,7 +134,7 @@ func Install(ctx *context.Context) {
form.SSHPort = setting.SSH.Port form.SSHPort = setting.SSH.Port
form.HTTPPort = setting.HTTPPort form.HTTPPort = setting.HTTPPort
form.AppURL = setting.AppURL form.AppURL = setting.AppURL
form.LogRootPath = setting.LogRootPath form.LogRootPath = setting.Log.RootPath
// E-mail service settings // E-mail service settings
if setting.MailService != nil { if setting.MailService != nil {
@ -467,7 +467,7 @@ func SubmitInstall(ctx *context.Context) {
cfg.Section("session").Key("PROVIDER").SetValue("file") cfg.Section("session").Key("PROVIDER").SetValue("file")
cfg.Section("log").Key("MODE").SetValue("console") cfg.Section("log").Key("MODE").SetValue("console")
cfg.Section("log").Key("LEVEL").SetValue(setting.LogLevel.String()) cfg.Section("log").Key("LEVEL").SetValue(setting.Log.Level.String())
cfg.Section("log").Key("ROOT_PATH").SetValue(form.LogRootPath) cfg.Section("log").Key("ROOT_PATH").SetValue(form.LogRootPath)
cfg.Section("log").Key("ROUTER").SetValue("console") cfg.Section("log").Key("ROUTER").SetValue("console")

View File

@ -15,20 +15,21 @@ import (
// PreloadSettings preloads the configuration to check if we need to run install // PreloadSettings preloads the configuration to check if we need to run install
func PreloadSettings(ctx context.Context) bool { func PreloadSettings(ctx context.Context) bool {
setting.LoadAllowEmpty() setting.InitProviderAllowEmpty()
setting.LoadCommonSettings()
if !setting.InstallLock { if !setting.InstallLock {
log.Info("AppPath: %s", setting.AppPath) log.Info("AppPath: %s", setting.AppPath)
log.Info("AppWorkPath: %s", setting.AppWorkPath) log.Info("AppWorkPath: %s", setting.AppWorkPath)
log.Info("Custom path: %s", setting.CustomPath) log.Info("Custom path: %s", setting.CustomPath)
log.Info("Log path: %s", setting.LogRootPath) log.Info("Log path: %s", setting.Log.RootPath)
log.Info("Configuration file: %s", setting.CustomConf) log.Info("Configuration file: %s", setting.CustomConf)
log.Info("Prepare to run install page") log.Info("Prepare to run install page")
translation.InitLocales(ctx) translation.InitLocales(ctx)
if setting.EnableSQLite3 { if setting.EnableSQLite3 {
log.Info("SQLite3 is supported") log.Info("SQLite3 is supported")
} }
setting.InitDBConfig()
setting.NewServicesForInstall() setting.LoadSettingsForInstall()
svg.Init() svg.Init()
} }
@ -37,8 +38,9 @@ func PreloadSettings(ctx context.Context) bool {
// reloadSettings reloads the existing settings and starts up the database // reloadSettings reloads the existing settings and starts up the database
func reloadSettings(ctx context.Context) { func reloadSettings(ctx context.Context) {
setting.LoadFromExisting() setting.InitProviderFromExistingFile()
setting.InitDBConfig() setting.LoadCommonSettings()
setting.LoadDBSetting()
if setting.InstallLock { if setting.InstallLock {
if err := common.InitDBEngine(ctx); err == nil { if err := common.InitDBEngine(ctx); err == nil {
log.Info("ORM engine initialization successful!") log.Info("ORM engine initialization successful!")

View File

@ -116,11 +116,11 @@ func AddLogger(ctx *context.PrivateContext) {
} }
if _, ok := opts.Config["level"]; !ok { if _, ok := opts.Config["level"]; !ok {
opts.Config["level"] = setting.LogLevel opts.Config["level"] = setting.Log.Level
} }
if _, ok := opts.Config["stacktraceLevel"]; !ok { if _, ok := opts.Config["stacktraceLevel"]; !ok {
opts.Config["stacktraceLevel"] = setting.StacktraceLogLevel opts.Config["stacktraceLevel"] = setting.Log.StacktraceLogLevel
} }
if opts.Mode == "file" { if opts.Mode == "file" {
@ -135,7 +135,7 @@ func AddLogger(ctx *context.PrivateContext) {
} }
} }
bufferLen := setting.Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000) bufferLen := setting.Log.BufferLength
byteConfig, err := json.Marshal(opts.Config) byteConfig, err := json.Marshal(opts.Config)
if err != nil { if err != nil {
log.Error("Failed to marshal log configuration: %v %v", opts.Config, err) log.Error("Failed to marshal log configuration: %v %v", opts.Config, err)

View File

@ -15,7 +15,7 @@ import (
// SSHLog hook to response ssh log // SSHLog hook to response ssh log
func SSHLog(ctx *context.PrivateContext) { func SSHLog(ctx *context.PrivateContext) {
if !setting.EnableSSHLog { if !setting.Log.EnableSSHLog {
ctx.Status(http.StatusOK) ctx.Status(http.StatusOK)
return return
} }

View File

@ -117,7 +117,7 @@ func Config(ctx *context.Context) {
ctx.Data["AppUrl"] = setting.AppURL ctx.Data["AppUrl"] = setting.AppURL
ctx.Data["Domain"] = setting.Domain ctx.Data["Domain"] = setting.Domain
ctx.Data["OfflineMode"] = setting.OfflineMode ctx.Data["OfflineMode"] = setting.OfflineMode
ctx.Data["DisableRouterLog"] = setting.DisableRouterLog ctx.Data["DisableRouterLog"] = setting.Log.DisableRouterLog
ctx.Data["RunUser"] = setting.RunUser ctx.Data["RunUser"] = setting.RunUser
ctx.Data["RunMode"] = util.ToTitleCase(setting.RunMode) ctx.Data["RunMode"] = util.ToTitleCase(setting.RunMode)
ctx.Data["GitVersion"] = git.VersionInfo() ctx.Data["GitVersion"] = git.VersionInfo()
@ -125,7 +125,7 @@ func Config(ctx *context.Context) {
ctx.Data["RepoRootPath"] = setting.RepoRootPath ctx.Data["RepoRootPath"] = setting.RepoRootPath
ctx.Data["CustomRootPath"] = setting.CustomPath ctx.Data["CustomRootPath"] = setting.CustomPath
ctx.Data["StaticRootPath"] = setting.StaticRootPath ctx.Data["StaticRootPath"] = setting.StaticRootPath
ctx.Data["LogRootPath"] = setting.LogRootPath ctx.Data["LogRootPath"] = setting.Log.RootPath
ctx.Data["ScriptType"] = setting.ScriptType ctx.Data["ScriptType"] = setting.ScriptType
ctx.Data["ReverseProxyAuthUser"] = setting.ReverseProxyAuthUser ctx.Data["ReverseProxyAuthUser"] = setting.ReverseProxyAuthUser
ctx.Data["ReverseProxyAuthEmail"] = setting.ReverseProxyAuthEmail ctx.Data["ReverseProxyAuthEmail"] = setting.ReverseProxyAuthEmail
@ -183,10 +183,10 @@ func Config(ctx *context.Context) {
ctx.Data["EnvVars"] = envVars ctx.Data["EnvVars"] = envVars
ctx.Data["Loggers"] = setting.GetLogDescriptions() ctx.Data["Loggers"] = setting.GetLogDescriptions()
ctx.Data["EnableAccessLog"] = setting.EnableAccessLog ctx.Data["EnableAccessLog"] = setting.Log.EnableAccessLog
ctx.Data["AccessLogTemplate"] = setting.AccessLogTemplate ctx.Data["AccessLogTemplate"] = setting.Log.AccessLogTemplate
ctx.Data["DisableRouterLog"] = setting.DisableRouterLog ctx.Data["DisableRouterLog"] = setting.Log.DisableRouterLog
ctx.Data["EnableXORMLog"] = setting.EnableXORMLog ctx.Data["EnableXORMLog"] = setting.Log.EnableXORMLog
ctx.Data["LogSQL"] = setting.Database.LogSQL ctx.Data["LogSQL"] = setting.Database.LogSQL
ctx.HTML(http.StatusOK, tplConfig) ctx.HTML(http.StatusOK, tplConfig)

View File

@ -15,8 +15,8 @@ import (
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
setting.LoadForTest() setting.InitProviderAndLoadCommonSettingsForTest()
setting.NewQueueService() setting.LoadQueueSettings()
// for tests, allow only loopback IPs // for tests, allow only loopback IPs
setting.Webhook.AllowedHostList = hostmatcher.MatchBuiltinLoopback setting.Webhook.AllowedHostList = hostmatcher.MatchBuiltinLoopback

View File

@ -87,8 +87,8 @@ func TestMain(m *testing.M) {
c = routers.NormalRoutes(context.TODO()) c = routers.NormalRoutes(context.TODO())
// integration test settings... // integration test settings...
if setting.Cfg != nil { if setting.CfgProvider != nil {
testingCfg := setting.Cfg.Section("integration-tests") testingCfg := setting.CfgProvider.Section("integration-tests")
tests.SlowTest = testingCfg.Key("SLOW_TEST").MustDuration(tests.SlowTest) tests.SlowTest = testingCfg.Key("SLOW_TEST").MustDuration(tests.SlowTest)
tests.SlowFlush = testingCfg.Key("SLOW_FLUSH").MustDuration(tests.SlowFlush) tests.SlowFlush = testingCfg.Key("SLOW_FLUSH").MustDuration(tests.SlowFlush)
} }

View File

@ -57,7 +57,7 @@ func initMigrationTest(t *testing.T) func() {
setting.CustomConf = giteaConf setting.CustomConf = giteaConf
} }
setting.LoadForTest() setting.InitProviderAndLoadCommonSettingsForTest()
assert.True(t, len(setting.RepoRootPath) != 0) assert.True(t, len(setting.RepoRootPath) != 0)
assert.NoError(t, util.RemoveAll(setting.RepoRootPath)) assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
@ -83,8 +83,8 @@ func initMigrationTest(t *testing.T) func() {
} }
assert.NoError(t, git.InitFull(context.Background())) assert.NoError(t, git.InitFull(context.Background()))
setting.InitDBConfig() setting.LoadDBSetting()
setting.NewLogServices(true) setting.InitLogs(true)
return deferFn return deferFn
} }
@ -292,7 +292,7 @@ func doMigrationTest(t *testing.T, version string) {
return return
} }
setting.NewXORMLogService(false) setting.InitSQLLog(false)
err := db.InitEngineWithMigration(context.Background(), wrappedMigrate) err := db.InitEngineWithMigration(context.Background(), wrappedMigrate)
assert.NoError(t, err) assert.NoError(t, err)

View File

@ -57,7 +57,7 @@ func InitTest(requireGitea bool) {
} }
setting.SetCustomPathAndConf("", "", "") setting.SetCustomPathAndConf("", "", "")
setting.LoadForTest() setting.InitProviderAndLoadCommonSettingsForTest()
setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master" setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master"
_ = util.RemoveAll(repo_module.LocalCopyPath()) _ = util.RemoveAll(repo_module.LocalCopyPath())
@ -65,7 +65,7 @@ func InitTest(requireGitea bool) {
log.Fatal("git.InitOnceWithSync: %v", err) log.Fatal("git.InitOnceWithSync: %v", err)
} }
setting.InitDBConfig() setting.LoadDBSetting()
if err := storage.Init(); err != nil { if err := storage.Init(); err != nil {
fmt.Printf("Init storage failed: %v", err) fmt.Printf("Init storage failed: %v", err)
os.Exit(1) os.Exit(1)