Changes
14 changed files (+313/-180)
-
-
@@ -17,7 +17,7 @@ var (ErrNameEmpty = errors.New("Name is empty") // AlphaDashDotPattern characters prohibited in a user name (anything except A-Za-z0-9_.-) AlphaDashDotPattern = regexp.MustCompile(`[^\w-\.@]`) AlphaDashDotPattern = regexp.MustCompile(`[^\w-\.@]`) // Ugly hack to allow remote usernames to contain @ ) // ErrNameReserved represents a "reserved name" error.
-
-
-
@@ -8,6 +8,8 @@ import (ap "github.com/go-ap/activitypub" ) const ForgeFedNamespaceURI = "https://forgefed.org/ns" // GetItemByType instantiates a new ForgeFed object if the type matches // otherwise it defaults to existing activitypub package typer function. func GetItemByType(typ ap.ActivityVocabularyType) (ap.Item, error) {
-
-
-
@@ -27,6 +27,10 @@ type Ticket struct {ResolvedBy ap.Item `jsonld:"resolvedBy,omitempty"` // Resolved When the ticket has been marked as resolved Resolved time.Time `jsonld:"resolved,omitempty"` // Origin The head branch if this ticket is a pull request Origin ap.Item `jsonld:"origin,omitempty"` // Target The base branch if this ticket is a pull request Target ap.Item `jsonld:"target,omitempty"` } // TicketNew initializes a Ticket type Object
-
@@ -56,6 +60,12 @@ func (t Ticket) MarshalJSON() ([]byte, error) {if !t.Resolved.IsZero() { ap.WriteTimeJSONProp(&b, "resolved", t.Resolved) } if t.Origin != nil { ap.WriteItemJSONProp(&b, "origin", t.Origin) } if t.Target != nil { ap.WriteItemJSONProp(&b, "target", t.Target) } ap.Write(&b, '}') return b, nil }
-
@@ -72,6 +82,8 @@ func (t *Ticket) UnmarshalJSON(data []byte) error {t.IsResolved = ap.JSONGetBoolean(val, "isResolved") t.ResolvedBy = ap.JSONGetItem(val, "resolvedBy") t.Resolved = ap.JSONGetTime(val, "resolved") t.Origin = ap.JSONGetItem(val, "origin") t.Target = ap.JSONGetItem(val, "target") return ap.OnObject(&t.Object, func(a *ap.Object) error { return ap.LoadObject(val, a)
-
-
-
@@ -0,0 +1,41 @@// Copyright 2022 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package activitypub import ( "context" "strconv" "strings" "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/log" ap "github.com/go-ap/activitypub" ) // Create a comment func Comment(ctx context.Context, note ap.Note) { actorUser, err := personIRIToUser(ctx, note.AttributedTo.GetLink()) if err != nil { log.Warn("Couldn't find actor", err) } // TODO: Move IRI processing stuff to iri.go context := note.Context.GetLink() contextSplit := strings.Split(context.String(), "/") username := contextSplit[3] reponame := contextSplit[4] repo, _ := repo_model.GetRepositoryByOwnerAndName(username, reponame) idx, _ := strconv.ParseInt(contextSplit[len(contextSplit)-1], 10, 64) issue, _ := issues.GetIssueByIndex(repo.ID, idx) issues.CreateCommentCtx(ctx, &issues.CreateCommentOptions{ Doer: actorUser, Repo: repo, Issue: issue, Content: note.Content.String(), }) }
-
-
-
@@ -14,33 +14,61 @@ import (ap "github.com/go-ap/activitypub" ) func Follow(ctx context.Context, activity ap.Follow) { actorIRI := activity.Actor.GetID() objectIRI := activity.Object.GetID() actorIRISplit := strings.Split(actorIRI.String(), "/") objectIRISplit := strings.Split(objectIRI.String(), "/") actorName := actorIRISplit[len(actorIRISplit)-1] + "@" + actorIRISplit[2] objectName := objectIRISplit[len(objectIRISplit)-1] err := FederatedUserNew(actorName, actorIRI) // Process a Follow activity func Follow(ctx context.Context, follow ap.Follow) { // Actor is the user performing the follow actorIRI := follow.Actor.GetID() actorUser, err := personIRIToUser(ctx, actorIRI) if err != nil { log.Warn("Couldn't create new user", err) log.Warn("Couldn't find actor user for follow", err) return } actorUser, err := user_model.GetUserByName(ctx, actorName) if err != nil { log.Warn("Couldn't find actor", err) } objectUser, err := user_model.GetUserByName(ctx, objectName) if err != nil { log.Warn("Couldn't find object", err) // Object is the user being followed objectIRI := follow.Object.GetID() objectUser, err := personIRIToUser(ctx, objectIRI) // Must be a local user if strings.Contains(objectUser.Name, "@") || err != nil { log.Warn("Couldn't find object user for follow", err) return } user_model.FollowUser(actorUser.ID, objectUser.ID) accept := ap.AcceptNew(objectIRI, activity) // Send back an Accept activity accept := ap.AcceptNew(objectIRI, follow) accept.Actor = ap.Person{ID: objectIRI} accept.To = ap.ItemCollection{ap.IRI(actorIRI.String() + "/inbox")} accept.Object = activity accept.Object = follow Send(objectUser, accept) } // Process a Undo follow activity // I haven't tried this yet so hopefully it works func Unfollow(ctx context.Context, unfollow ap.Undo) { // Actor is the user performing the undo follow actorIRI := unfollow.Actor.GetID() actorUser, err := personIRIToUser(ctx, actorIRI) if err != nil { log.Warn("Couldn't find actor user for follow", err) return } // Object is the user being unfollowed objectIRI := unfollow.Object.GetID() objectUser, err := personIRIToUser(ctx, objectIRI) // Must be a local user if strings.Contains(objectUser.Name, "@") || err != nil { log.Warn("Couldn't find object user for follow", err) return } user_model.UnfollowUser(actorUser.ID, objectUser.ID) // Send back an Accept activity accept := ap.AcceptNew(objectIRI, unfollow) accept.Actor = ap.Person{ID: objectIRI} accept.To = ap.ItemCollection{ap.IRI(actorIRI.String() + "/inbox")} accept.Object = unfollow Send(objectUser, accept) }
-
-
-
@@ -8,7 +8,7 @@ import ("context" "strings" //"code.gitea.io/gitea/models/forgefed" "code.gitea.io/gitea/models/forgefed" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log"
-
@@ -20,6 +20,8 @@ import () func Fork(ctx context.Context, instance, username, reponame, destUsername string) { // TODO: Clean this up // Migrate repository code user, _ := user_model.GetUserByName(ctx, destUsername) _, err := migrations.MigrateRepository(ctx, user, destUsername, migrations.MigrateOptions{
-
@@ -30,25 +32,27 @@ func Fork(ctx context.Context, instance, username, reponame, destUsername stringlog.Warn("Couldn't create fork", err) } // Make the migrated repo a fork // TODO: Make the migrated repo a fork // Send a Create activity to the instance we are forking from create := ap.Create{Type: ap.CreateType} create.To = ap.ItemCollection{ap.IRI("https://" + instance + "/api/v1/activitypub/repo/" + username + "/" + reponame + "/inbox")} repo := ap.IRI(strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/repo/" + destUsername + "/" + reponame) // repo := forgefed.RepositoryNew(ap.IRI(strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/repo/" + destUsername + "/" + reponame)) repo := ap.IRI(setting.AppURL + "api/v1/activitypub/repo/" + destUsername + "/" + reponame) // repo := forgefed.RepositoryNew(ap.IRI(setting.AppURL + "api/v1/activitypub/repo/" + destUsername + "/" + reponame)) // repo.ForkedFrom = forgefed.RepositoryNew(ap.IRI()) create.Object = repo Send(user, &create) } func ForkFromCreate(ctx context.Context, activity ap.Create) { func ForkFromCreate(ctx context.Context, repository forgefed.Repository) { // TODO: Clean this up // Don't create an actual copy of the remote repo! // https://gitea.com/Ta180m/gitea/issues/7 // Create the fork repoIRI := activity.Object.GetID() repoIRI := repository.GetID() repoIRISplit := strings.Split(repoIRI.String(), "/") instance := repoIRISplit[2] username := repoIRISplit[7]
-
@@ -63,6 +67,4 @@ func ForkFromCreate(ctx context.Context, activity ap.Create) {_, err := repo_service.ForkRepository(ctx, user, user, repo_service.ForkRepoOptions{BaseRepo: repo, Name: reponame, Description: "this is a remote fork"}) log.Warn("Couldn't create copy of remote fork", err) // TODO: send back accept }
-
-
-
@@ -0,0 +1,80 @@// Copyright 2022 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package activitypub import ( "context" "errors" "strings" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" ap "github.com/go-ap/activitypub" ) // Returns the username corresponding to a Person actor IRI func personIRIToName(personIRI ap.IRI) (string, error) { personIRISplit := strings.Split(personIRI.String(), "/") if len(personIRISplit) < 3 { return "", errors.New("Not a Person actor IRI") } instance := personIRISplit[2] name := personIRISplit[len(personIRISplit)-1] if instance == setting.Domain { // Local user return name, nil } else { // Remote user // Get name in username@instance.com format return name + "@" + instance, nil } } // Returns the user corresponding to a Person actor IRI func personIRIToUser(ctx context.Context, personIRI ap.IRI) (*user_model.User, error) { name, err := personIRIToName(personIRI) if err != nil { return nil, err } user, err := user_model.GetUserByName(ctx, name) if err != nil || !strings.Contains(name, "@") { return user, err } FederatedUserNew(personIRI) return user_model.GetUserByName(ctx, name) } // Returns the owner and name corresponding to a Repository actor IRI func repositoryIRIToName(repoIRI ap.IRI) (string, string, error) { repoIRISplit := strings.Split(repoIRI.String(), "/") if len(repoIRISplit) < 5 { return "", "", errors.New("Not a Repository actor IRI") } instance := repoIRISplit[2] username := repoIRISplit[len(repoIRISplit)-2] reponame := repoIRISplit[len(repoIRISplit)-1] if instance == setting.Domain { // Local repo return username, reponame, nil } else { // Remote repo return username + "@" + instance, reponame, nil } } // Returns the repository corresponding to a Repository actor IRI func repositoryIRIToRepository(ctx context.Context, repoIRI ap.IRI) (*repo_model.Repository, error) { username, reponame, err := repositoryIRIToName(repoIRI) if err != nil { return nil, err } return repo_model.GetRepositoryByOwnerAndName(username, reponame) }
-
-
-
@@ -6,41 +6,11 @@ package activitypubimport ( "context" "strconv" "strings" "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/log" ap "github.com/go-ap/activitypub" "code.gitea.io/gitea/models/forgefed" ) func Comment(ctx context.Context, activity ap.Note) { actorIRI := activity.AttributedTo.GetLink() actorIRISplit := strings.Split(actorIRI.String(), "/") actorName := actorIRISplit[len(actorIRISplit)-1] + "@" + actorIRISplit[2] err := FederatedUserNew(actorName, actorIRI) if err != nil { log.Warn("Couldn't create new user", err) } actorUser, err := user_model.GetUserByName(ctx, actorName) if err != nil { log.Warn("Couldn't find actor", err) } context := activity.Context.GetLink() contextSplit := strings.Split(context.String(), "/") username := contextSplit[3] reponame := contextSplit[4] repo, _ := repo_model.GetRepositoryByOwnerAndName(username, reponame) idx, _ := strconv.ParseInt(contextSplit[len(contextSplit)-1], 10, 64) issue, _ := issues.GetIssueByIndex(repo.ID, idx) issues.CreateCommentCtx(ctx, &issues.CreateCommentOptions{ Doer: actorUser, Repo: repo, Issue: issue, Content: activity.Content.String(), }) // Create an issue func Issue(ctx context.Context, ticket forgefed.Ticket) { // TODO }
-
-
-
@@ -9,31 +9,23 @@ import ("fmt" "strings" "code.gitea.io/gitea/models/forgefed" 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/log" pull_service "code.gitea.io/gitea/services/pull" ap "github.com/go-ap/activitypub" ) func PullRequest(ctx context.Context, activity ap.Move) { actorIRI := activity.AttributedTo.GetLink() actorIRISplit := strings.Split(actorIRI.String(), "/") actorName := actorIRISplit[len(actorIRISplit)-1] + "@" + actorIRISplit[2] err := FederatedUserNew(actorName, actorIRI) if err != nil { log.Warn("Couldn't create new user", err) } actorUser, err := user_model.GetUserByName(ctx, actorName) func PullRequest(ctx context.Context, ticket forgefed.Ticket) { // TODO: Clean this up actorUser, err := personIRIToUser(ctx, ticket.AttributedTo.GetLink()) if err != nil { log.Warn("Couldn't find actor", err) log.Warn("Couldn't find ticket actor user", err) } // This code is really messy // The IRI processing stuff should be in a separate function originIRI := activity.Origin.GetLink() // TODO: The IRI processing stuff should be moved to iri.go originIRI := ticket.Origin.GetLink() originIRISplit := strings.Split(originIRI.String(), "/") originInstance := originIRISplit[2] originUsername := originIRISplit[3]
-
@@ -41,7 +33,7 @@ func PullRequest(ctx context.Context, activity ap.Move) {originBranch := originIRISplit[len(originIRISplit)-1] originRepo, _ := repo_model.GetRepositoryByOwnerAndName(originUsername+"@"+originInstance, originReponame) targetIRI := activity.Target.GetLink() targetIRI := ticket.Target.GetLink() targetIRISplit := strings.Split(targetIRI.String(), "/") // targetInstance := targetIRISplit[2] targetUsername := targetIRISplit[3]
-
@@ -56,19 +48,18 @@ func PullRequest(ctx context.Context, activity ap.Move) {PosterID: actorUser.ID, Poster: actorUser, IsPull: true, Content: "🎉", Content: "🎉", // TODO: Get content from Ticket object } pr := &issues_model.PullRequest{ HeadRepoID: originRepo.ID, BaseRepoID: targetRepo.ID, HeadBranch: originBranch, HeadCommitID: "73f228996f27fad2c7bb60435f912d943b66b0ee", // hardcoded for now BaseBranch: targetBranch, HeadRepo: originRepo, BaseRepo: targetRepo, MergeBase: "", Type: issues_model.PullRequestGitea, HeadRepoID: originRepo.ID, BaseRepoID: targetRepo.ID, HeadBranch: originBranch, BaseBranch: targetBranch, HeadRepo: originRepo, BaseRepo: targetRepo, MergeBase: "", Type: issues_model.PullRequestGitea, } err = pull_service.NewPullRequest(ctx, targetRepo, prIssue, []int64{}, []string{}, pr, []int64{})
-
-
-
@@ -10,15 +10,17 @@ import ("net/http" "net/url" "code.gitea.io/gitea/models/forgefed" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" ap "github.com/go-ap/activitypub" "github.com/go-ap/jsonld" ) // Fetch a remote ActivityStreams object func Fetch(iri *url.URL) (b []byte, err error) { req := httplib.NewRequest(iri.String(), http.MethodGet) req.Header("Accept", ActivityStreamsContentType)
-
@@ -37,22 +39,21 @@ func Fetch(iri *url.URL) (b []byte, err error) {return b, err } // Send an activity func Send(user *user_model.User, activity *ap.Activity) { body, err := activity.MarshalJSON() binary, err := jsonld.WithContext( jsonld.IRI(ap.ActivityBaseURI), jsonld.IRI(ap.SecurityContextURI), jsonld.IRI(forgefed.ForgeFedNamespaceURI), ).Marshal(activity) if err != nil { log.Warn("Marshal", err) return } var jsonmap map[string]interface{} err = json.Unmarshal(body, &jsonmap) if err != nil { return } jsonmap["@context"] = "https://www.w3.org/ns/activitystreams" body, _ = json.Marshal(jsonmap) for _, to := range activity.To { client, _ := NewClient(user, setting.AppURL+"api/v1/activitypub/user/"+user.Name+"#main-key") resp, _ := client.Post(body, to.GetID().String()) resp, _ := client.Post(binary, to.GetID().String()) respBody, _ := io.ReadAll(io.LimitReader(resp.Body, setting.Federation.MaxSize)) log.Debug(string(respBody)) }
-
-
-
@@ -11,10 +11,15 @@ import (ap "github.com/go-ap/activitypub" ) func FederatedUserNew(name string, IRI ap.IRI) error { func FederatedUserNew(IRI ap.IRI) error { name, err := personIRIToName(IRI) if err != nil { return err } user := &user_model.User{ Name: name, Email: name, // TODO: change this to something else to prevent collisions with normal users Email: name, // TODO: change this to something else to prevent collisions with normal users, maybe fetch email using Gitea API LoginType: auth.Federated, Website: IRI.String(), }
-
-
-
@@ -7,7 +7,6 @@ package activitypubimport ( "io" "net/http" "strings" "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/forgefed"
-
@@ -39,7 +38,7 @@ func Person(ctx *context.APIContext) {// "200": // "$ref": "#/responses/ActivityPub" link := strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/user/" + ctx.ContextUser.Name link := setting.AppURL + "api/v1/activitypub/user/" + ctx.ContextUser.Name person := ap.PersonNew(ap.IRI(link)) person.Name = ap.NaturalLanguageValuesNew()
-
@@ -66,15 +65,12 @@ func Person(ctx *context.APIContext) {person.Inbox = ap.IRI(link + "/inbox") person.Outbox = ap.IRI(link + "/outbox") person.Following = ap.IRI(link + "/following") person.Followers = ap.IRI(link + "/followers") person.Liked = ap.IRI(link + "/liked") person.PublicKey.ID = ap.IRI(link + "#main-key") person.PublicKey.Owner = ap.IRI(link) publicKeyPem, err := activitypub.GetPublicKey(ctx.ContextUser) if err != nil { ctx.ServerError("GetPublicKey", err)
-
@@ -109,10 +105,13 @@ func PersonInbox(ctx *context.APIContext) {var activity ap.Activity activity.UnmarshalJSON(body) if activity.Type == ap.FollowType { switch activity.Type { case ap.FollowType: activitypub.Follow(ctx, activity) } else { log.Warn("ActivityStreams type not supported", activity) case ap.UndoType: activitypub.Unfollow(ctx, activity) default: log.Debug("ActivityStreams type not supported", activity) ctx.PlainText(http.StatusNotImplemented, "ActivityStreams type not supported") return }
-
@@ -137,7 +136,7 @@ func PersonOutbox(ctx *context.APIContext) {// "200": // "$ref": "#/responses/ActivityPub" link := strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/user/" + ctx.ContextUser.Name link := setting.AppURL + "api/v1/activitypub/user/" + ctx.ContextUser.Name feed, err := models.GetFeeds(ctx, models.GetFeedsOptions{ RequestedUser: ctx.ContextUser,
-
@@ -186,7 +185,7 @@ func PersonFollowing(ctx *context.APIContext) {// "200": // "$ref": "#/responses/ActivityPub" link := strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/user/" + ctx.ContextUser.Name link := setting.AppURL + "api/v1/activitypub/user/" + ctx.ContextUser.Name users, _, err := user_model.GetUserFollowing(ctx, ctx.ContextUser, ctx.Doer, utils.GetListOptions(ctx)) if err != nil {
-
@@ -223,7 +222,7 @@ func PersonFollowers(ctx *context.APIContext) {// "200": // "$ref": "#/responses/ActivityPub" link := strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/user/" + ctx.ContextUser.Name link := setting.AppURL + "api/v1/activitypub/user/" + ctx.ContextUser.Name users, _, err := user_model.GetUserFollowers(ctx, ctx.ContextUser, ctx.Doer, utils.GetListOptions(ctx)) if err != nil {
-
@@ -235,6 +234,7 @@ func PersonFollowers(ctx *context.APIContext) {followers.TotalItems = uint(len(users)) for _, user := range users { // TODO: handle non-Federated users person := ap.PersonNew(ap.IRI(user.Website)) followers.OrderedItems.Append(person) }
-
@@ -259,7 +259,7 @@ func PersonLiked(ctx *context.APIContext) {// "200": // "$ref": "#/responses/ActivityPub" link := strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/user/" + ctx.ContextUser.Name link := setting.AppURL + "api/v1/activitypub/user/" + ctx.ContextUser.Name repos, count, err := repo_model.SearchRepository(&repo_model.SearchRepoOptions{ Actor: ctx.Doer,
-
@@ -275,7 +275,8 @@ func PersonLiked(ctx *context.APIContext) {liked.TotalItems = uint(count) for _, repo := range repos { repo := forgefed.RepositoryNew(ap.IRI(strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/repo/" + repo.OwnerName + "/" + repo.Name)) // TODO: Handle remote starred repos repo := forgefed.RepositoryNew(ap.IRI(setting.AppURL + "api/v1/activitypub/repo/" + repo.OwnerName + "/" + repo.Name)) liked.OrderedItems.Append(repo) }
-
-
-
@@ -7,21 +7,18 @@ package activitypubimport ( "io" "net/http" "strings" "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/forgefed" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/activitypub" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/routers/api/v1/utils" ap "github.com/go-ap/activitypub" ) // Repo function // Repo function returns the Repository actor of a repo func Repo(ctx *context.APIContext) { // swagger:operation GET /activitypub/repo/{username}/{reponame} activitypub activitypubRepo // ---
-
@@ -43,7 +40,7 @@ func Repo(ctx *context.APIContext) {// "200": // "$ref": "#/responses/ActivityPub" link := strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/repo/" + ctx.ContextUser.Name + "/" + ctx.Repo.Repository.Name link := setting.AppURL + "api/v1/activitypub/repo/" + ctx.ContextUser.Name + "/" + ctx.Repo.Repository.Name repo := forgefed.RepositoryNew(ap.IRI(link)) repo.Name = ap.NaturalLanguageValuesNew()
-
@@ -53,7 +50,7 @@ func Repo(ctx *context.APIContext) {return } repo.AttributedTo = ap.IRI(strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/user/" + ctx.ContextUser.Name) repo.AttributedTo = ap.IRI(setting.AppURL + "api/v1/activitypub/user/" + ctx.ContextUser.Name) repo.Summary = ap.NaturalLanguageValuesNew() err = repo.Summary.Set("en", ap.Content(ctx.Repo.Repository.Description))
-
@@ -70,7 +67,7 @@ func Repo(ctx *context.APIContext) {response(ctx, repo) } // RepoInbox function // RepoInbox function handles the incoming data for a repo inbox func RepoInbox(ctx *context.APIContext) { // swagger:operation POST /activitypub/repo/{username}/{reponame}/inbox activitypub activitypubRepoInbox // ---
-
@@ -95,29 +92,63 @@ func RepoInbox(ctx *context.APIContext) {body, err := io.ReadAll(ctx.Req.Body) if err != nil { ctx.ServerError("Error reading request body", err) return } var activity map[string]interface{} err = json.Unmarshal(body, activity) if err != nil { ctx.ServerError("Unmarshal", err) return } var activity ap.Activity activity.UnmarshalJSON(body) // This function doesn't support ForgeFed types!!! log.Warn("Debug", activity) switch activity.Type { case ap.NoteType: // activitypub.Comment(ctx, activity) switch activity["type"].(ap.ActivityVocabularyType) { case ap.CreateType: // if activity.Object.GetType() == forgefed.RepositoryType { // Fork created by remote instance activitypub.ForkFromCreate(ctx, activity) //} case ap.MoveType: // This should actually be forgefed.TicketType but that the UnmarshalJSON function above doesn't support ForgeFed! activitypub.PullRequest(ctx, activity) // Create activity, extract the object object, ok := activity["object"].(map[string]interface{}) if ok { ctx.ServerError("Activity does not contain object", err) return } objectBinary, err := json.Marshal(object) if err != nil { ctx.ServerError("Marshal", err) return } switch object["type"].(ap.ActivityVocabularyType) { case forgefed.RepositoryType: // Fork created by remote instance var repository forgefed.Repository repository.UnmarshalJSON(objectBinary) activitypub.ForkFromCreate(ctx, repository) case forgefed.TicketType: // New issue or pull request var ticket forgefed.Ticket ticket.UnmarshalJSON(objectBinary) if ticket.Origin != nil { // New pull request activitypub.PullRequest(ctx, ticket) } else { // New issue activitypub.Issue(ctx, ticket) } case ap.NoteType: // New comment var note ap.Note note.UnmarshalJSON(objectBinary) activitypub.Comment(ctx, note) } default: log.Warn("ActivityStreams type not supported", activity) ctx.PlainText(http.StatusNotImplemented, "ActivityStreams type not supported") return } ctx.Status(http.StatusNoContent) } // RepoOutbox function // RepoOutbox function returns the repo's Outbox OrderedCollection func RepoOutbox(ctx *context.APIContext) { // swagger:operation GET /activitypub/repo/{username}/outbox activitypub activitypubPersonOutbox // ---
-
@@ -139,34 +170,11 @@ func RepoOutbox(ctx *context.APIContext) {// "200": // "$ref": "#/responses/ActivityPub" link := strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/repo/" + ctx.ContextUser.Name + "/" + ctx.Repo.Repository.Name feed, err := models.GetFeeds(ctx, models.GetFeedsOptions{ RequestedUser: ctx.ContextUser, Actor: ctx.ContextUser, IncludePrivate: false, OnlyPerformedBy: true, IncludeDeleted: false, Date: ctx.FormString("date"), }) if err != nil { ctx.ServerError("Couldn't fetch outbox", err) } outbox := ap.OrderedCollectionNew(ap.IRI(link + "/outbox")) for _, action := range feed { /*if action.OpType == ExampleType { activity := ap.ExampleNew() outbox.OrderedItems.Append(activity) }*/ log.Debug(action.Content) } outbox.TotalItems = uint(len(outbox.OrderedItems)) response(ctx, outbox) // TODO ctx.Status(http.StatusNotImplemented) } // RepoFollowers function // RepoFollowers function returns the repo's Followers OrderedCollection func RepoFollowers(ctx *context.APIContext) { // swagger:operation GET /activitypub/repo/{username}/{reponame}/followers activitypub activitypubRepoFollowers // ---
-
@@ -188,21 +196,6 @@ func RepoFollowers(ctx *context.APIContext) {// "200": // "$ref": "#/responses/ActivityPub" link := strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/repo/" + ctx.ContextUser.Name + "/" + ctx.Repo.Repository.Name users, _, err := user_model.GetUserFollowers(ctx, ctx.ContextUser, ctx.Doer, utils.GetListOptions(ctx)) if err != nil { ctx.ServerError("GetUserFollowers", err) return } followers := ap.OrderedCollectionNew(ap.IRI(link + "/followers")) followers.TotalItems = uint(len(users)) for _, user := range users { person := ap.PersonNew(ap.IRI(user.Website)) followers.OrderedItems.Append(person) } response(ctx, followers) // TODO ctx.Status(http.StatusNotImplemented) }
-
-
-
@@ -7,6 +7,7 @@ package activitypubimport ( "net/http" "code.gitea.io/gitea/models/forgefed" "code.gitea.io/gitea/modules/activitypub" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log"
-
@@ -15,12 +16,18 @@ import ("github.com/go-ap/jsonld" ) // Respond with an ActivityStreams object func response(ctx *context.APIContext, v interface{}) { binary, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI), jsonld.IRI(ap.SecurityContextURI)).Marshal(v) binary, err := jsonld.WithContext( jsonld.IRI(ap.ActivityBaseURI), jsonld.IRI(ap.SecurityContextURI), jsonld.IRI(forgefed.ForgeFedNamespaceURI), ).Marshal(v) if err != nil { ctx.ServerError("Marshal", err) return } ctx.Resp.Header().Add("Content-Type", activitypub.ActivityStreamsContentType) ctx.Resp.WriteHeader(http.StatusOK) if _, err = ctx.Resp.Write(binary); err != nil {
-