Lots of work

This commit is contained in:
mStar 2024-05-31 11:54:39 +02:00
parent a2a937791d
commit 3086b0e9b4
28 changed files with 1284 additions and 2 deletions

22
storage/mediaFile.go Normal file
View file

@ -0,0 +1,22 @@
package storage
import (
"time"
"gorm.io/gorm"
)
type MediaFile struct {
ID string `gorm:"primarykey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
Remote bool // whether the attachment is a remote one
Link string // url if remote attachment, identifier if local
// Whether this media has been cached locally
// Only really used for user and server icons, not attachments
// Reason: Attachments would take way to much space considering that they are often only loaded a few times at most
// 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
}

28
storage/noteTargets.go Normal file
View file

@ -0,0 +1,28 @@
package storage
import (
"database/sql/driver"
"errors"
)
type NoteTarget uint8
const (
NOTE_TARGET_PUBLIC = NoteTarget(0)
NOTE_TARGET_HOME = NoteTarget(1 << iota)
NOTE_TARGET_FOLLOWERS
NOTE_TARGET_DM
)
func (n *NoteTarget) Value() (driver.Value, error) {
return n, nil
}
func (n *NoteTarget) Scan(value any) error {
vBig, ok := value.(int64)
if !ok {
return errors.New("not an int64")
}
*n = NoteTarget(vBig)
return nil
}

28
storage/notes.go Normal file
View file

@ -0,0 +1,28 @@
package storage
import (
"time"
"gorm.io/gorm"
)
type Note struct {
ID string `gorm:"primarykey"` // Make ID a string (uuid) for other implementations
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
Creator string // Full handle of the creator, eg: @max@example.com
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"` // Links to attachments
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
Target NoteTarget // 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
}

View file

@ -0,0 +1,18 @@
package storage
import (
"time"
"gorm.io/gorm"
)
type RemoteServer struct {
ID string `gorm:"primarykey"` // ID is also server url
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
ServerType RemoteServerType // What software the server is running. Useful for formatting
Name string // What the server wants to be known as (usually same as url)
Icon string // ID of a media file
IsSelf bool // Whether this server is yours truly
}

33
storage/serverTypes.go Normal file
View file

@ -0,0 +1,33 @@
package storage
import (
"database/sql/driver"
"errors"
)
// Mostly important for rendering
type RemoteServerType string
const (
// Includes forks like glitch-soc, etc
REMOTE_SERVER_MASTODON = RemoteServerType("Mastodon")
// Includes forks like Ice Shrimp, Sharkey, Cutiekey, etc
REMOTE_SERVER_MISSKEY = RemoteServerType("Misskey")
// Includes Akkoma
REMOTE_SERVER_PLEMORA = RemoteServerType("Plemora")
// And of course, yours truly
REMOTE_SERVER_LINSTROM = RemoteServerType("Linstrom")
)
func (r *RemoteServerType) Value() (driver.Value, error) {
return r, nil
}
func (r *RemoteServerType) Scan(raw any) error {
if v, ok := raw.(string); ok {
*r = RemoteServerType(v)
return nil
} else {
return errors.New("value not a string")
}
}

36
storage/storage.go Normal file
View file

@ -0,0 +1,36 @@
package storage
import (
"github.com/glebarez/sqlite"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
type Storage struct {
db *gorm.DB
}
func NewStorageSqlite(filePath string) (*Storage, error) {
db, err := gorm.Open(sqlite.Open(filePath))
if err != nil {
return nil, err
}
return &Storage{
db: db,
}, nil
}
func NewStoragePostgres(dbUrl string) (*Storage, error) {
db, err := gorm.Open(postgres.Open(dbUrl))
if err != nil {
return nil, err
}
return &Storage{
db: db,
}, nil
}
// TODO: Placeholder. Update to proper implementation later. Including signature
func (s *Storage) FindLocalAccount(handle string) (string, error) {
return handle, nil
}

67
storage/user.go Normal file
View file

@ -0,0 +1,67 @@
package storage
import (
"time"
"github.com/go-webauthn/webauthn/webauthn"
"github.com/google/uuid"
"gorm.io/gorm"
)
// Database representation of a user account
// This can be a bot, remote or not
// If remote, this is used for caching the account
type User struct {
ID string `gorm:"primarykey"` // ID is the full handle, eg @max@example.com
CreatedAt time.Time // When this entry was created
UpdatedAt time.Time // When this account was last updated. Will also be used for refreshing remote accounts
DeletedAt gorm.DeletedAt `gorm:"index"`
Remote bool // Whether the account is a local or remote one
Server string // The url of the server this account is from
DisplayName string // The display name of the user. Can be different from the handle
CustomFields []uint `gorm:"serializer:json"` // IDs to the custom fields a user has
Description string // The description of a user account
Tags []string `gorm:"serializer:json"` // Hashtags
IsBot bool // Whether to mark this account as a script controlled one
Follows []string `gorm:"serializer:json"` // List of handles this account follows
Followers []string `gorm:"serializer:json"` // List of handles that follow this account
Icon string // ID of a media file used as icon
Background string // ID of a media file used as background image
Banner string // ID of a media file used as banner
Indexable bool // Whether this account can be found by crawlers
PublicKeyPem string // The public key of the account
// Whether this account restricts following
// If true, the owner must approve of a follow request first
RestrictedFollow bool
// List of things the owner identifies as
// Example [cat human robot] means that the owner probably identifies as
// a cyborg-catgirl/boy/human
IdentifiesAs []Being
// List of pronouns the owner identifies with
// An unordered list since the owner can freely set it
// Examples: [she her], [it they its them]
Gender []string
// --- And internal account stuff ---
// Still public fields since they wouldn't be able to be stored in the db otherwise
PasswordHash []byte // Hash of the user's password
TotpToken []byte // Token for totp verification
// All the registered passkeys, name of passkey to credentials
// Could this be exported to another table? Yes
// Would it make sense? Probably not
// Will just take the performance hit of json conversion
// 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
}
func NewEmptyUser() *User {
return &User{
ID: uuid.NewString(),
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
Remote: false,
Server: "placeholder",
}
}

30
storage/userIdentType.go Normal file
View file

@ -0,0 +1,30 @@
package storage
import (
"database/sql/driver"
"errors"
)
type Being string
const (
BEING_HUMAN = Being("human")
BEING_CAT = Being("cat")
BEING_FOX = Being("fox")
BEING_DOG = Being("dog")
BEING_ROBOT = Being("robot")
BEING_DOLL = Being("doll")
)
func (r *Being) Value() (driver.Value, error) {
return r, nil
}
func (r *Being) Scan(raw any) error {
if v, ok := raw.(string); ok {
*r = Being(v)
return nil
} else {
return errors.New("value not a string")
}
}

14
storage/userInfoFields.go Normal file
View file

@ -0,0 +1,14 @@
package storage
import (
"time"
"gorm.io/gorm"
)
type UserInfoField struct {
gorm.Model // Can actually just embed this as is here as those are not something directly exposed :3
Name string
Value string
LastUrlCheckDate *time.Time // Used if the value is an url to somewhere. Empty if value is not an url
}