sync for backup

This commit is contained in:
Melody Becker 2024-08-22 19:57:53 +02:00
parent 1fbdf7fc9d
commit a9916acea5
Signed by: mstar
SSH key fingerprint: SHA256:vkXfS9FG2pVNVfvDrzd1VW9n8VJzqqdKQGljxxX8uK8
92 changed files with 35330 additions and 882 deletions

View file

@ -21,6 +21,7 @@ type MediaFile struct {
// And caching a file for those few times would be a waste of storage
// Caching user and server icons locally however should reduce burden on remote servers by quite a bit though
LocallyCached bool
Sensitive bool // Whether the media is marked as sensitive. If so, hide it in the UI by default
}
// Placeholder media file. Acts as placeholder for media file fields that have not been initialised yet but need a value

View file

@ -42,3 +42,13 @@ var placeholderNote = &Note{
OriginServer: "placeholder",
Tags: []string{},
}
// Try and find a note with a given ID
func (store *Storage) FindNoteById(id string) (*Note, error) {
note := Note{}
res := store.db.First(&note, id)
if res.Error != nil {
return nil, res.Error
}
return &note, nil
}

View file

@ -1,6 +1,7 @@
package storage
import (
"errors"
"fmt"
"github.com/glebarez/sqlite"
@ -14,6 +15,8 @@ type Storage struct {
db *gorm.DB
}
var ErrAccountNotFound = errors.New("account not found")
// Build a new storage using sqlite as database backend
func NewStorageSqlite(filePath string) (*Storage, error) {
db, err := gorm.Open(sqlite.Open(filePath))
@ -34,12 +37,14 @@ func NewStoragePostgres(dbUrl string) (*Storage, error) {
func storageFromEmptyDb(db *gorm.DB) (*Storage, error) {
// AutoMigrate ensures the db is in a state where all the structs given here
// have their own tables and relations setup. It also updates tables if necessary
db.AutoMigrate(
if err := db.AutoMigrate(
placeholderMediaFile,
placeholderUser(),
placeholderUser,
placeholderNote,
placeholderServer,
)
); err != nil {
return nil, fmt.Errorf("problem while auto migrating: %w", err)
}
// Afterwards add the placeholder entries for each table.
// FirstOrCreate either creates a new entry or retrieves the first matching one
// We only care about the creation if there is none yet, so no need to carry the result over
@ -62,7 +67,14 @@ func storageFromEmptyDb(db *gorm.DB) (*Storage, error) {
}, nil
}
// TODO: Placeholder. Update to proper implementation later. Including signature
func (s *Storage) FindLocalAccount(handle string) (string, error) {
return handle, nil
func (s *Storage) FindLocalAccount(handle string) (*User, error) {
acc := User{}
res := s.db.Where("handle = ?", handle).First(&acc)
if res.Error != nil {
return nil, fmt.Errorf("failed to query db: %w", res.Error)
}
if res.RowsAffected == 0 {
return nil, ErrAccountNotFound
}
return nil, errors.New("unimplemented")
}

View file

@ -1,6 +1,9 @@
package storage
import (
"errors"
"fmt"
"strings"
"time"
"github.com/go-webauthn/webauthn/webauthn"
@ -55,6 +58,35 @@ type User struct {
// Access should be rare enough anyway
Passkeys map[string]webauthn.Credential `gorm:"serializer:json"`
PrivateKeyPem *string // The private key of the account. Nil if remote user
// Restrictions applied to the account
// Flag value, can be multiple
Restrictions AccountRestriction
}
var placeholderUser = &User{
ID: "placeholder",
Handle: "placeholder",
Remote: false,
Server: "placeholder",
DisplayName: "placeholder",
CustomFields: []uint{},
Description: "placeholder",
Tags: []string{},
IsBot: true,
Follows: []string{},
Followers: []string{},
Icon: "placeholder",
Background: "placeholder",
Banner: "placeholder",
Indexable: false,
PublicKeyPem: nil,
RestrictedFollow: false,
IdentifiesAs: []Being{BEING_ROBOT},
Gender: []string{"it", "its"},
PasswordHash: []byte("placeholder"),
TotpToken: []byte("placeholder"),
Passkeys: map[string]webauthn.Credential{},
PrivateKeyPem: nil,
}
func NewEmptyUser() *User {
@ -85,8 +117,32 @@ func NewEmptyUser() *User {
}
}
func placeholderUser() *User {
tmp := NewEmptyUser()
tmp.ID = "placeholder"
return tmp
// Get a stored user by the ID the user has been stored with
// Either returns a valid user and nil for the error
// Or nil for the user and an error
func (s *Storage) GetUserByID(id string) (*User, error) {
user := User{}
res := s.db.First(&user, "id = ?", id)
// Check if any error except NotFound occured
// If so, wrap it a little
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("problem while getting user from db: %w", res.Error)
}
// Then check if an error occured and said error is NotFound
// If it is, just pass it forward
if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) {
return nil, res.Error
}
return &user, nil
}
// Get only the name part of the handle a user has
func (u *User) GetHandleNameOnly() string {
// First remove the leading @ (TrimPrefix) to achieve a format of username@server
// Then split at the @ to the server and user seperately
// And return the first element since that is the username
// Note: Getting the first element will always be safe
// since trim returns a string guaranteed (empty is ok)
// and if Split doesn't do anything (eg empty string) it just returns the input in the first elemen it just returns the input in the first element
return strings.Split(strings.TrimPrefix(u.Handle, "@"), "@")[0]
}

View file

@ -0,0 +1,34 @@
package storage
type AccountRestriction int64
const (
// Account has no restrictions applied
ACCOUNT_RESTRICTION_NONE = AccountRestriction(0)
// All messages of the account get a content warning applied if none is set
// Warning could be something like "Message auto-CWd by server"
ACCOUNT_RESTRICTION_AUTO_CW = AccountRestriction(1 << iota)
// Disable accessing the account via login or access token
ACCOUNT_RESTRICTION_DISABLE_LOGIN
// Disable sending activities to other servers if the account is local
// Or reject all activities if the account is remote
ACCOUNT_RESTRICTION_NO_FEDERATION
// Disallow sending direct messages from that account
ACCOUNT_RESTRICTION_NO_DMS
// Disallow sending follower only messages from that account
ACCOUNT_RESTRICTION_NO_FOLLOWER_POSTS
// Disable outbound follow requests (restricted account can't send follow requests)
ACCOUNT_RESTRICTION_DISABLE_OUTBOUND_FOLLOWS
// Disable inbound follow requests (all follow requests to that account are automatically rejected)
ACCOUNT_RESTRICTION_DISABLE_INBOUND_FOLLOWS
// Forces all posts by that account to be follower only
ACCOUNT_RESTRICTION_FORCE_FOLLOWERS_ONLY
// Disable all outbound activities of an account.
// Includes sending, updating or deleting own notes
// as well as boosting or reacting to any notes
ACCOUNT_RESTRICTION_DISABLE_ACTIVITIES
// Account can only be viewed while logged in or via verified requests to the AP endpoints
ACCOUNT_RESTRICTIONS_NO_PUBLIC_ACCESS
// Force tag all media the account posts as sensitive
ACCOUNT_RESTRICTIONS_FORCE_MEDIA_SENSITIVE
)