diff --git a/routers/api/v1/activitypub/create.go b/routers/api/v1/activitypub/create.go index 4b0ba5edfd..d29c393bfa 100644 --- a/routers/api/v1/activitypub/create.go +++ b/routers/api/v1/activitypub/create.go @@ -6,21 +6,22 @@ package activitypub import ( "context" "errors" + "fmt" "strconv" "strings" "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/forgefed" "code.gitea.io/gitea/modules/json" - repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/services/activitypub" issue_service "code.gitea.io/gitea/services/issue" + "code.gitea.io/gitea/services/migrations" pull_service "code.gitea.io/gitea/services/pull" - repo_service "code.gitea.io/gitea/services/repository" user_service "code.gitea.io/gitea/services/user" ap "github.com/go-ap/activitypub" @@ -29,8 +30,10 @@ import ( // Create a new federated user from a Person object func createPerson(ctx context.Context, person *ap.Person) error { _, err := user_model.GetUserByIRI(ctx, person.GetLink().String()) - if !user_model.IsErrUserNotExist(err) { + if err == nil { // User already exists + return nil + } else if !user_model.IsErrUserNotExist(err) { return err } @@ -143,27 +146,43 @@ func createRepository(ctx context.Context, repository *forgefed.Repository) erro // Check if repo exists _, err = repo_model.GetRepositoryByIRI(ctx, repository.GetLink().String()) - if !repo_model.IsErrRepoNotExist(err) { + if err == nil { + return nil + } else if !repo_model.IsErrRepoNotExist(err) { return err } - repo, err := repo_service.CreateRepository(ctx, user, user, repo_module.CreateRepoOptions{ - Name: repository.Name.String(), - OriginalURL: repository.GetLink().String(), - }) + iri := repository.GetLink().String() + iriSplit := strings.Split(iri, "/") + username, reponame, instance := iriSplit[len(iriSplit)-2], iriSplit[len(iriSplit)-1], iriSplit[2] + repo, err := migrations.MigrateRepository(ctx, user, username+"@"+instance, migrations.MigrateOptions{ + CloneAddr: "https://" + instance + "/" + username + "/" + reponame + ".git", + RepoName: reponame, + }, nil) if err != nil { return err } - + ctx, committer, err := db.TxContext(db.DefaultContext) + if err != nil { + return err + } + defer committer.Close() + if _, err := db.Exec(ctx, "UPDATE `repository` SET original_url = ? WHERE id = ?", iri, repo.ID); err != nil { + return err + } if repository.ForkedFrom != nil { - repo.IsFork = true - forkedFrom, err := repo_model.GetRepositoryByIRI(ctx, repository.ForkedFrom.GetLink().String()) + base_repo, err := repo_model.GetRepositoryByIRI(ctx, repository.ForkedFrom.GetLink().String()) if err != nil { return err } - repo.ForkID = forkedFrom.ID + if _, err := db.Exec(ctx, "UPDATE `repository` SET fork_id = ? WHERE id = ?", base_repo.ID, repo.ID); err != nil { + return err + } + if _, err := db.Exec(ctx, "UPDATE `repository` SET is_fork = true WHERE id = ?", repo.ID); err != nil { + return err + } } - return nil + return committer.Commit() } // Create a new federated repo from a Repository IRI @@ -222,6 +241,8 @@ func createIssue(ctx context.Context, ticket *forgefed.Ticket) error { // Create a pull request func createPullRequest(ctx context.Context, ticket *forgefed.Ticket) error { + fmt.Println("HIIIIIIIIIIII") + fmt.Println(ticket.Context) // TODO: don't call this function here err := createRepositoryFromIRI(ctx, ticket.Context.GetLink()) if err != nil { diff --git a/routers/api/v1/activitypub/repo.go b/routers/api/v1/activitypub/repo.go index a05b62cb4c..cb44b5988d 100644 --- a/routers/api/v1/activitypub/repo.go +++ b/routers/api/v1/activitypub/repo.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "net/http" + "strings" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/forgefed" @@ -39,27 +40,17 @@ func Repo(ctx *context.APIContext) { iri := ctx.Repo.Repository.GetIRI() repo := forgefed.RepositoryNew(ap.IRI(iri)) - - repo.Name = ap.NaturalLanguageValuesNew() - err := repo.Name.Set("en", ap.Content(ctx.Repo.Repository.Name)) - if err != nil { - ctx.ServerError("Set Name", err) - return - } - + repo.Name = ap.DefaultNaturalLanguageValue(ctx.Repo.Repository.Name) repo.AttributedTo = ap.IRI(ctx.Repo.Owner.GetIRI()) - - repo.Summary = ap.NaturalLanguageValuesNew() - err = repo.Summary.Set("en", ap.Content(ctx.Repo.Repository.Description)) - if err != nil { - ctx.ServerError("Set Description", err) - return - } - + repo.Summary = ap.DefaultNaturalLanguageValue(ctx.Repo.Repository.Description) repo.Inbox = ap.IRI(iri + "/inbox") repo.Outbox = ap.IRI(iri + "/outbox") repo.Followers = ap.IRI(iri + "/followers") repo.Team = ap.IRI(iri + "/team") + if ctx.Repo.Repository.IsFork { + _ = ctx.Repo.Repository.GetBaseRepo(ctx) + repo.ForkedFrom = ap.IRI(ctx.Repo.Repository.BaseRepo.GetIRI()) + } response(ctx, repo) } @@ -86,65 +77,27 @@ func RepoInbox(ctx *context.APIContext) { // "202": // "$ref": "#/responses/empty" - body, err := io.ReadAll(io.LimitReader(ctx.Req.Body, setting.Federation.MaxSize)) - if err != nil { - ctx.ServerError("Error reading request body", err) - return - } + body, _ := io.ReadAll(io.LimitReader(ctx.Req.Body, setting.Federation.MaxSize)) + // body := []byte("{\"type\":\"Ticket\",\"context\":\"https://test.exozy.me/api/v1/activitypub/repo/test/Hello-world\",\"summary\":\"Update 'README.md'\",\"content\":\"WIIJLJk\",\"attributedTo\":\"https://test.exozy.me/api/v1/activitypub/user/test\",\"isResolved\":\"false\",\"origin\":\"https://test.exozy.me/branch/test/Hello-world/main\",\"target\":\"https://git.exozy.me/branch/a/Hello-world/main\",\"name\":\"#1\"}") - ap.ItemTyperFunc = forgefed.GetItemByType - ap.JSONItemUnmarshal = forgefed.JSONUnmarshalerFn - ap.IsNotEmpty = forgefed.NotEmpty - var activity ap.Activity - err = activity.UnmarshalJSON(body) + s := string(body) + fmt.Println(s) + + // HUGE HACK + // VERY BAD IDEA + x := strings.Index(s, "ct\":{") + body = []byte(strings.Replace(s[x+4:len(s)-1], "Object", "Ticket", 1)) + fmt.Println(string(body)) + + var ticket forgefed.Ticket + fmt.Println(ticket) + err := ticket.UnmarshalJSON(body) if err != nil { ctx.ServerError("UnmarshalJSON", err) return } - - // Make sure keyID matches the user doing the activity - _, keyID, _ := getKeyID(ctx.Req) - err = checkActivityAndKeyID(activity, keyID) - if err != nil { - ctx.ServerError("keyID does not match activity", err) - return - } - - // Process activity - switch activity.Type { - case ap.CreateType: - switch activity.Object.GetType() { - case forgefed.RepositoryType: - // Fork created by remote instance - err = forgefed.OnRepository(activity.Object, func(r *forgefed.Repository) error { - return createRepository(ctx, r) - }) - case forgefed.TicketType: - // New issue or pull request - err = forgefed.OnTicket(activity.Object, func(t *forgefed.Ticket) error { - return createTicket(ctx, t) - }) - case ap.NoteType: - // New comment - err = ap.On(activity.Object, func(n *ap.Note) error { - return createComment(ctx, n) - }) - default: - err = fmt.Errorf("unsupported ActivityStreams object type: %s", activity.Object.GetType()) - } - case ap.LikeType: - // Starring a repo - err = star(ctx, activity) - case ap.UndoType: - // Unstarring a repo - err = unstar(ctx, activity) - default: - err = fmt.Errorf("unsupported ActivityStreams activity type: %s", activity.GetType()) - } - if err != nil { - ctx.ServerError("Could not process activity", err) - return - } + fmt.Println(ticket) + fmt.Println(createTicket(ctx, &ticket)) ctx.Status(http.StatusNoContent) } diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 4f99687738..461fe1a29c 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -113,11 +113,12 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository { return nil } - if forkRepo.IsEmpty { + // NOTE: This can be uncommented, but keep in mind this could cause problems in the future with F3 + /*if forkRepo.IsEmpty { log.Trace("Empty repository %-v", forkRepo) ctx.NotFound("getForkRepository", nil) return nil - } + }*/ if err := forkRepo.LoadOwner(ctx); err != nil { ctx.ServerError("LoadOwner", err) diff --git a/services/activitypub/transport.go b/services/activitypub/transport.go index 934852ac7d..3756901a65 100644 --- a/services/activitypub/transport.go +++ b/services/activitypub/transport.go @@ -77,7 +77,7 @@ func Send(ctx context.Context, user *user_model.User, activity *ap.Activity) err } } } else { - recipients = append(recipients, to.GetLink().String()) + recipients = append(recipients, to.GetLink().String()+"/inbox") } } diff --git a/services/pull/pull.go b/services/pull/pull.go index e40e59a2c5..d62f845c97 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -18,6 +18,7 @@ import ( 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/forgefed" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/json" @@ -28,7 +29,10 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/sync" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/activitypub" issue_service "code.gitea.io/gitea/services/issue" + + ap "github.com/go-ap/activitypub" ) // TODO: use clustered lock (unique queue? or *abuse* cache) @@ -125,6 +129,23 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, pull *issu _, _ = issue_service.CreateComment(ops) } + if strings.Contains(repo.OwnerName, "@") { + fmt.Println("HEEEEEEEEEEEEEEYYYYY") + ticket := forgefed.TicketNew() + ticket.AttributedTo = ap.IRI(pull.Poster.GetIRI()) + head_repo, _ := repo_model.GetRepositoryByID(ctx, pr.HeadRepoID) + ticket.Context = ap.IRI(head_repo.GetIRI()) + ticket.Origin = ap.IRI(head_repo.GetIRI()+"/"+pr.BaseBranch) + ticket.Target = ap.IRI(repo.GetIRI()+"/"+pr.HeadBranch) + ticket.Summary = ap.DefaultNaturalLanguageValue(pull.Title) + ticket.Content = ap.DefaultNaturalLanguageValue(pull.Content) + ticket.Name = ap.DefaultNaturalLanguageValue("#1") + create := activitypub.Create(pull.Poster, ticket, repo.GetIRI()) + fmt.Println(create) + fmt.Println("AAAAAAAA", repo.GetIRI()) + activitypub.Send(ctx, pull.Poster, create) + } + return nil } diff --git a/services/repository/fork.go b/services/repository/fork.go index fb93b10f1c..7aa2b3adaf 100644 --- a/services/repository/fork.go +++ b/services/repository/fork.go @@ -58,6 +58,36 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork } } + /*if strings.Contains(opts.BaseRepo.OwnerName, "@") { + // TODO: This is a huge hack + // The current IRI format sucks + iri := opts.BaseRepo.GetIRI() + fmt.Println(iri) + iriSplit := strings.Split(iri, "/") + username, instance := iriSplit[len(iriSplit)-2], iriSplit[2] + fmt.Println("https://" + instance + "/" + username + "/" + opts.Name + ".git") + repo, err := migrations.MigrateRepository(ctx, doer, owner.Name, migrations.MigrateOptions{ + CloneAddr: "https://" + instance + "/" + username + "/" + opts.Name + ".git", + RepoName: opts.Name, + }, nil) + if err != nil { + return nil, err + } + // Really hacky stuff + ctx, committer, err := db.TxContext(db.DefaultContext) + if err != nil { + return nil, err + } + defer committer.Close() + if _, err := db.Exec(ctx, "UPDATE `repository` SET is_fork = true WHERE id = ?", repo.ID); err != nil { + return nil, err + } + if _, err := db.Exec(ctx, "UPDATE `repository` SET fork_id = ? WHERE id = ?", opts.BaseRepo.ID, repo.ID); err != nil { + return nil, err + } + return repo, committer.Commit() + }*/ + forkedRepo, err := repo_model.GetUserFork(ctx, opts.BaseRepo.ID, owner.ID) if err != nil { return nil, err