diff --git a/internal/core/models/action_token.go b/internal/core/models/action_token.go index c86b106..d226a14 100644 --- a/internal/core/models/action_token.go +++ b/internal/core/models/action_token.go @@ -2,13 +2,11 @@ package models import "time" -type ActionTokenTarget int +type ActionTokenTarget string const ( - _ ActionTokenTarget = iota - ActionTokenTargetForgotPassword - ActionTokenTargetLogin2FA - ActionTokenVerifyEmail + ActionTokenTargetRestorePassword ActionTokenTarget = "restore" + ActionTokenTargetVerifyEmail ActionTokenTarget = "verify" ) type ActionTokenDTO struct { diff --git a/internal/core/repos/shortlink_repo.go b/internal/core/repos/shortlink_repo.go index e48c501..2db06ce 100644 --- a/internal/core/repos/shortlink_repo.go +++ b/internal/core/repos/shortlink_repo.go @@ -11,9 +11,9 @@ import ( ) type ShortlinkDTO struct { - Id string - Url string - Expiration time.Time + Id string + Url string + ExpiresAt time.Time } type ShortlinkRepo interface { @@ -35,8 +35,8 @@ func (u *shortlinkRepo) AddShortlink(ctx context.Context, dto ShortlinkDTO) erro _, span := u.tracer.Start(ctx, "postgres::AddShortlink") defer span.End() - query := `insert into shortlinks (id, url, expiration) values ($1, $2, $3);` - _, err := u.db.ExecContext(ctx, query, dto.Id, dto.Url, dto.Expiration) + query := `insert into shortlinks (url, expires_at) values ($1, $2);` + _, err := u.db.ExecContext(ctx, query, dto.Url, dto.ExpiresAt) return err } @@ -44,14 +44,14 @@ func (u *shortlinkRepo) GetShortlink(ctx context.Context, id string) (*Shortlink _, span := u.tracer.Start(ctx, "postgres::GetShortlink") defer span.End() - query := `select url, expiration from shortlinks where id = $1;` + query := `select url, expires_at from shortlinks where id = $1;` row := u.db.QueryRowContext(ctx, query, id) if err := row.Err(); err != nil { return nil, err } dto := &ShortlinkDTO{Id: id} - err := row.Scan(&dto.Url, &dto.Expiration) + err := row.Scan(&dto.Url, &dto.ExpiresAt) if err == nil { return dto, nil } diff --git a/internal/core/repos/user_repo.go b/internal/core/repos/user_repo.go index b98af47..f66ae11 100644 --- a/internal/core/repos/user_repo.go +++ b/internal/core/repos/user_repo.go @@ -10,16 +10,10 @@ import ( "go.opentelemetry.io/otel/trace" ) -// type userDAO struct { -// Id string `json:"id"` -// Login string `json:"login"` -// Secret string `json:"secret"` -// Name string `json:"name"` -// } - type UserRepo interface { CreateUser(ctx context.Context, dto models.UserDTO) (*models.UserDTO, error) UpdateUser(ctx context.Context, userId string, dto models.UserUpdateDTO) error + DeactivateUser(ctx context.Context, userId string) error SetUserEmailVerified(ctx context.Context, userId string) error GetUserById(ctx context.Context, id string) (*models.UserDTO, error) GetUserByEmail(ctx context.Context, login string) (*models.UserDTO, error) @@ -67,6 +61,19 @@ func (u *userRepo) UpdateUser(ctx context.Context, userId string, dto models.Use return nil } +func (u *userRepo) DeactivateUser(ctx context.Context, userId string) error { + _, span := u.tracer.Start(ctx, "postgres::DeactivateUser") + defer span.End() + + query := `update users set active=false where id = $1;` + _, err := u.db.ExecContext(ctx, query, userId) + if err != nil { + return err + } + + return nil +} + func (u *userRepo) SetUserEmailVerified(ctx context.Context, userId string) error { _, span := u.tracer.Start(ctx, "postgres::SetUserEmailVerified") defer span.End() diff --git a/internal/core/services/shortlink_service.go b/internal/core/services/shortlink_service.go index 5228349..1dbdbbb 100644 --- a/internal/core/services/shortlink_service.go +++ b/internal/core/services/shortlink_service.go @@ -48,9 +48,9 @@ func (s *shortlinkService) CreateShortlink(ctx context.Context, url string) (str expiration := time.Now().Add(7 * 24 * time.Hour) dto := repos.ShortlinkDTO{ - Id: id, - Url: url, - Expiration: expiration, + Id: id, + Url: url, + ExpiresAt: expiration, } if err := s.repo.AddShortlink(ctx, dto); err != nil { return "", err @@ -73,7 +73,7 @@ func (s *shortlinkService) GetShortlink(ctx context.Context, id string) (string, if link == nil { return "", ErrShortlinkNotexist } - if time.Now().After(link.Expiration) { + if time.Now().After(link.ExpiresAt) { return "", ErrShortlinkExpired } diff --git a/internal/core/services/user_service.go b/internal/core/services/user_service.go index 8900a80..673b10d 100644 --- a/internal/core/services/user_service.go +++ b/internal/core/services/user_service.go @@ -133,7 +133,7 @@ func (u *userService) AuthenticateUser(ctx context.Context, email, password stri } func (u *userService) VerifyEmail(ctx context.Context, actionToken string) error { - token, err := u.deps.ActionTokenRepo.GetActionToken(ctx, actionToken, models.ActionTokenVerifyEmail) + token, err := u.deps.ActionTokenRepo.GetActionToken(ctx, actionToken, models.ActionTokenTargetVerifyEmail) if err != nil { return err } @@ -162,7 +162,7 @@ func (u *userService) SendEmailForgotPassword(ctx context.Context, email string) models.ActionTokenDTO{ UserId: user.Id, Value: uuid.New().String(), - Target: models.ActionTokenTargetForgotPassword, + Target: models.ActionTokenTargetRestorePassword, Expiration: time.Now().Add(15 * time.Minute), }, ) @@ -179,7 +179,7 @@ func (u *userService) sendEmailVerifyUser(ctx context.Context, userId, email str models.ActionTokenDTO{ UserId: userId, Value: uuid.New().String(), - Target: models.ActionTokenVerifyEmail, + Target: models.ActionTokenTargetVerifyEmail, Expiration: time.Now().Add(1 * time.Hour), }, ) @@ -207,7 +207,7 @@ func (u *userService) SendEmailVerifyUser(ctx context.Context, email string) err } func (u *userService) ChangePasswordWithToken(ctx context.Context, actionToken, newPassword string) error { - token, err := u.deps.ActionTokenRepo.GetActionToken(ctx, actionToken, models.ActionTokenTargetForgotPassword) + token, err := u.deps.ActionTokenRepo.GetActionToken(ctx, actionToken, models.ActionTokenTargetRestorePassword) if err != nil { return err } diff --git a/sql/00_common.sql b/sql/00_common.sql new file mode 100644 index 0000000..c70d27c --- /dev/null +++ b/sql/00_common.sql @@ -0,0 +1,18 @@ +create or replace function trg_proc_row_updated() +returns trigger as $$ +begin + if new is distinct from old then + new.updated_at = now(); + end if; + return new; +end; +$$ language plpgsql; + +create or replace function trg_proc_row_created() +returns trigger as $$ +begin + new.created_at = now(); + new.updated_at = now(); + return new; +end; +$$ language plpgsql; \ No newline at end of file diff --git a/sql/01_user.sql b/sql/01_user.sql index 14b8a43..9206ec9 100644 --- a/sql/01_user.sql +++ b/sql/01_user.sql @@ -4,38 +4,20 @@ create table if not exists users ( secret varchar(256) not null, full_name varchar(256) not null, email_verified boolean not null default false, + active boolean, created_at timestamp, updated_at timestamp ); create index if not exists idx_users_email on users(email); -create or replace function set_created_at() -returns trigger as $$ -begin - new.created_at = now(); - new.updated_at = now(); - return new; -end; -$$ language plpgsql; - create or replace trigger trg_user_created before insert on users for each row - execute function set_created_at(); - -create or replace function set_updated_at() -returns trigger as $$ -begin - if new is distinct from old then - new.updated_at = now(); - end if; - return new; -end; -$$ language plpgsql; + execute function trg_proc_row_created(); create or replace trigger trg_user_updated before update on users for each row when(new is distinct from old) - execute function set_updated_at(); \ No newline at end of file + execute function trg_proc_row_updated(); \ No newline at end of file diff --git a/sql/02_shortlinks.sql b/sql/02_shortlinks.sql index 92f0699..4f6df69 100644 --- a/sql/02_shortlinks.sql +++ b/sql/02_shortlinks.sql @@ -1,5 +1,17 @@ create table if not exists shortlinks ( - id text primary key, - url text, - expiration date -); \ No newline at end of file + id int generated always as identity, + url text not null, + expires_at timestamp not null, + created_at timestamp, + updated_at timestamp + +create or replace trigger trg_shortlink_created + before insert on shortlinks + for each row + when new is distinct from old + execute function trg_proc_row_created(); + +create or replace trigger trg_shortlink_updated + before update on shortlinks + for each row when new is distinct from old + execute function trg_proc_row_updated(); \ No newline at end of file diff --git a/sql/03_action_token.sql b/sql/03_action_token.sql index 697f750..e8adf1b 100644 --- a/sql/03_action_token.sql +++ b/sql/03_action_token.sql @@ -1,9 +1,26 @@ create table if not exists action_tokens ( - id int generated always as identity, - user_id int, - value text, - target int, - expiration timestamp, + id int primary key generated always as identity, + user_id int references users(id), + value text not null, + target text not null, + expires_at timestamp not null, + created_at timestamp, + updated_at timestamp - primary key(id) -); \ No newline at end of file + constraint pk_action_tokens_id primary key(id), + constraint check chk_action_tokens_target target in ('verify', 'restore') +); + +create index if not exists idx_action_tokens_value on actions_tokens(value); + +create or replace trigger trg_action_token_created + before insert on action_tokens + for each row + when new is distinct from old + execute function trg_proc_row_created(); + +create or replace trigger trg_action_token_updated + before update on action_tokens + for each row + when new is distinct from old + execute function trg_proc_row_updated(); \ No newline at end of file