163 lines
5 KiB
Go
163 lines
5 KiB
Go
package storage
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
"git.mstar.dev/mstar/linstrom/util"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// Note represents an ActivityPub note
|
|
// ActivityPub notes can be quite a few things, depending on fields provided.
|
|
// A survey, a reply, a quote of another note, etc
|
|
// And depending on the origin server of a note, they are treated differently
|
|
// with for example rendering or available actions
|
|
// This struct attempts to contain all information necessary for easily working with a note
|
|
type Note struct {
|
|
ID string `gorm:"primarykey"` // Make ID a string (uuid) for other implementations
|
|
CreatedAt time.Time // When this entry was created
|
|
UpdatedAt time.Time // When this entry was last updated
|
|
// When this entry was deleted (for soft deletions)
|
|
// Soft delete means that this entry still exists in the db, but gorm won't include it anymore unless specifically told to
|
|
// If not null, this entry is marked as deleted
|
|
DeletedAt gorm.DeletedAt `gorm:"index"`
|
|
// Creator Account // `gorm:"foreignKey:CreatorId;references:ID"` // Account that created the post
|
|
CreatorId string
|
|
Remote bool // Whether the note is originally a remote one and just "cached"
|
|
// Raw content of the note. So without additional formatting applied
|
|
// Might already have formatting applied beforehand from the origin server
|
|
RawContent string
|
|
ContentWarning *string // Content warnings of the note, if it contains any
|
|
Attachments []string `gorm:"serializer:json"` // List of Ids for mediaFiles
|
|
Emotes []string `gorm:"serializer:json"` // Emotes used in that message
|
|
RepliesTo *string // Url of the message this replies to
|
|
Quotes *string // url of the message this note quotes
|
|
AccessLevel NoteAccessLevel // Where to send this message to (public, home, followers, dm)
|
|
Pings []string `gorm:"serializer:json"` // Who is being tagged in this message. Also serves as DM targets
|
|
OriginServer string // Url of the origin server. Also the primary key for those
|
|
Tags []string `gorm:"serializer:json"` // Hashtags
|
|
}
|
|
|
|
func (s *Storage) FindNoteById(id string) (*Note, error) {
|
|
defer util.Untrace(util.Trace(&log.Logger))
|
|
note := &Note{}
|
|
cacheNote, err := s.cacheNoteIdToData(id)
|
|
switch err {
|
|
case nil:
|
|
return cacheNote, nil
|
|
// Empty case, not found in cache means check db
|
|
case errCacheNotFound:
|
|
default:
|
|
return nil, err
|
|
}
|
|
switch err {
|
|
|
|
}
|
|
err = s.db.Find(note, id).Error
|
|
switch err {
|
|
case nil:
|
|
if err = s.cache.Set(cacheNoteIdToNotePrefix+id, note); err != nil {
|
|
log.Warn().Err(err).Str("note-id", id).Msg("Failed to place note in cache")
|
|
}
|
|
return note, nil
|
|
case gorm.ErrRecordNotFound:
|
|
return nil, ErrEntryNotFound
|
|
default:
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
func (s *Storage) FindNotesByFuzzyContent(fuzzyContent string) ([]Note, error) {
|
|
defer util.Untrace(util.Trace(&log.Logger))
|
|
notes := []Note{}
|
|
// TODO: Figure out if cache can be used here too
|
|
err := s.db.Where("raw_content LIKE %?%", fuzzyContent).Find(notes).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return notes, nil
|
|
}
|
|
|
|
func (s *Storage) FindNotesByAuthorHandle(handle string) ([]Note, error) {
|
|
defer util.Untrace(util.Trace(&log.Logger))
|
|
acc, err := s.FindAccountByFullHandle(handle)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("account with handle %s not found: %w", handle, err)
|
|
}
|
|
return s.FindNotesByAuthorId(acc.ID)
|
|
}
|
|
|
|
func (s *Storage) FindNotesByAuthorId(id string) ([]Note, error) {
|
|
defer util.Untrace(util.Trace(&log.Logger))
|
|
notes := []Note{}
|
|
err := s.db.Where("creator = ?", id).Find(notes).Error
|
|
switch err {
|
|
case nil:
|
|
return notes, nil
|
|
case gorm.ErrRecordNotFound:
|
|
return nil, ErrEntryNotFound
|
|
default:
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
func (s *Storage) UpdateNote(note *Note) error {
|
|
defer util.Untrace(util.Trace(&log.Logger))
|
|
if note == nil || note.ID == "" {
|
|
return ErrInvalidData
|
|
}
|
|
err := s.db.Save(note).Error
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = s.cache.Set(cacheNoteIdToNotePrefix+note.ID, note)
|
|
if err != nil {
|
|
log.Warn().
|
|
Err(err).
|
|
Msg("Failed to update note into cache. Cache and db might be out of sync, a force sync is recommended")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *Storage) CreateNoteLocal(
|
|
creatorId string,
|
|
rawContent string,
|
|
contentWarning *string,
|
|
timestamp time.Time,
|
|
attachmentIds []string,
|
|
emoteIds []string,
|
|
repliesToId *string,
|
|
quotesId *string,
|
|
accessLevel NoteAccessLevel,
|
|
tags []string,
|
|
) (*Note, error) {
|
|
defer util.Untrace(util.Trace(&log.Logger))
|
|
// TODO: Implement me
|
|
panic("not implemented")
|
|
}
|
|
|
|
func (s *Storage) CreateNoteRemote(
|
|
creatorId string,
|
|
rawContent string,
|
|
contentWarning *string,
|
|
timestamp time.Time,
|
|
attachmentIds []string,
|
|
emoteIds []string,
|
|
repliesToId *string,
|
|
quotesId *string,
|
|
accessLevel NoteAccessLevel,
|
|
tags []string,
|
|
originId string,
|
|
) (*Note, error) {
|
|
defer util.Untrace(util.Trace(&log.Logger))
|
|
// TODO: Implement me
|
|
panic("not implemented")
|
|
}
|
|
|
|
func (s *Storage) DeleteNote(id string) {
|
|
defer util.Untrace(util.Trace(&log.Logger))
|
|
s.cache.Delete(cacheNoteIdToNotePrefix + id)
|
|
s.db.Delete(Note{ID: id})
|
|
}
|