transactions everywhere
This commit is contained in:
parent
a4b2fcdb7c
commit
e67f1ff7cc
|
@ -65,6 +65,8 @@ func (s *MemoryStorage) CreateShort(ctx context.Context, short *models.Short) (*
|
|||
return &models.Short{}, errs.ErrShortExists
|
||||
}
|
||||
|
||||
short.CreatedAt = time.Now()
|
||||
|
||||
s.shortMap[short.Name] = short
|
||||
|
||||
return short, nil
|
||||
|
@ -120,6 +122,8 @@ func (s *MemoryStorage) CreateUser(ctx context.Context, user *models.User) (*mod
|
|||
return &models.User{}, errs.ErrUserExists
|
||||
}
|
||||
|
||||
user.CreatedAt = time.Now()
|
||||
|
||||
s.userMap[user.Username] = user
|
||||
|
||||
return user, nil
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
type Short struct {
|
||||
// Name is the shortened name of the URL.
|
||||
Name string `json:"name,omitempty"`
|
||||
// URL is the URL that is shortened.
|
||||
URL string `json:"url"`
|
||||
// CreatedAt is the time the short was created.
|
||||
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||
|
||||
// User is the user that created the short.
|
||||
User *User `json:"-"`
|
||||
|
|
|
@ -2,6 +2,7 @@ package models
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.maronato.dev/maronato/goshort/internal/util/passwords"
|
||||
)
|
||||
|
@ -9,6 +10,9 @@ import (
|
|||
type User struct {
|
||||
Username string `json:"username"`
|
||||
password string `json:"-"`
|
||||
|
||||
// CreatedAt is the time the user was created.
|
||||
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||
}
|
||||
|
||||
// NewAuthenticatableUser is a elper function for storages that takes
|
||||
|
@ -16,8 +20,9 @@ type User struct {
|
|||
func NewAuthenticatableUser(user *User, hashedPass string) *User {
|
||||
|
||||
return &User{
|
||||
Username: user.Username,
|
||||
password: hashedPass,
|
||||
Username: user.Username,
|
||||
CreatedAt: user.CreatedAt,
|
||||
password: hashedPass,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,8 +15,12 @@ type ShortModel struct {
|
|||
Name string `bun:",unique,notnull" json:"name"`
|
||||
// URL is the URL that the short will redirect to
|
||||
URL string `bun:",notnull" json:"url"`
|
||||
// Active is whether the short is active or not
|
||||
Active bool `bun:",notnull,default:true" json:"-"`
|
||||
// Deleted is whether the short is soft-deleted or not
|
||||
Deleted bool `bun:",notnull,default:false" json:"-"`
|
||||
// CreatedAt is when the short was created (initialized by the storage)
|
||||
CreatedAt time.Time `bun:",notnull,default:current_timestamp" json:"createdAt"`
|
||||
// DeletedAt is when the short was deleted
|
||||
DeletedAt time.Time `bun:",null" json:"-"`
|
||||
|
||||
// UserID is the ID of the user that created the short
|
||||
// This can be null if the short was deleted
|
||||
|
@ -27,9 +31,10 @@ type ShortModel struct {
|
|||
|
||||
func (s *ShortModel) toShort() *models.Short {
|
||||
return &models.Short{
|
||||
Name: s.Name,
|
||||
URL: s.URL,
|
||||
User: s.User.toUser(),
|
||||
Name: s.Name,
|
||||
URL: s.URL,
|
||||
CreatedAt: s.CreatedAt,
|
||||
User: s.User.toUser(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,6 +47,8 @@ type UserModel struct {
|
|||
Username string `bun:",unique,notnull" json:"username"`
|
||||
// Password is the user's password
|
||||
Password string `bun:",notnull" json:"-"`
|
||||
// CreatedAt is when the user was created (initialized by the storage)
|
||||
CreatedAt time.Time `bun:",notnull,default:current_timestamp" json:"createdAt"`
|
||||
|
||||
// Shorts is the list of shorts created by the user
|
||||
Shorts []*ShortModel `bun:"rel:has-many,join:id=user_id" json:"shorts,omitempty"`
|
||||
|
@ -51,7 +58,8 @@ type UserModel struct {
|
|||
|
||||
func (u *UserModel) toUser() *models.User {
|
||||
return models.NewAuthenticatableUser(&models.User{
|
||||
Username: u.Username,
|
||||
Username: u.Username,
|
||||
CreatedAt: u.CreatedAt,
|
||||
}, u.Password)
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.maronato.dev/maronato/goshort/internal/config"
|
||||
"git.maronato.dev/maronato/goshort/internal/errs"
|
||||
|
@ -39,57 +40,60 @@ func NewSQLiteStorage(cfg *config.Config) storage.Storage {
|
|||
}
|
||||
|
||||
func (s *SQLiteStorage) Start(ctx context.Context) error {
|
||||
_, err := s.db.
|
||||
NewCreateTable().
|
||||
IfNotExists().
|
||||
Model((*UserModel)(nil)).
|
||||
WithForeignKeys().
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create users table: %w", err)
|
||||
}
|
||||
_, err = s.db.
|
||||
NewCreateTable().
|
||||
IfNotExists().
|
||||
Model((*ShortModel)(nil)).
|
||||
ForeignKey(`("user_id") REFERENCES "users" ("id") ON DELETE CASCADE`).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create shorts table: %w", err)
|
||||
}
|
||||
_, err = s.db.
|
||||
NewCreateTable().
|
||||
IfNotExists().
|
||||
Model((*TokenModel)(nil)).
|
||||
ForeignKey(`("user_id") REFERENCES "users" ("id") ON DELETE CASCADE`).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create tokens table: %w", err)
|
||||
}
|
||||
return s.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
|
||||
|
||||
// shorts user_id index
|
||||
_, err = s.db.NewCreateIndex().
|
||||
IfNotExists().
|
||||
Model((*ShortModel)(nil)).
|
||||
Index("idx_shorts_user_id").
|
||||
Column("user_id").
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create shorts user_id index: %w", err)
|
||||
}
|
||||
_, err := tx.NewCreateTable().
|
||||
IfNotExists().
|
||||
Model((*UserModel)(nil)).
|
||||
WithForeignKeys().
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create users table: %w", err)
|
||||
}
|
||||
|
||||
// tokens user_id index
|
||||
_, err = s.db.NewCreateIndex().
|
||||
IfNotExists().
|
||||
Model((*TokenModel)(nil)).
|
||||
Index("idx_tokens_user_id").
|
||||
Column("user_id").
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create tokens user_id index: %w", err)
|
||||
}
|
||||
_, err = tx.NewCreateTable().
|
||||
IfNotExists().
|
||||
Model((*ShortModel)(nil)).
|
||||
ForeignKey(`("user_id") REFERENCES "users" ("id") ON DELETE SET NULL`).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create shorts table: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
_, err = tx.NewCreateTable().
|
||||
IfNotExists().
|
||||
Model((*TokenModel)(nil)).
|
||||
ForeignKey(`("user_id") REFERENCES "users" ("id") ON DELETE CASCADE`).
|
||||
Exec(ctx)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create tokens table: %w", err)
|
||||
}
|
||||
|
||||
// shorts user_id index
|
||||
_, err = tx.NewCreateIndex().
|
||||
IfNotExists().
|
||||
Model((*ShortModel)(nil)).
|
||||
Index("idx_shorts_user_id").
|
||||
Column("user_id").
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create shorts user_id index: %w", err)
|
||||
}
|
||||
|
||||
// tokens user_id index
|
||||
_, err = tx.NewCreateIndex().
|
||||
IfNotExists().
|
||||
Model((*TokenModel)(nil)).
|
||||
Index("idx_tokens_user_id").
|
||||
Column("user_id").
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create tokens user_id index: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) Stop(ctx context.Context) error {
|
||||
|
@ -97,85 +101,124 @@ func (s *SQLiteStorage) Stop(ctx context.Context) error {
|
|||
}
|
||||
|
||||
func (s *SQLiteStorage) FindShort(ctx context.Context, name string) (*models.Short, error) {
|
||||
short := new(ShortModel)
|
||||
err := s.db.NewSelect().Model(short).Where("name = ?", name).Relation("User").Scan(ctx)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errs.ErrShortDoesNotExist
|
||||
shortModel := new(ShortModel)
|
||||
|
||||
err := s.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
|
||||
err := tx.NewSelect().
|
||||
Model(shortModel).
|
||||
Where("name = ? and deleted = false", name).
|
||||
Relation("User").
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return errs.ErrShortDoesNotExist
|
||||
}
|
||||
return fmt.Errorf("failed to find short: %w", err)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to find short: %w", err)
|
||||
}
|
||||
|
||||
return short.toShort(), nil
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) CreateShort(ctx context.Context, short *models.Short) (*models.Short, error) {
|
||||
// Get user from username
|
||||
user, err := s.findUser(ctx, short.User.Username)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
shortModel := ShortModel{
|
||||
Name: short.Name,
|
||||
URL: short.URL,
|
||||
UserID: &user.ID,
|
||||
User: user,
|
||||
}
|
||||
return shortModel.toShort(), err
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) CreateShort(ctx context.Context, short *models.Short) (*models.Short, error) {
|
||||
shortModel := new(ShortModel)
|
||||
|
||||
err := s.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
|
||||
// Get user from username
|
||||
user, err := findUser(ctx, tx, short.User.Username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
shortModel.Name = short.Name
|
||||
shortModel.URL = short.URL
|
||||
shortModel.UserID = &user.ID
|
||||
shortModel.User = user
|
||||
|
||||
_, err = tx.NewInsert().
|
||||
Model(shortModel).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return errs.ErrShortExists
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
_, err = s.db.NewInsert().Model(&shortModel).Exec(ctx)
|
||||
if err != nil {
|
||||
return nil, errs.ErrShortExists
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return shortModel.toShort(), nil
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) DeleteShort(ctx context.Context, short *models.Short) error {
|
||||
_, err := s.db.NewDelete().Model(short).Where("name = ?", short.Name).Exec(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete short: %w", err)
|
||||
}
|
||||
return s.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
|
||||
_, err := withShortDeleteUpdates(
|
||||
tx.NewUpdate().
|
||||
Model((*ShortModel)(nil)).
|
||||
Where("name = ?", short.Name),
|
||||
).Exec(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete short: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) ListShorts(ctx context.Context, user *models.User) ([]*models.Short, error) {
|
||||
// Get user ID from username
|
||||
userID, err := s.findUserIDFromUsername(ctx, user.Username)
|
||||
shortModels := []*ShortModel{}
|
||||
|
||||
err := s.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
|
||||
// Get user ID from username
|
||||
userID, err := findUserIDFromUsername(ctx, tx, user.Username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = tx.NewSelect().
|
||||
Model(&shortModels).
|
||||
Where("user_id = ? and deleted = false", userID).
|
||||
Relation("User").
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list shorts: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
shortModels := []*ShortModel{}
|
||||
err = s.db.NewSelect().Model(&shortModels).Where("user_id = ?", userID).Relation("User").Scan(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list shorts: %w", err)
|
||||
}
|
||||
|
||||
shorts := []*models.Short{}
|
||||
for _, shortModel := range shortModels {
|
||||
shorts = append(shorts, shortModel.toShort())
|
||||
}
|
||||
|
||||
return shorts, nil
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) findUser(ctx context.Context, username string) (*UserModel, error) {
|
||||
user := new(UserModel)
|
||||
err := s.db.NewSelect().Model(user).Where("username = ?", username).Scan(ctx)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errs.ErrUserDoesNotExist
|
||||
}
|
||||
return nil, fmt.Errorf("failed to find user: %w", err)
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) FindUser(ctx context.Context, username string) (*models.User, error) {
|
||||
user, err := s.findUser(ctx, username)
|
||||
var user *UserModel
|
||||
|
||||
err := s.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
|
||||
userFound, err := findUser(ctx, tx, username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user = userFound
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -189,60 +232,117 @@ func (s *SQLiteStorage) CreateUser(ctx context.Context, user *models.User) (*mod
|
|||
Password: user.GetPasswordHash(),
|
||||
}
|
||||
|
||||
_, err := s.db.NewInsert().Model(userModel).Exec(ctx)
|
||||
err := s.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
|
||||
_, err := tx.NewInsert().
|
||||
Model(userModel).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return errs.ErrUserExists
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, errs.ErrUserExists
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return userModel.toUser(), nil
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) DeleteUser(ctx context.Context, user *models.User) error {
|
||||
_, err := s.db.NewDelete().Model(user).Where("username = ?", user.Username).Exec(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete user: %w", err)
|
||||
}
|
||||
return s.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
|
||||
// Delete user shorts
|
||||
err := deleteUserShorts(ctx, tx, user)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete user: %w", err)
|
||||
}
|
||||
_, err = tx.NewDelete().
|
||||
Model(user).
|
||||
Where("username = ?", user.Username).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete user: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) FindToken(ctx context.Context, value string) (*models.Token, error) {
|
||||
token := new(TokenModel)
|
||||
err := s.db.NewSelect().Model(token).Where("value = ?", value).Relation("User").Scan(ctx)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errs.ErrTokenDoesNotExist
|
||||
tokenModel := new(TokenModel)
|
||||
err := s.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
|
||||
err := tx.NewSelect().
|
||||
Model(tokenModel).
|
||||
Where("value = ?", value).
|
||||
Relation("User").
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return errs.ErrTokenDoesNotExist
|
||||
}
|
||||
return fmt.Errorf("failed to find token: %w", err)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to find token: %w", err)
|
||||
}
|
||||
|
||||
return token.toToken(), nil
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
func (s *SQLiteStorage) FindTokenByID(ctx context.Context, id string) (*models.Token, error) {
|
||||
token := new(TokenModel)
|
||||
err := s.db.NewSelect().Model(token).Where("t.id = ?", id).Relation("User").Scan(ctx)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errs.ErrTokenDoesNotExist
|
||||
}
|
||||
return nil, fmt.Errorf("failed to find token: %w", err)
|
||||
}
|
||||
|
||||
return token.toToken(), nil
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) ListTokens(ctx context.Context, user *models.User) ([]*models.Token, error) {
|
||||
// Get user ID from username
|
||||
userID, err := s.findUserIDFromUsername(ctx, user.Username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tokenModels := []*TokenModel{}
|
||||
err = s.db.NewSelect().Model(&tokenModels).Where("user_id = ?", userID).Relation("User").Scan(ctx)
|
||||
return tokenModel.toToken(), nil
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) FindTokenByID(ctx context.Context, id string) (*models.Token, error) {
|
||||
tokenModel := new(TokenModel)
|
||||
err := s.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
|
||||
err := tx.NewSelect().
|
||||
Model(tokenModel).
|
||||
Where("t.id = ?", id).
|
||||
Relation("User").
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return errs.ErrTokenDoesNotExist
|
||||
}
|
||||
return fmt.Errorf("failed to find token: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list tokens: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tokenModel.toToken(), nil
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) ListTokens(ctx context.Context, user *models.User) ([]*models.Token, error) {
|
||||
tokenModels := []*TokenModel{}
|
||||
|
||||
err := s.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
|
||||
// Get user ID from username
|
||||
userID, err := findUserIDFromUsername(ctx, tx, user.Username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = tx.NewSelect().
|
||||
Model(&tokenModels).
|
||||
Where("user_id = ?", userID).
|
||||
Relation("User").
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list tokens: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tokens := []*models.Token{}
|
||||
|
@ -254,40 +354,74 @@ func (s *SQLiteStorage) ListTokens(ctx context.Context, user *models.User) ([]*m
|
|||
}
|
||||
|
||||
func (s *SQLiteStorage) CreateToken(ctx context.Context, token *models.Token) (*models.Token, error) {
|
||||
// Get user ID from username
|
||||
user, err := s.findUser(ctx, token.User.Username)
|
||||
tokenModel := new(TokenModel)
|
||||
|
||||
err := s.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
|
||||
// Get user ID from username
|
||||
user, err := findUser(ctx, tx, token.User.Username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tokenModel.ID = token.ID
|
||||
tokenModel.Name = token.Name
|
||||
tokenModel.Value = token.Value
|
||||
tokenModel.UserID = &user.ID
|
||||
tokenModel.User = user
|
||||
|
||||
_, err = tx.NewInsert().
|
||||
Model(tokenModel).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return errs.ErrTokenExists
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tokenModel := &TokenModel{
|
||||
ID: token.ID,
|
||||
Name: token.Name,
|
||||
Value: token.Value,
|
||||
UserID: &user.ID,
|
||||
User: user,
|
||||
}
|
||||
|
||||
_, err = s.db.NewInsert().Model(tokenModel).Exec(ctx)
|
||||
if err != nil {
|
||||
return nil, errs.ErrTokenExists
|
||||
}
|
||||
|
||||
return tokenModel.toToken(), nil
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) DeleteToken(ctx context.Context, token *models.Token) error {
|
||||
_, err := s.db.NewDelete().Model(token).Where("id = ?", token.ID).Exec(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete token: %w", err)
|
||||
}
|
||||
return s.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
|
||||
// Delete in transaction
|
||||
_, err := tx.NewDelete().
|
||||
Model(token).
|
||||
Where("id = ?", token.ID).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete token: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) findUserIDFromUsername(ctx context.Context, username string) (*int64, error) {
|
||||
func findUser(ctx context.Context, db bun.IDB, username string) (*UserModel, error) {
|
||||
userModel := new(UserModel)
|
||||
|
||||
err := db.NewSelect().
|
||||
Model(userModel).
|
||||
Where("username = ?", username).
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errs.ErrUserDoesNotExist
|
||||
}
|
||||
return nil, fmt.Errorf("failed to find user: %w", err)
|
||||
}
|
||||
|
||||
return userModel, nil
|
||||
}
|
||||
|
||||
func findUserIDFromUsername(ctx context.Context, db bun.IDB, username string) (*int64, error) {
|
||||
var userID int64
|
||||
err := s.db.NewSelect().
|
||||
|
||||
err := db.NewSelect().
|
||||
Table("users").
|
||||
Column("id").
|
||||
Where("username = ?", username).
|
||||
|
@ -302,3 +436,27 @@ func (s *SQLiteStorage) findUserIDFromUsername(ctx context.Context, username str
|
|||
|
||||
return &userID, nil
|
||||
}
|
||||
|
||||
func withShortDeleteUpdates(q *bun.UpdateQuery) *bun.UpdateQuery {
|
||||
return q.Set("deleted_at = ?", time.Now()).
|
||||
Set("deleted = ?", true).
|
||||
Set("user_id = ?", nil)
|
||||
}
|
||||
|
||||
func deleteUserShorts(ctx context.Context, db bun.IDB, user *models.User) error {
|
||||
userID, err := findUserIDFromUsername(ctx, db, user.Username)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete user shorts: %w", err)
|
||||
}
|
||||
|
||||
_, err = withShortDeleteUpdates(
|
||||
db.NewUpdate().
|
||||
Model((*ShortModel)(nil)).
|
||||
Where("user_id = ?", userID),
|
||||
).Exec(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete user shorts: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user