diff --git a/Makefile b/Makefile index 4bd93acb89..13a2d064fe 100644 --- a/Makefile +++ b/Makefile @@ -519,7 +519,8 @@ test-mssql-migration: migrations.mssql.test migrations.individual.mssql.test .PHONY: playwright playwright: deps-frontend - @pnpm exec playwright install --with-deps chromium firefox webkit $(PLAYWRIGHT_FLAGS) + @# on GitHub Actions VMs, playwright's system deps are pre-installed + @pnpm exec playwright install $(if $(GITHUB_ACTIONS),,--with-deps) chromium firefox $(PLAYWRIGHT_FLAGS) .PHONY: test-e2e test-e2e: $(EXECUTABLE_E2E) diff --git a/models/actions/run_list.go b/models/actions/run_list.go index 2628c4712f..8b8c132a48 100644 --- a/models/actions/run_list.go +++ b/models/actions/run_list.go @@ -7,7 +7,6 @@ import ( "context" "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/translation" @@ -25,12 +24,6 @@ func (runs RunList) GetUserIDs() []int64 { }) } -func (runs RunList) GetRepoIDs() []int64 { - return container.FilterSlice(runs, func(run *ActionRun) (int64, bool) { - return run.RepoID, true - }) -} - func (runs RunList) LoadTriggerUser(ctx context.Context) error { userIDs := runs.GetUserIDs() users := make(map[int64]*user_model.User, len(userIDs)) @@ -50,18 +43,6 @@ func (runs RunList) LoadTriggerUser(ctx context.Context) error { return nil } -func (runs RunList) LoadRepos(ctx context.Context) error { - repoIDs := runs.GetRepoIDs() - repos, err := repo_model.GetRepositoriesMapByIDs(ctx, repoIDs) - if err != nil { - return err - } - for _, run := range runs { - run.Repo = repos[run.RepoID] - } - return nil -} - type FindRunOptions struct { db.ListOptions RepoID int64 diff --git a/models/actions/schedule_list.go b/models/actions/schedule_list.go index 5361b94801..6b5cae94fe 100644 --- a/models/actions/schedule_list.go +++ b/models/actions/schedule_list.go @@ -4,62 +4,13 @@ package actions import ( - "context" - "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/container" "xorm.io/builder" ) type ScheduleList []*ActionSchedule -// GetUserIDs returns a slice of user's id -func (schedules ScheduleList) GetUserIDs() []int64 { - return container.FilterSlice(schedules, func(schedule *ActionSchedule) (int64, bool) { - return schedule.TriggerUserID, true - }) -} - -func (schedules ScheduleList) GetRepoIDs() []int64 { - return container.FilterSlice(schedules, func(schedule *ActionSchedule) (int64, bool) { - return schedule.RepoID, true - }) -} - -func (schedules ScheduleList) LoadTriggerUser(ctx context.Context) error { - userIDs := schedules.GetUserIDs() - users := make(map[int64]*user_model.User, len(userIDs)) - if err := db.GetEngine(ctx).In("id", userIDs).Find(&users); err != nil { - return err - } - for _, schedule := range schedules { - if schedule.TriggerUserID == user_model.ActionsUserID { - schedule.TriggerUser = user_model.NewActionsUser() - } else { - schedule.TriggerUser = users[schedule.TriggerUserID] - if schedule.TriggerUser == nil { - schedule.TriggerUser = user_model.NewGhostUser() - } - } - } - return nil -} - -func (schedules ScheduleList) LoadRepos(ctx context.Context) error { - repoIDs := schedules.GetRepoIDs() - repos, err := repo_model.GetRepositoriesMapByIDs(ctx, repoIDs) - if err != nil { - return err - } - for _, schedule := range schedules { - schedule.Repo = repos[schedule.RepoID] - } - return nil -} - type FindScheduleOptions struct { db.ListOptions RepoID int64 diff --git a/models/activities/action_list.go b/models/activities/action_list.go index 29ff2fdf7a..5b07a8e080 100644 --- a/models/activities/action_list.go +++ b/models/activities/action_list.go @@ -282,9 +282,3 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err return actions, count, nil } - -func CountUserFeeds(ctx context.Context, userID int64) (int64, error) { - return db.GetEngine(ctx).Where("user_id = ?", userID). - And("is_deleted = ?", false). - Count(&Action{}) -} diff --git a/models/asymkey/error.go b/models/asymkey/error.go index b765624579..5df7beb8cd 100644 --- a/models/asymkey/error.go +++ b/models/asymkey/error.go @@ -192,28 +192,6 @@ func (err ErrGPGKeyIDAlreadyUsed) Unwrap() error { return util.ErrAlreadyExist } -// ErrGPGKeyAccessDenied represents a "GPGKeyAccessDenied" kind of Error. -type ErrGPGKeyAccessDenied struct { - UserID int64 - KeyID int64 -} - -// IsErrGPGKeyAccessDenied checks if an error is a ErrGPGKeyAccessDenied. -func IsErrGPGKeyAccessDenied(err error) bool { - _, ok := err.(ErrGPGKeyAccessDenied) - return ok -} - -// Error pretty-prints an error of type ErrGPGKeyAccessDenied. -func (err ErrGPGKeyAccessDenied) Error() string { - return fmt.Sprintf("user does not have access to the key [user_id: %d, key_id: %d]", - err.UserID, err.KeyID) -} - -func (err ErrGPGKeyAccessDenied) Unwrap() error { - return util.ErrPermissionDenied -} - // ErrKeyAccessDenied represents a "KeyAccessDenied" kind of error. type ErrKeyAccessDenied struct { UserID int64 diff --git a/models/asymkey/ssh_key_deploy.go b/models/asymkey/ssh_key_deploy.go index 4ab84eabcf..ea3d93e8c8 100644 --- a/models/asymkey/ssh_key_deploy.go +++ b/models/asymkey/ssh_key_deploy.go @@ -105,14 +105,6 @@ func addDeployKey(ctx context.Context, keyID, repoID int64, name, fingerprint st return key, db.Insert(ctx, key) } -// HasDeployKey returns true if public key is a deploy key of given repository. -func HasDeployKey(ctx context.Context, keyID, repoID int64) bool { - has, _ := db.GetEngine(ctx). - Where("key_id = ? AND repo_id = ?", keyID, repoID). - Get(new(DeployKey)) - return has -} - // AddDeployKey add new deploy key to database and authorized_keys file. func AddDeployKey(ctx context.Context, repoID int64, name, content string, readOnly bool) (*DeployKey, error) { fingerprint, err := CalcFingerprint(content) diff --git a/models/auth/webauthn.go b/models/auth/webauthn.go index 6d8b542957..7bd79ed3f5 100644 --- a/models/auth/webauthn.go +++ b/models/auth/webauthn.go @@ -200,13 +200,3 @@ func DeleteCredential(ctx context.Context, id, userID int64) (bool, error) { had, err := db.GetEngine(ctx).ID(id).Where("user_id = ?", userID).Delete(&WebAuthnCredential{}) return had > 0, err } - -// WebAuthnCredentials implements the webauthn.User interface -func WebAuthnCredentials(ctx context.Context, userID int64) ([]webauthn.Credential, error) { - dbCreds, err := GetWebAuthnCredentialsByUID(ctx, userID) - if err != nil { - return nil, err - } - - return dbCreds.ToCredentials(), nil -} diff --git a/models/git/branch_list.go b/models/git/branch_list.go index 25e84526d2..1445f3a5a0 100644 --- a/models/git/branch_list.go +++ b/models/git/branch_list.go @@ -7,7 +7,6 @@ import ( "context" "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/optional" @@ -60,24 +59,6 @@ func (branches BranchList) LoadPusher(ctx context.Context) error { return nil } -func (branches BranchList) LoadRepo(ctx context.Context) error { - ids := container.FilterSlice(branches, func(branch *Branch) (int64, bool) { - return branch.RepoID, branch.RepoID > 0 && branch.Repo == nil - }) - - reposMap := make(map[int64]*repo_model.Repository, len(ids)) - if err := db.GetEngine(ctx).In("id", ids).Find(&reposMap); err != nil { - return err - } - for _, branch := range branches { - if branch.RepoID <= 0 || branch.Repo != nil { - continue - } - branch.Repo = reposMap[branch.RepoID] - } - return nil -} - type FindBranchOptions struct { db.ListOptions RepoID int64 diff --git a/models/issues/dependency.go b/models/issues/dependency.go index 0eaa47e359..db8054e161 100644 --- a/models/issues/dependency.go +++ b/models/issues/dependency.go @@ -89,12 +89,6 @@ type ErrUnknownDependencyType struct { Type DependencyType } -// IsErrUnknownDependencyType checks if an error is ErrUnknownDependencyType -func IsErrUnknownDependencyType(err error) bool { - _, ok := err.(ErrUnknownDependencyType) - return ok -} - func (err ErrUnknownDependencyType) Error() string { return fmt.Sprintf("unknown dependency type [type: %d]", err.Type) } diff --git a/models/issues/issue.go b/models/issues/issue.go index 838d41a300..ec58cd04f6 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -48,21 +48,6 @@ func (err ErrIssueNotExist) Unwrap() error { return util.ErrNotExist } -// ErrNewIssueInsert is used when the INSERT statement in newIssue fails -type ErrNewIssueInsert struct { - OriginalError error -} - -// IsErrNewIssueInsert checks if an error is a ErrNewIssueInsert. -func IsErrNewIssueInsert(err error) bool { - _, ok := err.(ErrNewIssueInsert) - return ok -} - -func (err ErrNewIssueInsert) Error() string { - return err.OriginalError.Error() -} - var ErrIssueAlreadyChanged = util.NewInvalidArgumentErrorf("the issue is already changed") // Issue represents an issue or pull request of repository. diff --git a/models/issues/issue_pin.go b/models/issues/issue_pin.go index ae6195b05d..753c96ed18 100644 --- a/models/issues/issue_pin.go +++ b/models/issues/issue_pin.go @@ -165,27 +165,6 @@ func MovePin(ctx context.Context, issue *Issue, newPosition int) error { }) } -func GetPinnedIssueIDs(ctx context.Context, repoID int64, isPull bool) ([]int64, error) { - var issuePins []IssuePin - if err := db.GetEngine(ctx). - Table("issue_pin"). - Where("repo_id = ?", repoID). - And("is_pull = ?", isPull). - Find(&issuePins); err != nil { - return nil, err - } - - sort.Slice(issuePins, func(i, j int) bool { - return issuePins[i].PinOrder < issuePins[j].PinOrder - }) - - var ids []int64 - for _, pin := range issuePins { - ids = append(ids, pin.IssueID) - } - return ids, nil -} - func GetIssuePinsByRepoID(ctx context.Context, repoID int64, isPull bool) ([]*IssuePin, error) { var pins []*IssuePin if err := db.GetEngine(ctx).Where("repo_id = ? AND is_pull = ?", repoID, isPull).Find(&pins); err != nil { diff --git a/models/issues/issue_update.go b/models/issues/issue_update.go index 01a3eb9a2a..c58a2f319d 100644 --- a/models/issues/issue_update.go +++ b/models/issues/issue_update.go @@ -93,12 +93,6 @@ type ErrIssueIsOpen struct { Index int64 } -// IsErrIssueIsOpen checks if an error is a ErrIssueIsOpen. -func IsErrIssueIsOpen(err error) bool { - _, ok := err.(ErrIssueIsOpen) - return ok -} - func (err ErrIssueIsOpen) Error() string { return fmt.Sprintf("%s [id: %d, repo_id: %d, index: %d] is already open", util.Iif(err.IsPull, "Pull Request", "Issue"), err.ID, err.RepoID, err.Index) } @@ -441,7 +435,7 @@ func NewIssue(ctx context.Context, repo *repo_model.Repository, issue *Issue, la LabelIDs: labelIDs, Attachments: uuids, }); err != nil { - if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) { + if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) { return err } return fmt.Errorf("newIssue: %w", err) diff --git a/models/issues/pull.go b/models/issues/pull.go index 1b883f2981..1fb42915f6 100644 --- a/models/issues/pull.go +++ b/models/issues/pull.go @@ -475,7 +475,7 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *Iss LabelIDs: labelIDs, Attachments: uuids, }); err != nil { - if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) { + if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) { return err } return fmt.Errorf("newIssue: %w", err) diff --git a/models/organization/team_unit.go b/models/organization/team_unit.go index c6ec6b39b2..b5237c2c58 100644 --- a/models/organization/team_unit.go +++ b/models/organization/team_unit.go @@ -28,19 +28,3 @@ func (t *TeamUnit) Unit() unit.Unit { func getUnitsByTeamID(ctx context.Context, teamID int64) (units []*TeamUnit, err error) { return units, db.GetEngine(ctx).Where("team_id = ?", teamID).Find(&units) } - -// UpdateTeamUnits updates a teams's units -func UpdateTeamUnits(ctx context.Context, team *Team, units []TeamUnit) (err error) { - return db.WithTx(ctx, func(ctx context.Context) error { - if _, err = db.GetEngine(ctx).Where("team_id = ?", team.ID).Delete(new(TeamUnit)); err != nil { - return err - } - - if len(units) > 0 { - if err = db.Insert(ctx, units); err != nil { - return err - } - } - return nil - }) -} diff --git a/models/organization/team_user.go b/models/organization/team_user.go index d6d0a5054d..d24a4c5126 100644 --- a/models/organization/team_user.go +++ b/models/organization/team_user.go @@ -36,14 +36,6 @@ type SearchMembersOptions struct { TeamID int64 } -func (opts SearchMembersOptions) ToConds() builder.Cond { - cond := builder.NewCond() - if opts.TeamID > 0 { - cond = cond.And(builder.Eq{"": opts.TeamID}) - } - return cond -} - // GetTeamMembers returns all members in given team of organization. func GetTeamMembers(ctx context.Context, opts *SearchMembersOptions) ([]*user_model.User, error) { var members []*user_model.User diff --git a/models/project/column.go b/models/project/column.go index 997e82ddf9..9c9abb4599 100644 --- a/models/project/column.go +++ b/models/project/column.go @@ -337,20 +337,6 @@ func SetDefaultColumn(ctx context.Context, projectID, columnID int64) error { }) } -// UpdateColumnSorting update project column sorting -func UpdateColumnSorting(ctx context.Context, cl ColumnList) error { - return db.WithTx(ctx, func(ctx context.Context) error { - for i := range cl { - if _, err := db.GetEngine(ctx).ID(cl[i].ID).Cols( - "sorting", - ).Update(cl[i]); err != nil { - return err - } - } - return nil - }) -} - func GetColumnsByIDs(ctx context.Context, projectID int64, columnsIDs []int64) (ColumnList, error) { columns := make([]*Column, 0, 5) if len(columnsIDs) == 0 { diff --git a/models/repo/topic.go b/models/repo/topic.go index 6d5209d821..14017da2d0 100644 --- a/models/repo/topic.go +++ b/models/repo/topic.go @@ -44,12 +44,6 @@ type ErrTopicNotExist struct { Name string } -// IsErrTopicNotExist checks if an error is an ErrTopicNotExist. -func IsErrTopicNotExist(err error) bool { - _, ok := err.(ErrTopicNotExist) - return ok -} - // Error implements error interface func (err ErrTopicNotExist) Error() string { return fmt.Sprintf("topic is not exist [name: %s]", err.Name) diff --git a/models/system/notice.go b/models/system/notice.go index f39188f8fb..4b919dffc9 100644 --- a/models/system/notice.go +++ b/models/system/notice.go @@ -13,7 +13,6 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" ) // NoticeType describes the notice type @@ -60,18 +59,6 @@ func CreateRepositoryNotice(desc string, args ...any) error { return CreateNotice(graceful.GetManager().ShutdownContext(), NoticeRepository, desc, args...) } -// RemoveAllWithNotice removes all directories in given path and -// creates a system notice when error occurs. -func RemoveAllWithNotice(ctx context.Context, title, path string) { - if err := util.RemoveAll(path); err != nil { - desc := fmt.Sprintf("%s [%s]: %v", title, path, err) - log.Warn(title+" [%s]: %v", path, err) - if err = CreateNotice(graceful.GetManager().ShutdownContext(), NoticeRepository, desc); err != nil { - log.Error("CreateRepositoryNotice: %v", err) - } - } -} - // RemoveStorageWithNotice removes a file from the storage and // creates a system notice when error occurs. func RemoveStorageWithNotice(ctx context.Context, bucket storage.ObjectStorage, title, path string) { diff --git a/models/user/badge.go b/models/user/badge.go index fbba865926..a41eda07b5 100644 --- a/models/user/badge.go +++ b/models/user/badge.go @@ -212,12 +212,6 @@ func RemoveUserBadges(ctx context.Context, u *User, badges []*Badge) error { }) } -// RemoveAllUserBadges removes all badges from a user. -func RemoveAllUserBadges(ctx context.Context, u *User) error { - _, err := db.GetEngine(ctx).Where("user_id=?", u.ID).Delete(&UserBadge{}) - return err -} - // SearchBadgeOptions represents the options when finding badges type SearchBadgeOptions struct { db.ListOptions @@ -258,16 +252,3 @@ func (opts *SearchBadgeOptions) ToOrders() string { func SearchBadges(ctx context.Context, opts *SearchBadgeOptions) ([]*Badge, int64, error) { return db.FindAndCount[Badge](ctx, opts) } - -// GetBadgeByID returns a specific badge by ID -func GetBadgeByID(ctx context.Context, id int64) (*Badge, error) { - badge := new(Badge) - has, err := db.GetEngine(ctx).ID(id).Get(badge) - if err != nil { - return nil, err - } - if !has { - return nil, util.NewNotExistErrorf("badge does not exist [id: %d]", id) - } - return badge, nil -} diff --git a/models/user/email_address.go b/models/user/email_address.go index aa483d5f00..670d417f9e 100644 --- a/models/user/email_address.go +++ b/models/user/email_address.go @@ -147,11 +147,6 @@ func InsertEmailAddress(ctx context.Context, email *EmailAddress) (*EmailAddress return email, nil } -func UpdateEmailAddress(ctx context.Context, email *EmailAddress) error { - _, err := db.GetEngine(ctx).ID(email.ID).AllCols().Update(email) - return err -} - // ValidateEmail check if email is a valid & allowed address func ValidateEmail(email string) error { if err := validateEmailBasic(email); err != nil { diff --git a/models/user/error.go b/models/user/error.go index cbf19998d1..5a956a2afe 100644 --- a/models/user/error.go +++ b/models/user/error.go @@ -71,27 +71,6 @@ func (err ErrUserProhibitLogin) Unwrap() error { return util.ErrPermissionDenied } -// ErrUserInactive represents a "ErrUserInactive" kind of error. -type ErrUserInactive struct { - UID int64 - Name string -} - -// IsErrUserInactive checks if an error is a ErrUserInactive -func IsErrUserInactive(err error) bool { - _, ok := err.(ErrUserInactive) - return ok -} - -func (err ErrUserInactive) Error() string { - return fmt.Sprintf("user is inactive [uid: %d, name: %s]", err.UID, err.Name) -} - -// Unwrap unwraps this error as a ErrPermission error -func (err ErrUserInactive) Unwrap() error { - return util.ErrPermissionDenied -} - // ErrUserIsNotLocal represents a "ErrUserIsNotLocal" kind of error. type ErrUserIsNotLocal struct { UID int64 diff --git a/models/user/external_login_user.go b/models/user/external_login_user.go index 0e764efb9f..636a2007ee 100644 --- a/models/user/external_login_user.go +++ b/models/user/external_login_user.go @@ -21,12 +21,6 @@ type ErrExternalLoginUserAlreadyExist struct { LoginSourceID int64 } -// IsErrExternalLoginUserAlreadyExist checks if an error is a ExternalLoginUserAlreadyExist. -func IsErrExternalLoginUserAlreadyExist(err error) bool { - _, ok := err.(ErrExternalLoginUserAlreadyExist) - return ok -} - func (err ErrExternalLoginUserAlreadyExist) Error() string { return fmt.Sprintf("external login user already exists [externalID: %s, userID: %d, loginSourceID: %d]", err.ExternalID, err.UserID, err.LoginSourceID) } @@ -41,12 +35,6 @@ type ErrExternalLoginUserNotExist struct { LoginSourceID int64 } -// IsErrExternalLoginUserNotExist checks if an error is a ExternalLoginUserNotExist. -func IsErrExternalLoginUserNotExist(err error) bool { - _, ok := err.(ErrExternalLoginUserNotExist) - return ok -} - func (err ErrExternalLoginUserNotExist) Error() string { return fmt.Sprintf("external login user link does not exists [userID: %d, loginSourceID: %d]", err.UserID, err.LoginSourceID) } diff --git a/models/user/setting.go b/models/user/setting.go index a16fc86e55..67b45208fc 100644 --- a/models/user/setting.go +++ b/models/user/setting.go @@ -50,12 +50,6 @@ func (err ErrUserSettingIsNotExist) Unwrap() error { return util.ErrNotExist } -// IsErrUserSettingIsNotExist return true if err is ErrSettingIsNotExist -func IsErrUserSettingIsNotExist(err error) bool { - _, ok := err.(ErrUserSettingIsNotExist) - return ok -} - // genSettingCacheKey returns the cache key for some configuration func genSettingCacheKey(userID int64, key string) string { return fmt.Sprintf("user_%d.setting.%s", userID, key) diff --git a/modules/actions/jobparser/jobparser.go b/modules/actions/jobparser/jobparser.go index 1d4c4c1756..76f229a54b 100644 --- a/modules/actions/jobparser/jobparser.go +++ b/modules/actions/jobparser/jobparser.go @@ -83,12 +83,6 @@ func Parse(content []byte, options ...ParseOption) ([]*SingleWorkflow, error) { return ret, nil } -func WithJobResults(results map[string]string) ParseOption { - return func(c *parseContext) { - c.jobResults = results - } -} - func WithGitContext(context *model.GithubContext) ParseOption { return func(c *parseContext) { c.gitContext = context diff --git a/modules/eventsource/event.go b/modules/eventsource/event.go index ebcca50903..c72cc466a0 100644 --- a/modules/eventsource/event.go +++ b/modules/eventsource/event.go @@ -7,7 +7,6 @@ import ( "bytes" "fmt" "io" - "strings" "time" "code.gitea.io/gitea/modules/json" @@ -110,9 +109,3 @@ func (e *Event) WriteTo(w io.Writer) (int64, error) { return sum, err } - -func (e *Event) String() string { - buf := new(strings.Builder) - _, _ = e.WriteTo(buf) - return buf.String() -} diff --git a/modules/gitrepo/commit.go b/modules/gitrepo/commit.go index 0ab17862fe..0a8cb0544c 100644 --- a/modules/gitrepo/commit.go +++ b/modules/gitrepo/commit.go @@ -44,23 +44,6 @@ func CommitsCount(ctx context.Context, repo Repository, opts CommitsCountOptions return strconv.ParseInt(strings.TrimSpace(stdout), 10, 64) } -// CommitsCountBetween return numbers of commits between two commits -func CommitsCountBetween(ctx context.Context, repo Repository, start, end string) (int64, error) { - count, err := CommitsCount(ctx, repo, CommitsCountOptions{ - Revision: []string{start + ".." + end}, - }) - - if err != nil && strings.Contains(err.Error(), "no merge base") { - // future versions of git >= 2.28 are likely to return an error if before and last have become unrelated. - // previously it would return the results of git rev-list before last so let's try that... - return CommitsCount(ctx, repo, CommitsCountOptions{ - Revision: []string{start, end}, - }) - } - - return count, err -} - // FileCommitsCount return the number of files at a revision func FileCommitsCount(ctx context.Context, repo Repository, revision, file string) (int64, error) { return CommitsCount(ctx, repo, diff --git a/modules/gitrepo/config.go b/modules/gitrepo/config.go index 9be3ef94ae..4940f1c078 100644 --- a/modules/gitrepo/config.go +++ b/modules/gitrepo/config.go @@ -5,21 +5,11 @@ package gitrepo import ( "context" - "strings" "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/globallock" ) -func GitConfigGet(ctx context.Context, repo Repository, key string) (string, error) { - result, _, err := RunCmdString(ctx, repo, gitcmd.NewCommand("config", "--get"). - AddDynamicArguments(key)) - if err != nil { - return "", err - } - return strings.TrimSpace(result), nil -} - func getRepoConfigLockKey(repoStoragePath string) string { return "repo-config:" + repoStoragePath } diff --git a/modules/hostmatcher/hostmatcher.go b/modules/hostmatcher/hostmatcher.go index 15c6371422..044dba679a 100644 --- a/modules/hostmatcher/hostmatcher.go +++ b/modules/hostmatcher/hostmatcher.go @@ -78,11 +78,6 @@ func (hl *HostMatchList) AppendBuiltin(builtin string) { hl.builtins = append(hl.builtins, builtin) } -// AppendPattern appends more pattern to match -func (hl *HostMatchList) AppendPattern(pattern string) { - hl.patterns = append(hl.patterns, pattern) -} - // IsEmpty checks if the checklist is empty func (hl *HostMatchList) IsEmpty() bool { return hl == nil || (len(hl.builtins) == 0 && len(hl.patterns) == 0 && len(hl.ipNets) == 0) diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go index f6a6cb26c6..1cc75d763d 100644 --- a/modules/markup/markdown/markdown.go +++ b/modules/markup/markdown/markdown.go @@ -74,10 +74,6 @@ func (r *GlodmarkRender) Convert(source []byte, writer io.Writer, opts ...parser return r.goldmarkMarkdown.Convert(source, writer, opts...) } -func (r *GlodmarkRender) Renderer() renderer.Renderer { - return r.goldmarkMarkdown.Renderer() -} - func (r *GlodmarkRender) highlightingRenderer(w util.BufWriter, c highlighting.CodeBlockContext, entering bool) { if entering { languageBytes, _ := c.Language() diff --git a/modules/storage/storage.go b/modules/storage/storage.go index e19c421ba8..1271440e5a 100644 --- a/modules/storage/storage.go +++ b/modules/storage/storage.go @@ -21,25 +21,6 @@ import ( // ErrURLNotSupported represents url is not supported var ErrURLNotSupported = errors.New("url method not supported") -// ErrInvalidConfiguration is called when there is invalid configuration for a storage -type ErrInvalidConfiguration struct { - cfg any - err error -} - -func (err ErrInvalidConfiguration) Error() string { - if err.err != nil { - return fmt.Sprintf("Invalid Configuration Argument: %v: Error: %v", err.cfg, err.err) - } - return fmt.Sprintf("Invalid Configuration Argument: %v", err.cfg) -} - -// IsErrInvalidConfiguration checks if an error is an ErrInvalidConfiguration -func IsErrInvalidConfiguration(err error) bool { - _, ok := err.(ErrInvalidConfiguration) - return ok -} - type Type = setting.StorageType // NewStorageFunc is a function that creates a storage diff --git a/modules/test/utils.go b/modules/test/utils.go index a55deeb2e3..331dc4a959 100644 --- a/modules/test/utils.go +++ b/modules/test/utils.go @@ -47,7 +47,7 @@ func ParseJSONError(buf []byte) (ret struct { } func ParseJSONRedirect(buf []byte) (ret struct { - Redirect string `json:"redirect"` + Redirect *string `json:"redirect"` }, ) { _ = json.Unmarshal(buf, &ret) diff --git a/modules/web/middleware/locale.go b/modules/web/middleware/locale.go index 34a16f04e7..fc396f0808 100644 --- a/modules/web/middleware/locale.go +++ b/modules/web/middleware/locale.go @@ -51,9 +51,3 @@ func Locale(resp http.ResponseWriter, req *http.Request) translation.Locale { func SetLocaleCookie(resp http.ResponseWriter, lang string, maxAge int) { SetSiteCookie(resp, "lang", lang, maxAge) } - -// DeleteLocaleCookie convenience function to delete the locale cookie consistently -// Setting the lang cookie will trigger the middleware to reset the language to previous state. -func DeleteLocaleCookie(resp http.ResponseWriter) { - SetSiteCookie(resp, "lang", "", -1) -} diff --git a/package.json b/package.json index b090b32098..9dcbf411d3 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,6 @@ "@replit/codemirror-lang-svelte": "6.0.0", "@replit/codemirror-vscode-keymap": "6.0.2", "@resvg/resvg-wasm": "2.6.2", - "@silverwind/vue3-calendar-heatmap": "2.1.1", "@vitejs/plugin-vue": "6.0.6", "ansi_up": "6.0.6", "asciinema-player": "3.15.1", diff --git a/playwright.config.ts b/playwright.config.ts index 8fdd777ee4..9dc8a7c1b5 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -36,11 +36,5 @@ export default defineConfig({ ...devices['Desktop Firefox'], }, }, - { - name: 'webkit', - use: { - ...devices['Desktop Safari'], - }, - }, ], }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a05156011e..fdc569187a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -112,9 +112,6 @@ importers: '@resvg/resvg-wasm': specifier: 2.6.2 version: 2.6.2 - '@silverwind/vue3-calendar-heatmap': - specifier: 2.1.1 - version: 2.1.1(tippy.js@6.3.7)(vue@3.5.32(typescript@6.0.2)) '@vitejs/plugin-vue': specifier: 6.0.6 version: 6.0.6(vite@8.0.8(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1))(vue@3.5.32(typescript@6.0.2)) @@ -1249,13 +1246,6 @@ packages: '@scarf/scarf@1.4.0': resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==} - '@silverwind/vue3-calendar-heatmap@2.1.1': - resolution: {integrity: sha512-RQtLOpkysm0LR3PbUoc+aDcYxzy7xboygb1SQEwrUm2/XB2nmt0BEra2ADXpu4kwFxtk0+IyNwzFvbBai/wvTg==} - engines: {node: '>=16'} - peerDependencies: - tippy.js: ^6.3.7 - vue: ^3.2.29 - '@simonwep/pickr@1.9.0': resolution: {integrity: sha512-oEYvv15PyfZzjoAzvXYt3UyNGwzsrpFxLaZKzkOSd0WYBVwLd19iJerePDONxC1iF6+DpcswPdLIM2KzCJuYFg==} @@ -5052,11 +5042,6 @@ snapshots: '@scarf/scarf@1.4.0': {} - '@silverwind/vue3-calendar-heatmap@2.1.1(tippy.js@6.3.7)(vue@3.5.32(typescript@6.0.2))': - dependencies: - tippy.js: 6.3.7 - vue: 3.5.32(typescript@6.0.2) - '@simonwep/pickr@1.9.0': dependencies: core-js: 3.32.2 diff --git a/routers/api/v1/user/gpg_key.go b/routers/api/v1/user/gpg_key.go index 9ec4d2c938..39ded31bf4 100644 --- a/routers/api/v1/user/gpg_key.go +++ b/routers/api/v1/user/gpg_key.go @@ -281,11 +281,7 @@ func DeleteGPGKey(ctx *context.APIContext) { } if err := asymkey_model.DeleteGPGKey(ctx, ctx.Doer, ctx.PathParamInt64("id")); err != nil { - if asymkey_model.IsErrGPGKeyAccessDenied(err) { - ctx.APIError(http.StatusForbidden, "You do not have access to this key") - } else { - ctx.APIErrorInternal(err) - } + ctx.APIErrorInternal(err) return } @@ -295,8 +291,6 @@ func DeleteGPGKey(ctx *context.APIContext) { // HandleAddGPGKeyError handle add GPGKey error func HandleAddGPGKeyError(ctx *context.APIContext, err error, token string) { switch { - case asymkey_model.IsErrGPGKeyAccessDenied(err): - ctx.APIError(http.StatusUnprocessableEntity, "You do not have access to this GPG key") case asymkey_model.IsErrGPGKeyIDAlreadyUsed(err): ctx.APIError(http.StatusUnprocessableEntity, "A key with the same id already exists") case asymkey_model.IsErrGPGKeyParsing(err): diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index d705062b3d..eb2045781d 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -314,15 +314,6 @@ func SignInPost(ctx *context.Context) { log.Warn("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err) ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") ctx.HTML(http.StatusOK, "user/auth/prohibit_login") - } else if user_model.IsErrUserInactive(err) { - if setting.Service.RegisterEmailConfirm { - ctx.Data["Title"] = ctx.Tr("auth.active_your_account") - ctx.HTML(http.StatusOK, TplActivate) - } else { - log.Warn("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err) - ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") - ctx.HTML(http.StatusOK, "user/auth/prohibit_login") - } } else { ctx.ServerError("UserSignIn", err) } diff --git a/routers/web/auth/linkaccount.go b/routers/web/auth/linkaccount.go index 02e1b7acd2..ea3aa3f7ca 100644 --- a/routers/web/auth/linkaccount.go +++ b/routers/web/auth/linkaccount.go @@ -105,16 +105,6 @@ func handleSignInError(ctx *context.Context, userName string, ptrForm any, tmpl log.Info("Failed authentication attempt for %s from %s: %v", userName, ctx.RemoteAddr(), err) ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") ctx.HTML(http.StatusOK, "user/auth/prohibit_login") - } else if user_model.IsErrUserInactive(err) { - ctx.Data["user_exists"] = true - if setting.Service.RegisterEmailConfirm { - ctx.Data["Title"] = ctx.Tr("auth.active_your_account") - ctx.HTML(http.StatusOK, TplActivate) - } else { - log.Info("Failed authentication attempt for %s from %s: %v", userName, ctx.RemoteAddr(), err) - ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") - ctx.HTML(http.StatusOK, "user/auth/prohibit_login") - } } else { ctx.ServerError(invoker, err) } diff --git a/routers/web/home.go b/routers/web/home.go index 7efa5f344e..d14a7bab13 100644 --- a/routers/web/home.go +++ b/routers/web/home.go @@ -109,9 +109,3 @@ func HomeSitemap(ctx *context.Context) { log.Error("Failed writing sitemap: %v", err) } } - -// NotFound render 404 page -func NotFound(ctx *context.Context) { - ctx.Data["Title"] = "Page Not Found" - ctx.NotFound(nil) -} diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 4cdf81c155..59aee81974 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -35,14 +35,6 @@ const ( tplProjectsView templates.TplName = "org/projects/view" ) -// MustEnableProjects check if projects are enabled in settings -func MustEnableProjects(ctx *context.Context) { - if unit.TypeProjects.UnitGlobalDisabled() { - ctx.NotFound(nil) - return - } -} - // Projects renders the home page of projects func Projects(ctx *context.Context) { if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil { diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index 29f3e62b8f..118f16d8f1 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -725,16 +725,16 @@ func handleSettingsPostConvert(ctx *context.Context) { form := web.GetForm(ctx).(*forms.RepoSettingForm) repo := ctx.Repo.Repository if !ctx.Repo.IsOwner() { - ctx.HTTPError(http.StatusNotFound) + ctx.JSONErrorNotFound() return } if repo.Name != form.RepoName { - ctx.RenderWithErrDeprecated(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil) + ctx.JSONError(ctx.Tr("form.enterred_invalid_repo_name")) return } if !repo.IsMirror { - ctx.HTTPError(http.StatusNotFound) + ctx.JSONErrorNotFound() return } repo.IsMirror = false @@ -748,14 +748,14 @@ func handleSettingsPostConvert(ctx *context.Context) { } log.Trace("Repository converted from mirror to regular: %s", repo.FullName()) ctx.Flash.Success(ctx.Tr("repo.settings.convert_succeed")) - ctx.Redirect(repo.Link()) + ctx.JSONRedirect(repo.Link()) } func handleSettingsPostConvertFork(ctx *context.Context) { form := web.GetForm(ctx).(*forms.RepoSettingForm) repo := ctx.Repo.Repository if !ctx.Repo.IsOwner() { - ctx.HTTPError(http.StatusNotFound) + ctx.JSONErrorNotFound() return } if err := repo.LoadOwner(ctx); err != nil { @@ -763,12 +763,12 @@ func handleSettingsPostConvertFork(ctx *context.Context) { return } if repo.Name != form.RepoName { - ctx.RenderWithErrDeprecated(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil) + ctx.JSONError(ctx.Tr("form.enterred_invalid_repo_name")) return } if !repo.IsFork { - ctx.HTTPError(http.StatusNotFound) + ctx.JSONErrorNotFound() return } @@ -776,7 +776,7 @@ func handleSettingsPostConvertFork(ctx *context.Context) { maxCreationLimit := ctx.Repo.Owner.MaxCreationLimit() msg := ctx.TrN(maxCreationLimit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", maxCreationLimit) ctx.Flash.Error(msg) - ctx.Redirect(repo.Link() + "/settings") + ctx.JSONRedirect(repo.Link() + "/settings") return } @@ -788,25 +788,25 @@ func handleSettingsPostConvertFork(ctx *context.Context) { log.Trace("Repository converted from fork to regular: %s", repo.FullName()) ctx.Flash.Success(ctx.Tr("repo.settings.convert_fork_succeed")) - ctx.Redirect(repo.Link()) + ctx.JSONRedirect(repo.Link()) } func handleSettingsPostTransfer(ctx *context.Context) { form := web.GetForm(ctx).(*forms.RepoSettingForm) repo := ctx.Repo.Repository if !ctx.Repo.IsOwner() { - ctx.HTTPError(http.StatusNotFound) + ctx.JSONErrorNotFound() return } if repo.Name != form.RepoName { - ctx.RenderWithErrDeprecated(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil) + ctx.JSONError(ctx.Tr("form.enterred_invalid_repo_name")) return } newOwner, err := user_model.GetUserByName(ctx, ctx.FormString("new_owner_name")) if err != nil { if user_model.IsErrUserNotExist(err) { - ctx.RenderWithErrDeprecated(ctx.Tr("form.enterred_invalid_owner_name"), tplSettingsOptions, nil) + ctx.JSONError(ctx.Tr("form.enterred_invalid_owner_name")) return } ctx.ServerError("IsUserExist", err) @@ -816,7 +816,7 @@ func handleSettingsPostTransfer(ctx *context.Context) { if newOwner.Type == user_model.UserTypeOrganization { if !ctx.Doer.IsAdmin && newOwner.Visibility == structs.VisibleTypePrivate && !organization.OrgFromUser(newOwner).HasMemberWithUserID(ctx, ctx.Doer.ID) { // The user shouldn't know about this organization - ctx.RenderWithErrDeprecated(ctx.Tr("form.enterred_invalid_owner_name"), tplSettingsOptions, nil) + ctx.JSONError(ctx.Tr("form.enterred_invalid_owner_name")) return } } @@ -830,14 +830,14 @@ func handleSettingsPostTransfer(ctx *context.Context) { oldFullname := repo.FullName() if err := repo_service.StartRepositoryTransfer(ctx, ctx.Doer, newOwner, repo, nil); err != nil { if repo_model.IsErrRepoAlreadyExist(err) { - ctx.RenderWithErrDeprecated(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplSettingsOptions, nil) + ctx.JSONError(ctx.Tr("repo.settings.new_owner_has_same_repo")) } else if repo_model.IsErrRepoTransferInProgress(err) { - ctx.RenderWithErrDeprecated(ctx.Tr("repo.settings.transfer_in_progress"), tplSettingsOptions, nil) + ctx.JSONError(ctx.Tr("repo.settings.transfer_in_progress")) } else if repo_service.IsRepositoryLimitReached(err) { limit := err.(repo_service.LimitReachedError).Limit - ctx.RenderWithErrDeprecated(ctx.TrN(limit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", limit), tplSettingsOptions, nil) + ctx.JSONError(ctx.TrN(limit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", limit)) } else if errors.Is(err, user_model.ErrBlockedUser) { - ctx.RenderWithErrDeprecated(ctx.Tr("repo.settings.transfer.blocked_user"), tplSettingsOptions, nil) + ctx.JSONError(ctx.Tr("repo.settings.transfer.blocked_user")) } else { ctx.ServerError("TransferOwnership", err) } @@ -852,7 +852,7 @@ func handleSettingsPostTransfer(ctx *context.Context) { log.Trace("Repository transferred: %s -> %s", oldFullname, ctx.Repo.Repository.FullName()) ctx.Flash.Success(ctx.Tr("repo.settings.transfer_succeed")) } - ctx.Redirect(repo.Link() + "/settings") + ctx.JSONRedirect(repo.Link() + "/settings") } func handleSettingsPostCancelTransfer(ctx *context.Context) { @@ -887,11 +887,11 @@ func handleSettingsPostDelete(ctx *context.Context) { form := web.GetForm(ctx).(*forms.RepoSettingForm) repo := ctx.Repo.Repository if !ctx.Repo.IsOwner() { - ctx.HTTPError(http.StatusNotFound) + ctx.JSONErrorNotFound() return } if repo.Name != form.RepoName { - ctx.RenderWithErrDeprecated(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil) + ctx.JSONError(ctx.Tr("form.enterred_invalid_repo_name")) return } @@ -907,18 +907,18 @@ func handleSettingsPostDelete(ctx *context.Context) { log.Trace("Repository deleted: %s/%s", ctx.Repo.Owner.Name, repo.Name) ctx.Flash.Success(ctx.Tr("repo.settings.deletion_success")) - ctx.Redirect(ctx.Repo.Owner.DashboardLink()) + ctx.JSONRedirect(ctx.Repo.Owner.DashboardLink()) } func handleSettingsPostDeleteWiki(ctx *context.Context) { form := web.GetForm(ctx).(*forms.RepoSettingForm) repo := ctx.Repo.Repository if !ctx.Repo.IsOwner() { - ctx.HTTPError(http.StatusNotFound) + ctx.JSONErrorNotFound() return } if repo.Name != form.RepoName { - ctx.RenderWithErrDeprecated(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil) + ctx.JSONError(ctx.Tr("form.enterred_invalid_repo_name")) return } @@ -929,7 +929,7 @@ func handleSettingsPostDeleteWiki(ctx *context.Context) { log.Trace("Repository wiki deleted: %s/%s", ctx.Repo.Owner.Name, repo.Name) ctx.Flash.Success(ctx.Tr("repo.settings.wiki_deletion_success")) - ctx.Redirect(ctx.Repo.RepoLink + "/settings") + ctx.JSONRedirect(ctx.Repo.RepoLink + "/settings") } func handleSettingsPostArchive(ctx *context.Context) { diff --git a/routers/web/shared/actions/runners.go b/routers/web/shared/actions/runners.go index 3609258440..9d8b2f08eb 100644 --- a/routers/web/shared/actions/runners.go +++ b/routers/web/shared/actions/runners.go @@ -362,10 +362,6 @@ func RunnerUpdatePost(ctx *context.Context) { ctx.JSONRedirect("") } -func RedirectToDefaultSetting(ctx *context.Context) { - ctx.Redirect(ctx.Repo.RepoLink + "/settings/actions/runners") -} - func findActionsRunner(ctx *context.Context, rCtx *runnersCtx) *actions_model.ActionRunner { runnerID := ctx.PathParamInt64("runnerid") opts := &actions_model.FindRunnerOptions{ diff --git a/services/context/user.go b/services/context/user.go index 19c055e2a3..138cdeae93 100644 --- a/services/context/user.go +++ b/services/context/user.go @@ -30,27 +30,6 @@ func UserAssignmentWeb() func(ctx *Context) { } } -// UserIDAssignmentAPI returns a middleware to handle context-user assignment for api routes -func UserIDAssignmentAPI() func(ctx *APIContext) { - return func(ctx *APIContext) { - userID := ctx.PathParamInt64("user-id") - - if ctx.IsSigned && ctx.Doer.ID == userID { - ctx.ContextUser = ctx.Doer - } else { - var err error - ctx.ContextUser, err = user_model.GetUserByID(ctx, userID) - if err != nil { - if user_model.IsErrUserNotExist(err) { - ctx.APIError(http.StatusNotFound, err) - } else { - ctx.APIErrorInternal(err) - } - } - } - } -} - // UserAssignmentAPI returns a middleware to handle context-user assignment for api routes func UserAssignmentAPI() func(ctx *APIContext) { return func(ctx *APIContext) { diff --git a/services/convert/secret.go b/services/convert/secret.go deleted file mode 100644 index dd7b9f0a6a..0000000000 --- a/services/convert/secret.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package convert - -import ( - secret_model "code.gitea.io/gitea/models/secret" - api "code.gitea.io/gitea/modules/structs" -) - -// ToSecret converts Secret to API format -func ToSecret(secret *secret_model.Secret) *api.Secret { - result := &api.Secret{ - Name: secret.Name, - } - - return result -} diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 01e57a596e..09b9b2690c 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -740,14 +740,3 @@ func (f *AddTimeManuallyForm) Validate(req *http.Request, errs binding.Errors) b type SaveTopicForm struct { Topics []string `binding:"topics;Required;"` } - -// DeadlineForm hold the validation rules for deadlines -type DeadlineForm struct { - DateString string `form:"date" binding:"Required;Size(10)"` -} - -// Validate validates the fields -func (f *DeadlineForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { - ctx := context.GetValidateContext(req) - return middleware.Validate(errs, ctx.Data, f, ctx.Locale) -} diff --git a/services/repository/files/content.go b/services/repository/files/content.go index fc0e00a1a7..9b042f9e67 100644 --- a/services/repository/files/content.go +++ b/services/repository/files/content.go @@ -32,11 +32,6 @@ const ( ContentTypeSubmodule ContentType = "submodule" // submodule content type (submodule) ) -// String gets the string of ContentType -func (ct *ContentType) String() string { - return string(*ct) -} - type GetContentsOrListOptions struct { TreePath string IncludeSingleFileContent bool // include the file's content when the tree path is a file diff --git a/services/repository/files/tree.go b/services/repository/files/tree.go index e678b07f56..fc36127338 100644 --- a/services/repository/files/tree.go +++ b/services/repository/files/tree.go @@ -26,12 +26,6 @@ type ErrSHANotFound struct { SHA string } -// IsErrSHANotFound checks if an error is a ErrSHANotFound. -func IsErrSHANotFound(err error) bool { - _, ok := err.(ErrSHANotFound) - return ok -} - func (err ErrSHANotFound) Error() string { return fmt.Sprintf("sha not found [%s]", err.SHA) } diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index a1112def0c..9cad252e67 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -8,13 +8,19 @@ description: | icon: public/assets/img/logo.png confinement: strict -base: core22 +base: core24 adopt-info: gitea -architectures: - - build-on: armhf - - build-on: amd64 - - build-on: arm64 +platforms: + armhf: + build-on: [armhf] + build-for: [armhf] + amd64: + build-on: [amd64] + build-for: [amd64] + arm64: + build-on: [arm64] + build-for: [arm64] environment: GITEA_CUSTOM: "$SNAP_COMMON" diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index 09c2ab660b..6a18c63fd2 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -885,7 +885,7 @@
{{ctx.Locale.Tr "repo.settings.convert_notices_1"}}
-
+ {{template "repo/settings/repo_name_confirm_fields" (dict "RepoName" .Repository.Name)}} {{template "base/modal_actions_confirm" (dict "ModalButtonDangerText" (ctx.Locale.Tr "repo.settings.convert_confirm"))}} @@ -902,7 +902,7 @@
{{ctx.Locale.Tr "repo.settings.convert_fork_notices_1"}}
- + {{template "repo/settings/repo_name_confirm_fields" (dict "RepoName" .Repository.Name)}} {{template "base/modal_actions_confirm" (dict "ModalButtonDangerText" (ctx.Locale.Tr "repo.settings.convert_fork_confirm"))}} @@ -921,7 +921,7 @@ {{ctx.Locale.Tr "repo.settings.transfer_notices_3"}}
{{ctx.Locale.Tr "repo.settings.transfer_notices_4"}} - + {{template "repo/settings/repo_name_confirm_fields" (dict "RepoName" .Repository.Name)}}
@@ -946,7 +946,7 @@ {{ctx.Locale.Tr "repo.settings.delete_notices_fork_1"}} {{end}}
- + {{template "repo/settings/repo_name_confirm_fields" (dict "RepoName" .Repository.Name)}} {{template "base/modal_actions_confirm" (dict "ModalButtonDangerText" (ctx.Locale.Tr "repo.settings.confirm_delete"))}} @@ -1012,7 +1012,7 @@ {{ctx.Locale.Tr "repo.settings.delete_notices_1"}}
{{ctx.Locale.Tr "repo.settings.wiki_delete_notices_1" .Repository.Name}} - + {{template "repo/settings/repo_name_confirm_fields" (dict "RepoName" .Repository.Name)}} {{template "base/modal_actions_confirm" (dict "ModalButtonDangerText" (ctx.Locale.Tr "repo.settings.confirm_wiki_delete"))}} diff --git a/tests/e2e/heatmap.test.ts b/tests/e2e/heatmap.test.ts new file mode 100644 index 0000000000..5aabfab196 --- /dev/null +++ b/tests/e2e/heatmap.test.ts @@ -0,0 +1,9 @@ +import {test, expect} from '@playwright/test'; +import {login} from './utils.ts'; + +test('heatmap tooltip shows on hover', async ({page}) => { + await login(page); + await page.goto('/'); + await page.locator('.heatmap-day').first().hover(); + await expect(page.locator('.tippy-box[data-state="visible"]')).toBeVisible(); +}); diff --git a/tests/integration/editor_test.go b/tests/integration/editor_test.go index ac106bc776..2ddee70440 100644 --- a/tests/integration/editor_test.go +++ b/tests/integration/editor_test.go @@ -372,7 +372,8 @@ func testForkToEditFile(t *testing.T, session *TestSession, user, owner, repo, b "action": "convert_fork", }, ) - session.MakeRequest(t, req, http.StatusSeeOther) + resp = session.MakeRequest(t, req, http.StatusOK) + assert.NotNil(t, test.ParseJSONRedirect(resp.Body.Bytes()).Redirect) }) // Fork repository again, and check the existence of the forked repo with unique name diff --git a/tests/integration/pull_create_test.go b/tests/integration/pull_create_test.go index 2c17557eb0..a8bf6689a3 100644 --- a/tests/integration/pull_create_test.go +++ b/tests/integration/pull_create_test.go @@ -189,7 +189,8 @@ func testDeleteRepository(t *testing.T, session *TestSession, ownerName, repoNam req := NewRequestWithValues(t, "POST", relURL+"?action=delete", map[string]string{ "repo_name": repoName, }) - session.MakeRequest(t, req, http.StatusSeeOther) + resp := session.MakeRequest(t, req, http.StatusOK) + assert.NotNil(t, test.ParseJSONRedirect(resp.Body.Bytes()).Redirect) } func TestPullBranchDelete(t *testing.T) { diff --git a/tests/integration/repo_visibility_test.go b/tests/integration/repo_visibility_test.go index e7ad8ddd87..f9a2ab8369 100644 --- a/tests/integration/repo_visibility_test.go +++ b/tests/integration/repo_visibility_test.go @@ -39,7 +39,7 @@ func TestRepositoryVisibilityChange(t *testing.T) { "confirm_repo_name": "user2/repo1", }) resp = session.MakeRequest(t, req, http.StatusOK) - assert.NotEmpty(t, test.ParseJSONRedirect(resp.Body.Bytes()).Redirect) + assert.NotNil(t, test.ParseJSONRedirect(resp.Body.Bytes()).Redirect) repo1 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) assert.True(t, repo1.IsPrivate) @@ -51,7 +51,7 @@ func TestRepositoryVisibilityChange(t *testing.T) { "private": "false", }) resp := session.MakeRequest(t, req, http.StatusOK) - assert.NotEmpty(t, test.ParseJSONRedirect(resp.Body.Bytes()).Redirect) + assert.NotNil(t, test.ParseJSONRedirect(resp.Body.Bytes()).Redirect) repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) assert.False(t, repo2.IsPrivate) diff --git a/tsconfig.json b/tsconfig.json index 92cf43fbf8..bcecb17217 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -35,8 +35,6 @@ "skipLibCheck": true, "sourceMap": true, "strict": true, - "strictPropertyInitialization": false, - "useUnknownInCatchVariables": false, "stripInternal": true, "verbatimModuleSyntax": true, "types": [ diff --git a/vite.config.ts b/vite.config.ts index 731726c318..d3080a5d12 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -86,7 +86,7 @@ function iifeBuildOpts({sourceFileName, write}: {sourceFileName: string, write?: } // Build iife.js as a blocking IIFE bundle. In dev mode, serves it from memory -// and rebuilds on file changes. In prod mode, writes to disk during closeBundle. +// and rebuilds on file changes. In prod mode, writes to disk and updates "manifest.json". function iifePlugin(sourceFileName: string): Plugin { let iifeCode = '', iifeMap = ''; const iifeModules = new Set(); @@ -143,7 +143,7 @@ function iifePlugin(sourceFileName: string): Plugin { } }); }, - async closeBundle() { + async writeBundle() { for (const file of globSync(`js/${sourceBaseName}.*.js*`, {cwd: outDir})) unlinkSync(join(outDir, file)); const result = await build(iifeBuildOpts({sourceFileName})); @@ -152,15 +152,9 @@ function iifePlugin(sourceFileName: string): Plugin { if (!entry) throw new Error('IIFE build produced no output'); const manifestPath = join(outDir, '.vite', 'manifest.json'); - try { - const manifestData = JSON.parse(readFileSync(manifestPath, 'utf8')); - manifestData[`web_src/js/${sourceFileName}`] = {file: entry.fileName, name: sourceBaseName, isEntry: true}; - writeFileSync(manifestPath, JSON.stringify(manifestData, null, 2)); - } catch { - // FIXME: if it throws error here, the real Vite compilation error will be hidden, and makes the debug very difficult - // Need to find a correct way to handle errors. - console.error(`Failed to update manifest for ${sourceFileName}`); - } + const manifestData = JSON.parse(readFileSync(manifestPath, 'utf8')); + manifestData[`web_src/js/${sourceFileName}`] = {file: entry.fileName, name: sourceBaseName, isEntry: true}; + writeFileSync(manifestPath, JSON.stringify(manifestData, null, 2)); }, }; } diff --git a/web_src/css/features/heatmap.css b/web_src/css/features/heatmap.css index e40adf1fe4..a2d8c4ea59 100644 --- a/web_src/css/features/heatmap.css +++ b/web_src/css/features/heatmap.css @@ -32,26 +32,29 @@ fill: currentcolor !important; } -/* root legend */ -#user-heatmap .vch__container > .vch__legend { +#user-heatmap .heatmap-footer { display: flex; font-size: 11px; justify-content: space-between; } -/* for the "Less" and "More" legend */ -#user-heatmap .vch__legend .vch__legend { +/* "Less [colors] More" scale */ +#user-heatmap .heatmap-legend { display: flex; align-items: center; justify-content: right; } -#user-heatmap .vch__legend .vch__legend div:first-child, -#user-heatmap .vch__legend .vch__legend div:last-child { +#user-heatmap .heatmap-legend-svg { + margin-right: -12px; +} + +#user-heatmap .heatmap-legend > div:first-child, +#user-heatmap .heatmap-legend > div:last-child { display: inline-block; padding: 0 5px; } -#user-heatmap .vch__day__square:hover { +#user-heatmap .heatmap-day:hover { outline: 1.5px solid var(--color-text); } diff --git a/web_src/js/components/ActivityHeatmap.vue b/web_src/js/components/ActivityHeatmap.vue index 7c7e0cd94c..08c590aa16 100644 --- a/web_src/js/components/ActivityHeatmap.vue +++ b/web_src/js/components/ActivityHeatmap.vue @@ -1,21 +1,23 @@ diff --git a/web_src/js/components/RepoCodeFrequency.vue b/web_src/js/components/RepoCodeFrequency.vue index 97ce07cc35..146a65995a 100644 --- a/web_src/js/components/RepoCodeFrequency.vue +++ b/web_src/js/components/RepoCodeFrequency.vue @@ -21,6 +21,7 @@ import { type DayDataObject, } from '../utils/time.ts'; import {chartJsColors} from '../utils/color.ts'; +import {errorMessage} from '../modules/errors.ts'; import {sleep} from '../utils.ts'; import 'chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm'; import {onMounted, shallowRef} from 'vue'; @@ -78,7 +79,7 @@ async function fetchGraphData() { errorText.value = response.statusText; } } catch (err) { - errorText.value = err.message; + errorText.value = errorMessage(err); } finally { isLoading.value = false; } diff --git a/web_src/js/components/RepoContributors.vue b/web_src/js/components/RepoContributors.vue index db2f84ecde..3fd34081e0 100644 --- a/web_src/js/components/RepoContributors.vue +++ b/web_src/js/components/RepoContributors.vue @@ -24,6 +24,7 @@ import { fillEmptyStartDaysWithZeroes, } from '../utils/time.ts'; import {chartJsColors} from '../utils/color.ts'; +import {errorMessage} from '../modules/errors.ts'; import {sleep} from '../utils.ts'; import 'chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm'; import {fomanticQuery} from '../modules/fomantic/base.ts'; @@ -166,7 +167,7 @@ export default defineComponent({ this.errorText = response.statusText; } } catch (err) { - this.errorText = err.message; + this.errorText = errorMessage(err); } finally { this.isLoading = false; } diff --git a/web_src/js/components/RepoRecentCommits.vue b/web_src/js/components/RepoRecentCommits.vue index 7eb5c7f289..b58e84e55d 100644 --- a/web_src/js/components/RepoRecentCommits.vue +++ b/web_src/js/components/RepoRecentCommits.vue @@ -20,6 +20,7 @@ import { type DayDataObject, } from '../utils/time.ts'; import {chartJsColors} from '../utils/color.ts'; +import {errorMessage} from '../modules/errors.ts'; import {sleep} from '../utils.ts'; import 'chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm'; import {onMounted, ref, shallowRef} from 'vue'; @@ -74,7 +75,7 @@ async function fetchGraphData() { errorText.value = response.statusText; } } catch (err) { - errorText.value = err.message; + errorText.value = errorMessage(err); } finally { isLoading.value = false; } diff --git a/web_src/js/features/admin/config.ts b/web_src/js/features/admin/config.ts index 5df81412f9..96c7dcf84b 100644 --- a/web_src/js/features/admin/config.ts +++ b/web_src/js/features/admin/config.ts @@ -2,6 +2,7 @@ import {showTemporaryTooltip} from '../../modules/tippy.ts'; import {POST} from '../../modules/fetch.ts'; import {registerGlobalInitFunc} from '../../modules/observer.ts'; import {queryElems} from '../../utils/dom.ts'; +import {errorMessage} from '../../modules/errors.ts'; import {submitFormFetchAction} from '../common-fetch-action.ts'; const {appSubUrl} = window.config; @@ -26,7 +27,7 @@ function initSystemConfigAutoCheckbox(el: HTMLInputElement) { const json: Record = await resp.json(); if (json.errorMessage) throw new Error(json.errorMessage); } catch (ex) { - showTemporaryTooltip(el, ex.toString()); + showTemporaryTooltip(el, errorMessage(ex)); el.checked = !el.checked; } }); diff --git a/web_src/js/features/citation.ts b/web_src/js/features/citation.ts index 1abd960366..80eaabcc58 100644 --- a/web_src/js/features/citation.ts +++ b/web_src/js/features/citation.ts @@ -1,4 +1,5 @@ import {getCurrentLocale} from '../utils.ts'; +import {errorMessage} from '../modules/errors.ts'; import {fomanticQuery} from '../modules/fomantic/base.ts'; import {localUserSettings} from '../modules/user-settings.ts'; @@ -46,7 +47,7 @@ export async function initCitationFileCopyContent() { try { await initInputCitationValue(citationCopyApa, citationCopyBibtex); } catch (e) { - console.error(`initCitationFileCopyContent error: ${e}`, e); + console.error(`initCitationFileCopyContent error: ${errorMessage(e)}`, e); return; } updateUi(); diff --git a/web_src/js/features/common-fetch-action.ts b/web_src/js/features/common-fetch-action.ts index 24f4ad2e02..a96e69915f 100644 --- a/web_src/js/features/common-fetch-action.ts +++ b/web_src/js/features/common-fetch-action.ts @@ -1,6 +1,7 @@ import {GET, request} from '../modules/fetch.ts'; import {hideToastsAll, showErrorToast} from '../modules/toast.ts'; import {addDelegatedEventListener, createElementFromHTML} from '../utils/dom.ts'; +import {errorMessage} from '../modules/errors.ts'; import {confirmModal, createConfirmModal} from './comp/ConfirmModal.ts'; import {ignoreAreYouSure} from '../vendor/jquery.are-you-sure.ts'; import {registerGlobalSelectorFunc} from '../modules/observer.ts'; @@ -90,7 +91,7 @@ async function handleFetchActionSuccess(el: HTMLElement, opt: FetchActionOpts, r async function handleFetchActionError(resp: Response) { const isRespJson = resp.headers.get('content-type')?.includes('application/json'); const respText = await resp.text(); - const respJson = isRespJson ? JSON.parse(await resp.text()) : null; + const respJson = isRespJson ? JSON.parse(respText) : null; if (respJson?.errorMessage) { // the code was quite messy, sometimes the backend uses "err", sometimes it uses "error", and even "user_error" // but at the moment, as a new approach, we only use "errorMessage" here, backend can use JSONError() to respond. @@ -134,10 +135,10 @@ async function performActionRequest(el: HTMLElement, opt: FetchActionOpts) { return; } await handleFetchActionError(resp); - } catch (e) { - if (e.name !== 'AbortError') { - console.error(`Fetch action request error:`, e); - showErrorToast(`Error: ${e.message ?? e}`); + } catch (err) { + if ((err as Error).name !== 'AbortError') { + console.error(`Fetch action request error:`, err); + showErrorToast(`Error: ${errorMessage(err)}`); } } finally { toggleLoadingIndicator(el, opt, false); diff --git a/web_src/js/features/comp/ComboMarkdownEditor.ts b/web_src/js/features/comp/ComboMarkdownEditor.ts index f16a71a6c5..da7fcd8586 100644 --- a/web_src/js/features/comp/ComboMarkdownEditor.ts +++ b/web_src/js/features/comp/ComboMarkdownEditor.ts @@ -71,26 +71,26 @@ export class ComboMarkdownEditor { options: ComboMarkdownEditorOptions; - tabEditor: HTMLElement; - tabPreviewer: HTMLElement; + tabEditor?: HTMLElement; + tabPreviewer?: HTMLElement; - supportEasyMDE: boolean; + supportEasyMDE!: boolean; easyMDE: any; easyMDEToolbarActions: any; easyMDEToolbarDefault: any; - textarea: ComboMarkdownEditorTextarea; - textareaMarkdownToolbar: HTMLElement; + textarea!: ComboMarkdownEditorTextarea; + textareaMarkdownToolbar!: HTMLElement; textareaAutosize: any; - buttonMonospace: HTMLButtonElement; + buttonMonospace!: HTMLButtonElement; - dropzone: HTMLElement | null; + dropzone: HTMLElement | null = null; attachedDropzoneInst: any; - previewMode: string; - previewUrl: string; - previewContext: string; + previewMode!: string; + previewUrl!: string; + previewContext!: string; constructor(container: ComboMarkdownEditorContainer, options:ComboMarkdownEditorOptions = {}) { if (container._giteaComboMarkdownEditor) throw new Error('ComboMarkdownEditor already initialized'); @@ -291,7 +291,7 @@ export class ComboMarkdownEditor { } switchTabToEditor() { - this.tabEditor.click(); + this.tabEditor!.click(); // when this function is called, the tab must exist } prepareEasyMDEToolbarActions() { diff --git a/web_src/js/features/dropzone.ts b/web_src/js/features/dropzone.ts index 6ff0cc5a3a..84076dc93b 100644 --- a/web_src/js/features/dropzone.ts +++ b/web_src/js/features/dropzone.ts @@ -5,6 +5,7 @@ import {showTemporaryTooltip} from '../modules/tippy.ts'; import {GET, POST} from '../modules/fetch.ts'; import {showErrorToast} from '../modules/toast.ts'; import {createElementFromHTML, createElementFromAttrs} from '../utils/dom.ts'; +import {errorMessage} from '../modules/errors.ts'; import {isImageFile, isVideoFile} from '../utils.ts'; import type Dropzone from '@deltablot/dropzone'; @@ -149,7 +150,7 @@ export async function initDropzone(dropzoneEl: HTMLElement) { } catch (error) { // TODO: if listing the existing attachments failed, it should stop from operating the content or attachments, // otherwise the attachments might be lost. - showErrorToast(`Failed to load attachments: ${error}`); + showErrorToast(`Failed to load attachments: ${errorMessage(error)}`); console.error(error); } }); diff --git a/web_src/js/features/file-view.ts b/web_src/js/features/file-view.ts index b9a5dd5094..eed4ba0cd3 100644 --- a/web_src/js/features/file-view.ts +++ b/web_src/js/features/file-view.ts @@ -2,6 +2,7 @@ import type {InplaceRenderPlugin} from '../render/plugin.ts'; import {newInplacePluginPdfViewer} from '../render/plugins/inplace-pdf-viewer.ts'; import {registerGlobalInitFunc} from '../modules/observer.ts'; import {createElementFromHTML} from '../utils/dom.ts'; +import {errorMessage} from '../modules/errors.ts'; import {html} from '../utils/html.ts'; import {basename} from '../utils.ts'; @@ -30,7 +31,7 @@ async function renderRawFileToContainer(container: HTMLElement, rawFileLink: str rendered = true; } } catch (e) { - errorMsg = `${e}`; + errorMsg = errorMessage(e); } finally { container.classList.remove('is-loading'); } diff --git a/web_src/js/features/imagediff.ts b/web_src/js/features/imagediff.ts index 1e89aa8b6e..5351d35ffe 100644 --- a/web_src/js/features/imagediff.ts +++ b/web_src/js/features/imagediff.ts @@ -94,8 +94,8 @@ function createContext(imageAfter: HTMLImageElement, imageBefore: HTMLImageEleme } class ImageDiff { - containerEl: HTMLElement; - diffContainerWidth: number; + containerEl!: HTMLElement; + diffContainerWidth!: number; async init(containerEl: HTMLElement) { this.containerEl = containerEl; diff --git a/web_src/js/features/repo-common.ts b/web_src/js/features/repo-common.ts index e8fb257c18..65b89cf261 100644 --- a/web_src/js/features/repo-common.ts +++ b/web_src/js/features/repo-common.ts @@ -1,4 +1,5 @@ import {queryElems} from '../utils/dom.ts'; +import {errorMessage} from '../modules/errors.ts'; import {POST} from '../modules/fetch.ts'; import {showErrorToast} from '../modules/toast.ts'; import {sleep} from '../utils.ts'; @@ -26,7 +27,7 @@ async function onDownloadArchive(e: Event) { window.location.href = el.href; // the archive is ready, start real downloading } catch (e) { console.error(e); - showErrorToast(`Failed to download the archive: ${e}`, {duration: 2500}); + showErrorToast(`Failed to download the archive: ${errorMessage(e)}`, {duration: 2500}); } finally { targetLoading.classList.remove('is-loading', 'loading-icon-2px'); } diff --git a/web_src/js/features/repo-diff.ts b/web_src/js/features/repo-diff.ts index 500c86fec6..f866df3526 100644 --- a/web_src/js/features/repo-diff.ts +++ b/web_src/js/features/repo-diff.ts @@ -6,6 +6,7 @@ import {initViewedCheckboxListenerFor, initExpandAndCollapseFilesButton} from '. import {initImageDiff} from './imagediff.ts'; import {showErrorToast} from '../modules/toast.ts'; import {queryElemSiblings, hideElem, showElem, animateOnce, addDelegatedEventListener, createElementFromHTML, queryElems} from '../utils/dom.ts'; +import {errorMessage} from '../modules/errors.ts'; import {POST, GET} from '../modules/fetch.ts'; import {createTippy} from '../modules/tippy.ts'; import {invertFileFolding} from './file-fold.ts'; @@ -85,7 +86,7 @@ function initRepoDiffConversationForm() { } } catch (error) { console.error('Error:', error); - showErrorToast(`Submit form failed: ${error}`); + showErrorToast(`Submit form failed: ${errorMessage(error)}`); } finally { form?.classList.remove('is-loading'); } diff --git a/web_src/js/features/repo-issue-edit.ts b/web_src/js/features/repo-issue-edit.ts index 4a112368f4..198951d41f 100644 --- a/web_src/js/features/repo-issue-edit.ts +++ b/web_src/js/features/repo-issue-edit.ts @@ -3,6 +3,7 @@ import {getComboMarkdownEditor, initComboMarkdownEditor, ComboMarkdownEditor} fr import {POST} from '../modules/fetch.ts'; import {showErrorToast} from '../modules/toast.ts'; import {hideElem, querySingleVisibleElem, showElem} from '../utils/dom.ts'; +import {errorMessage} from '../modules/errors.ts'; import {triggerUploadStateChanged} from './comp/EditorUpload.ts'; import {convertHtmlToMarkdown} from '../markup/html2markdown.ts'; import {applyAreYouSure, reinitializeAreYouSure} from '../vendor/jquery.are-you-sure.ts'; @@ -73,7 +74,7 @@ async function tryOnEditContent(e: Event) { } comboMarkdownEditor.dropzoneSubmitReload(); } catch (error) { - showErrorToast(`Failed to save the content: ${error}`); + showErrorToast(`Failed to save the content: ${errorMessage(error)}`); console.error(error); } finally { renderContent.classList.remove('is-loading'); diff --git a/web_src/js/features/repo-issue-list.ts b/web_src/js/features/repo-issue-list.ts index c0cd3c7f22..046c1beee5 100644 --- a/web_src/js/features/repo-issue-list.ts +++ b/web_src/js/features/repo-issue-list.ts @@ -2,6 +2,7 @@ import {updateIssuesMeta} from './repo-common.ts'; import {toggleElem, queryElems, isElemVisible} from '../utils/dom.ts'; import {html, htmlRaw} from '../utils/html.ts'; import {confirmModal} from './comp/ConfirmModal.ts'; +import {errorMessage} from '../modules/errors.ts'; import {showErrorToast} from '../modules/toast.ts'; import {createSortable} from '../modules/sortable.ts'; import {DELETE, POST} from '../modules/fetch.ts'; @@ -87,7 +88,9 @@ function initRepoIssueListCheckboxes() { await updateIssuesMeta(url, action, issueIDs, elementId); window.location.reload(); } catch (err) { - showErrorToast(err.responseJSON?.error ?? err.message); + // FIXME: this logic (including updateIssuesMeta) is not right, should refactor to our JSONError framework + const e = err as {responseJSON?: {error: string}}; + showErrorToast(e.responseJSON?.error ?? errorMessage(err)); } }, )); diff --git a/web_src/js/features/repo-issue-sidebar-combolist.ts b/web_src/js/features/repo-issue-sidebar-combolist.ts index ab29a18f92..0cea54bbbd 100644 --- a/web_src/js/features/repo-issue-sidebar-combolist.ts +++ b/web_src/js/features/repo-issue-sidebar-combolist.ts @@ -2,6 +2,7 @@ import {fomanticQuery} from '../modules/fomantic/base.ts'; import {GET, POST} from '../modules/fetch.ts'; import {showErrorToast} from '../modules/toast.ts'; import {addDelegatedEventListener, queryElemChildren, queryElems, toggleElem} from '../utils/dom.ts'; +import {errorMessage} from '../modules/errors.ts'; import {parseDom} from '../utils.ts'; export function syncIssueMainContentTimelineItems(oldMainContent: Element, newMainContent: Element) { @@ -42,7 +43,7 @@ export class IssueSidebarComboList { elDropdown: HTMLElement; elList: HTMLElement | null; elComboValue: HTMLInputElement; - initialValues: string[]; + initialValues: string[] = []; container: HTMLElement; elIssueMainContent: HTMLElement; @@ -129,7 +130,7 @@ export class IssueSidebarComboList { await this.reloadPagePartially(); } catch (e) { console.error('Failed to update to backend', e); - showErrorToast(`Failed to update to backend: ${e}`); + showErrorToast(`Failed to update to backend: ${errorMessage(e)}`); } finally { this.elIssueSidebar.classList.remove('is-loading'); } diff --git a/web_src/js/features/repo-issue.ts b/web_src/js/features/repo-issue.ts index 03b0c93d85..7322432fe8 100644 --- a/web_src/js/features/repo-issue.ts +++ b/web_src/js/features/repo-issue.ts @@ -1,3 +1,4 @@ +import {errorMessage} from '../modules/errors.ts'; import {htmlEscape} from '../utils/html.ts'; import {createTippy} from '../modules/tippy.ts'; import { @@ -425,7 +426,7 @@ export function initRepoIssueTitleEdit() { window.location.reload(); } catch (error) { console.error(error); - showErrorToast(error.message); + showErrorToast(errorMessage(error)); } }); } diff --git a/web_src/js/features/repo-settings-branches.ts b/web_src/js/features/repo-settings-branches.ts index 858140adc2..4e7e74cbc7 100644 --- a/web_src/js/features/repo-settings-branches.ts +++ b/web_src/js/features/repo-settings-branches.ts @@ -2,6 +2,7 @@ import {createSortable} from '../modules/sortable.ts'; import {POST} from '../modules/fetch.ts'; import {showErrorToast} from '../modules/toast.ts'; import {queryElemChildren} from '../utils/dom.ts'; +import {errorMessage} from '../modules/errors.ts'; export function initRepoSettingsBranchesDrag() { const protectedBranchesList = document.querySelector('#protected-branches-list'); @@ -23,8 +24,7 @@ export function initRepoSettingsBranchesDrag() { }, }); } catch (err) { - const errorMessage = String(err); - showErrorToast(`Failed to update branch protection rule priority:, error: ${errorMessage}`); + showErrorToast(`Failed to update branch protection rule priority: ${errorMessage(err)}`); } })(); }, diff --git a/web_src/js/features/user-auth-webauthn.ts b/web_src/js/features/user-auth-webauthn.ts index 774d41dce0..f30f4a7f5f 100644 --- a/web_src/js/features/user-auth-webauthn.ts +++ b/web_src/js/features/user-auth-webauthn.ts @@ -1,5 +1,6 @@ import {encodeURLEncodedBase64, decodeURLEncodedBase64} from '../utils.ts'; import {hideElem, showElem} from '../utils/dom.ts'; +import {errorMessage} from '../modules/errors.ts'; import {GET, POST} from '../modules/fetch.ts'; const {appSubUrl} = window.config; @@ -80,7 +81,7 @@ async function loginPasskey() { window.location.href = reply?.redirect ?? `${appSubUrl}/`; } catch (err) { - webAuthnError('general', err.message); + webAuthnError('general', errorMessage(err)); } } @@ -104,7 +105,7 @@ async function login2FA() { await verifyAssertion(credential); } catch (err) { if (!options.publicKey.extensions?.appid) { - webAuthnError('general', err.message); + webAuthnError('general', errorMessage(err)); return; } delete options.publicKey.extensions.appid; @@ -114,7 +115,7 @@ async function login2FA() { }); await verifyAssertion(credential); } catch (err) { - webAuthnError('general', err.message); + webAuthnError('general', errorMessage(err)); } } } @@ -262,6 +263,6 @@ async function webAuthnRegisterRequest() { }); await webauthnRegistered(credential); } catch (err) { - webAuthnError('unknown', err); + webAuthnError('unknown', errorMessage(err)); } } diff --git a/web_src/js/markup/common.ts b/web_src/js/markup/common.ts index e826c8fd86..096d8ddf66 100644 --- a/web_src/js/markup/common.ts +++ b/web_src/js/markup/common.ts @@ -1,8 +1,10 @@ -export function displayError(el: Element, err: Error): void { +import {errorMessage} from '../modules/errors.ts'; + +export function displayError(el: Element, err: unknown): void { el.classList.remove('is-loading'); const errorNode = document.createElement('pre'); errorNode.setAttribute('class', 'ui message error markup-block-error'); - errorNode.textContent = err.message || String(err); + errorNode.textContent = errorMessage(err); el.before(errorNode); el.setAttribute('data-render-done', 'true'); } diff --git a/web_src/js/markup/render-iframe.ts b/web_src/js/markup/render-iframe.ts index 2b1b06e5c0..f05523943a 100644 --- a/web_src/js/markup/render-iframe.ts +++ b/web_src/js/markup/render-iframe.ts @@ -1,4 +1,5 @@ import {generateElemId} from '../utils/dom.ts'; +import {errorMessage} from '../modules/errors.ts'; import {isDarkTheme} from '../utils.ts'; import {GET} from '../modules/fetch.ts'; @@ -11,7 +12,7 @@ function safeRenderIframeLink(link: any): string | null { } return url.href; } catch (e) { - console.error(`Failed to parse link: ${link}, error: ${e}`); + console.error(`Failed to parse link: ${link}, error: ${errorMessage(e)}`); return null; } } diff --git a/web_src/js/modules/errors.ts b/web_src/js/modules/errors.ts index ddda0ebe42..276b498ead 100644 --- a/web_src/js/modules/errors.ts +++ b/web_src/js/modules/errors.ts @@ -2,6 +2,10 @@ import {html} from '../utils/html.ts'; import type {Intent} from '../types.ts'; +export function errorMessage(err: unknown): string { + return (err as Error)?.message || String(err); +} + export function showGlobalErrorMessage(msg: string, msgType: Intent = 'error') { const msgContainer = document.querySelector('.page-content') ?? document.body; if (!msgContainer) { diff --git a/web_src/js/utils/match.ts b/web_src/js/utils/match.ts index f364481f46..41333d9fa4 100644 --- a/web_src/js/utils/match.ts +++ b/web_src/js/utils/match.ts @@ -2,6 +2,7 @@ import emojis from '../../../assets/emoji.json' with {type: 'json'}; import {GET} from '../modules/fetch.ts'; import {showErrorToast} from '../modules/toast.ts'; import {parseIssuePageInfo} from '../utils.ts'; +import {errorMessage} from '../modules/errors.ts'; import type {Issue, Mention} from '../types.ts'; const maxMatches = 6; @@ -47,7 +48,7 @@ export function fetchMentions(mentionsUrl: string): Promise { if (!res.ok) throw new Error(res.statusText); return await res.json() as Mention[]; } catch (e) { - showErrorToast(`Failed to load mentions: ${e}`); + showErrorToast(`Failed to load mentions: ${errorMessage(e)}`); return []; } })(); diff --git a/web_src/js/webcomponents/overflow-menu.ts b/web_src/js/webcomponents/overflow-menu.ts index 879ce36e6e..b20d11e243 100644 --- a/web_src/js/webcomponents/overflow-menu.ts +++ b/web_src/js/webcomponents/overflow-menu.ts @@ -3,13 +3,13 @@ import {addDelegatedEventListener, generateElemId, isDocumentFragmentOrElementNo import octiconKebabHorizontal from '../../../public/assets/img/svg/octicon-kebab-horizontal.svg'; window.customElements.define('overflow-menu', class extends HTMLElement { - popup: HTMLDivElement; - overflowItems: Array; - button: HTMLButtonElement | null; - menuItemsEl: HTMLElement; - resizeObserver: ResizeObserver; - mutationObserver: MutationObserver; - lastWidth: number; + popup!: HTMLDivElement; + overflowItems: Array = []; + button: HTMLButtonElement | null = null; + menuItemsEl!: HTMLElement; + resizeObserver!: ResizeObserver; + mutationObserver!: MutationObserver; + lastWidth!: number; updateButtonActivationState() { if (!this.button || !this.popup) return; @@ -100,7 +100,7 @@ window.customElements.define('overflow-menu', class extends HTMLElement { const itemOverFlowMenuButton = this.querySelector('.overflow-menu-button'); // move items in popup back into the menu items for subsequent measurement - for (const item of this.overflowItems || []) { + for (const item of this.overflowItems) { if (!itemFlexSpace || item.getAttribute('data-after-flex-space')) { this.menuItemsEl.append(item); } else {