diff --git a/storage-new/dbgen/users.gen.go b/storage-new/dbgen/users.gen.go index ccd070e..15b9013 100644 --- a/storage-new/dbgen/users.gen.go +++ b/storage-new/dbgen/users.gen.go @@ -1457,17 +1457,20 @@ type IUserDo interface { schema.Tabler GetByUsername(username string) (result *models.User, err error) + GetPagedTruePublic(pageNr uint) (result []models.User, err error) + GetPagedAllDeleted(pageNr uint) (result []models.User, err error) + GetPagedAllNonDeleted(pageNr uint) (result []models.User, err error) } // Get a user by a username // -// SELECT * FROM @@table WHERE username = @username LIMIT 1 +// SELECT * FROM @@table WHERE username = @username AND deleted_at IS NULL LIMIT 1 func (u userDo) GetByUsername(username string) (result *models.User, err error) { var params []interface{} var generateSQL strings.Builder params = append(params, username) - generateSQL.WriteString("SELECT * FROM users WHERE username = ? LIMIT 1 ") + generateSQL.WriteString("SELECT * FROM users WHERE username = ? AND deleted_at IS NULL LIMIT 1 ") var executeSQL *gorm.DB executeSQL = u.UnderlyingDB().Raw(generateSQL.String(), params...).Take(&result) // ignore_security_alert @@ -1476,6 +1479,79 @@ func (u userDo) GetByUsername(username string) (result *models.User, err error) return } +// 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 +func (u userDo) GetPagedTruePublic(pageNr uint) (result []models.User, err error) { + var params []interface{} + + var generateSQL strings.Builder + params = append(params, pageNr) + generateSQL.WriteString("SELECT * FROM users WHERE deleted_at IS NULL AND verified = true AND restricted_follow = false AND indexable = true ORDER BY created_at ASC LIMIT 50 OFFSET ? * 50 ") + + var executeSQL *gorm.DB + executeSQL = u.UnderlyingDB().Raw(generateSQL.String(), params...).Find(&result) // ignore_security_alert + err = executeSQL.Error + + return +} + +// 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 +func (u userDo) GetPagedAllDeleted(pageNr uint) (result []models.User, err error) { + var params []interface{} + + var generateSQL strings.Builder + params = append(params, pageNr) + generateSQL.WriteString("SELECT * FROM users WHERE deleted_at IS NOT NULL AND ORDER BY created_at ASC LIMIT 50 OFFSET ? * 50 ") + + var executeSQL *gorm.DB + executeSQL = u.UnderlyingDB().Raw(generateSQL.String(), params...).Find(&result) // ignore_security_alert + err = executeSQL.Error + + return +} + +// 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 +func (u userDo) GetPagedAllNonDeleted(pageNr uint) (result []models.User, err error) { + var params []interface{} + + var generateSQL strings.Builder + params = append(params, pageNr) + generateSQL.WriteString("SELECT * FROM users WHERE deleted_at IS NULL ORDER BY created_at ASC LIMIT 50 OFFSET ? * 50 ") + + var executeSQL *gorm.DB + executeSQL = u.UnderlyingDB().Raw(generateSQL.String(), params...).Find(&result) // ignore_security_alert + err = executeSQL.Error + + return +} + func (u userDo) Debug() IUserDo { return u.withDO(u.DO.Debug()) } diff --git a/storage-new/models/User.go b/storage-new/models/User.go index 808fa9d..3ffd08d 100644 --- a/storage-new/models/User.go +++ b/storage-new/models/User.go @@ -25,63 +25,91 @@ type User struct { // 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()"` + 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"` - CreatedAt time.Time // When this entry was created. Automatically set by gorm + 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 + 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"` - Server RemoteServer - ServerId uint // Id of the server this user is from, needed for including RemoteServer - DisplayName string // The display name of the user. Can be different from the handle - Description string // The description of a user account - IsBot bool // Whether to mark this account as a script controlled one - Icon *MediaMetadata - IconId sql.NullString // ID of a media file used as icon - Background *MediaMetadata - BackgroundId sql.NullString // ID of a media file used as background image - Banner *MediaMetadata - BannerId sql.NullString // ID of a media file used as banner - Indexable bool // Whether this account can be found by crawlers - PublicKey []byte // The public key of the account + 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 + RestrictedFollow bool ` json:"restricted_follow"` - Location sql.NullString - Birthday sql.NullTime + 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 + 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 - FinishedRegistration bool // Whether this account has completed registration yet - PrivateKey []byte + PasskeyId []byte `json:"-"` + FinishedRegistration bool `json:"-"` // Whether this account has completed registration yet + PrivateKey []byte `json:"-"` // ---- "Remote" linked values - InfoFields []UserInfoField - BeingTypes []UserToBeing - Tags []UserToTag - Relations []UserToUserRelation - Pronouns []UserToPronoun - Roles []UserToRole - RemoteInfo *UserRemoteLinks - AuthMethods []UserAuthMethod + 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 LIMIT 1 + // 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) }