Skip to content

Commit e3e44a5

Browse files
lunnylafriks
authored andcommitted
Update migrated repositories' issues/comments/prs poster id if user has a github external user saved (#7751)
* update migrated issues/comments when login as github * add get userid when migrating or login with github oauth2 * fix lint * add migrations for repository service type * fix build * remove unnecessary dependencies on migrations * add cron task to update migrations poster ids and fix posterid when migrating * fix lint * fix lint * improve code * fix lint * improve code * replace releases publish id to actual author id * fix import * fix bug * fix lint * fix rawdata definition * fix some bugs * fix error message
1 parent ba201aa commit e3e44a5

File tree

21 files changed

+737
-156
lines changed

21 files changed

+737
-156
lines changed

custom/conf/app.ini.sample

+5
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,11 @@ SCHEDULE = @every 24h
690690
; or only create new users if UPDATE_EXISTING is set to false
691691
UPDATE_EXISTING = true
692692

693+
; Update migrated repositories' issues and comments' posterid, it will always attempt synchronization when the instance starts.
694+
[cron.update_migration_post_id]
695+
; Interval as a duration between each synchronization. (default every 24h)
696+
SCHEDULE = @every 24h
697+
693698
[git]
694699
; The path of git executable. If empty, Gitea searches through the PATH environment.
695700
PATH =

docs/content/doc/advanced/config-cheat-sheet.en-us.md

+4
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,10 @@ NB: You must `REDIRECT_MACARON_LOG` and have `DISABLE_ROUTER_LOG` set to `false`
419419
- `RUN_AT_START`: **true**: Run repository statistics check at start time.
420420
- `SCHEDULE`: **@every 24h**: Cron syntax for scheduling repository statistics check.
421421

422+
### Cron - Update Migration Poster ID (`cron.update_migration_post_id`)
423+
424+
- `SCHEDULE`: **@every 24h** : Interval as a duration between each synchronization, it will always attempt synchronization when the instance starts.
425+
422426
## Git (`git`)
423427

424428
- `PATH`: **""**: The path of git executable. If empty, Gitea searches through the PATH environment.

docs/content/doc/advanced/config-cheat-sheet.zh-cn.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,11 @@ menu:
196196
### Cron - Repository Statistics Check (`cron.check_repo_stats`)
197197

198198
- `RUN_AT_START`: 是否启动时自动运行仓库统计。
199-
- `SCHEDULE`: 藏亏统计时的Cron 语法,比如:`@every 24h`.
199+
- `SCHEDULE`: 仓库统计时的Cron 语法,比如:`@every 24h`.
200+
201+
### Cron - Update Migration Poster ID (`cron.update_migration_post_id`)
202+
203+
- `SCHEDULE`: **@every 24h** : 每次同步的间隔时间。此任务总是在启动时自动进行。
200204

201205
## Git (`git`)
202206

models/external_login_user.go

+125-18
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,34 @@
44

55
package models
66

7-
import "github.com/markbates/goth"
7+
import (
8+
"time"
9+
10+
"code.gitea.io/gitea/modules/structs"
11+
12+
"github.com/markbates/goth"
13+
"xorm.io/builder"
14+
)
815

916
// ExternalLoginUser makes the connecting between some existing user and additional external login sources
1017
type ExternalLoginUser struct {
11-
ExternalID string `xorm:"pk NOT NULL"`
12-
UserID int64 `xorm:"INDEX NOT NULL"`
13-
LoginSourceID int64 `xorm:"pk NOT NULL"`
18+
ExternalID string `xorm:"pk NOT NULL"`
19+
UserID int64 `xorm:"INDEX NOT NULL"`
20+
LoginSourceID int64 `xorm:"pk NOT NULL"`
21+
RawData map[string]interface{} `xorm:"TEXT JSON"`
22+
Provider string `xorm:"index VARCHAR(25)"`
23+
Email string
24+
Name string
25+
FirstName string
26+
LastName string
27+
NickName string
28+
Description string
29+
AvatarURL string
30+
Location string
31+
AccessToken string
32+
AccessTokenSecret string
33+
RefreshToken string
34+
ExpiresAt time.Time
1435
}
1536

1637
// GetExternalLogin checks if a externalID in loginSourceID scope already exists
@@ -32,23 +53,15 @@ func ListAccountLinks(user *User) ([]*ExternalLoginUser, error) {
3253
return externalAccounts, nil
3354
}
3455

35-
// LinkAccountToUser link the gothUser to the user
36-
func LinkAccountToUser(user *User, gothUser goth.User) error {
37-
loginSource, err := GetActiveOAuth2LoginSourceByName(gothUser.Provider)
38-
if err != nil {
39-
return err
40-
}
41-
42-
externalLoginUser := &ExternalLoginUser{
43-
ExternalID: gothUser.UserID,
44-
UserID: user.ID,
45-
LoginSourceID: loginSource.ID,
46-
}
47-
has, err := x.Get(externalLoginUser)
56+
// LinkExternalToUser link the external user to the user
57+
func LinkExternalToUser(user *User, externalLoginUser *ExternalLoginUser) error {
58+
has, err := x.Where("external_id=? AND login_source_id=?", externalLoginUser.ExternalID, externalLoginUser.LoginSourceID).
59+
NoAutoCondition().
60+
Exist(externalLoginUser)
4861
if err != nil {
4962
return err
5063
} else if has {
51-
return ErrExternalLoginUserAlreadyExist{gothUser.UserID, user.ID, loginSource.ID}
64+
return ErrExternalLoginUserAlreadyExist{externalLoginUser.ExternalID, user.ID, externalLoginUser.LoginSourceID}
5265
}
5366

5467
_, err = x.Insert(externalLoginUser)
@@ -72,3 +85,97 @@ func removeAllAccountLinks(e Engine, user *User) error {
7285
_, err := e.Delete(&ExternalLoginUser{UserID: user.ID})
7386
return err
7487
}
88+
89+
// GetUserIDByExternalUserID get user id according to provider and userID
90+
func GetUserIDByExternalUserID(provider string, userID string) (int64, error) {
91+
var id int64
92+
_, err := x.Table("external_login_user").
93+
Select("user_id").
94+
Where("provider=?", provider).
95+
And("external_id=?", userID).
96+
Get(&id)
97+
if err != nil {
98+
return 0, err
99+
}
100+
return id, nil
101+
}
102+
103+
// UpdateExternalUser updates external user's information
104+
func UpdateExternalUser(user *User, gothUser goth.User) error {
105+
loginSource, err := GetActiveOAuth2LoginSourceByName(gothUser.Provider)
106+
if err != nil {
107+
return err
108+
}
109+
externalLoginUser := &ExternalLoginUser{
110+
ExternalID: gothUser.UserID,
111+
UserID: user.ID,
112+
LoginSourceID: loginSource.ID,
113+
RawData: gothUser.RawData,
114+
Provider: gothUser.Provider,
115+
Email: gothUser.Email,
116+
Name: gothUser.Name,
117+
FirstName: gothUser.FirstName,
118+
LastName: gothUser.LastName,
119+
NickName: gothUser.NickName,
120+
Description: gothUser.Description,
121+
AvatarURL: gothUser.AvatarURL,
122+
Location: gothUser.Location,
123+
AccessToken: gothUser.AccessToken,
124+
AccessTokenSecret: gothUser.AccessTokenSecret,
125+
RefreshToken: gothUser.RefreshToken,
126+
ExpiresAt: gothUser.ExpiresAt,
127+
}
128+
129+
has, err := x.Where("external_id=? AND login_source_id=?", gothUser.UserID, loginSource.ID).
130+
NoAutoCondition().
131+
Exist(externalLoginUser)
132+
if err != nil {
133+
return err
134+
} else if !has {
135+
return ErrExternalLoginUserNotExist{user.ID, loginSource.ID}
136+
}
137+
138+
_, err = x.Where("external_id=? AND login_source_id=?", gothUser.UserID, loginSource.ID).AllCols().Update(externalLoginUser)
139+
return err
140+
}
141+
142+
// FindExternalUserOptions represents an options to find external users
143+
type FindExternalUserOptions struct {
144+
Provider string
145+
Limit int
146+
Start int
147+
}
148+
149+
func (opts FindExternalUserOptions) toConds() builder.Cond {
150+
var cond = builder.NewCond()
151+
if len(opts.Provider) > 0 {
152+
cond = cond.And(builder.Eq{"provider": opts.Provider})
153+
}
154+
return cond
155+
}
156+
157+
// FindExternalUsersByProvider represents external users via provider
158+
func FindExternalUsersByProvider(opts FindExternalUserOptions) ([]ExternalLoginUser, error) {
159+
var users []ExternalLoginUser
160+
err := x.Where(opts.toConds()).
161+
Limit(opts.Limit, opts.Start).
162+
Asc("id").
163+
Find(&users)
164+
if err != nil {
165+
return nil, err
166+
}
167+
return users, nil
168+
}
169+
170+
// UpdateMigrationsByType updates all migrated repositories' posterid from gitServiceType to replace originalAuthorID to posterID
171+
func UpdateMigrationsByType(tp structs.GitServiceType, externalUserID, userID int64) error {
172+
if err := UpdateIssuesMigrationsByType(tp, externalUserID, userID); err != nil {
173+
return err
174+
}
175+
176+
if err := UpdateCommentsMigrationsByType(tp, externalUserID, userID); err != nil {
177+
return err
178+
}
179+
180+
return UpdateReleasesMigrationsByType(tp, externalUserID, userID)
181+
}

models/issue.go

+15-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"code.gitea.io/gitea/modules/base"
1515
"code.gitea.io/gitea/modules/log"
1616
"code.gitea.io/gitea/modules/setting"
17+
"code.gitea.io/gitea/modules/structs"
1718
api "code.gitea.io/gitea/modules/structs"
1819
"code.gitea.io/gitea/modules/timeutil"
1920
"code.gitea.io/gitea/modules/util"
@@ -32,7 +33,7 @@ type Issue struct {
3233
PosterID int64 `xorm:"INDEX"`
3334
Poster *User `xorm:"-"`
3435
OriginalAuthor string
35-
OriginalAuthorID int64
36+
OriginalAuthorID int64 `xorm:"index"`
3637
Title string `xorm:"name"`
3738
Content string `xorm:"TEXT"`
3839
RenderedContent string `xorm:"-"`
@@ -1947,3 +1948,16 @@ func (issue *Issue) ResolveMentionsByVisibility(ctx DBContext, doer *User, menti
19471948

19481949
return
19491950
}
1951+
1952+
// UpdateIssuesMigrationsByType updates all migrated repositories' issues from gitServiceType to replace originalAuthorID to posterID
1953+
func UpdateIssuesMigrationsByType(gitServiceType structs.GitServiceType, originalAuthorID, posterID int64) error {
1954+
_, err := x.Table("issue").
1955+
Where("repo_id IN (SELECT id FROM repository WHERE original_service_type = ?)", gitServiceType).
1956+
And("original_author_id = ?", originalAuthorID).
1957+
Update(map[string]interface{}{
1958+
"poster_id": posterID,
1959+
"original_author": "",
1960+
"original_author_id": 0,
1961+
})
1962+
return err
1963+
}

models/issue_comment.go

+21
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"code.gitea.io/gitea/modules/log"
1515
"code.gitea.io/gitea/modules/markup/markdown"
1616
"code.gitea.io/gitea/modules/references"
17+
"code.gitea.io/gitea/modules/structs"
1718
api "code.gitea.io/gitea/modules/structs"
1819
"code.gitea.io/gitea/modules/timeutil"
1920

@@ -1022,3 +1023,23 @@ func fetchCodeCommentsByReview(e Engine, issue *Issue, currentUser *User, review
10221023
func FetchCodeComments(issue *Issue, currentUser *User) (CodeComments, error) {
10231024
return fetchCodeComments(x, issue, currentUser)
10241025
}
1026+
1027+
// UpdateCommentsMigrationsByType updates comments' migrations information via given git service type and original id and poster id
1028+
func UpdateCommentsMigrationsByType(tp structs.GitServiceType, originalAuthorID, posterID int64) error {
1029+
_, err := x.Table("comment").
1030+
Where(builder.In("issue_id",
1031+
builder.Select("issue.id").
1032+
From("issue").
1033+
InnerJoin("repository", "issue.repo_id = repository.id").
1034+
Where(builder.Eq{
1035+
"repository.original_service_type": tp,
1036+
}),
1037+
)).
1038+
And("comment.original_author_id = ?", originalAuthorID).
1039+
Update(map[string]interface{}{
1040+
"poster_id": posterID,
1041+
"original_author": "",
1042+
"original_author_id": 0,
1043+
})
1044+
return err
1045+
}

models/migrations/migrations.go

+2
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,8 @@ var migrations = []Migration{
254254
NewMigration("add original author name and id on migrated release", addOriginalAuthorOnMigratedReleases),
255255
// v99 -> v100
256256
NewMigration("add task table and status column for repository table", addTaskTable),
257+
// v100 -> v101
258+
NewMigration("update migration repositories' service type", updateMigrationServiceTypes),
257259
}
258260

259261
// Migrate database to current version

models/migrations/v100.go

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright 2019 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package migrations
6+
7+
import (
8+
"net/url"
9+
"strings"
10+
"time"
11+
12+
"github.com/go-xorm/xorm"
13+
)
14+
15+
func updateMigrationServiceTypes(x *xorm.Engine) error {
16+
type Repository struct {
17+
ID int64
18+
OriginalServiceType int `xorm:"index default(0)"`
19+
OriginalURL string `xorm:"VARCHAR(2048)"`
20+
}
21+
22+
if err := x.Sync2(new(Repository)); err != nil {
23+
return err
24+
}
25+
26+
var last int
27+
const batchSize = 50
28+
for {
29+
var results = make([]Repository, 0, batchSize)
30+
err := x.Where("original_url <> '' AND original_url IS NOT NULL").
31+
And("original_service_type = 0 OR original_service_type IS NULL").
32+
OrderBy("id").
33+
Limit(batchSize, last).
34+
Find(&results)
35+
if err != nil {
36+
return err
37+
}
38+
if len(results) == 0 {
39+
break
40+
}
41+
last += len(results)
42+
43+
const PlainGitService = 1 // 1 plain git service
44+
const GithubService = 2 // 2 github.com
45+
46+
for _, res := range results {
47+
u, err := url.Parse(res.OriginalURL)
48+
if err != nil {
49+
return err
50+
}
51+
var serviceType = PlainGitService
52+
if strings.EqualFold(u.Host, "github.com") {
53+
serviceType = GithubService
54+
}
55+
_, err = x.Exec("UPDATE repository SET original_service_type = ? WHERE id = ?", serviceType, res.ID)
56+
if err != nil {
57+
return err
58+
}
59+
}
60+
}
61+
62+
type ExternalLoginUser struct {
63+
ExternalID string `xorm:"pk NOT NULL"`
64+
UserID int64 `xorm:"INDEX NOT NULL"`
65+
LoginSourceID int64 `xorm:"pk NOT NULL"`
66+
RawData map[string]interface{} `xorm:"TEXT JSON"`
67+
Provider string `xorm:"index VARCHAR(25)"`
68+
Email string
69+
Name string
70+
FirstName string
71+
LastName string
72+
NickName string
73+
Description string
74+
AvatarURL string
75+
Location string
76+
AccessToken string
77+
AccessTokenSecret string
78+
RefreshToken string
79+
ExpiresAt time.Time
80+
}
81+
82+
return x.Sync2(new(ExternalLoginUser))
83+
}

models/release.go

+14
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
"code.gitea.io/gitea/modules/git"
1414
"code.gitea.io/gitea/modules/setting"
15+
"code.gitea.io/gitea/modules/structs"
1516
api "code.gitea.io/gitea/modules/structs"
1617
"code.gitea.io/gitea/modules/timeutil"
1718

@@ -366,3 +367,16 @@ func SyncReleasesWithTags(repo *Repository, gitRepo *git.Repository) error {
366367
}
367368
return nil
368369
}
370+
371+
// UpdateReleasesMigrationsByType updates all migrated repositories' releases from gitServiceType to replace originalAuthorID to posterID
372+
func UpdateReleasesMigrationsByType(gitServiceType structs.GitServiceType, originalAuthorID, posterID int64) error {
373+
_, err := x.Table("release").
374+
Where("repo_id IN (SELECT id FROM repository WHERE original_service_type = ?)", gitServiceType).
375+
And("original_author_id = ?", originalAuthorID).
376+
Update(map[string]interface{}{
377+
"publisher_id": posterID,
378+
"original_author": "",
379+
"original_author_id": 0,
380+
})
381+
return err
382+
}

0 commit comments

Comments
 (0)