linstrom/storage/notes.go
2024-11-20 13:46:15 +01:00

134 lines
4.5 KiB
Go

package storage
import (
"fmt"
"time"
"github.com/rs/zerolog/log"
"gitlab.com/mstarongitlab/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) CreateNote() (*Note, error) {
defer util.Untrace(util.Trace(&log.Logger))
// TODO: Think of good arguments and 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})
}