forked from gitea/gitea
		
	Add system setting table with cache and also add cache supports for user setting (#18058)
This commit is contained in:
		
							parent
							
								
									5d3dbffa15
								
							
						
					
					
						commit
						f860a6d2e4
					
				| @ -14,7 +14,6 @@ import ( | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| 	"os/user" | ||||
| @ -62,11 +61,7 @@ func runPR() { | ||||
| 	} | ||||
| 	setting.AppWorkPath = curDir | ||||
| 	setting.StaticRootPath = curDir | ||||
| 	setting.GravatarSourceURL, err = url.Parse("https://secure.gravatar.com/avatar/") | ||||
| 	if err != nil { | ||||
| 		log.Fatalf("url.Parse: %v\n", err) | ||||
| 	} | ||||
| 
 | ||||
| 	setting.GravatarSource = "https://secure.gravatar.com/avatar/" | ||||
| 	setting.AppURL = "http://localhost:8080/" | ||||
| 	setting.HTTPPort = "8080" | ||||
| 	setting.SSH.Domain = "localhost" | ||||
|  | ||||
| @ -1,117 +0,0 @@ | ||||
| // Copyright 2017 The Gitea Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package admin_test | ||||
| 
 | ||||
| import ( | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/models/admin" | ||||
| 	"code.gitea.io/gitea/models/db" | ||||
| 	"code.gitea.io/gitea/models/unittest" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
| 
 | ||||
| func TestNotice_TrStr(t *testing.T) { | ||||
| 	notice := &admin.Notice{ | ||||
| 		Type:        admin.NoticeRepository, | ||||
| 		Description: "test description", | ||||
| 	} | ||||
| 	assert.Equal(t, "admin.notices.type_1", notice.TrStr()) | ||||
| } | ||||
| 
 | ||||
| func TestCreateNotice(t *testing.T) { | ||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
| 
 | ||||
| 	noticeBean := &admin.Notice{ | ||||
| 		Type:        admin.NoticeRepository, | ||||
| 		Description: "test description", | ||||
| 	} | ||||
| 	unittest.AssertNotExistsBean(t, noticeBean) | ||||
| 	assert.NoError(t, admin.CreateNotice(db.DefaultContext, noticeBean.Type, noticeBean.Description)) | ||||
| 	unittest.AssertExistsAndLoadBean(t, noticeBean) | ||||
| } | ||||
| 
 | ||||
| func TestCreateRepositoryNotice(t *testing.T) { | ||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
| 
 | ||||
| 	noticeBean := &admin.Notice{ | ||||
| 		Type:        admin.NoticeRepository, | ||||
| 		Description: "test description", | ||||
| 	} | ||||
| 	unittest.AssertNotExistsBean(t, noticeBean) | ||||
| 	assert.NoError(t, admin.CreateRepositoryNotice(noticeBean.Description)) | ||||
| 	unittest.AssertExistsAndLoadBean(t, noticeBean) | ||||
| } | ||||
| 
 | ||||
| // TODO TestRemoveAllWithNotice | ||||
| 
 | ||||
| func TestCountNotices(t *testing.T) { | ||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
| 	assert.Equal(t, int64(3), admin.CountNotices()) | ||||
| } | ||||
| 
 | ||||
| func TestNotices(t *testing.T) { | ||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
| 
 | ||||
| 	notices, err := admin.Notices(1, 2) | ||||
| 	assert.NoError(t, err) | ||||
| 	if assert.Len(t, notices, 2) { | ||||
| 		assert.Equal(t, int64(3), notices[0].ID) | ||||
| 		assert.Equal(t, int64(2), notices[1].ID) | ||||
| 	} | ||||
| 
 | ||||
| 	notices, err = admin.Notices(2, 2) | ||||
| 	assert.NoError(t, err) | ||||
| 	if assert.Len(t, notices, 1) { | ||||
| 		assert.Equal(t, int64(1), notices[0].ID) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestDeleteNotice(t *testing.T) { | ||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
| 
 | ||||
| 	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3}) | ||||
| 	assert.NoError(t, admin.DeleteNotice(3)) | ||||
| 	unittest.AssertNotExistsBean(t, &admin.Notice{ID: 3}) | ||||
| } | ||||
| 
 | ||||
| func TestDeleteNotices(t *testing.T) { | ||||
| 	// delete a non-empty range | ||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
| 
 | ||||
| 	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 1}) | ||||
| 	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2}) | ||||
| 	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3}) | ||||
| 	assert.NoError(t, admin.DeleteNotices(1, 2)) | ||||
| 	unittest.AssertNotExistsBean(t, &admin.Notice{ID: 1}) | ||||
| 	unittest.AssertNotExistsBean(t, &admin.Notice{ID: 2}) | ||||
| 	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3}) | ||||
| } | ||||
| 
 | ||||
| func TestDeleteNotices2(t *testing.T) { | ||||
| 	// delete an empty range | ||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
| 
 | ||||
| 	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 1}) | ||||
| 	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2}) | ||||
| 	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3}) | ||||
| 	assert.NoError(t, admin.DeleteNotices(3, 2)) | ||||
| 	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 1}) | ||||
| 	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2}) | ||||
| 	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3}) | ||||
| } | ||||
| 
 | ||||
| func TestDeleteNoticesByIDs(t *testing.T) { | ||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
| 
 | ||||
| 	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 1}) | ||||
| 	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2}) | ||||
| 	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3}) | ||||
| 	assert.NoError(t, admin.DeleteNoticesByIDs([]int64{1, 3})) | ||||
| 	unittest.AssertNotExistsBean(t, &admin.Notice{ID: 1}) | ||||
| 	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2}) | ||||
| 	unittest.AssertNotExistsBean(t, &admin.Notice{ID: 3}) | ||||
| } | ||||
| @ -13,6 +13,7 @@ import ( | ||||
| 	"sync" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/models/db" | ||||
| 	system_model "code.gitea.io/gitea/models/system" | ||||
| 	"code.gitea.io/gitea/modules/base" | ||||
| 	"code.gitea.io/gitea/modules/cache" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| @ -72,7 +73,7 @@ func GetEmailForHash(md5Sum string) (string, error) { | ||||
| // LibravatarURL returns the URL for the given email. Slow due to the DNS lookup. | ||||
| // This function should only be called if a federated avatar service is enabled. | ||||
| func LibravatarURL(email string) (*url.URL, error) { | ||||
| 	urlStr, err := setting.LibravatarService.FromEmail(email) | ||||
| 	urlStr, err := system_model.LibravatarService.FromEmail(email) | ||||
| 	if err != nil { | ||||
| 		log.Error("LibravatarService.FromEmail(email=%s): error %v", email, err) | ||||
| 		return nil, err | ||||
| @ -149,8 +150,10 @@ func generateEmailAvatarLink(email string, size int, final bool) string { | ||||
| 		return DefaultAvatarLink() | ||||
| 	} | ||||
| 
 | ||||
| 	enableFederatedAvatar, _ := system_model.GetSetting(system_model.KeyPictureEnableFederatedAvatar) | ||||
| 
 | ||||
| 	var err error | ||||
| 	if setting.EnableFederatedAvatar && setting.LibravatarService != nil { | ||||
| 	if enableFederatedAvatar != nil && enableFederatedAvatar.GetValueBool() && system_model.LibravatarService != nil { | ||||
| 		emailHash := saveEmailHash(email) | ||||
| 		if final { | ||||
| 			// for final link, we can spend more time on slow external query | ||||
| @ -166,12 +169,16 @@ func generateEmailAvatarLink(email string, size int, final bool) string { | ||||
| 			urlStr += "?size=" + strconv.Itoa(size) | ||||
| 		} | ||||
| 		return urlStr | ||||
| 	} else if !setting.DisableGravatar { | ||||
| 	} | ||||
| 
 | ||||
| 	disableGravatar, _ := system_model.GetSetting(system_model.KeyPictureDisableGravatar) | ||||
| 	if disableGravatar != nil && !disableGravatar.GetValueBool() { | ||||
| 		// copy GravatarSourceURL, because we will modify its Path. | ||||
| 		avatarURLCopy := *setting.GravatarSourceURL | ||||
| 		avatarURLCopy := *system_model.GravatarSourceURL | ||||
| 		avatarURLCopy.Path = path.Join(avatarURLCopy.Path, HashEmail(email)) | ||||
| 		return generateRecognizedAvatarURL(avatarURLCopy, size) | ||||
| 	} | ||||
| 
 | ||||
| 	return DefaultAvatarLink() | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -2,12 +2,13 @@ | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package avatars | ||||
| package avatars_test | ||||
| 
 | ||||
| import ( | ||||
| 	"net/url" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	avatars_model "code.gitea.io/gitea/models/avatars" | ||||
| 	system_model "code.gitea.io/gitea/models/system" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| @ -15,40 +16,43 @@ import ( | ||||
| 
 | ||||
| const gravatarSource = "https://secure.gravatar.com/avatar/" | ||||
| 
 | ||||
| func disableGravatar() { | ||||
| 	setting.EnableFederatedAvatar = false | ||||
| 	setting.LibravatarService = nil | ||||
| 	setting.DisableGravatar = true | ||||
| func disableGravatar(t *testing.T) { | ||||
| 	err := system_model.SetSettingNoVersion(system_model.KeyPictureEnableFederatedAvatar, "false") | ||||
| 	assert.NoError(t, err) | ||||
| 	err = system_model.SetSettingNoVersion(system_model.KeyPictureDisableGravatar, "true") | ||||
| 	assert.NoError(t, err) | ||||
| 	system_model.LibravatarService = nil | ||||
| } | ||||
| 
 | ||||
| func enableGravatar(t *testing.T) { | ||||
| 	setting.DisableGravatar = false | ||||
| 	var err error | ||||
| 	setting.GravatarSourceURL, err = url.Parse(gravatarSource) | ||||
| 	err := system_model.SetSettingNoVersion(system_model.KeyPictureDisableGravatar, "false") | ||||
| 	assert.NoError(t, err) | ||||
| 	setting.GravatarSource = gravatarSource | ||||
| 	err = system_model.Init() | ||||
| 	assert.NoError(t, err) | ||||
| } | ||||
| 
 | ||||
| func TestHashEmail(t *testing.T) { | ||||
| 	assert.Equal(t, | ||||
| 		"d41d8cd98f00b204e9800998ecf8427e", | ||||
| 		HashEmail(""), | ||||
| 		avatars_model.HashEmail(""), | ||||
| 	) | ||||
| 	assert.Equal(t, | ||||
| 		"353cbad9b58e69c96154ad99f92bedc7", | ||||
| 		HashEmail("gitea@example.com"), | ||||
| 		avatars_model.HashEmail("gitea@example.com"), | ||||
| 	) | ||||
| } | ||||
| 
 | ||||
| func TestSizedAvatarLink(t *testing.T) { | ||||
| 	setting.AppSubURL = "/testsuburl" | ||||
| 
 | ||||
| 	disableGravatar() | ||||
| 	disableGravatar(t) | ||||
| 	assert.Equal(t, "/testsuburl/assets/img/avatar_default.png", | ||||
| 		GenerateEmailAvatarFastLink("gitea@example.com", 100)) | ||||
| 		avatars_model.GenerateEmailAvatarFastLink("gitea@example.com", 100)) | ||||
| 
 | ||||
| 	enableGravatar(t) | ||||
| 	assert.Equal(t, | ||||
| 		"https://secure.gravatar.com/avatar/353cbad9b58e69c96154ad99f92bedc7?d=identicon&s=100", | ||||
| 		GenerateEmailAvatarFastLink("gitea@example.com", 100), | ||||
| 		avatars_model.GenerateEmailAvatarFastLink("gitea@example.com", 100), | ||||
| 	) | ||||
| } | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package admin_test | ||||
| package avatars_test | ||||
| 
 | ||||
| import ( | ||||
| 	"path/filepath" | ||||
							
								
								
									
										15
									
								
								models/fixtures/system_setting.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								models/fixtures/system_setting.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| - | ||||
|   id: 1 | ||||
|   setting_key: 'disable_gravatar' | ||||
|   setting_value: 'false' | ||||
|   version: 1 | ||||
|   created: 1653533198 | ||||
|   updated: 1653533198 | ||||
| 
 | ||||
| - | ||||
|   id: 2 | ||||
|   setting_key: 'enable_federated_avatar' | ||||
|   setting_value: 'false' | ||||
|   version: 1 | ||||
|   created: 1653533198 | ||||
|   updated: 1653533198 | ||||
| @ -13,7 +13,6 @@ import ( | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	admin_model "code.gitea.io/gitea/models/admin" | ||||
| 	"code.gitea.io/gitea/models/db" | ||||
| 	"code.gitea.io/gitea/models/foreignreference" | ||||
| 	"code.gitea.io/gitea/models/organization" | ||||
| @ -21,6 +20,7 @@ import ( | ||||
| 	access_model "code.gitea.io/gitea/models/perm/access" | ||||
| 	project_model "code.gitea.io/gitea/models/project" | ||||
| 	repo_model "code.gitea.io/gitea/models/repo" | ||||
| 	system_model "code.gitea.io/gitea/models/system" | ||||
| 	"code.gitea.io/gitea/models/unit" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/modules/base" | ||||
| @ -2470,7 +2470,7 @@ func DeleteOrphanedIssues() error { | ||||
| 
 | ||||
| 	// Remove issue attachment files. | ||||
| 	for i := range attachmentPaths { | ||||
| 		admin_model.RemoveAllWithNotice(db.DefaultContext, "Delete issue attachment", attachmentPaths[i]) | ||||
| 		system_model.RemoveAllWithNotice(db.DefaultContext, "Delete issue attachment", attachmentPaths[i]) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @ -14,6 +14,8 @@ import ( | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 
 | ||||
| 	_ "code.gitea.io/gitea/models/system" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
| 
 | ||||
|  | ||||
| @ -415,6 +415,8 @@ var migrations = []Migration{ | ||||
| 	NewMigration("Alter gpg_key/public_key content TEXT fields to MEDIUMTEXT", alterPublicGPGKeyContentFieldsToMediumText), | ||||
| 	// v226 -> v227 | ||||
| 	NewMigration("Conan and generic packages do not need to be semantically versioned", fixPackageSemverField), | ||||
| 	// v227 -> v228 | ||||
| 	NewMigration("Create key/value table for system settings", createSystemSettingsTable), | ||||
| } | ||||
| 
 | ||||
| // GetCurrentDBVersion returns the current db version | ||||
|  | ||||
							
								
								
									
										64
									
								
								models/migrations/v227.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								models/migrations/v227.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,64 @@ | ||||
| // Copyright 2022 The Gitea Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package migrations | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/timeutil" | ||||
| 
 | ||||
| 	"xorm.io/xorm" | ||||
| ) | ||||
| 
 | ||||
| type SystemSetting struct { | ||||
| 	ID           int64              `xorm:"pk autoincr"` | ||||
| 	SettingKey   string             `xorm:"varchar(255) unique"` // ensure key is always lowercase | ||||
| 	SettingValue string             `xorm:"text"` | ||||
| 	Version      int                `xorm:"version"` // prevent to override | ||||
| 	Created      timeutil.TimeStamp `xorm:"created"` | ||||
| 	Updated      timeutil.TimeStamp `xorm:"updated"` | ||||
| } | ||||
| 
 | ||||
| func insertSettingsIfNotExist(x *xorm.Engine, sysSettings []*SystemSetting) error { | ||||
| 	sess := x.NewSession() | ||||
| 	defer sess.Close() | ||||
| 	if err := sess.Begin(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	for _, setting := range sysSettings { | ||||
| 		exist, err := sess.Table("system_setting").Where("setting_key=?", setting.SettingKey).Exist() | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if !exist { | ||||
| 			if _, err := sess.Insert(setting); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return sess.Commit() | ||||
| } | ||||
| 
 | ||||
| func createSystemSettingsTable(x *xorm.Engine) error { | ||||
| 	if err := x.Sync2(new(SystemSetting)); err != nil { | ||||
| 		return fmt.Errorf("sync2: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// migrate xx to database | ||||
| 	sysSettings := []*SystemSetting{ | ||||
| 		{ | ||||
| 			SettingKey:   "picture.disable_gravatar", | ||||
| 			SettingValue: strconv.FormatBool(setting.DisableGravatar), | ||||
| 		}, | ||||
| 		{ | ||||
| 			SettingKey:   "picture.enable_federated_avatar", | ||||
| 			SettingValue: strconv.FormatBool(setting.EnableFederatedAvatar), | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	return insertSettingsIfNotExist(x, sysSettings) | ||||
| } | ||||
| @ -22,6 +22,7 @@ import ( | ||||
| 	access_model "code.gitea.io/gitea/models/perm/access" | ||||
| 	project_model "code.gitea.io/gitea/models/project" | ||||
| 	repo_model "code.gitea.io/gitea/models/repo" | ||||
| 	system_model "code.gitea.io/gitea/models/system" | ||||
| 	"code.gitea.io/gitea/models/unit" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/models/webhook" | ||||
| @ -32,9 +33,13 @@ import ( | ||||
| 	"xorm.io/builder" | ||||
| ) | ||||
| 
 | ||||
| // NewRepoContext creates a new repository context | ||||
| func NewRepoContext() { | ||||
| // ItemsPerPage maximum items per page in forks, watchers and stars of a repo | ||||
| var ItemsPerPage = 40 | ||||
| 
 | ||||
| // Init initialize model | ||||
| func Init() error { | ||||
| 	unit.LoadUnitConfig() | ||||
| 	return system_model.Init() | ||||
| } | ||||
| 
 | ||||
| // DeleteRepository deletes a repository for a user or organization. | ||||
| @ -267,36 +272,36 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error { | ||||
| 
 | ||||
| 	// Remove repository files. | ||||
| 	repoPath := repo.RepoPath() | ||||
| 	admin_model.RemoveAllWithNotice(db.DefaultContext, "Delete repository files", repoPath) | ||||
| 	system_model.RemoveAllWithNotice(db.DefaultContext, "Delete repository files", repoPath) | ||||
| 
 | ||||
| 	// Remove wiki files | ||||
| 	if repo.HasWiki() { | ||||
| 		admin_model.RemoveAllWithNotice(db.DefaultContext, "Delete repository wiki", repo.WikiPath()) | ||||
| 		system_model.RemoveAllWithNotice(db.DefaultContext, "Delete repository wiki", repo.WikiPath()) | ||||
| 	} | ||||
| 
 | ||||
| 	// Remove archives | ||||
| 	for _, archive := range archivePaths { | ||||
| 		admin_model.RemoveStorageWithNotice(db.DefaultContext, storage.RepoArchives, "Delete repo archive file", archive) | ||||
| 		system_model.RemoveStorageWithNotice(db.DefaultContext, storage.RepoArchives, "Delete repo archive file", archive) | ||||
| 	} | ||||
| 
 | ||||
| 	// Remove lfs objects | ||||
| 	for _, lfsObj := range lfsPaths { | ||||
| 		admin_model.RemoveStorageWithNotice(db.DefaultContext, storage.LFS, "Delete orphaned LFS file", lfsObj) | ||||
| 		system_model.RemoveStorageWithNotice(db.DefaultContext, storage.LFS, "Delete orphaned LFS file", lfsObj) | ||||
| 	} | ||||
| 
 | ||||
| 	// Remove issue attachment files. | ||||
| 	for _, attachment := range attachmentPaths { | ||||
| 		admin_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete issue attachment", attachment) | ||||
| 		system_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete issue attachment", attachment) | ||||
| 	} | ||||
| 
 | ||||
| 	// Remove release attachment files. | ||||
| 	for _, releaseAttachment := range releaseAttachments { | ||||
| 		admin_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete release attachment", releaseAttachment) | ||||
| 		system_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete release attachment", releaseAttachment) | ||||
| 	} | ||||
| 
 | ||||
| 	// Remove attachment with no issue_id and release_id. | ||||
| 	for _, newAttachment := range newAttachmentPaths { | ||||
| 		admin_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete issue attachment", newAttachment) | ||||
| 		system_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete issue attachment", newAttachment) | ||||
| 	} | ||||
| 
 | ||||
| 	if len(repo.Avatar) > 0 { | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package appstate | ||||
| package system | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
							
								
								
									
										21
									
								
								models/system/main_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								models/system/main_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| // Copyright 2020 The Gitea Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package system_test | ||||
| 
 | ||||
| import ( | ||||
| 	"path/filepath" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/models/unittest" | ||||
| 
 | ||||
| 	_ "code.gitea.io/gitea/models"        // register models | ||||
| 	_ "code.gitea.io/gitea/models/system" // register models of system | ||||
| ) | ||||
| 
 | ||||
| func TestMain(m *testing.M) { | ||||
| 	unittest.MainTest(m, &unittest.TestOptions{ | ||||
| 		GiteaRootPath: filepath.Join("..", ".."), | ||||
| 	}) | ||||
| } | ||||
| @ -2,7 +2,7 @@ | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package admin | ||||
| package system | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
							
								
								
									
										117
									
								
								models/system/notice_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								models/system/notice_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,117 @@ | ||||
| // Copyright 2017 The Gitea Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package system_test | ||||
| 
 | ||||
| import ( | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/models/db" | ||||
| 	"code.gitea.io/gitea/models/system" | ||||
| 	"code.gitea.io/gitea/models/unittest" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
| 
 | ||||
| func TestNotice_TrStr(t *testing.T) { | ||||
| 	notice := &system.Notice{ | ||||
| 		Type:        system.NoticeRepository, | ||||
| 		Description: "test description", | ||||
| 	} | ||||
| 	assert.Equal(t, "admin.notices.type_1", notice.TrStr()) | ||||
| } | ||||
| 
 | ||||
| func TestCreateNotice(t *testing.T) { | ||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
| 
 | ||||
| 	noticeBean := &system.Notice{ | ||||
| 		Type:        system.NoticeRepository, | ||||
| 		Description: "test description", | ||||
| 	} | ||||
| 	unittest.AssertNotExistsBean(t, noticeBean) | ||||
| 	assert.NoError(t, system.CreateNotice(db.DefaultContext, noticeBean.Type, noticeBean.Description)) | ||||
| 	unittest.AssertExistsAndLoadBean(t, noticeBean) | ||||
| } | ||||
| 
 | ||||
| func TestCreateRepositoryNotice(t *testing.T) { | ||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
| 
 | ||||
| 	noticeBean := &system.Notice{ | ||||
| 		Type:        system.NoticeRepository, | ||||
| 		Description: "test description", | ||||
| 	} | ||||
| 	unittest.AssertNotExistsBean(t, noticeBean) | ||||
| 	assert.NoError(t, system.CreateRepositoryNotice(noticeBean.Description)) | ||||
| 	unittest.AssertExistsAndLoadBean(t, noticeBean) | ||||
| } | ||||
| 
 | ||||
| // TODO TestRemoveAllWithNotice | ||||
| 
 | ||||
| func TestCountNotices(t *testing.T) { | ||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
| 	assert.Equal(t, int64(3), system.CountNotices()) | ||||
| } | ||||
| 
 | ||||
| func TestNotices(t *testing.T) { | ||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
| 
 | ||||
| 	notices, err := system.Notices(1, 2) | ||||
| 	assert.NoError(t, err) | ||||
| 	if assert.Len(t, notices, 2) { | ||||
| 		assert.Equal(t, int64(3), notices[0].ID) | ||||
| 		assert.Equal(t, int64(2), notices[1].ID) | ||||
| 	} | ||||
| 
 | ||||
| 	notices, err = system.Notices(2, 2) | ||||
| 	assert.NoError(t, err) | ||||
| 	if assert.Len(t, notices, 1) { | ||||
| 		assert.Equal(t, int64(1), notices[0].ID) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestDeleteNotice(t *testing.T) { | ||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
| 
 | ||||
| 	unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 3}) | ||||
| 	assert.NoError(t, system.DeleteNotice(3)) | ||||
| 	unittest.AssertNotExistsBean(t, &system.Notice{ID: 3}) | ||||
| } | ||||
| 
 | ||||
| func TestDeleteNotices(t *testing.T) { | ||||
| 	// delete a non-empty range | ||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
| 
 | ||||
| 	unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 1}) | ||||
| 	unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 2}) | ||||
| 	unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 3}) | ||||
| 	assert.NoError(t, system.DeleteNotices(1, 2)) | ||||
| 	unittest.AssertNotExistsBean(t, &system.Notice{ID: 1}) | ||||
| 	unittest.AssertNotExistsBean(t, &system.Notice{ID: 2}) | ||||
| 	unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 3}) | ||||
| } | ||||
| 
 | ||||
| func TestDeleteNotices2(t *testing.T) { | ||||
| 	// delete an empty range | ||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
| 
 | ||||
| 	unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 1}) | ||||
| 	unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 2}) | ||||
| 	unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 3}) | ||||
| 	assert.NoError(t, system.DeleteNotices(3, 2)) | ||||
| 	unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 1}) | ||||
| 	unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 2}) | ||||
| 	unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 3}) | ||||
| } | ||||
| 
 | ||||
| func TestDeleteNoticesByIDs(t *testing.T) { | ||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
| 
 | ||||
| 	unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 1}) | ||||
| 	unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 2}) | ||||
| 	unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 3}) | ||||
| 	assert.NoError(t, system.DeleteNoticesByIDs([]int64{1, 3})) | ||||
| 	unittest.AssertNotExistsBean(t, &system.Notice{ID: 1}) | ||||
| 	unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 2}) | ||||
| 	unittest.AssertNotExistsBean(t, &system.Notice{ID: 3}) | ||||
| } | ||||
							
								
								
									
										261
									
								
								models/system/setting.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										261
									
								
								models/system/setting.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,261 @@ | ||||
| // Copyright 2021 The Gitea Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package system | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"net/url" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/models/db" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/timeutil" | ||||
| 
 | ||||
| 	"strk.kbt.io/projects/go/libravatar" | ||||
| 	"xorm.io/builder" | ||||
| ) | ||||
| 
 | ||||
| // Setting is a key value store of user settings | ||||
| type Setting struct { | ||||
| 	ID           int64              `xorm:"pk autoincr"` | ||||
| 	SettingKey   string             `xorm:"varchar(255) unique"` // ensure key is always lowercase | ||||
| 	SettingValue string             `xorm:"text"` | ||||
| 	Version      int                `xorm:"version"` // prevent to override | ||||
| 	Created      timeutil.TimeStamp `xorm:"created"` | ||||
| 	Updated      timeutil.TimeStamp `xorm:"updated"` | ||||
| } | ||||
| 
 | ||||
| // TableName sets the table name for the settings struct | ||||
| func (s *Setting) TableName() string { | ||||
| 	return "system_setting" | ||||
| } | ||||
| 
 | ||||
| func (s *Setting) GetValueBool() bool { | ||||
| 	b, _ := strconv.ParseBool(s.SettingValue) | ||||
| 	return b | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	db.RegisterModel(new(Setting)) | ||||
| } | ||||
| 
 | ||||
| // ErrSettingIsNotExist represents an error that a setting is not exist with special key | ||||
| type ErrSettingIsNotExist struct { | ||||
| 	Key string | ||||
| } | ||||
| 
 | ||||
| // Error implements error | ||||
| func (err ErrSettingIsNotExist) Error() string { | ||||
| 	return fmt.Sprintf("System setting[%s] is not exist", err.Key) | ||||
| } | ||||
| 
 | ||||
| // IsErrSettingIsNotExist return true if err is ErrSettingIsNotExist | ||||
| func IsErrSettingIsNotExist(err error) bool { | ||||
| 	_, ok := err.(ErrSettingIsNotExist) | ||||
| 	return ok | ||||
| } | ||||
| 
 | ||||
| // ErrDataExpired represents an error that update a record which has been updated by another thread | ||||
| type ErrDataExpired struct { | ||||
| 	Key string | ||||
| } | ||||
| 
 | ||||
| // Error implements error | ||||
| func (err ErrDataExpired) Error() string { | ||||
| 	return fmt.Sprintf("System setting[%s] has been updated by another thread", err.Key) | ||||
| } | ||||
| 
 | ||||
| // IsErrDataExpired return true if err is ErrDataExpired | ||||
| func IsErrDataExpired(err error) bool { | ||||
| 	_, ok := err.(ErrDataExpired) | ||||
| 	return ok | ||||
| } | ||||
| 
 | ||||
| // GetSetting returns specific setting | ||||
| func GetSetting(key string) (*Setting, error) { | ||||
| 	v, err := GetSettings([]string{key}) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if len(v) == 0 { | ||||
| 		return nil, ErrSettingIsNotExist{key} | ||||
| 	} | ||||
| 	return v[key], nil | ||||
| } | ||||
| 
 | ||||
| // GetSettings returns specific settings | ||||
| func GetSettings(keys []string) (map[string]*Setting, error) { | ||||
| 	for i := 0; i < len(keys); i++ { | ||||
| 		keys[i] = strings.ToLower(keys[i]) | ||||
| 	} | ||||
| 	settings := make([]*Setting, 0, len(keys)) | ||||
| 	if err := db.GetEngine(db.DefaultContext). | ||||
| 		Where(builder.In("setting_key", keys)). | ||||
| 		Find(&settings); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	settingsMap := make(map[string]*Setting) | ||||
| 	for _, s := range settings { | ||||
| 		settingsMap[s.SettingKey] = s | ||||
| 	} | ||||
| 	return settingsMap, nil | ||||
| } | ||||
| 
 | ||||
| type AllSettings map[string]*Setting | ||||
| 
 | ||||
| func (settings AllSettings) Get(key string) Setting { | ||||
| 	if v, ok := settings[key]; ok { | ||||
| 		return *v | ||||
| 	} | ||||
| 	return Setting{} | ||||
| } | ||||
| 
 | ||||
| func (settings AllSettings) GetBool(key string) bool { | ||||
| 	b, _ := strconv.ParseBool(settings.Get(key).SettingValue) | ||||
| 	return b | ||||
| } | ||||
| 
 | ||||
| func (settings AllSettings) GetVersion(key string) int { | ||||
| 	return settings.Get(key).Version | ||||
| } | ||||
| 
 | ||||
| // GetAllSettings returns all settings from user | ||||
| func GetAllSettings() (AllSettings, error) { | ||||
| 	settings := make([]*Setting, 0, 5) | ||||
| 	if err := db.GetEngine(db.DefaultContext). | ||||
| 		Find(&settings); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	settingsMap := make(map[string]*Setting) | ||||
| 	for _, s := range settings { | ||||
| 		settingsMap[s.SettingKey] = s | ||||
| 	} | ||||
| 	return settingsMap, nil | ||||
| } | ||||
| 
 | ||||
| // DeleteSetting deletes a specific setting for a user | ||||
| func DeleteSetting(setting *Setting) error { | ||||
| 	_, err := db.GetEngine(db.DefaultContext).Delete(setting) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func SetSettingNoVersion(key, value string) error { | ||||
| 	s, err := GetSetting(key) | ||||
| 	if IsErrSettingIsNotExist(err) { | ||||
| 		return SetSetting(&Setting{ | ||||
| 			SettingKey:   key, | ||||
| 			SettingValue: value, | ||||
| 		}) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	s.SettingValue = value | ||||
| 	return SetSetting(s) | ||||
| } | ||||
| 
 | ||||
| // SetSetting updates a users' setting for a specific key | ||||
| func SetSetting(setting *Setting) error { | ||||
| 	if err := upsertSettingValue(strings.ToLower(setting.SettingKey), setting.SettingValue, setting.Version); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	setting.Version++ | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func upsertSettingValue(key, value string, version int) error { | ||||
| 	return db.WithTx(func(ctx context.Context) error { | ||||
| 		e := db.GetEngine(ctx) | ||||
| 
 | ||||
| 		// here we use a general method to do a safe upsert for different databases (and most transaction levels) | ||||
| 		// 1. try to UPDATE the record and acquire the transaction write lock | ||||
| 		//    if UPDATE returns non-zero rows are changed, OK, the setting is saved correctly | ||||
| 		//    if UPDATE returns "0 rows changed", two possibilities: (a) record doesn't exist  (b) value is not changed | ||||
| 		// 2. do a SELECT to check if the row exists or not (we already have the transaction lock) | ||||
| 		// 3. if the row doesn't exist, do an INSERT (we are still protected by the transaction lock, so it's safe) | ||||
| 		// | ||||
| 		// to optimize the SELECT in step 2, we can use an extra column like `revision=revision+1` | ||||
| 		//    to make sure the UPDATE always returns a non-zero value for existing (unchanged) records. | ||||
| 
 | ||||
| 		res, err := e.Exec("UPDATE system_setting SET setting_value=?, version = version+1 WHERE setting_key=? AND version=?", value, key, version) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		rows, _ := res.RowsAffected() | ||||
| 		if rows > 0 { | ||||
| 			// the existing row is updated, so we can return | ||||
| 			return nil | ||||
| 		} | ||||
| 
 | ||||
| 		// in case the value isn't changed, update would return 0 rows changed, so we need this check | ||||
| 		has, err := e.Exist(&Setting{SettingKey: key}) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if has { | ||||
| 			return ErrDataExpired{Key: key} | ||||
| 		} | ||||
| 
 | ||||
| 		// if no existing row, insert a new row | ||||
| 		_, err = e.Insert(&Setting{SettingKey: key, SettingValue: value}) | ||||
| 		return err | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	GravatarSourceURL *url.URL | ||||
| 	LibravatarService *libravatar.Libravatar | ||||
| ) | ||||
| 
 | ||||
| func Init() error { | ||||
| 	var disableGravatar bool | ||||
| 	disableGravatarSetting, err := GetSetting(KeyPictureDisableGravatar) | ||||
| 	if IsErrSettingIsNotExist(err) { | ||||
| 		disableGravatar = setting.GetDefaultDisableGravatar() | ||||
| 		disableGravatarSetting = &Setting{SettingValue: strconv.FormatBool(disableGravatar)} | ||||
| 	} else if err != nil { | ||||
| 		return err | ||||
| 	} else { | ||||
| 		disableGravatar = disableGravatarSetting.GetValueBool() | ||||
| 	} | ||||
| 
 | ||||
| 	var enableFederatedAvatar bool | ||||
| 	enableFederatedAvatarSetting, err := GetSetting(KeyPictureEnableFederatedAvatar) | ||||
| 	if IsErrSettingIsNotExist(err) { | ||||
| 		enableFederatedAvatar = setting.GetDefaultEnableFederatedAvatar(disableGravatar) | ||||
| 		enableFederatedAvatarSetting = &Setting{SettingValue: strconv.FormatBool(enableFederatedAvatar)} | ||||
| 	} else if err != nil { | ||||
| 		return err | ||||
| 	} else { | ||||
| 		enableFederatedAvatar = disableGravatarSetting.GetValueBool() | ||||
| 	} | ||||
| 
 | ||||
| 	if setting.OfflineMode { | ||||
| 		disableGravatar = true | ||||
| 		enableFederatedAvatar = false | ||||
| 	} | ||||
| 
 | ||||
| 	if disableGravatar || !enableFederatedAvatar { | ||||
| 		var err error | ||||
| 		GravatarSourceURL, err = url.Parse(setting.GravatarSource) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("Failed to parse Gravatar URL(%s): %v", setting.GravatarSource, err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if enableFederatedAvatarSetting.GetValueBool() { | ||||
| 		LibravatarService = libravatar.New() | ||||
| 		if GravatarSourceURL.Scheme == "https" { | ||||
| 			LibravatarService.SetUseHTTPS(true) | ||||
| 			LibravatarService.SetSecureFallbackHost(GravatarSourceURL.Host) | ||||
| 		} else { | ||||
| 			LibravatarService.SetUseHTTPS(false) | ||||
| 			LibravatarService.SetFallbackHost(GravatarSourceURL.Host) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										11
									
								
								models/system/setting_key.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								models/system/setting_key.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| // Copyright 2022 The Gitea Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package system | ||||
| 
 | ||||
| // enumerate all system setting keys | ||||
| const ( | ||||
| 	KeyPictureDisableGravatar       = "picture.disable_gravatar" | ||||
| 	KeyPictureEnableFederatedAvatar = "picture.enable_federated_avatar" | ||||
| ) | ||||
							
								
								
									
										53
									
								
								models/system/setting_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								models/system/setting_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | ||||
| // Copyright 2021 The Gitea Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package system_test | ||||
| 
 | ||||
| import ( | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/models/system" | ||||
| 	"code.gitea.io/gitea/models/unittest" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
| 
 | ||||
| func TestSettings(t *testing.T) { | ||||
| 	keyName := "server.LFS_LOCKS_PAGING_NUM" | ||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
| 
 | ||||
| 	newSetting := &system.Setting{SettingKey: keyName, SettingValue: "50"} | ||||
| 
 | ||||
| 	// create setting | ||||
| 	err := system.SetSetting(newSetting) | ||||
| 	assert.NoError(t, err) | ||||
| 	// test about saving unchanged values | ||||
| 	err = system.SetSetting(newSetting) | ||||
| 	assert.NoError(t, err) | ||||
| 
 | ||||
| 	// get specific setting | ||||
| 	settings, err := system.GetSettings([]string{keyName}) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Len(t, settings, 1) | ||||
| 	assert.EqualValues(t, newSetting.SettingValue, settings[strings.ToLower(keyName)].SettingValue) | ||||
| 
 | ||||
| 	// updated setting | ||||
| 	updatedSetting := &system.Setting{SettingKey: keyName, SettingValue: "100", Version: newSetting.Version} | ||||
| 	err = system.SetSetting(updatedSetting) | ||||
| 	assert.NoError(t, err) | ||||
| 
 | ||||
| 	// get all settings | ||||
| 	settings, err = system.GetAllSettings() | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Len(t, settings, 3) | ||||
| 	assert.EqualValues(t, updatedSetting.SettingValue, settings[strings.ToLower(updatedSetting.SettingKey)].SettingValue) | ||||
| 
 | ||||
| 	// delete setting | ||||
| 	err = system.DeleteSetting(&system.Setting{SettingKey: strings.ToLower(keyName)}) | ||||
| 	assert.NoError(t, err) | ||||
| 	settings, err = system.GetAllSettings() | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Len(t, settings, 2) | ||||
| } | ||||
| @ -7,12 +7,12 @@ package unittest | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"net/url" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/models/db" | ||||
| 	system_model "code.gitea.io/gitea/models/system" | ||||
| 	"code.gitea.io/gitea/modules/base" | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| @ -91,10 +91,8 @@ func MainTest(m *testing.M, testOpts *TestOptions) { | ||||
| 	setting.AppDataPath = appDataPath | ||||
| 	setting.AppWorkPath = testOpts.GiteaRootPath | ||||
| 	setting.StaticRootPath = testOpts.GiteaRootPath | ||||
| 	setting.GravatarSourceURL, err = url.Parse("https://secure.gravatar.com/avatar/") | ||||
| 	if err != nil { | ||||
| 		fatalTestError("url.Parse: %v\n", err) | ||||
| 	} | ||||
| 	setting.GravatarSource = "https://secure.gravatar.com/avatar/" | ||||
| 
 | ||||
| 	setting.Attachment.Storage.Path = filepath.Join(setting.AppDataPath, "attachments") | ||||
| 
 | ||||
| 	setting.LFS.Storage.Path = filepath.Join(setting.AppDataPath, "lfs") | ||||
| @ -112,6 +110,9 @@ func MainTest(m *testing.M, testOpts *TestOptions) { | ||||
| 	if err = storage.Init(); err != nil { | ||||
| 		fatalTestError("storage.Init: %v\n", err) | ||||
| 	} | ||||
| 	if err = system_model.Init(); err != nil { | ||||
| 		fatalTestError("models.Init: %v\n", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if err = util.RemoveAll(repoRootPath); err != nil { | ||||
| 		fatalTestError("util.RemoveAll: %v\n", err) | ||||
|  | ||||
| @ -14,6 +14,7 @@ import ( | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/models/avatars" | ||||
| 	"code.gitea.io/gitea/models/db" | ||||
| 	system_model "code.gitea.io/gitea/models/system" | ||||
| 	"code.gitea.io/gitea/modules/avatar" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| @ -67,10 +68,16 @@ func (u *User) AvatarLinkWithSize(size int) string { | ||||
| 	useLocalAvatar := false | ||||
| 	autoGenerateAvatar := false | ||||
| 
 | ||||
| 	var disableGravatar bool | ||||
| 	disableGravatarSetting, _ := system_model.GetSetting(system_model.KeyPictureDisableGravatar) | ||||
| 	if disableGravatarSetting != nil { | ||||
| 		disableGravatar = disableGravatarSetting.GetValueBool() | ||||
| 	} | ||||
| 
 | ||||
| 	switch { | ||||
| 	case u.UseCustomAvatar: | ||||
| 		useLocalAvatar = true | ||||
| 	case setting.DisableGravatar, setting.OfflineMode: | ||||
| 	case disableGravatar, setting.OfflineMode: | ||||
| 		useLocalAvatar = true | ||||
| 		autoGenerateAvatar = true | ||||
| 	} | ||||
|  | ||||
| @ -31,6 +31,34 @@ func init() { | ||||
| 	db.RegisterModel(new(Setting)) | ||||
| } | ||||
| 
 | ||||
| // ErrUserSettingIsNotExist represents an error that a setting is not exist with special key | ||||
| type ErrUserSettingIsNotExist struct { | ||||
| 	Key string | ||||
| } | ||||
| 
 | ||||
| // Error implements error | ||||
| func (err ErrUserSettingIsNotExist) Error() string { | ||||
| 	return fmt.Sprintf("Setting[%s] is not exist", err.Key) | ||||
| } | ||||
| 
 | ||||
| // IsErrUserSettingIsNotExist return true if err is ErrSettingIsNotExist | ||||
| func IsErrUserSettingIsNotExist(err error) bool { | ||||
| 	_, ok := err.(ErrUserSettingIsNotExist) | ||||
| 	return ok | ||||
| } | ||||
| 
 | ||||
| // GetSetting returns specific setting | ||||
| func GetSetting(uid int64, key string) (*Setting, error) { | ||||
| 	v, err := GetUserSettings(uid, []string{key}) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if len(v) == 0 { | ||||
| 		return nil, ErrUserSettingIsNotExist{key} | ||||
| 	} | ||||
| 	return v[key], nil | ||||
| } | ||||
| 
 | ||||
| // GetUserSettings returns specific settings from user | ||||
| func GetUserSettings(uid int64, keys []string) (map[string]*Setting, error) { | ||||
| 	settings := make([]*Setting, 0, len(keys)) | ||||
|  | ||||
| @ -11,8 +11,10 @@ import ( | ||||
| 	"time" | ||||
| 
 | ||||
| 	repo_model "code.gitea.io/gitea/models/repo" | ||||
| 	system_model "code.gitea.io/gitea/models/system" | ||||
| 	"code.gitea.io/gitea/models/unittest" | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
| @ -100,6 +102,14 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) { | ||||
| 	assert.EqualValues(t, []string{"readme.md"}, headCommit.Modified) | ||||
| } | ||||
| 
 | ||||
| func enableGravatar(t *testing.T) { | ||||
| 	err := system_model.SetSettingNoVersion(system_model.KeyPictureDisableGravatar, "false") | ||||
| 	assert.NoError(t, err) | ||||
| 	setting.GravatarSource = "https://secure.gravatar.com/avatar" | ||||
| 	err = system_model.Init() | ||||
| 	assert.NoError(t, err) | ||||
| } | ||||
| 
 | ||||
| func TestPushCommits_AvatarLink(t *testing.T) { | ||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
| 
 | ||||
| @ -123,6 +133,8 @@ func TestPushCommits_AvatarLink(t *testing.T) { | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	enableGravatar(t) | ||||
| 
 | ||||
| 	assert.Equal(t, | ||||
| 		"https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon&s=84", | ||||
| 		pushCommits.AvatarLink("user2@example.com")) | ||||
|  | ||||
| @ -4,14 +4,6 @@ | ||||
| 
 | ||||
| package setting | ||||
| 
 | ||||
| import ( | ||||
| 	"net/url" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 
 | ||||
| 	"strk.kbt.io/projects/go/libravatar" | ||||
| ) | ||||
| 
 | ||||
| // settings | ||||
| var ( | ||||
| 	// Picture settings | ||||
| @ -30,10 +22,8 @@ var ( | ||||
| 	} | ||||
| 
 | ||||
| 	GravatarSource        string | ||||
| 	GravatarSourceURL     *url.URL | ||||
| 	DisableGravatar       bool | ||||
| 	EnableFederatedAvatar bool | ||||
| 	LibravatarService     *libravatar.Libravatar | ||||
| 	DisableGravatar       bool // Depreciated: migrated to database | ||||
| 	EnableFederatedAvatar bool // Depreciated: migrated to database | ||||
| 
 | ||||
| 	RepoAvatar = struct { | ||||
| 		Storage | ||||
| @ -69,38 +59,30 @@ func newPictureService() { | ||||
| 	default: | ||||
| 		GravatarSource = source | ||||
| 	} | ||||
| 	DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool() | ||||
| 	EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(!InstallLock) | ||||
| 	if OfflineMode { | ||||
| 		DisableGravatar = true | ||||
| 		EnableFederatedAvatar = false | ||||
| 	} | ||||
| 	if DisableGravatar { | ||||
| 		EnableFederatedAvatar = false | ||||
| 	} | ||||
| 	if EnableFederatedAvatar || !DisableGravatar { | ||||
| 		var err error | ||||
| 		GravatarSourceURL, err = url.Parse(GravatarSource) | ||||
| 		if err != nil { | ||||
| 			log.Fatal("Failed to parse Gravatar URL(%s): %v", | ||||
| 				GravatarSource, err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if EnableFederatedAvatar { | ||||
| 		LibravatarService = libravatar.New() | ||||
| 		if GravatarSourceURL.Scheme == "https" { | ||||
| 			LibravatarService.SetUseHTTPS(true) | ||||
| 			LibravatarService.SetSecureFallbackHost(GravatarSourceURL.Host) | ||||
| 		} else { | ||||
| 			LibravatarService.SetUseHTTPS(false) | ||||
| 			LibravatarService.SetFallbackHost(GravatarSourceURL.Host) | ||||
| 		} | ||||
| 	} | ||||
| 	DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool(GetDefaultDisableGravatar()) | ||||
| 	deprecatedSettingDB("", "DISABLE_GRAVATAR") | ||||
| 	EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(GetDefaultEnableFederatedAvatar(DisableGravatar)) | ||||
| 	deprecatedSettingDB("", "ENABLE_FEDERATED_AVATAR") | ||||
| 
 | ||||
| 	newRepoAvatarService() | ||||
| } | ||||
| 
 | ||||
| func GetDefaultDisableGravatar() bool { | ||||
| 	return !OfflineMode | ||||
| } | ||||
| 
 | ||||
| func GetDefaultEnableFederatedAvatar(disableGravatar bool) bool { | ||||
| 	v := !InstallLock | ||||
| 	if OfflineMode { | ||||
| 		v = false | ||||
| 	} | ||||
| 	if disableGravatar { | ||||
| 		v = false | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
| 
 | ||||
| func newRepoAvatarService() { | ||||
| 	sec := Cfg.Section("picture") | ||||
| 
 | ||||
|  | ||||
| @ -606,6 +606,13 @@ func deprecatedSetting(oldSection, oldKey, newSection, newKey string) { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini | ||||
| func deprecatedSettingDB(oldSection, oldKey string) { | ||||
| 	if Cfg.Section(oldSection).HasKey(oldKey) { | ||||
| 		log.Error("Deprecated `[%s]` `%s` present which has been copied to database table sys_setting", oldSection, oldKey) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // loadFromConf initializes configuration context. | ||||
| // NOTE: do not print any log except error. | ||||
| func loadFromConf(allowEmpty bool, extraConfig string) { | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package appstate | ||||
| package system | ||||
| 
 | ||||
| // StateStore is the interface to get/set app state items | ||||
| type StateStore interface { | ||||
| @ -2,7 +2,7 @@ | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package appstate | ||||
| package system | ||||
| 
 | ||||
| import ( | ||||
| 	"path/filepath" | ||||
| @ -2,10 +2,10 @@ | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package appstate | ||||
| package system | ||||
| 
 | ||||
| import ( | ||||
| 	"code.gitea.io/gitea/models/appstate" | ||||
| 	"code.gitea.io/gitea/models/system" | ||||
| 	"code.gitea.io/gitea/modules/json" | ||||
| 
 | ||||
| 	"github.com/yuin/goldmark/util" | ||||
| @ -16,7 +16,7 @@ type DBStore struct{} | ||||
| 
 | ||||
| // Get reads the state item | ||||
| func (f *DBStore) Get(item StateItem) error { | ||||
| 	content, err := appstate.GetAppStateContent(item.Name()) | ||||
| 	content, err := system.GetAppStateContent(item.Name()) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @ -32,5 +32,5 @@ func (f *DBStore) Set(item StateItem) error { | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return appstate.SaveAppStateContent(item.Name(), util.BytesToReadOnlyString(b)) | ||||
| 	return system.SaveAppStateContent(item.Name(), util.BytesToReadOnlyString(b)) | ||||
| } | ||||
| @ -2,7 +2,7 @@ | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package appstate | ||||
| package system | ||||
| 
 | ||||
| // RuntimeState contains app state for runtime, and we can save remote version for update checker here in future | ||||
| type RuntimeState struct { | ||||
							
								
								
									
										46
									
								
								modules/system/setting.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								modules/system/setting.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | ||||
| // Copyright 2021 The Gitea Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package system | ||||
| 
 | ||||
| import ( | ||||
| 	"strconv" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/models/system" | ||||
| 	"code.gitea.io/gitea/modules/cache" | ||||
| ) | ||||
| 
 | ||||
| func genKey(key string) string { | ||||
| 	return "system.setting." + key | ||||
| } | ||||
| 
 | ||||
| // GetSetting returns the setting value via the key | ||||
| func GetSetting(key string) (string, error) { | ||||
| 	return cache.GetString(genKey(key), func() (string, error) { | ||||
| 		res, err := system.GetSetting(key) | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
| 		return res.SettingValue, nil | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // GetSettingBool return bool value of setting, | ||||
| // none existing keys and errors are ignored and result in false | ||||
| func GetSettingBool(key string) bool { | ||||
| 	s, _ := GetSetting(key) | ||||
| 	b, _ := strconv.ParseBool(s) | ||||
| 	return b | ||||
| } | ||||
| 
 | ||||
| // SetSetting sets the setting value | ||||
| func SetSetting(key, value string, version int) error { | ||||
| 	cache.Remove(genKey(key)) | ||||
| 
 | ||||
| 	return system.SetSetting(&system.Setting{ | ||||
| 		SettingKey:   key, | ||||
| 		SettingValue: value, | ||||
| 		Version:      version, | ||||
| 	}) | ||||
| } | ||||
							
								
								
									
										34
									
								
								modules/system/user_setting.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								modules/system/user_setting.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | ||||
| // Copyright 2021 The Gitea Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package system | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/modules/cache" | ||||
| ) | ||||
| 
 | ||||
| func genUserKey(userID int64, key string) string { | ||||
| 	return fmt.Sprintf("user_%d.setting.%s", userID, key) | ||||
| } | ||||
| 
 | ||||
| // GetUserSetting returns the user setting value via the key | ||||
| func GetUserSetting(userID int64, key string) (string, error) { | ||||
| 	return cache.GetString(genUserKey(userID, key), func() (string, error) { | ||||
| 		res, err := user.GetSetting(userID, key) | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
| 		return res.SettingValue, nil | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // SetUserSetting sets the user setting value | ||||
| func SetUserSetting(userID int64, key, value string) error { | ||||
| 	cache.Remove(genUserKey(userID, key)) | ||||
| 
 | ||||
| 	return user.SetUserSetting(userID, key, value) | ||||
| } | ||||
| @ -29,6 +29,7 @@ import ( | ||||
| 	issues_model "code.gitea.io/gitea/models/issues" | ||||
| 	"code.gitea.io/gitea/models/organization" | ||||
| 	repo_model "code.gitea.io/gitea/models/repo" | ||||
| 	system_model "code.gitea.io/gitea/models/system" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/modules/base" | ||||
| 	"code.gitea.io/gitea/modules/emoji" | ||||
| @ -41,6 +42,7 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/repository" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/svg" | ||||
| 	system_module "code.gitea.io/gitea/modules/system" | ||||
| 	"code.gitea.io/gitea/modules/timeutil" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 	"code.gitea.io/gitea/services/gitdiff" | ||||
| @ -85,7 +87,7 @@ func NewFuncMap() []template.FuncMap { | ||||
| 			return setting.AssetVersion | ||||
| 		}, | ||||
| 		"DisableGravatar": func() bool { | ||||
| 			return setting.DisableGravatar | ||||
| 			return system_module.GetSettingBool(system_model.KeyPictureDisableGravatar) | ||||
| 		}, | ||||
| 		"DefaultShowFullName": func() bool { | ||||
| 			return setting.UI.DefaultShowFullName | ||||
|  | ||||
| @ -8,10 +8,10 @@ import ( | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/modules/appstate" | ||||
| 	"code.gitea.io/gitea/modules/json" | ||||
| 	"code.gitea.io/gitea/modules/proxy" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/system" | ||||
| 
 | ||||
| 	"github.com/hashicorp/go-version" | ||||
| ) | ||||
| @ -64,13 +64,13 @@ func GiteaUpdateChecker(httpEndpoint string) error { | ||||
| 
 | ||||
| // UpdateRemoteVersion updates the latest available version of Gitea | ||||
| func UpdateRemoteVersion(version string) (err error) { | ||||
| 	return appstate.AppState.Set(&CheckerState{LatestVersion: version}) | ||||
| 	return system.AppState.Set(&CheckerState{LatestVersion: version}) | ||||
| } | ||||
| 
 | ||||
| // GetRemoteVersion returns the current remote version (or currently installed version if fail to fetch from DB) | ||||
| func GetRemoteVersion() string { | ||||
| 	item := new(CheckerState) | ||||
| 	if err := appstate.AppState.Get(item); err != nil { | ||||
| 	if err := system.AppState.Get(item); err != nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return item.LatestVersion | ||||
|  | ||||
| @ -2879,6 +2879,9 @@ config.access_log_template = Template | ||||
| config.xorm_log_mode = XORM Log Mode | ||||
| config.xorm_log_sql = Log SQL | ||||
| 
 | ||||
| config.get_setting_failed = Get setting %s failed | ||||
| config.set_setting_failed = Set setting %s failed | ||||
| 
 | ||||
| monitor.cron = Cron Tasks | ||||
| monitor.name = Name | ||||
| monitor.schedule = Schedule | ||||
|  | ||||
| @ -11,7 +11,6 @@ import ( | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/models" | ||||
| 	asymkey_model "code.gitea.io/gitea/models/asymkey" | ||||
| 	"code.gitea.io/gitea/modules/appstate" | ||||
| 	"code.gitea.io/gitea/modules/cache" | ||||
| 	"code.gitea.io/gitea/modules/eventsource" | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
| @ -27,6 +26,7 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/ssh" | ||||
| 	"code.gitea.io/gitea/modules/storage" | ||||
| 	"code.gitea.io/gitea/modules/svg" | ||||
| 	"code.gitea.io/gitea/modules/system" | ||||
| 	"code.gitea.io/gitea/modules/templates" | ||||
| 	"code.gitea.io/gitea/modules/translation" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| @ -76,8 +76,8 @@ func InitGitServices() { | ||||
| } | ||||
| 
 | ||||
| func syncAppPathForGit(ctx context.Context) error { | ||||
| 	runtimeState := new(appstate.RuntimeState) | ||||
| 	if err := appstate.AppState.Get(runtimeState); err != nil { | ||||
| 	runtimeState := new(system.RuntimeState) | ||||
| 	if err := system.AppState.Get(runtimeState); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if runtimeState.LastAppPath != setting.AppPath { | ||||
| @ -90,7 +90,7 @@ func syncAppPathForGit(ctx context.Context) error { | ||||
| 		mustInit(asymkey_model.RewriteAllPublicKeys) | ||||
| 
 | ||||
| 		runtimeState.LastAppPath = setting.AppPath | ||||
| 		return appstate.AppState.Set(runtimeState) | ||||
| 		return system.AppState.Set(runtimeState) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| @ -133,10 +133,10 @@ func GlobalInitInstalled(ctx context.Context) { | ||||
| 
 | ||||
| 	mustInitCtx(ctx, common.InitDBEngine) | ||||
| 	log.Info("ORM engine initialization successful!") | ||||
| 	mustInit(appstate.Init) | ||||
| 	mustInit(system.Init) | ||||
| 	mustInit(oauth2.Init) | ||||
| 
 | ||||
| 	models.NewRepoContext() | ||||
| 	mustInit(models.Init) | ||||
| 	mustInit(repo_service.Init) | ||||
| 
 | ||||
| 	// Booting long running goroutines. | ||||
|  | ||||
| @ -12,12 +12,14 @@ import ( | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| 	"path/filepath" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/models/db" | ||||
| 	db_install "code.gitea.io/gitea/models/db/install" | ||||
| 	"code.gitea.io/gitea/models/migrations" | ||||
| 	system_model "code.gitea.io/gitea/models/system" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/modules/base" | ||||
| 	"code.gitea.io/gitea/modules/context" | ||||
| @ -147,8 +149,19 @@ func Install(ctx *context.Context) { | ||||
| 
 | ||||
| 	// Server and other services settings | ||||
| 	form.OfflineMode = setting.OfflineMode | ||||
| 	form.DisableGravatar = setting.DisableGravatar | ||||
| 	form.EnableFederatedAvatar = setting.EnableFederatedAvatar | ||||
| 	disableGravatarSetting, _ := system_model.GetSetting(system_model.KeyPictureDisableGravatar) | ||||
| 	if disableGravatarSetting != nil { | ||||
| 		form.DisableGravatar = disableGravatarSetting.GetValueBool() | ||||
| 	} else { | ||||
| 		form.DisableGravatar = false | ||||
| 	} | ||||
| 
 | ||||
| 	enableFederatedAvatarSetting, _ := system_model.GetSetting(system_model.KeyPictureEnableFederatedAvatar) | ||||
| 	if enableFederatedAvatarSetting != nil { | ||||
| 		form.EnableFederatedAvatar = enableFederatedAvatarSetting.GetValueBool() | ||||
| 	} else { | ||||
| 		form.EnableFederatedAvatar = false | ||||
| 	} | ||||
| 	form.EnableOpenIDSignIn = setting.Service.EnableOpenIDSignIn | ||||
| 	form.EnableOpenIDSignUp = setting.Service.EnableOpenIDSignUp | ||||
| 	form.DisableRegistration = setting.Service.DisableRegistration | ||||
| @ -439,7 +452,11 @@ func SubmitInstall(ctx *context.Context) { | ||||
| 	cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").SetValue(fmt.Sprint(form.MailNotify)) | ||||
| 
 | ||||
| 	cfg.Section("server").Key("OFFLINE_MODE").SetValue(fmt.Sprint(form.OfflineMode)) | ||||
| 	cfg.Section("picture").Key("DISABLE_GRAVATAR").SetValue(fmt.Sprint(form.DisableGravatar)) | ||||
| 	// if you are reinstalling, this maybe not right because of missing version | ||||
| 	if err := system_model.SetSettingNoVersion(system_model.KeyPictureDisableGravatar, strconv.FormatBool(form.DisableGravatar)); err != nil { | ||||
| 		ctx.RenderWithErr(ctx.Tr("install.secret_key_failed", err), tplInstall, &form) | ||||
| 		return | ||||
| 	} | ||||
| 	cfg.Section("picture").Key("ENABLE_FEDERATED_AVATAR").SetValue(fmt.Sprint(form.EnableFederatedAvatar)) | ||||
| 	cfg.Section("openid").Key("ENABLE_OPENID_SIGNIN").SetValue(fmt.Sprint(form.EnableOpenIDSignIn)) | ||||
| 	cfg.Section("openid").Key("ENABLE_OPENID_SIGNUP").SetValue(fmt.Sprint(form.EnableOpenIDSignUp)) | ||||
|  | ||||
| @ -8,18 +8,13 @@ package admin | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"os" | ||||
| 	"runtime" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	activities_model "code.gitea.io/gitea/models/activities" | ||||
| 	"code.gitea.io/gitea/modules/base" | ||||
| 	"code.gitea.io/gitea/modules/context" | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
| 	"code.gitea.io/gitea/modules/json" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	"code.gitea.io/gitea/modules/process" | ||||
| 	"code.gitea.io/gitea/modules/queue" | ||||
| @ -27,18 +22,13 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/timeutil" | ||||
| 	"code.gitea.io/gitea/modules/translation" | ||||
| 	"code.gitea.io/gitea/modules/updatechecker" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 	"code.gitea.io/gitea/modules/web" | ||||
| 	"code.gitea.io/gitea/services/cron" | ||||
| 	"code.gitea.io/gitea/services/forms" | ||||
| 	"code.gitea.io/gitea/services/mailer" | ||||
| 
 | ||||
| 	"gitea.com/go-chi/session" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	tplDashboard  base.TplName = "admin/dashboard" | ||||
| 	tplConfig     base.TplName = "admin/config" | ||||
| 	tplMonitor    base.TplName = "admin/monitor" | ||||
| 	tplStacktrace base.TplName = "admin/stacktrace" | ||||
| 	tplQueue      base.TplName = "admin/queue" | ||||
| @ -165,165 +155,6 @@ func DashboardPost(ctx *context.Context) { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // SendTestMail send test mail to confirm mail service is OK | ||||
| func SendTestMail(ctx *context.Context) { | ||||
| 	email := ctx.FormString("email") | ||||
| 	// Send a test email to the user's email address and redirect back to Config | ||||
| 	if err := mailer.SendTestMail(email); err != nil { | ||||
| 		ctx.Flash.Error(ctx.Tr("admin.config.test_mail_failed", email, err)) | ||||
| 	} else { | ||||
| 		ctx.Flash.Info(ctx.Tr("admin.config.test_mail_sent", email)) | ||||
| 	} | ||||
| 
 | ||||
| 	ctx.Redirect(setting.AppSubURL + "/admin/config") | ||||
| } | ||||
| 
 | ||||
| func shadowPasswordKV(cfgItem, splitter string) string { | ||||
| 	fields := strings.Split(cfgItem, splitter) | ||||
| 	for i := 0; i < len(fields); i++ { | ||||
| 		if strings.HasPrefix(fields[i], "password=") { | ||||
| 			fields[i] = "password=******" | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	return strings.Join(fields, splitter) | ||||
| } | ||||
| 
 | ||||
| func shadowURL(provider, cfgItem string) string { | ||||
| 	u, err := url.Parse(cfgItem) | ||||
| 	if err != nil { | ||||
| 		log.Error("Shadowing Password for %v failed: %v", provider, err) | ||||
| 		return cfgItem | ||||
| 	} | ||||
| 	if u.User != nil { | ||||
| 		atIdx := strings.Index(cfgItem, "@") | ||||
| 		if atIdx > 0 { | ||||
| 			colonIdx := strings.LastIndex(cfgItem[:atIdx], ":") | ||||
| 			if colonIdx > 0 { | ||||
| 				return cfgItem[:colonIdx+1] + "******" + cfgItem[atIdx:] | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return cfgItem | ||||
| } | ||||
| 
 | ||||
| func shadowPassword(provider, cfgItem string) string { | ||||
| 	switch provider { | ||||
| 	case "redis": | ||||
| 		return shadowPasswordKV(cfgItem, ",") | ||||
| 	case "mysql": | ||||
| 		// root:@tcp(localhost:3306)/macaron?charset=utf8 | ||||
| 		atIdx := strings.Index(cfgItem, "@") | ||||
| 		if atIdx > 0 { | ||||
| 			colonIdx := strings.Index(cfgItem[:atIdx], ":") | ||||
| 			if colonIdx > 0 { | ||||
| 				return cfgItem[:colonIdx+1] + "******" + cfgItem[atIdx:] | ||||
| 			} | ||||
| 		} | ||||
| 		return cfgItem | ||||
| 	case "postgres": | ||||
| 		// user=jiahuachen dbname=macaron port=5432 sslmode=disable | ||||
| 		if !strings.HasPrefix(cfgItem, "postgres://") { | ||||
| 			return shadowPasswordKV(cfgItem, " ") | ||||
| 		} | ||||
| 		fallthrough | ||||
| 	case "couchbase": | ||||
| 		return shadowURL(provider, cfgItem) | ||||
| 		// postgres://pqgotest:password@localhost/pqgotest?sslmode=verify-full | ||||
| 		// Notice: use shadowURL | ||||
| 	} | ||||
| 	return cfgItem | ||||
| } | ||||
| 
 | ||||
| // Config show admin config page | ||||
| func Config(ctx *context.Context) { | ||||
| 	ctx.Data["Title"] = ctx.Tr("admin.config") | ||||
| 	ctx.Data["PageIsAdmin"] = true | ||||
| 	ctx.Data["PageIsAdminConfig"] = true | ||||
| 
 | ||||
| 	ctx.Data["CustomConf"] = setting.CustomConf | ||||
| 	ctx.Data["AppUrl"] = setting.AppURL | ||||
| 	ctx.Data["Domain"] = setting.Domain | ||||
| 	ctx.Data["OfflineMode"] = setting.OfflineMode | ||||
| 	ctx.Data["DisableRouterLog"] = setting.DisableRouterLog | ||||
| 	ctx.Data["RunUser"] = setting.RunUser | ||||
| 	ctx.Data["RunMode"] = util.ToTitleCase(setting.RunMode) | ||||
| 	ctx.Data["GitVersion"] = git.VersionInfo() | ||||
| 
 | ||||
| 	ctx.Data["RepoRootPath"] = setting.RepoRootPath | ||||
| 	ctx.Data["CustomRootPath"] = setting.CustomPath | ||||
| 	ctx.Data["StaticRootPath"] = setting.StaticRootPath | ||||
| 	ctx.Data["LogRootPath"] = setting.LogRootPath | ||||
| 	ctx.Data["ScriptType"] = setting.ScriptType | ||||
| 	ctx.Data["ReverseProxyAuthUser"] = setting.ReverseProxyAuthUser | ||||
| 	ctx.Data["ReverseProxyAuthEmail"] = setting.ReverseProxyAuthEmail | ||||
| 	ctx.Data["ReverseProxyAuthFullName"] = setting.ReverseProxyAuthFullName | ||||
| 
 | ||||
| 	ctx.Data["SSH"] = setting.SSH | ||||
| 	ctx.Data["LFS"] = setting.LFS | ||||
| 
 | ||||
| 	ctx.Data["Service"] = setting.Service | ||||
| 	ctx.Data["DbCfg"] = setting.Database | ||||
| 	ctx.Data["Webhook"] = setting.Webhook | ||||
| 
 | ||||
| 	ctx.Data["MailerEnabled"] = false | ||||
| 	if setting.MailService != nil { | ||||
| 		ctx.Data["MailerEnabled"] = true | ||||
| 		ctx.Data["Mailer"] = setting.MailService | ||||
| 	} | ||||
| 
 | ||||
| 	ctx.Data["CacheAdapter"] = setting.CacheService.Adapter | ||||
| 	ctx.Data["CacheInterval"] = setting.CacheService.Interval | ||||
| 
 | ||||
| 	ctx.Data["CacheConn"] = shadowPassword(setting.CacheService.Adapter, setting.CacheService.Conn) | ||||
| 	ctx.Data["CacheItemTTL"] = setting.CacheService.TTL | ||||
| 
 | ||||
| 	sessionCfg := setting.SessionConfig | ||||
| 	if sessionCfg.Provider == "VirtualSession" { | ||||
| 		var realSession session.Options | ||||
| 		if err := json.Unmarshal([]byte(sessionCfg.ProviderConfig), &realSession); err != nil { | ||||
| 			log.Error("Unable to unmarshall session config for virtualed provider config: %s\nError: %v", sessionCfg.ProviderConfig, err) | ||||
| 		} | ||||
| 		sessionCfg.Provider = realSession.Provider | ||||
| 		sessionCfg.ProviderConfig = realSession.ProviderConfig | ||||
| 		sessionCfg.CookieName = realSession.CookieName | ||||
| 		sessionCfg.CookiePath = realSession.CookiePath | ||||
| 		sessionCfg.Gclifetime = realSession.Gclifetime | ||||
| 		sessionCfg.Maxlifetime = realSession.Maxlifetime | ||||
| 		sessionCfg.Secure = realSession.Secure | ||||
| 		sessionCfg.Domain = realSession.Domain | ||||
| 	} | ||||
| 	sessionCfg.ProviderConfig = shadowPassword(sessionCfg.Provider, sessionCfg.ProviderConfig) | ||||
| 	ctx.Data["SessionConfig"] = sessionCfg | ||||
| 
 | ||||
| 	ctx.Data["DisableGravatar"] = setting.DisableGravatar | ||||
| 	ctx.Data["EnableFederatedAvatar"] = setting.EnableFederatedAvatar | ||||
| 
 | ||||
| 	ctx.Data["Git"] = setting.Git | ||||
| 
 | ||||
| 	type envVar struct { | ||||
| 		Name, Value string | ||||
| 	} | ||||
| 
 | ||||
| 	envVars := map[string]*envVar{} | ||||
| 	if len(os.Getenv("GITEA_WORK_DIR")) > 0 { | ||||
| 		envVars["GITEA_WORK_DIR"] = &envVar{"GITEA_WORK_DIR", os.Getenv("GITEA_WORK_DIR")} | ||||
| 	} | ||||
| 	if len(os.Getenv("GITEA_CUSTOM")) > 0 { | ||||
| 		envVars["GITEA_CUSTOM"] = &envVar{"GITEA_CUSTOM", os.Getenv("GITEA_CUSTOM")} | ||||
| 	} | ||||
| 
 | ||||
| 	ctx.Data["EnvVars"] = envVars | ||||
| 	ctx.Data["Loggers"] = setting.GetLogDescriptions() | ||||
| 	ctx.Data["EnableAccessLog"] = setting.EnableAccessLog | ||||
| 	ctx.Data["AccessLogTemplate"] = setting.AccessLogTemplate | ||||
| 	ctx.Data["DisableRouterLog"] = setting.DisableRouterLog | ||||
| 	ctx.Data["EnableXORMLog"] = setting.EnableXORMLog | ||||
| 	ctx.Data["LogSQL"] = setting.Database.LogSQL | ||||
| 
 | ||||
| 	ctx.HTML(http.StatusOK, tplConfig) | ||||
| } | ||||
| 
 | ||||
| // Monitor show admin monitor page | ||||
| func Monitor(ctx *context.Context) { | ||||
| 	ctx.Data["Title"] = ctx.Tr("admin.monitor") | ||||
|  | ||||
							
								
								
									
										217
									
								
								routers/web/admin/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								routers/web/admin/config.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,217 @@ | ||||
| // Copyright 2014 The Gogs Authors. All rights reserved. | ||||
| // Copyright 2019 The Gitea Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package admin | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	system_model "code.gitea.io/gitea/models/system" | ||||
| 	"code.gitea.io/gitea/modules/base" | ||||
| 	"code.gitea.io/gitea/modules/context" | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
| 	"code.gitea.io/gitea/modules/json" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	system_module "code.gitea.io/gitea/modules/system" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 	"code.gitea.io/gitea/services/mailer" | ||||
| 
 | ||||
| 	"gitea.com/go-chi/session" | ||||
| ) | ||||
| 
 | ||||
| const tplConfig base.TplName = "admin/config" | ||||
| 
 | ||||
| // SendTestMail send test mail to confirm mail service is OK | ||||
| func SendTestMail(ctx *context.Context) { | ||||
| 	email := ctx.FormString("email") | ||||
| 	// Send a test email to the user's email address and redirect back to Config | ||||
| 	if err := mailer.SendTestMail(email); err != nil { | ||||
| 		ctx.Flash.Error(ctx.Tr("admin.config.test_mail_failed", email, err)) | ||||
| 	} else { | ||||
| 		ctx.Flash.Info(ctx.Tr("admin.config.test_mail_sent", email)) | ||||
| 	} | ||||
| 
 | ||||
| 	ctx.Redirect(setting.AppSubURL + "/admin/config") | ||||
| } | ||||
| 
 | ||||
| func shadowPasswordKV(cfgItem, splitter string) string { | ||||
| 	fields := strings.Split(cfgItem, splitter) | ||||
| 	for i := 0; i < len(fields); i++ { | ||||
| 		if strings.HasPrefix(fields[i], "password=") { | ||||
| 			fields[i] = "password=******" | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	return strings.Join(fields, splitter) | ||||
| } | ||||
| 
 | ||||
| func shadowURL(provider, cfgItem string) string { | ||||
| 	u, err := url.Parse(cfgItem) | ||||
| 	if err != nil { | ||||
| 		log.Error("Shadowing Password for %v failed: %v", provider, err) | ||||
| 		return cfgItem | ||||
| 	} | ||||
| 	if u.User != nil { | ||||
| 		atIdx := strings.Index(cfgItem, "@") | ||||
| 		if atIdx > 0 { | ||||
| 			colonIdx := strings.LastIndex(cfgItem[:atIdx], ":") | ||||
| 			if colonIdx > 0 { | ||||
| 				return cfgItem[:colonIdx+1] + "******" + cfgItem[atIdx:] | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return cfgItem | ||||
| } | ||||
| 
 | ||||
| func shadowPassword(provider, cfgItem string) string { | ||||
| 	switch provider { | ||||
| 	case "redis": | ||||
| 		return shadowPasswordKV(cfgItem, ",") | ||||
| 	case "mysql": | ||||
| 		// root:@tcp(localhost:3306)/macaron?charset=utf8 | ||||
| 		atIdx := strings.Index(cfgItem, "@") | ||||
| 		if atIdx > 0 { | ||||
| 			colonIdx := strings.Index(cfgItem[:atIdx], ":") | ||||
| 			if colonIdx > 0 { | ||||
| 				return cfgItem[:colonIdx+1] + "******" + cfgItem[atIdx:] | ||||
| 			} | ||||
| 		} | ||||
| 		return cfgItem | ||||
| 	case "postgres": | ||||
| 		// user=jiahuachen dbname=macaron port=5432 sslmode=disable | ||||
| 		if !strings.HasPrefix(cfgItem, "postgres://") { | ||||
| 			return shadowPasswordKV(cfgItem, " ") | ||||
| 		} | ||||
| 		fallthrough | ||||
| 	case "couchbase": | ||||
| 		return shadowURL(provider, cfgItem) | ||||
| 		// postgres://pqgotest:password@localhost/pqgotest?sslmode=verify-full | ||||
| 		// Notice: use shadowURL | ||||
| 	} | ||||
| 	return cfgItem | ||||
| } | ||||
| 
 | ||||
| // Config show admin config page | ||||
| func Config(ctx *context.Context) { | ||||
| 	ctx.Data["Title"] = ctx.Tr("admin.config") | ||||
| 	ctx.Data["PageIsAdmin"] = true | ||||
| 	ctx.Data["PageIsAdminConfig"] = true | ||||
| 
 | ||||
| 	systemSettings, err := system_model.GetAllSettings() | ||||
| 	if err != nil { | ||||
| 		ctx.ServerError("system_model.GetAllSettings", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// All editable settings from UI | ||||
| 	ctx.Data["SystemSettings"] = systemSettings | ||||
| 	ctx.PageData["adminConfigPage"] = true | ||||
| 
 | ||||
| 	ctx.Data["CustomConf"] = setting.CustomConf | ||||
| 	ctx.Data["AppUrl"] = setting.AppURL | ||||
| 	ctx.Data["Domain"] = setting.Domain | ||||
| 	ctx.Data["OfflineMode"] = setting.OfflineMode | ||||
| 	ctx.Data["DisableRouterLog"] = setting.DisableRouterLog | ||||
| 	ctx.Data["RunUser"] = setting.RunUser | ||||
| 	ctx.Data["RunMode"] = util.ToTitleCase(setting.RunMode) | ||||
| 	ctx.Data["GitVersion"] = git.VersionInfo() | ||||
| 
 | ||||
| 	ctx.Data["RepoRootPath"] = setting.RepoRootPath | ||||
| 	ctx.Data["CustomRootPath"] = setting.CustomPath | ||||
| 	ctx.Data["StaticRootPath"] = setting.StaticRootPath | ||||
| 	ctx.Data["LogRootPath"] = setting.LogRootPath | ||||
| 	ctx.Data["ScriptType"] = setting.ScriptType | ||||
| 	ctx.Data["ReverseProxyAuthUser"] = setting.ReverseProxyAuthUser | ||||
| 	ctx.Data["ReverseProxyAuthEmail"] = setting.ReverseProxyAuthEmail | ||||
| 
 | ||||
| 	ctx.Data["SSH"] = setting.SSH | ||||
| 	ctx.Data["LFS"] = setting.LFS | ||||
| 
 | ||||
| 	ctx.Data["Service"] = setting.Service | ||||
| 	ctx.Data["DbCfg"] = setting.Database | ||||
| 	ctx.Data["Webhook"] = setting.Webhook | ||||
| 
 | ||||
| 	ctx.Data["MailerEnabled"] = false | ||||
| 	if setting.MailService != nil { | ||||
| 		ctx.Data["MailerEnabled"] = true | ||||
| 		ctx.Data["Mailer"] = setting.MailService | ||||
| 	} | ||||
| 
 | ||||
| 	ctx.Data["CacheAdapter"] = setting.CacheService.Adapter | ||||
| 	ctx.Data["CacheInterval"] = setting.CacheService.Interval | ||||
| 
 | ||||
| 	ctx.Data["CacheConn"] = shadowPassword(setting.CacheService.Adapter, setting.CacheService.Conn) | ||||
| 	ctx.Data["CacheItemTTL"] = setting.CacheService.TTL | ||||
| 
 | ||||
| 	sessionCfg := setting.SessionConfig | ||||
| 	if sessionCfg.Provider == "VirtualSession" { | ||||
| 		var realSession session.Options | ||||
| 		if err := json.Unmarshal([]byte(sessionCfg.ProviderConfig), &realSession); err != nil { | ||||
| 			log.Error("Unable to unmarshall session config for virtual provider config: %s\nError: %v", sessionCfg.ProviderConfig, err) | ||||
| 		} | ||||
| 		sessionCfg.Provider = realSession.Provider | ||||
| 		sessionCfg.ProviderConfig = realSession.ProviderConfig | ||||
| 		sessionCfg.CookieName = realSession.CookieName | ||||
| 		sessionCfg.CookiePath = realSession.CookiePath | ||||
| 		sessionCfg.Gclifetime = realSession.Gclifetime | ||||
| 		sessionCfg.Maxlifetime = realSession.Maxlifetime | ||||
| 		sessionCfg.Secure = realSession.Secure | ||||
| 		sessionCfg.Domain = realSession.Domain | ||||
| 	} | ||||
| 	sessionCfg.ProviderConfig = shadowPassword(sessionCfg.Provider, sessionCfg.ProviderConfig) | ||||
| 	ctx.Data["SessionConfig"] = sessionCfg | ||||
| 
 | ||||
| 	ctx.Data["Git"] = setting.Git | ||||
| 
 | ||||
| 	type envVar struct { | ||||
| 		Name, Value string | ||||
| 	} | ||||
| 
 | ||||
| 	envVars := map[string]*envVar{} | ||||
| 	if len(os.Getenv("GITEA_WORK_DIR")) > 0 { | ||||
| 		envVars["GITEA_WORK_DIR"] = &envVar{"GITEA_WORK_DIR", os.Getenv("GITEA_WORK_DIR")} | ||||
| 	} | ||||
| 	if len(os.Getenv("GITEA_CUSTOM")) > 0 { | ||||
| 		envVars["GITEA_CUSTOM"] = &envVar{"GITEA_CUSTOM", os.Getenv("GITEA_CUSTOM")} | ||||
| 	} | ||||
| 
 | ||||
| 	ctx.Data["EnvVars"] = envVars | ||||
| 	ctx.Data["Loggers"] = setting.GetLogDescriptions() | ||||
| 	ctx.Data["EnableAccessLog"] = setting.EnableAccessLog | ||||
| 	ctx.Data["AccessLogTemplate"] = setting.AccessLogTemplate | ||||
| 	ctx.Data["DisableRouterLog"] = setting.DisableRouterLog | ||||
| 	ctx.Data["EnableXORMLog"] = setting.EnableXORMLog | ||||
| 	ctx.Data["LogSQL"] = setting.Database.LogSQL | ||||
| 
 | ||||
| 	ctx.HTML(http.StatusOK, tplConfig) | ||||
| } | ||||
| 
 | ||||
| func ChangeConfig(ctx *context.Context) { | ||||
| 	key := strings.TrimSpace(ctx.FormString("key")) | ||||
| 	if key == "" { | ||||
| 		ctx.JSON(http.StatusOK, map[string]string{ | ||||
| 			"redirect": ctx.Req.URL.String(), | ||||
| 		}) | ||||
| 		return | ||||
| 	} | ||||
| 	value := ctx.FormString("value") | ||||
| 	version := ctx.FormInt("version") | ||||
| 
 | ||||
| 	if err := system_module.SetSetting(key, value, version); err != nil { | ||||
| 		log.Error("set setting failed: %v", err) | ||||
| 		ctx.JSON(http.StatusOK, map[string]string{ | ||||
| 			"err": ctx.Tr("admin.config.set_setting_failed", key), | ||||
| 		}) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	ctx.JSON(http.StatusOK, map[string]interface{}{ | ||||
| 		"version": version + 1, | ||||
| 	}) | ||||
| } | ||||
| @ -9,7 +9,7 @@ import ( | ||||
| 	"net/http" | ||||
| 	"strconv" | ||||
| 
 | ||||
| 	admin_model "code.gitea.io/gitea/models/admin" | ||||
| 	system_model "code.gitea.io/gitea/models/system" | ||||
| 	"code.gitea.io/gitea/modules/base" | ||||
| 	"code.gitea.io/gitea/modules/context" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| @ -26,13 +26,13 @@ func Notices(ctx *context.Context) { | ||||
| 	ctx.Data["PageIsAdmin"] = true | ||||
| 	ctx.Data["PageIsAdminNotices"] = true | ||||
| 
 | ||||
| 	total := admin_model.CountNotices() | ||||
| 	total := system_model.CountNotices() | ||||
| 	page := ctx.FormInt("page") | ||||
| 	if page <= 1 { | ||||
| 		page = 1 | ||||
| 	} | ||||
| 
 | ||||
| 	notices, err := admin_model.Notices(page, setting.UI.Admin.NoticePagingNum) | ||||
| 	notices, err := system_model.Notices(page, setting.UI.Admin.NoticePagingNum) | ||||
| 	if err != nil { | ||||
| 		ctx.ServerError("Notices", err) | ||||
| 		return | ||||
| @ -57,7 +57,7 @@ func DeleteNotices(ctx *context.Context) { | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if err := admin_model.DeleteNoticesByIDs(ids); err != nil { | ||||
| 	if err := system_model.DeleteNoticesByIDs(ids); err != nil { | ||||
| 		ctx.Flash.Error("DeleteNoticesByIDs: " + err.Error()) | ||||
| 		ctx.Status(http.StatusInternalServerError) | ||||
| 	} else { | ||||
| @ -68,7 +68,7 @@ func DeleteNotices(ctx *context.Context) { | ||||
| 
 | ||||
| // EmptyNotices delete all the notices | ||||
| func EmptyNotices(ctx *context.Context) { | ||||
| 	if err := admin_model.DeleteNotices(0, 0); err != nil { | ||||
| 	if err := system_model.DeleteNotices(0, 0); err != nil { | ||||
| 		ctx.ServerError("DeleteNotices", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| @ -7,7 +7,7 @@ package repo | ||||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	admin_model "code.gitea.io/gitea/models/admin" | ||||
| 	system_model "code.gitea.io/gitea/models/system" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/modules/context" | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
| @ -24,7 +24,7 @@ func SetEditorconfigIfExists(ctx *context.Context) { | ||||
| 
 | ||||
| 	if err != nil && !git.IsErrNotExist(err) { | ||||
| 		description := fmt.Sprintf("Error while getting .editorconfig file: %v", err) | ||||
| 		if err := admin_model.CreateRepositoryNotice(description); err != nil { | ||||
| 		if err := system_model.CreateRepositoryNotice(description); err != nil { | ||||
| 			ctx.ServerError("ErrCreatingReporitoryNotice", err) | ||||
| 		} | ||||
| 		return | ||||
|  | ||||
| @ -473,8 +473,13 @@ func RegisterRoutes(m *web.Route) { | ||||
| 	m.Group("/admin", func() { | ||||
| 		m.Get("", adminReq, admin.Dashboard) | ||||
| 		m.Post("", adminReq, bindIgnErr(forms.AdminDashboardForm{}), admin.DashboardPost) | ||||
| 		m.Get("/config", admin.Config) | ||||
| 		m.Post("/config/test_mail", admin.SendTestMail) | ||||
| 
 | ||||
| 		m.Group("/config", func() { | ||||
| 			m.Get("", admin.Config) | ||||
| 			m.Post("", admin.ChangeConfig) | ||||
| 			m.Post("/test_mail", admin.SendTestMail) | ||||
| 		}) | ||||
| 
 | ||||
| 		m.Group("/monitor", func() { | ||||
| 			m.Get("", admin.Monitor) | ||||
| 			m.Get("/stacktrace", admin.GoroutineStacktrace) | ||||
|  | ||||
| @ -10,8 +10,8 @@ import ( | ||||
| 	"reflect" | ||||
| 	"sync" | ||||
| 
 | ||||
| 	admin_model "code.gitea.io/gitea/models/admin" | ||||
| 	"code.gitea.io/gitea/models/db" | ||||
| 	system_model "code.gitea.io/gitea/models/system" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/modules/graceful" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| @ -114,7 +114,7 @@ func (t *Task) RunWithUser(doer *user_model.User, config Config) { | ||||
| 			t.LastDoer = doerName | ||||
| 			t.lock.Unlock() | ||||
| 
 | ||||
| 			if err := admin_model.CreateNotice(ctx, admin_model.NoticeTask, config.FormatMessage(translation.NewLocale("en-US"), t.Name, "cancelled", doerName, message)); err != nil { | ||||
| 			if err := system_model.CreateNotice(ctx, system_model.NoticeTask, config.FormatMessage(translation.NewLocale("en-US"), t.Name, "cancelled", doerName, message)); err != nil { | ||||
| 				log.Error("CreateNotice: %v", err) | ||||
| 			} | ||||
| 			return | ||||
| @ -127,7 +127,7 @@ func (t *Task) RunWithUser(doer *user_model.User, config Config) { | ||||
| 		t.lock.Unlock() | ||||
| 
 | ||||
| 		if config.DoNoticeOnSuccess() { | ||||
| 			if err := admin_model.CreateNotice(ctx, admin_model.NoticeTask, config.FormatMessage(translation.NewLocale("en-US"), t.Name, "finished", doerName)); err != nil { | ||||
| 			if err := system_model.CreateNotice(ctx, system_model.NoticeTask, config.FormatMessage(translation.NewLocale("en-US"), t.Name, "finished", doerName)); err != nil { | ||||
| 				log.Error("CreateNotice: %v", err) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| @ -9,9 +9,9 @@ import ( | ||||
| 	"time" | ||||
| 
 | ||||
| 	activities_model "code.gitea.io/gitea/models/activities" | ||||
| 	"code.gitea.io/gitea/models/admin" | ||||
| 	asymkey_model "code.gitea.io/gitea/models/asymkey" | ||||
| 	"code.gitea.io/gitea/models/db" | ||||
| 	"code.gitea.io/gitea/models/system" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/updatechecker" | ||||
| @ -166,7 +166,7 @@ func registerDeleteOldSystemNotices() { | ||||
| 		OlderThan: 365 * 24 * time.Hour, | ||||
| 	}, func(ctx context.Context, _ *user_model.User, config Config) error { | ||||
| 		olderThanConfig := config.(*OlderThanConfig) | ||||
| 		return admin.DeleteOldSystemNotices(olderThanConfig.OlderThan) | ||||
| 		return system.DeleteOldSystemNotices(olderThanConfig.OlderThan) | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -8,12 +8,12 @@ import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	activities_model "code.gitea.io/gitea/models/activities" | ||||
| 	admin_model "code.gitea.io/gitea/models/admin" | ||||
| 	"code.gitea.io/gitea/models/db" | ||||
| 	issues_model "code.gitea.io/gitea/models/issues" | ||||
| 	access_model "code.gitea.io/gitea/models/perm/access" | ||||
| 	project_model "code.gitea.io/gitea/models/project" | ||||
| 	repo_model "code.gitea.io/gitea/models/repo" | ||||
| 	system_model "code.gitea.io/gitea/models/system" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
| 	"code.gitea.io/gitea/modules/notification" | ||||
| @ -234,7 +234,7 @@ func deleteIssue(issue *issues_model.Issue) error { | ||||
| 	} | ||||
| 
 | ||||
| 	for i := range issue.Attachments { | ||||
| 		admin_model.RemoveStorageWithNotice(ctx, storage.Attachments, "Delete issue attachment", issue.Attachments[i].RelativePath()) | ||||
| 		system_model.RemoveStorageWithNotice(ctx, storage.Attachments, "Delete issue attachment", issue.Attachments[i].RelativePath()) | ||||
| 	} | ||||
| 
 | ||||
| 	// delete all database data still assigned to this issue | ||||
|  | ||||
| @ -8,7 +8,7 @@ import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	admin_model "code.gitea.io/gitea/models/admin" | ||||
| 	system_model "code.gitea.io/gitea/models/system" | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	base "code.gitea.io/gitea/modules/migration" | ||||
| @ -17,7 +17,7 @@ import ( | ||||
| // WarnAndNotice will log the provided message and send a repository notice | ||||
| func WarnAndNotice(fmtStr string, args ...interface{}) { | ||||
| 	log.Warn(fmtStr, args...) | ||||
| 	if err := admin_model.CreateRepositoryNotice(fmt.Sprintf(fmtStr, args...)); err != nil { | ||||
| 	if err := system_model.CreateRepositoryNotice(fmt.Sprintf(fmtStr, args...)); err != nil { | ||||
| 		log.Error("create repository notice failed: ", err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -14,8 +14,8 @@ import ( | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/models" | ||||
| 	admin_model "code.gitea.io/gitea/models/admin" | ||||
| 	repo_model "code.gitea.io/gitea/models/repo" | ||||
| 	system_model "code.gitea.io/gitea/models/system" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/modules/hostmatcher" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| @ -132,7 +132,7 @@ func MigrateRepository(ctx context.Context, doer *user_model.User, ownerName str | ||||
| 		if err1 := uploader.Rollback(); err1 != nil { | ||||
| 			log.Error("rollback failed: %v", err1) | ||||
| 		} | ||||
| 		if err2 := admin_model.CreateRepositoryNotice(fmt.Sprintf("Migrate repository from %s failed: %v", opts.OriginalURL, err)); err2 != nil { | ||||
| 		if err2 := system_model.CreateRepositoryNotice(fmt.Sprintf("Migrate repository from %s failed: %v", opts.OriginalURL, err)); err2 != nil { | ||||
| 			log.Error("create respotiry notice failed: ", err2) | ||||
| 		} | ||||
| 		return nil, err | ||||
|  | ||||
| @ -10,9 +10,9 @@ import ( | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	admin_model "code.gitea.io/gitea/models/admin" | ||||
| 	"code.gitea.io/gitea/models/db" | ||||
| 	repo_model "code.gitea.io/gitea/models/repo" | ||||
| 	system_model "code.gitea.io/gitea/models/system" | ||||
| 	"code.gitea.io/gitea/modules/cache" | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
| 	"code.gitea.io/gitea/modules/lfs" | ||||
| @ -188,7 +188,7 @@ func pruneBrokenReferences(ctx context.Context, | ||||
| 
 | ||||
| 		log.Error("Failed to prune mirror repository %s%-v references:\nStdout: %s\nStderr: %s\nErr: %v", wiki, m.Repo, stdoutMessage, stderrMessage, pruneErr) | ||||
| 		desc := fmt.Sprintf("Failed to prune mirror repository %s'%s' references: %s", wiki, repoPath, stderrMessage) | ||||
| 		if err := admin_model.CreateRepositoryNotice(desc); err != nil { | ||||
| 		if err := system_model.CreateRepositoryNotice(desc); err != nil { | ||||
| 			log.Error("CreateRepositoryNotice: %v", err) | ||||
| 		} | ||||
| 		// this if will only be reached on a successful prune so try to get the mirror again | ||||
| @ -267,7 +267,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo | ||||
| 		if err != nil { | ||||
| 			log.Error("SyncMirrors [repo: %-v]: failed to update mirror repository:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err) | ||||
| 			desc := fmt.Sprintf("Failed to update mirror repository '%s': %s", repoPath, stderrMessage) | ||||
| 			if err = admin_model.CreateRepositoryNotice(desc); err != nil { | ||||
| 			if err = system_model.CreateRepositoryNotice(desc); err != nil { | ||||
| 				log.Error("CreateRepositoryNotice: %v", err) | ||||
| 			} | ||||
| 			return nil, false | ||||
| @ -356,7 +356,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo | ||||
| 			if err != nil { | ||||
| 				log.Error("SyncMirrors [repo: %-v Wiki]: failed to update mirror repository wiki:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err) | ||||
| 				desc := fmt.Sprintf("Failed to update mirror repository wiki '%s': %s", wikiPath, stderrMessage) | ||||
| 				if err = admin_model.CreateRepositoryNotice(desc); err != nil { | ||||
| 				if err = system_model.CreateRepositoryNotice(desc); err != nil { | ||||
| 					log.Error("CreateRepositoryNotice: %v", err) | ||||
| 				} | ||||
| 				return nil, false | ||||
| @ -568,7 +568,7 @@ func checkAndUpdateEmptyRepository(m *repo_model.Mirror, gitRepo *git.Repository | ||||
| 			if !git.IsErrUnsupportedVersion(err) { | ||||
| 				log.Error("Failed to update default branch of underlying git repository %-v. Error: %v", m.Repo, err) | ||||
| 				desc := fmt.Sprintf("Failed to uupdate default branch of underlying git repository '%s': %v", m.Repo.RepoPath(), err) | ||||
| 				if err = admin_model.CreateRepositoryNotice(desc); err != nil { | ||||
| 				if err = system_model.CreateRepositoryNotice(desc); err != nil { | ||||
| 					log.Error("CreateRepositoryNotice: %v", err) | ||||
| 				} | ||||
| 				return false | ||||
| @ -579,7 +579,7 @@ func checkAndUpdateEmptyRepository(m *repo_model.Mirror, gitRepo *git.Repository | ||||
| 		if err := repo_model.UpdateRepositoryCols(db.DefaultContext, m.Repo, "default_branch", "is_empty"); err != nil { | ||||
| 			log.Error("Failed to update default branch of repository %-v. Error: %v", m.Repo, err) | ||||
| 			desc := fmt.Sprintf("Failed to uupdate default branch of repository '%s': %v", m.Repo.RepoPath(), err) | ||||
| 			if err = admin_model.CreateRepositoryNotice(desc); err != nil { | ||||
| 			if err = system_model.CreateRepositoryNotice(desc); err != nil { | ||||
| 				log.Error("CreateRepositoryNotice: %v", err) | ||||
| 			} | ||||
| 			return false | ||||
|  | ||||
| @ -11,9 +11,9 @@ import ( | ||||
| 	"time" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/models" | ||||
| 	admin_model "code.gitea.io/gitea/models/admin" | ||||
| 	"code.gitea.io/gitea/models/db" | ||||
| 	repo_model "code.gitea.io/gitea/models/repo" | ||||
| 	system_model "code.gitea.io/gitea/models/system" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| @ -42,7 +42,7 @@ func GitFsck(ctx context.Context, timeout time.Duration, args []string) error { | ||||
| 			repoPath := repo.RepoPath() | ||||
| 			if err := git.Fsck(ctx, repoPath, timeout, args...); err != nil { | ||||
| 				log.Warn("Failed to health check repository (%v): %v", repo, err) | ||||
| 				if err = admin_model.CreateRepositoryNotice("Failed to health check repository (%s): %v", repo.FullName(), err); err != nil { | ||||
| 				if err = system_model.CreateRepositoryNotice("Failed to health check repository (%s): %v", repo.FullName(), err); err != nil { | ||||
| 					log.Error("CreateRepositoryNotice: %v", err) | ||||
| 				} | ||||
| 			} | ||||
| @ -83,7 +83,7 @@ func GitGcRepos(ctx context.Context, timeout time.Duration, args ...string) erro | ||||
| 			if err != nil { | ||||
| 				log.Error("Repository garbage collection failed for %v. Stdout: %s\nError: %v", repo, stdout, err) | ||||
| 				desc := fmt.Sprintf("Repository garbage collection failed for %s. Stdout: %s\nError: %v", repo.RepoPath(), stdout, err) | ||||
| 				if err = admin_model.CreateRepositoryNotice(desc); err != nil { | ||||
| 				if err = system_model.CreateRepositoryNotice(desc); err != nil { | ||||
| 					log.Error("CreateRepositoryNotice: %v", err) | ||||
| 				} | ||||
| 				return fmt.Errorf("Repository garbage collection failed in repo: %s: Error: %v", repo.FullName(), err) | ||||
| @ -93,7 +93,7 @@ func GitGcRepos(ctx context.Context, timeout time.Duration, args ...string) erro | ||||
| 			if err := repo_module.UpdateRepoSize(ctx, repo); err != nil { | ||||
| 				log.Error("Updating size as part of garbage collection failed for %v. Stdout: %s\nError: %v", repo, stdout, err) | ||||
| 				desc := fmt.Sprintf("Updating size as part of garbage collection failed for %s. Stdout: %s\nError: %v", repo.RepoPath(), stdout, err) | ||||
| 				if err = admin_model.CreateRepositoryNotice(desc); err != nil { | ||||
| 				if err = system_model.CreateRepositoryNotice(desc); err != nil { | ||||
| 					log.Error("CreateRepositoryNotice: %v", err) | ||||
| 				} | ||||
| 				return fmt.Errorf("Updating size as part of garbage collection failed in repo: %s: Error: %v", repo.FullName(), err) | ||||
| @ -135,7 +135,7 @@ func gatherMissingRepoRecords(ctx context.Context) ([]*repo_model.Repository, er | ||||
| 		if strings.HasPrefix(err.Error(), "Aborted gathering missing repo") { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		if err2 := admin_model.CreateRepositoryNotice("gatherMissingRepoRecords: %v", err); err2 != nil { | ||||
| 		if err2 := system_model.CreateRepositoryNotice("gatherMissingRepoRecords: %v", err); err2 != nil { | ||||
| 			log.Error("CreateRepositoryNotice: %v", err2) | ||||
| 		} | ||||
| 		return nil, err | ||||
| @ -163,7 +163,7 @@ func DeleteMissingRepositories(ctx context.Context, doer *user_model.User) error | ||||
| 		log.Trace("Deleting %d/%d...", repo.OwnerID, repo.ID) | ||||
| 		if err := models.DeleteRepository(doer, repo.OwnerID, repo.ID); err != nil { | ||||
| 			log.Error("Failed to DeleteRepository %s [%d]: Error: %v", repo.FullName(), repo.ID, err) | ||||
| 			if err2 := admin_model.CreateRepositoryNotice("Failed to DeleteRepository %s [%d]: Error: %v", repo.FullName(), repo.ID, err); err2 != nil { | ||||
| 			if err2 := system_model.CreateRepositoryNotice("Failed to DeleteRepository %s [%d]: Error: %v", repo.FullName(), repo.ID, err); err2 != nil { | ||||
| 				log.Error("CreateRepositoryNotice: %v", err) | ||||
| 			} | ||||
| 		} | ||||
| @ -191,7 +191,7 @@ func ReinitMissingRepositories(ctx context.Context) error { | ||||
| 		log.Trace("Initializing %d/%d...", repo.OwnerID, repo.ID) | ||||
| 		if err := git.InitRepository(ctx, repo.RepoPath(), true); err != nil { | ||||
| 			log.Error("Unable (re)initialize repository %d at %s. Error: %v", repo.ID, repo.RepoPath(), err) | ||||
| 			if err2 := admin_model.CreateRepositoryNotice("InitRepository [%d]: %v", repo.ID, err); err2 != nil { | ||||
| 			if err2 := system_model.CreateRepositoryNotice("InitRepository [%d]: %v", repo.ID, err); err2 != nil { | ||||
| 				log.Error("CreateRepositoryNotice: %v", err2) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| @ -9,12 +9,12 @@ import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/models" | ||||
| 	admin_model "code.gitea.io/gitea/models/admin" | ||||
| 	"code.gitea.io/gitea/models/db" | ||||
| 	issues_model "code.gitea.io/gitea/models/issues" | ||||
| 	"code.gitea.io/gitea/models/organization" | ||||
| 	packages_model "code.gitea.io/gitea/models/packages" | ||||
| 	repo_model "code.gitea.io/gitea/models/repo" | ||||
| 	system_model "code.gitea.io/gitea/models/system" | ||||
| 	"code.gitea.io/gitea/models/unit" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| @ -83,8 +83,8 @@ func PushCreateRepo(authUser, owner *user_model.User, repoName string) (*repo_mo | ||||
| // Init start repository service | ||||
| func Init() error { | ||||
| 	repo_module.LoadRepoConfig() | ||||
| 	admin_model.RemoveAllWithNotice(db.DefaultContext, "Clean up temporary repository uploads", setting.Repository.Upload.TempPath) | ||||
| 	admin_model.RemoveAllWithNotice(db.DefaultContext, "Clean up temporary repositories", repo_module.LocalCopyPath()) | ||||
| 	system_model.RemoveAllWithNotice(db.DefaultContext, "Clean up temporary repository uploads", setting.Repository.Upload.TempPath) | ||||
| 	system_model.RemoveAllWithNotice(db.DefaultContext, "Clean up temporary repositories", repo_module.LocalCopyPath()) | ||||
| 	return initPushQueue() | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -13,12 +13,12 @@ import ( | ||||
| 	"time" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/models" | ||||
| 	admin_model "code.gitea.io/gitea/models/admin" | ||||
| 	asymkey_model "code.gitea.io/gitea/models/asymkey" | ||||
| 	"code.gitea.io/gitea/models/db" | ||||
| 	"code.gitea.io/gitea/models/organization" | ||||
| 	packages_model "code.gitea.io/gitea/models/packages" | ||||
| 	repo_model "code.gitea.io/gitea/models/repo" | ||||
| 	system_model "code.gitea.io/gitea/models/system" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/modules/avatar" | ||||
| 	"code.gitea.io/gitea/modules/eventsource" | ||||
| @ -186,7 +186,7 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error { | ||||
| 	path := user_model.UserPath(u.Name) | ||||
| 	if err := util.RemoveAll(path); err != nil { | ||||
| 		err = fmt.Errorf("Failed to RemoveAll %s: %v", path, err) | ||||
| 		_ = admin_model.CreateNotice(ctx, admin_model.NoticeTask, fmt.Sprintf("delete user '%s': %v", u.Name, err)) | ||||
| 		_ = system_model.CreateNotice(ctx, system_model.NoticeTask, fmt.Sprintf("delete user '%s': %v", u.Name, err)) | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| @ -194,7 +194,7 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error { | ||||
| 		avatarPath := u.CustomAvatarRelativePath() | ||||
| 		if err := storage.Avatars.Delete(avatarPath); err != nil { | ||||
| 			err = fmt.Errorf("Failed to remove %s: %v", avatarPath, err) | ||||
| 			_ = admin_model.CreateNotice(ctx, admin_model.NoticeTask, fmt.Sprintf("delete user '%s': %v", u.Name, err)) | ||||
| 			_ = system_model.CreateNotice(ctx, system_model.NoticeTask, fmt.Sprintf("delete user '%s': %v", u.Name, err)) | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @ -12,8 +12,8 @@ import ( | ||||
| 	"os" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	admin_model "code.gitea.io/gitea/models/admin" | ||||
| 	repo_model "code.gitea.io/gitea/models/repo" | ||||
| 	system_model "code.gitea.io/gitea/models/system" | ||||
| 	"code.gitea.io/gitea/models/unit" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
| @ -384,6 +384,6 @@ func DeleteWiki(ctx context.Context, repo *repo_model.Repository) error { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	admin_model.RemoveAllWithNotice(ctx, "Delete repository wiki", repo.WikiPath()) | ||||
| 	system_model.RemoveAllWithNotice(ctx, "Delete repository wiki", repo.WikiPath()) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @ -301,10 +301,18 @@ | ||||
| 		<div class="ui attached table segment"> | ||||
| 			<dl class="dl-horizontal admin-dl-horizontal"> | ||||
| 				<dt>{{.locale.Tr "admin.config.disable_gravatar"}}</dt> | ||||
| 				<dd>{{if .DisableGravatar}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd> | ||||
| 				<dd> | ||||
| 					<div class="ui toggle checkbox"> | ||||
| 						<input type="checkbox" name="picture.disable_gravatar" version="{{.SystemSettings.GetVersion "picture.disable_gravatar"}}"{{if .SystemSettings.GetBool "picture.disable_gravatar"}} checked{{end}}> | ||||
| 					</div> | ||||
| 				</dd> | ||||
| 				<div class="ui divider"></div> | ||||
| 				<dt>{{.locale.Tr "admin.config.enable_federated_avatar"}}</dt> | ||||
| 				<dd>{{if .EnableFederatedAvatar}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd> | ||||
| 				<dd> | ||||
| 					<div class="ui toggle checkbox"> | ||||
| 						<input type="checkbox" name="picture.enable_federated_avatar" version="{{.SystemSettings.GetVersion "picture.enable_federated_avatar"}}"{{if .SystemSettings.GetBool "picture.enable_federated_avatar"}} checked{{end}}> | ||||
| 					</div> | ||||
| 				</dd> | ||||
| 			</dl> | ||||
| 		</div> | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										37
									
								
								web_src/js/features/admin/config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								web_src/js/features/admin/config.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | ||||
| import $ from 'jquery'; | ||||
| import {showTemporaryTooltip} from '../../modules/tippy.js'; | ||||
| 
 | ||||
| const {appSubUrl, csrfToken, pageData} = window.config; | ||||
| 
 | ||||
| export function initAdminConfigs() { | ||||
|   const isAdminConfigPage = pageData?.adminConfigPage; | ||||
|   if (!isAdminConfigPage) return; | ||||
| 
 | ||||
|   $("input[type='checkbox']").on('change', (e) => { | ||||
|     const $this = $(e.currentTarget); | ||||
|     $.ajax({ | ||||
|       url: `${appSubUrl}/admin/config`, | ||||
|       type: 'POST', | ||||
|       data: { | ||||
|         _csrf: csrfToken, | ||||
|         key: $this.attr('name'), | ||||
|         value: $this.is(':checked'), | ||||
|         version: $this.attr('version'), | ||||
|       } | ||||
|     }).done((resp) => { | ||||
|       if (resp) { | ||||
|         if (resp.redirect) { | ||||
|           window.location.href = resp.redirect; | ||||
|         } else if (resp.version) { | ||||
|           $this.attr('version', resp.version); | ||||
|         } else if (resp.err) { | ||||
|           showTemporaryTooltip(e.currentTarget, resp.err); | ||||
|           $this.prop('checked', !$this.is(':checked')); | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
| 
 | ||||
|     e.preventDefault(); | ||||
|     return false; | ||||
|   }); | ||||
| } | ||||
| @ -16,7 +16,8 @@ import initRepoMigration from './features/repo-migration.js'; | ||||
| import initRepoProject from './features/repo-projects.js'; | ||||
| import initServiceWorker from './features/serviceworker.js'; | ||||
| import initTableSort from './features/tablesort.js'; | ||||
| import {initAdminUserListSearchForm} from './features/admin-users.js'; | ||||
| import {initAdminUserListSearchForm} from './features/admin/users.js'; | ||||
| import {initAdminConfigs} from './features/admin/config.js'; | ||||
| import {initMarkupAnchors} from './markup/anchors.js'; | ||||
| import {initNotificationCount, initNotificationsTable} from './features/notification.js'; | ||||
| import {initRepoIssueContentHistory} from './features/repo-issue-content.js'; | ||||
| @ -60,8 +61,8 @@ import { | ||||
|   initGlobalTooltips, | ||||
| } from './features/common-global.js'; | ||||
| import {initRepoTopicBar} from './features/repo-home.js'; | ||||
| import {initAdminEmails} from './features/admin-emails.js'; | ||||
| import {initAdminCommon} from './features/admin-common.js'; | ||||
| import {initAdminEmails} from './features/admin/emails.js'; | ||||
| import {initAdminCommon} from './features/admin/common.js'; | ||||
| import {initRepoTemplateSearch} from './features/repo-template.js'; | ||||
| import {initRepoCodeView} from './features/repo-code.js'; | ||||
| import {initSshKeyFormParser} from './features/sshkey-helper.js'; | ||||
| @ -139,6 +140,7 @@ $(document).ready(() => { | ||||
|   initAdminCommon(); | ||||
|   initAdminEmails(); | ||||
|   initAdminUserListSearchForm(); | ||||
|   initAdminConfigs(); | ||||
| 
 | ||||
|   initDashboardRepoList(); | ||||
| 
 | ||||
|  | ||||
| @ -131,6 +131,7 @@ | ||||
|   --color-timeline: #ececec; | ||||
|   --color-input-text: #212121; | ||||
|   --color-input-background: #ffffff; | ||||
|   --color-input-toggle-background: #dedede; | ||||
|   --color-input-border: #dedede; | ||||
|   --color-input-border-hover: #cecece; | ||||
|   --color-navbar: #f8f8f8; | ||||
|  | ||||
| @ -113,7 +113,7 @@ textarea:focus, | ||||
| } | ||||
| 
 | ||||
| .ui.toggle.checkbox label::before { | ||||
|   background: var(--color-input-background); | ||||
|   background: var(--color-input-toggle-background); | ||||
| } | ||||
| 
 | ||||
| .ui.toggle.checkbox label, | ||||
|  | ||||
| @ -106,6 +106,7 @@ | ||||
|   --color-timeline: #4c525e; | ||||
|   --color-input-text: #d5dbe6; | ||||
|   --color-input-background: #232933; | ||||
|   --color-input-toggle-background: #454a57; | ||||
|   --color-input-border: #454a57; | ||||
|   --color-input-border-hover: #505667; | ||||
|   --color-navbar: #2a2e3a; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 GitHub
							GitHub