linstrom/storage/user.go

149 lines
5.8 KiB
Go
Raw Normal View History

2024-05-31 09:54:39 +00:00
package storage
import (
2024-08-22 17:57:53 +00:00
"errors"
"fmt"
"strings"
2024-05-31 09:54:39 +00:00
"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 {
2024-05-31 15:21:29 +00:00
ID string `gorm:"primarykey"` // ID is a uuid for this account
Handle string // Handle is the full handle, eg @max@example.com
2024-05-31 09:54:39 +00:00
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
2024-05-31 15:21:29 +00:00
PublicKeyPem *string // The public key of the account
2024-05-31 09:54:39 +00:00
// 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
2024-08-22 17:57:53 +00:00
// 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,
2024-05-31 09:54:39 +00:00
}
func NewEmptyUser() *User {
return &User{
2024-05-31 15:21:29 +00:00
ID: uuid.NewString(),
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,
2024-05-31 09:54:39 +00:00
}
}
2024-05-31 15:21:29 +00:00
2024-08-22 17:57:53 +00:00
// 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]
2024-05-31 15:21:29 +00:00
}