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) { // 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}) }