From f65675771045936c924344579a477b98063aee0c Mon Sep 17 00:00:00 2001 From: mStar Date: Fri, 15 Nov 2024 16:15:07 +0100 Subject: [PATCH] Work on following relations api and small docs --- server/apiLinstrom.go | 121 ++++++++++++++++++++++++++++------ server/apiLinstromAccounts.go | 117 +++++++++++++++++++++++++++++++- server/apiLinstromTypes.go | 1 + 3 files changed, 217 insertions(+), 22 deletions(-) diff --git a/server/apiLinstrom.go b/server/apiLinstrom.go index 5c7109c..52c9d4b 100644 --- a/server/apiLinstrom.go +++ b/server/apiLinstrom.go @@ -1,6 +1,11 @@ package server -import "net/http" +import ( + "net/http" + + "gitlab.com/mstarongitlab/goutils/other" + "gitlab.com/mstarongitlab/linstrom/storage" +) func setupLinstromApiRouter() http.Handler { router := http.NewServeMux() @@ -12,25 +17,50 @@ func setupLinstromApiRouter() http.Handler { func setupLinstromApiV1Router() http.Handler { router := http.NewServeMux() + // Notes + + // Get a note router.HandleFunc("GET /notes/{noteId}", linstromGetNote) + // Send a new note router.HandleFunc("POST /notes", linstromNewNote) + // Update a note router.HandleFunc("PATCH /notes/{noteId}", linstromUpdateNote) + // Delete a note router.HandleFunc("DELETE /notes/{noteId}", linstromDeleteNote) + // Reactions + + // Get all reactions for a note router.HandleFunc("GET /notes/{noteId}/reactions", linstromGetReactions) + // Send a new reaction to a note router.HandleFunc("POST /notes/{noteId}/reactions", linstromAddReaction) + // Update own reaction on a note router.HandleFunc("PATCH /notes/{noteId}/reactions", linstromUpdateReaction) + // Remove own reaction on a note router.HandleFunc("DELETE /notes/{noteId}/reactions", linstromDeleteReaction) + // Boosts + + // Get all boosters of a note router.HandleFunc("GET /notes/{noteId}/boosts", linstromGetBoosts) + // Boost a note router.HandleFunc("POST /notes/{noteId}/boosts", linstromAddBoost) + // Unboost a note router.HandleFunc("DELETE /notes/{noteId}/boosts", linstromRemoveBoost) + // Quotes + + // Get all quotes of a note router.HandleFunc("GET /notes/{noteId}/quotes", linstromGetQuotes) + // Create a new quote message of a given note router.HandleFunc("POST /notes/{noteId}/quotes", linstromAddQuote) + // Pinning + + // Pin a note to account profile router.HandleFunc("POST /notes/{noteId}/pin", linstromPinNote) + // Unpin a note from account profile router.HandleFunc("DELETE /notes/{noteId}/pin", linstromUnpinNote) // Reports router.HandleFunc("POST /notes/{noteId}/report", linstromReportNote) @@ -40,7 +70,10 @@ func setupLinstromApiV1Router() http.Handler { // Accounts // Creating a new account happens either during fetch of a remote one or during registration with a passkey + + // Get an account router.HandleFunc("GET /accounts/{accountId}", linstromGetAccount) + // Update own account // Technically also requires authenticated account to also be owner or correct admin perms, // but that's annoying to handle in a general sense. So leaving that to the function // though figuring out a nice generic-ish way to handle those checks would be nice too @@ -48,6 +81,7 @@ func setupLinstromApiV1Router() http.Handler { "PATCH /accounts/{accountId}", requireValidSessionMiddleware(linstromUpdateAccount), ) + // Delete own account // Technically also requires authenticated account to also be owner or correct admin perms, // but that's annoying to handle in a general sense. So leaving that to the function router.HandleFunc( @@ -55,38 +89,87 @@ func setupLinstromApiV1Router() http.Handler { requireValidSessionMiddleware(linstromDeleteAccount), ) // Follow - router.HandleFunc("GET /accounts/{accountId}/follow", linstromIsFollowingAccount) - router.HandleFunc("POST /accounts/{accountId}/follow", linstromFollowAccount) - router.HandleFunc("DELETE /accounts/{accountId}/follow", linstromUnfollowAccount) - // Block - router.HandleFunc("GET /accounts/{accountId}/block", linstromIsBlockingAccount) - router.HandleFunc("POST /accounts/{accountId}/block", linstromBlockAccount) - router.HandleFunc("DELETE /accounts/{accountId}/block", linstromUnblockAccount) - // Mute - router.HandleFunc("GET /accounts/{accountId}/mute", linstromIsMutedAccount) - router.HandleFunc("POST /accounts/{accountId}/mute", linstromMuteAccount) - router.HandleFunc("DELETE /accounts/{accountId}/mute", linstromUnmuteAccount) - // Report - router.HandleFunc("POST /accounts/{accountId}/reports", linstromReportAccount) - router.HandleFunc("DELETE /accounts/{accountId}/reports", linstromRetractReportAccount) - // Admin - router.HandleFunc("POST /accounts/{accountId}/admin/roles", linstromAdminAddRoleAccount) + // Is logged in following accountId router.HandleFunc( - "DELETE /accounts/{accountId}/admin/roles/{roleName}", - linstromAdminRemoveRoleAccount, + "GET /accounts/{accountId}/follow/to", + requireValidSessionMiddleware(linstromIsFollowingToAccount), ) + // Is accountId following logged in + router.HandleFunc( + "GET /accounts/{accountId}/follow/from", + requireValidSessionMiddleware(linstromIsFollowingFromAccount), + ) + // Send follow request to accountId + router.HandleFunc("POST /accounts/{accountId}/follow", linstromFollowAccount) + // Undo follow request to accountId + router.HandleFunc("DELETE /accounts/{accountId}/follow", linstromUnfollowAccount) + + // Block + + // Is logged in account blocking target account + router.HandleFunc("GET /accounts/{accountId}/block", linstromIsBlockingAccount) + // Block target account + router.HandleFunc("POST /accounts/{accountId}/block", linstromBlockAccount) + // Unblock target account + router.HandleFunc("DELETE /accounts/{accountId}/block", linstromUnblockAccount) + + // Mute + + // Has logged in account muted target account? + router.HandleFunc("GET /accounts/{accountId}/mute", linstromIsMutedAccount) + // Mute target account + router.HandleFunc("POST /accounts/{accountId}/mute", linstromMuteAccount) + // Unmute target account + router.HandleFunc("DELETE /accounts/{accountId}/mute", linstromUnmuteAccount) + + // Report + + // Report a target account + router.HandleFunc("POST /accounts/{accountId}/reports", linstromReportAccount) + // Undo report on target account + router.HandleFunc("DELETE /accounts/{accountId}/reports", linstromRetractReportAccount) + + // Admin + + // Add new role to account + router.Handle( + "POST /accounts/{accountId}/admin/roles", + buildRequirePermissionsMiddleware( + &storage.Role{CanAssignRoles: other.IntoPointer(true)}, + )( + http.HandlerFunc(linstromAdminAddRoleAccount), + ), + ) + // Remove role from account + router.Handle( + "DELETE /accounts/{accountId}/admin/roles/{roleName}", + buildRequirePermissionsMiddleware(&storage.Role{CanAssignRoles: other.IntoPointer(true)})( + http.HandlerFunc(linstromAdminRemoveRoleAccount), + ), + ) + // Send a warning to account router.HandleFunc("POST /accounts/{accountId}/admin/warn", linstromAdminWarnAccount) // Roles + + // Get a role router.HandleFunc("GET /roles/{roleId}", linstromGetRole) + // Create a new role router.HandleFunc("POST /roles", linstromCreateRole) + // Update a role. Builtin roles cannot be edited router.HandleFunc("PATCH /roles/{roleId}", linstromUpdateRole) + // Delete a role. Builtin roles cannot be deleted router.HandleFunc("DELETE /roles/{roleId}", linstromDeleteRole) // Media metadata + + // Get the metadata for some media router.HandleFunc("GET /media/{mediaId}", linstromGetMediaMetadata) + // Upload new media router.HandleFunc("POST /media", linstromNewMediaMetadata) + // Update the metadata for some media router.HandleFunc("PATCH /media/{mediaId}", linstromUpdateMediaMetadata) + // Delete a media entry router.HandleFunc("DELETE /media/{mediaId}", linstromDeleteMediaMetadata) // Event streams diff --git a/server/apiLinstromAccounts.go b/server/apiLinstromAccounts.go index 16a7b5e..e47a6a0 100644 --- a/server/apiLinstromAccounts.go +++ b/server/apiLinstromAccounts.go @@ -165,6 +165,7 @@ func linstromUpdateAccount(w http.ResponseWriter, r *http.Request) { log.Error().Err(err).Msg("Failed to marshal and write updated account") } } + func linstromDeleteAccount(w http.ResponseWriter, r *http.Request) { actorId, _ := ActorIdFromRequest(r) log := hlog.FromRequest(r) @@ -238,9 +239,119 @@ func linstromDeleteAccount(w http.ResponseWriter, r *http.Request) { } } -func linstromIsFollowingAccount(w http.ResponseWriter, r *http.Request) {} -func linstromFollowAccount(w http.ResponseWriter, r *http.Request) {} -func linstromUnfollowAccount(w http.ResponseWriter, r *http.Request) {} +// Is logged in following accountId +func linstromIsFollowingToAccount(w http.ResponseWriter, r *http.Request) { + store := StorageFromRequest(r) + log := hlog.FromRequest(r) + actorId, _ := ActorIdFromRequest(r) + targetId := AccountIdFromRequest(r) + + relation, err := store.GetRelationBetween(actorId, targetId) + var outData linstromRelation + switch err { + case nil: + outData = linstromRelation{ + Id: relation.ID, + CreatedAt: relation.CreatedAt, + UpdatedAt: relation.UpdatedAt, + FromId: relation.FromId, + ToId: relation.ToId, + Accepted: relation.Accepted, + Requested: true, + } + case storage.ErrEntryNotFound: + outData = linstromRelation{ + Id: relation.ID, + CreatedAt: relation.CreatedAt, + UpdatedAt: relation.UpdatedAt, + FromId: relation.FromId, + ToId: relation.ToId, + Accepted: false, + Requested: false, + } + default: + log.Error(). + Err(err). + Str("from-id", actorId). + Str("to-id", targetId). + Msg("Failed to get follow relation") + other.HttpErr( + w, + HttpErrIdDbFailure, + "Failed to get relation", + http.StatusInternalServerError, + ) + } + + err = jsonapi.MarshalPayload(w, outData) + if err != nil { + log.Warn().Err(err).Msg("Failed to marshal response") + other.HttpErr( + w, + HttpErrIdJsonMarshalFail, + "Failed to marshal response", + http.StatusInternalServerError, + ) + } +} + +func linstromIsFollowingFromAccount(w http.ResponseWriter, r *http.Request) { + store := StorageFromRequest(r) + log := hlog.FromRequest(r) + actorId, _ := ActorIdFromRequest(r) + targetId := AccountIdFromRequest(r) + + relation, err := store.GetRelationBetween(targetId, actorId) + var outData linstromRelation + switch err { + case nil: + outData = linstromRelation{ + Id: relation.ID, + CreatedAt: relation.CreatedAt, + UpdatedAt: relation.UpdatedAt, + FromId: relation.FromId, + ToId: relation.ToId, + Accepted: relation.Accepted, + Requested: true, + } + case storage.ErrEntryNotFound: + outData = linstromRelation{ + Id: relation.ID, + CreatedAt: relation.CreatedAt, + UpdatedAt: relation.UpdatedAt, + FromId: relation.FromId, + ToId: relation.ToId, + Accepted: false, + Requested: false, + } + default: + log.Error(). + Err(err). + Str("from-id", targetId). + Str("to-id", actorId). + Msg("Failed to get follow relation") + other.HttpErr( + w, + HttpErrIdDbFailure, + "Failed to get relation", + http.StatusInternalServerError, + ) + } + + err = jsonapi.MarshalPayload(w, outData) + if err != nil { + log.Warn().Err(err).Msg("Failed to marshal response") + other.HttpErr( + w, + HttpErrIdJsonMarshalFail, + "Failed to marshal response", + http.StatusInternalServerError, + ) + } +} + +func linstromFollowAccount(w http.ResponseWriter, r *http.Request) {} +func linstromUnfollowAccount(w http.ResponseWriter, r *http.Request) {} func linstromIsBlockingAccount(w http.ResponseWriter, r *http.Request) {} func linstromBlockAccount(w http.ResponseWriter, r *http.Request) {} diff --git a/server/apiLinstromTypes.go b/server/apiLinstromTypes.go index 906405f..df0863d 100644 --- a/server/apiLinstromTypes.go +++ b/server/apiLinstromTypes.go @@ -96,6 +96,7 @@ type linstromRelation struct { UpdatedAt time.Time `jsonapi:"attr,updated-at"` FromId string `jsonapi:"attr,from-id"` ToId string `jsonapi:"attr,to-id"` + Requested bool `jsonapi:"attr,requested"` Accepted bool `jsonapi:"attr,accepted"` }