package models import ( "database/sql" "time" "gorm.io/gen" "gorm.io/gorm" ) // A user describes an account for creating content and events. // This may be controlled by either a human or some external script // // Data stored externally: // - Feed connections (which note belongs in the feed of this user, for what reason), see [NoteToFeed] type User struct { // ID is a uuid for this account // // NOTE: Performance and storage wise, using a UUID (as string) for the primary key // is not the best idea and it would be better to use incrementing integers // Additionally, there is a risk with inconsistency if postgres crashes during a commit // in which case the new entry might not have a valid Id with which it could be found // and the username is lost until manual recovery // However, a UUID is still necessary in some way to provide a (secondary) stable // identifier for users and other servers, especially when changing the username // (username != display name) might be a future feature // Same also applies for other types that use a UUID as primary key ID string `gorm:"primarykey;type:uuid;default:gen_random_uuid()" json:"id"` // Username of the user (eg "max" if the full username is @max@example.com) // Assume unchangable (once set by a user) to be kind to other implementations // Would be an easy avenue to fuck with them though Username string `gorm:"unique" json:"username"` CreatedAt time.Time ` json:"created_at"` // When this entry was created. Automatically set by gorm // When this account was last updated. Will also be used for refreshing remote accounts. Automatically set by gorm UpdatedAt time.Time ` json:"updated_at"` // 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" json:"deleted_at"` Server RemoteServer ` json:"-"` ServerId uint ` json:"server_id"` // Id of the server this user is from, needed for including RemoteServer DisplayName string ` json:"display_name"` // The display name of the user. Can be different from the handle Description string ` json:"description"` // The description of a user account IsBot bool ` json:"is_bot"` // Whether to mark this account as a script controlled one Icon *MediaMetadata ` json:"-"` IconId sql.NullString ` json:"icon_id"` // ID of a media file used as icon Background *MediaMetadata ` json:"-"` BackgroundId sql.NullString ` json:"background_id"` // ID of a media file used as background image Banner *MediaMetadata ` json:"-"` BannerId sql.NullString ` json:"banner_id"` // ID of a media file used as banner Indexable bool ` json:"indexable"` // Whether this account can be found by crawlers PublicKey []byte ` json:"public_key"` // The public key of the account // Whether this account restricts following // If true, the owner must approve of a follow request first RestrictedFollow bool ` json:"restricted_follow"` Location sql.NullString `json:"location"` Birthday sql.NullTime `json:"birthday"` // Whether the account got verified and is allowed to be active // For local accounts being active means being allowed to login and perform interactions // For remote users, if an account is not verified, any interactions it sends are discarded Verified bool `json:"verified"` // 64 byte unique id for passkeys, because UUIDs are 128 bytes and passkey spec says 64 bytes max // In theory, could also slash Id in half, but that would be a lot more calculations than the // saved space is worth PasskeyId []byte `json:"-"` FinishedRegistration bool `json:"-"` // Whether this account has completed registration yet PrivateKey []byte `json:"-"` // ---- "Remote" linked values InfoFields []UserInfoField `json:"-"` BeingTypes []UserToBeing `json:"-"` Tags []UserToTag `json:"-"` Relations []UserToUserRelation `json:"-"` Pronouns []UserToPronoun `json:"-"` Roles []UserToRole `json:"-"` RemoteInfo *UserRemoteLinks `json:"-"` AuthMethods []UserAuthMethod `json:"-"` } type IUser interface { // Get a user by a username // // SELECT * FROM @@table WHERE username = @username AND deleted_at IS NULL LIMIT 1 GetByUsername(username string) (*gen.T, error) // Get all true public accounts (verified & no restricted follow & indexable) // in a paged manner, sorted by date saved // // SELECT * FROM @@table WHERE // deleted_at IS NULL AND // verified = true AND // restricted_follow = false AND // indexable = true // ORDER BY created_at ASC // LIMIT 50 // OFFSET @pageNr * 50 GetPagedTruePublic(pageNr uint) ([]gen.T, error) // Get all deleted accounts in a paged manner, sorted by date saved // // SELECT * FROM @@table WHERE // deleted_at IS NOT NULL AND // ORDER BY created_at ASC // LIMIT 50 // OFFSET @pageNr * 50 GetPagedAllDeleted(pageNr uint) ([]gen.T, error) // Get all accounts that aren't deleted in a paged manner, sorted by date saved // // SELECT * FROM @@table WHERE // deleted_at IS NULL // ORDER BY created_at ASC // LIMIT 50 // OFFSET @pageNr * 50 GetPagedAllNonDeleted(pageNr uint) ([]gen.T, error) // Gdpr deleted users // // DELETE FROM @@table WHERE deleted_at IS NOT NULL AND deleted_at < NOW() GdprUsers() error }