Prep for reactions, some fixes and improvements
Some checks failed
/ docker (push) Failing after 1m35s
Some checks failed
/ docker (push) Failing after 1m35s
This commit is contained in:
parent
4b62c32247
commit
8947d97825
13 changed files with 262 additions and 43 deletions
30
activitypub/translators/emote.go
Normal file
30
activitypub/translators/emote.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
package translators
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.mstar.dev/mstar/linstrom/config"
|
||||
"git.mstar.dev/mstar/linstrom/storage-new/dbgen"
|
||||
)
|
||||
|
||||
func EmoteTagFromStorage(ctx context.Context, id string) (*Tag, error) {
|
||||
emote, err := dbgen.Emote.Where(dbgen.Emote.ID.Eq(id)).First()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
emoteId := config.GlobalConfig.General.GetFullPublicUrl() + "/api/activitypub/emote/" + id
|
||||
emoteMedia, err := MediaFromStorage(ctx, emote.MetadataId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := Tag{
|
||||
Type: "Emoji",
|
||||
Name: emote.Name,
|
||||
Href: nil,
|
||||
Id: &emoteId,
|
||||
Updated: &emote.UpdatedAt,
|
||||
Icon: emoteMedia,
|
||||
}
|
||||
|
||||
return &out, nil
|
||||
}
|
|
@ -1,5 +1,14 @@
|
|||
package translators
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"git.mstar.dev/mstar/linstrom/activitypub"
|
||||
"git.mstar.dev/mstar/linstrom/config"
|
||||
"git.mstar.dev/mstar/linstrom/storage-new/dbgen"
|
||||
)
|
||||
|
||||
type ActivityLikeOut struct {
|
||||
Context any `json:"@context"`
|
||||
Type string `json:"type"`
|
||||
|
@ -10,3 +19,37 @@ type ActivityLikeOut struct {
|
|||
MkReaction *string `json:"_misskey_reaction,omitempty"`
|
||||
Tags []Tag `json:"tag,omitempty"`
|
||||
}
|
||||
|
||||
func LikeFromStorage(ctx context.Context, id string) (*ActivityLikeOut, error) {
|
||||
reaction, err := dbgen.Reaction.
|
||||
Where(dbgen.Reaction.ID.Eq(id)).
|
||||
Preload(dbgen.Reaction.Emote).
|
||||
First()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
like := &ActivityLikeOut{
|
||||
Type: "Like",
|
||||
Id: config.GlobalConfig.General.GetFullPublicUrl() + "/api/activitypub/activity/like/" + id,
|
||||
Actor: activitypub.UserIdToApUrl(reaction.ReactorId),
|
||||
}
|
||||
if strings.HasPrefix(reaction.NoteId, "http") {
|
||||
like.Object = reaction.NoteId
|
||||
} else {
|
||||
like.Object = config.GlobalConfig.General.GetFullPublicUrl() + "/api/activitypub/note/" + reaction.NoteId
|
||||
}
|
||||
if reaction.Content.Valid {
|
||||
like.Content = &reaction.Content.String
|
||||
like.MkReaction = &reaction.Content.String
|
||||
}
|
||||
if reaction.Emote != nil {
|
||||
like.Content = &reaction.Emote.Name
|
||||
like.MkReaction = &reaction.Emote.Name
|
||||
emote, err := EmoteTagFromStorage(ctx, reaction.Emote.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
like.Tags = append(like.Tags, *emote)
|
||||
}
|
||||
return like, nil
|
||||
}
|
||||
|
|
26
activitypub/translators/media.go
Normal file
26
activitypub/translators/media.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package translators
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.mstar.dev/mstar/linstrom/storage-new/dbgen"
|
||||
)
|
||||
|
||||
type Media struct {
|
||||
Type string `json:"type"`
|
||||
Url string `json:"url"`
|
||||
MediaType string `json:"mediaType"`
|
||||
}
|
||||
|
||||
func MediaFromStorage(ctx context.Context, id string) (*Media, error) {
|
||||
metadata, err := dbgen.MediaMetadata.Where(dbgen.MediaMetadata.ID.Eq(id)).First()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := Media{
|
||||
Type: "Image", // FIXME: Change this to a sort of dynamic detection based on mimetype
|
||||
MediaType: metadata.Type,
|
||||
Url: metadata.Location,
|
||||
}
|
||||
return &data, nil
|
||||
}
|
|
@ -103,16 +103,17 @@ func NoteFromStorage(ctx context.Context, id string) (*ObjectNote, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pubUrl := webshared.UserPublicUrl(target.ID)
|
||||
data.Tags = append(data.Tags, Tag{
|
||||
Type: "Mention",
|
||||
Href: webshared.UserPublicUrl(target.ID),
|
||||
Href: &pubUrl,
|
||||
Name: target.Username,
|
||||
})
|
||||
}
|
||||
for _, tag := range note.Tags {
|
||||
data.Tags = append(data.Tags, Tag{
|
||||
Type: "Hashtag",
|
||||
Href: tag.TagUrl,
|
||||
Href: &tag.TagUrl,
|
||||
Name: tag.Tag,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -21,11 +21,7 @@ type UserKey struct {
|
|||
Owner string `json:"owner"`
|
||||
Pem string `json:"publicKeyPem"`
|
||||
}
|
||||
type Media struct {
|
||||
Type string `json:"type"`
|
||||
Url string `json:"url"`
|
||||
MediaType string `json:"mediaType"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Context []any `json:"@context"`
|
||||
Id string `json:"id"`
|
||||
|
|
|
@ -65,14 +65,6 @@ type ConfigStorage struct {
|
|||
Port int `toml:"port"`
|
||||
SslMode *string `toml:"ssl_mode"`
|
||||
TimeZone *string `toml:"time_zone"`
|
||||
// Url to redis server. If empty, no redis is used
|
||||
RedisUrl *string `toml:"redis_url,omitempty"`
|
||||
// The maximum size of the in-memory cache in bytes
|
||||
MaxInMemoryCacheSize int64 `toml:"max_in_memory_cache_size"`
|
||||
// The time to live for in app in memory cache items, in seconds
|
||||
MaxInMemoryCacheTTL int `toml:"max_in_memory_cache_ttl"`
|
||||
// The time to live for items in redis, in seconds
|
||||
MaxRedisCacheTTL *int `toml:"max_redis_cache_ttl"`
|
||||
// Key used for encrypting sensitive information in the db
|
||||
// DO NOT CHANGE THIS AFTER SETUP
|
||||
EncryptionKey string `toml:"encryption_key"`
|
||||
|
|
|
@ -14,6 +14,14 @@ type Server struct {
|
|||
client *minio.Client
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: Figure out an api for a microservice for transcoding media, see https://tech.lgbt/@lina/114682780787448797
|
||||
- Read endpoint from config
|
||||
- Try to reach transcoder
|
||||
- If transcoder is alive, use it for transcoding
|
||||
- If not alive, store files as is
|
||||
*/
|
||||
|
||||
var (
|
||||
ErrNoBucketAccess = errors.New("can't access configured bucket")
|
||||
)
|
||||
|
@ -50,7 +58,7 @@ func NewServer() (*Server, error) {
|
|||
return &Server{client: client}, nil
|
||||
}
|
||||
|
||||
// Convert a userId and filename into a proper filepath for s3.
|
||||
// UsernameFilename converts a userId and filename into a proper filepath for s3.
|
||||
// Reason for this is that the userId for external users is a valid url which needs to be encoded
|
||||
func UsernameFilename(userId, filename string) string {
|
||||
return userId + "//" + filename
|
||||
|
|
|
@ -27,7 +27,7 @@ func newEmote(db *gorm.DB, opts ...gen.DOOption) emote {
|
|||
|
||||
tableName := _emote.emoteDo.TableName()
|
||||
_emote.ALL = field.NewAsterisk(tableName)
|
||||
_emote.ID = field.NewUint(tableName, "id")
|
||||
_emote.ID = field.NewString(tableName, "id")
|
||||
_emote.CreatedAt = field.NewTime(tableName, "created_at")
|
||||
_emote.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||
_emote.DeletedAt = field.NewField(tableName, "deleted_at")
|
||||
|
@ -73,7 +73,7 @@ type emote struct {
|
|||
emoteDo
|
||||
|
||||
ALL field.Asterisk
|
||||
ID field.Uint
|
||||
ID field.String
|
||||
CreatedAt field.Time
|
||||
UpdatedAt field.Time
|
||||
DeletedAt field.Field
|
||||
|
@ -99,7 +99,7 @@ func (e emote) As(alias string) *emote {
|
|||
|
||||
func (e *emote) updateTableName(table string) *emote {
|
||||
e.ALL = field.NewAsterisk(table)
|
||||
e.ID = field.NewUint(table, "id")
|
||||
e.ID = field.NewString(table, "id")
|
||||
e.CreatedAt = field.NewTime(table, "created_at")
|
||||
e.UpdatedAt = field.NewTime(table, "updated_at")
|
||||
e.DeletedAt = field.NewField(table, "deleted_at")
|
||||
|
|
|
@ -30,6 +30,8 @@ func newNoteToFeed(db *gorm.DB, opts ...gen.DOOption) noteToFeed {
|
|||
_noteToFeed.ID = field.NewUint64(tableName, "id")
|
||||
_noteToFeed.CreatedAt = field.NewTime(tableName, "created_at")
|
||||
_noteToFeed.NoteId = field.NewString(tableName, "note_id")
|
||||
_noteToFeed.FeedId = field.NewUint64(tableName, "feed_id")
|
||||
_noteToFeed.Reason = field.NewString(tableName, "reason")
|
||||
_noteToFeed.Note = noteToFeedBelongsToNote{
|
||||
db: db.Session(&gorm.Session{}),
|
||||
|
||||
|
@ -399,6 +401,17 @@ func newNoteToFeed(db *gorm.DB, opts ...gen.DOOption) noteToFeed {
|
|||
},
|
||||
}
|
||||
|
||||
_noteToFeed.Feed = noteToFeedBelongsToFeed{
|
||||
db: db.Session(&gorm.Session{}),
|
||||
|
||||
RelationField: field.NewRelation("Feed", "models.Feed"),
|
||||
Owner: struct {
|
||||
field.RelationField
|
||||
}{
|
||||
RelationField: field.NewRelation("Feed.Owner", "models.User"),
|
||||
},
|
||||
}
|
||||
|
||||
_noteToFeed.fillFieldMap()
|
||||
|
||||
return _noteToFeed
|
||||
|
@ -411,8 +424,12 @@ type noteToFeed struct {
|
|||
ID field.Uint64
|
||||
CreatedAt field.Time
|
||||
NoteId field.String
|
||||
FeedId field.Uint64
|
||||
Reason field.String
|
||||
Note noteToFeedBelongsToNote
|
||||
|
||||
Feed noteToFeedBelongsToFeed
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
}
|
||||
|
||||
|
@ -431,6 +448,8 @@ func (n *noteToFeed) updateTableName(table string) *noteToFeed {
|
|||
n.ID = field.NewUint64(table, "id")
|
||||
n.CreatedAt = field.NewTime(table, "created_at")
|
||||
n.NoteId = field.NewString(table, "note_id")
|
||||
n.FeedId = field.NewUint64(table, "feed_id")
|
||||
n.Reason = field.NewString(table, "reason")
|
||||
|
||||
n.fillFieldMap()
|
||||
|
||||
|
@ -447,10 +466,12 @@ func (n *noteToFeed) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
|||
}
|
||||
|
||||
func (n *noteToFeed) fillFieldMap() {
|
||||
n.fieldMap = make(map[string]field.Expr, 4)
|
||||
n.fieldMap = make(map[string]field.Expr, 7)
|
||||
n.fieldMap["id"] = n.ID
|
||||
n.fieldMap["created_at"] = n.CreatedAt
|
||||
n.fieldMap["note_id"] = n.NoteId
|
||||
n.fieldMap["feed_id"] = n.FeedId
|
||||
n.fieldMap["reason"] = n.Reason
|
||||
|
||||
}
|
||||
|
||||
|
@ -458,12 +479,15 @@ func (n noteToFeed) clone(db *gorm.DB) noteToFeed {
|
|||
n.noteToFeedDo.ReplaceConnPool(db.Statement.ConnPool)
|
||||
n.Note.db = db.Session(&gorm.Session{Initialized: true})
|
||||
n.Note.db.Statement.ConnPool = db.Statement.ConnPool
|
||||
n.Feed.db = db.Session(&gorm.Session{Initialized: true})
|
||||
n.Feed.db.Statement.ConnPool = db.Statement.ConnPool
|
||||
return n
|
||||
}
|
||||
|
||||
func (n noteToFeed) replaceDB(db *gorm.DB) noteToFeed {
|
||||
n.noteToFeedDo.ReplaceDB(db)
|
||||
n.Note.db = db.Session(&gorm.Session{})
|
||||
n.Feed.db = db.Session(&gorm.Session{})
|
||||
return n
|
||||
}
|
||||
|
||||
|
@ -675,6 +699,91 @@ func (a noteToFeedBelongsToNoteTx) Unscoped() *noteToFeedBelongsToNoteTx {
|
|||
return &a
|
||||
}
|
||||
|
||||
type noteToFeedBelongsToFeed struct {
|
||||
db *gorm.DB
|
||||
|
||||
field.RelationField
|
||||
|
||||
Owner struct {
|
||||
field.RelationField
|
||||
}
|
||||
}
|
||||
|
||||
func (a noteToFeedBelongsToFeed) Where(conds ...field.Expr) *noteToFeedBelongsToFeed {
|
||||
if len(conds) == 0 {
|
||||
return &a
|
||||
}
|
||||
|
||||
exprs := make([]clause.Expression, 0, len(conds))
|
||||
for _, cond := range conds {
|
||||
exprs = append(exprs, cond.BeCond().(clause.Expression))
|
||||
}
|
||||
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a noteToFeedBelongsToFeed) WithContext(ctx context.Context) *noteToFeedBelongsToFeed {
|
||||
a.db = a.db.WithContext(ctx)
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a noteToFeedBelongsToFeed) Session(session *gorm.Session) *noteToFeedBelongsToFeed {
|
||||
a.db = a.db.Session(session)
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a noteToFeedBelongsToFeed) Model(m *models.NoteToFeed) *noteToFeedBelongsToFeedTx {
|
||||
return ¬eToFeedBelongsToFeedTx{a.db.Model(m).Association(a.Name())}
|
||||
}
|
||||
|
||||
func (a noteToFeedBelongsToFeed) Unscoped() *noteToFeedBelongsToFeed {
|
||||
a.db = a.db.Unscoped()
|
||||
return &a
|
||||
}
|
||||
|
||||
type noteToFeedBelongsToFeedTx struct{ tx *gorm.Association }
|
||||
|
||||
func (a noteToFeedBelongsToFeedTx) Find() (result *models.Feed, err error) {
|
||||
return result, a.tx.Find(&result)
|
||||
}
|
||||
|
||||
func (a noteToFeedBelongsToFeedTx) Append(values ...*models.Feed) (err error) {
|
||||
targetValues := make([]interface{}, len(values))
|
||||
for i, v := range values {
|
||||
targetValues[i] = v
|
||||
}
|
||||
return a.tx.Append(targetValues...)
|
||||
}
|
||||
|
||||
func (a noteToFeedBelongsToFeedTx) Replace(values ...*models.Feed) (err error) {
|
||||
targetValues := make([]interface{}, len(values))
|
||||
for i, v := range values {
|
||||
targetValues[i] = v
|
||||
}
|
||||
return a.tx.Replace(targetValues...)
|
||||
}
|
||||
|
||||
func (a noteToFeedBelongsToFeedTx) Delete(values ...*models.Feed) (err error) {
|
||||
targetValues := make([]interface{}, len(values))
|
||||
for i, v := range values {
|
||||
targetValues[i] = v
|
||||
}
|
||||
return a.tx.Delete(targetValues...)
|
||||
}
|
||||
|
||||
func (a noteToFeedBelongsToFeedTx) Clear() error {
|
||||
return a.tx.Clear()
|
||||
}
|
||||
|
||||
func (a noteToFeedBelongsToFeedTx) Count() int64 {
|
||||
return a.tx.Count()
|
||||
}
|
||||
|
||||
func (a noteToFeedBelongsToFeedTx) Unscoped() *noteToFeedBelongsToFeedTx {
|
||||
a.tx = a.tx.Unscoped()
|
||||
return &a
|
||||
}
|
||||
|
||||
type noteToFeedDo struct{ gen.DO }
|
||||
|
||||
type INoteToFeedDo interface {
|
||||
|
|
|
@ -27,13 +27,14 @@ func newReaction(db *gorm.DB, opts ...gen.DOOption) reaction {
|
|||
|
||||
tableName := _reaction.reactionDo.TableName()
|
||||
_reaction.ALL = field.NewAsterisk(tableName)
|
||||
_reaction.ID = field.NewUint(tableName, "id")
|
||||
_reaction.ID = field.NewString(tableName, "id")
|
||||
_reaction.CreatedAt = field.NewTime(tableName, "created_at")
|
||||
_reaction.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||
_reaction.DeletedAt = field.NewField(tableName, "deleted_at")
|
||||
_reaction.NoteId = field.NewString(tableName, "note_id")
|
||||
_reaction.ReactorId = field.NewString(tableName, "reactor_id")
|
||||
_reaction.EmoteId = field.NewField(tableName, "emote_id")
|
||||
_reaction.Content = field.NewField(tableName, "content")
|
||||
_reaction.Note = reactionBelongsToNote{
|
||||
db: db.Session(&gorm.Session{}),
|
||||
|
||||
|
@ -424,13 +425,14 @@ type reaction struct {
|
|||
reactionDo
|
||||
|
||||
ALL field.Asterisk
|
||||
ID field.Uint
|
||||
ID field.String
|
||||
CreatedAt field.Time
|
||||
UpdatedAt field.Time
|
||||
DeletedAt field.Field
|
||||
NoteId field.String
|
||||
ReactorId field.String
|
||||
EmoteId field.Field
|
||||
Content field.Field
|
||||
Note reactionBelongsToNote
|
||||
|
||||
Reactor reactionBelongsToReactor
|
||||
|
@ -452,13 +454,14 @@ func (r reaction) As(alias string) *reaction {
|
|||
|
||||
func (r *reaction) updateTableName(table string) *reaction {
|
||||
r.ALL = field.NewAsterisk(table)
|
||||
r.ID = field.NewUint(table, "id")
|
||||
r.ID = field.NewString(table, "id")
|
||||
r.CreatedAt = field.NewTime(table, "created_at")
|
||||
r.UpdatedAt = field.NewTime(table, "updated_at")
|
||||
r.DeletedAt = field.NewField(table, "deleted_at")
|
||||
r.NoteId = field.NewString(table, "note_id")
|
||||
r.ReactorId = field.NewString(table, "reactor_id")
|
||||
r.EmoteId = field.NewField(table, "emote_id")
|
||||
r.Content = field.NewField(table, "content")
|
||||
|
||||
r.fillFieldMap()
|
||||
|
||||
|
@ -475,7 +478,7 @@ func (r *reaction) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
|||
}
|
||||
|
||||
func (r *reaction) fillFieldMap() {
|
||||
r.fieldMap = make(map[string]field.Expr, 10)
|
||||
r.fieldMap = make(map[string]field.Expr, 11)
|
||||
r.fieldMap["id"] = r.ID
|
||||
r.fieldMap["created_at"] = r.CreatedAt
|
||||
r.fieldMap["updated_at"] = r.UpdatedAt
|
||||
|
@ -483,6 +486,7 @@ func (r *reaction) fillFieldMap() {
|
|||
r.fieldMap["note_id"] = r.NoteId
|
||||
r.fieldMap["reactor_id"] = r.ReactorId
|
||||
r.fieldMap["emote_id"] = r.EmoteId
|
||||
r.fieldMap["content"] = r.Content
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
package models
|
||||
|
||||
import "gorm.io/gorm"
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Emotes are combinations of a name, the server it originated from
|
||||
// and the media for it
|
||||
// and the media for it. Only represents custom emotes, not characters found in unicode
|
||||
//
|
||||
// TODO: Include the case of unicode icons being used as emote
|
||||
type Emote struct {
|
||||
gorm.Model // Standard gorm model for id and timestamps
|
||||
Metadata MediaMetadata // The media used by this emote
|
||||
MetadataId string // Id of the media information, primarily for gorm
|
||||
ID string `gorm:"primarykey"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||
Metadata MediaMetadata // The media used by this emote
|
||||
MetadataId string // Id of the media information, primarily for gorm
|
||||
// Name of the emote. Also the text for using it in a message (ex. :bob:)
|
||||
Name string
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package models
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
@ -10,12 +11,15 @@ import (
|
|||
// A reaction may contain some content text. If yes, this is the reaction.
|
||||
// It also may contain a specifically linked emote (via tag). If yes, this is the reaction and takes precedence over the content
|
||||
type Reaction struct {
|
||||
gorm.Model
|
||||
ID string `gorm:"primarykey"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||
Note Note
|
||||
NoteId string
|
||||
Reactor User
|
||||
ReactorId string
|
||||
Emote *Emote // Emote is optional. If not set, use the default emote of the server
|
||||
EmoteId sql.NullInt64
|
||||
EmoteId sql.NullString
|
||||
Content sql.NullString // Content/text of the reaction. Used for example in cases where a unicode character is sent as emote reaction
|
||||
}
|
||||
|
|
|
@ -6,17 +6,17 @@ import (
|
|||
|
||||
func BuildActivitypubRouter() http.Handler {
|
||||
router := http.NewServeMux()
|
||||
router.HandleFunc("/user/{id}", users)
|
||||
router.HandleFunc("/user/{id}/inbox", userInbox)
|
||||
router.HandleFunc("/user/{id}/followers", userFollowers)
|
||||
router.HandleFunc("/user/{id}/following", userFollowing)
|
||||
router.HandleFunc("/activity/accept/{id}", activityAccept)
|
||||
router.HandleFunc("/activity/create/{id}", activityCreate)
|
||||
router.HandleFunc("/activity/delete/{id}", activityDelete)
|
||||
router.HandleFunc("/activity/reject/{id}", activityReject)
|
||||
router.HandleFunc("/activity/update/{id}", activityUpdate)
|
||||
router.HandleFunc("/activity/follow/{id}", activityFollow)
|
||||
router.HandleFunc("/note/{id}", objectNote)
|
||||
router.HandleFunc("/note/{id}/reactions", noteReactions)
|
||||
router.HandleFunc("GET /user/{id}", users)
|
||||
router.HandleFunc("POST /user/{id}/inbox", userInbox)
|
||||
router.HandleFunc("GET /user/{id}/followers", userFollowers)
|
||||
router.HandleFunc("GET /user/{id}/following", userFollowing)
|
||||
router.HandleFunc("GET /activity/accept/{id}", activityAccept)
|
||||
router.HandleFunc("GET /activity/create/{id}", activityCreate)
|
||||
router.HandleFunc("GET /activity/delete/{id}", activityDelete)
|
||||
router.HandleFunc("GET /activity/reject/{id}", activityReject)
|
||||
router.HandleFunc("GET /activity/update/{id}", activityUpdate)
|
||||
router.HandleFunc("GET /activity/follow/{id}", activityFollow)
|
||||
router.HandleFunc("GET /note/{id}", objectNote)
|
||||
router.HandleFunc("GET /note/{id}/reactions", noteReactions)
|
||||
return router
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue