improve sql queries and tables

This commit is contained in:
Sergey Chubaryan 2025-02-21 17:35:25 +03:00
parent 162e7e2f50
commit 3d1fe25dcb
9 changed files with 93 additions and 59 deletions

View File

@ -2,13 +2,11 @@ package models
import "time" import "time"
type ActionTokenTarget int type ActionTokenTarget string
const ( const (
_ ActionTokenTarget = iota ActionTokenTargetRestorePassword ActionTokenTarget = "restore"
ActionTokenTargetForgotPassword ActionTokenTargetVerifyEmail ActionTokenTarget = "verify"
ActionTokenTargetLogin2FA
ActionTokenVerifyEmail
) )
type ActionTokenDTO struct { type ActionTokenDTO struct {

View File

@ -11,9 +11,9 @@ import (
) )
type ShortlinkDTO struct { type ShortlinkDTO struct {
Id string Id string
Url string Url string
Expiration time.Time ExpiresAt time.Time
} }
type ShortlinkRepo interface { type ShortlinkRepo interface {
@ -35,8 +35,8 @@ func (u *shortlinkRepo) AddShortlink(ctx context.Context, dto ShortlinkDTO) erro
_, span := u.tracer.Start(ctx, "postgres::AddShortlink") _, span := u.tracer.Start(ctx, "postgres::AddShortlink")
defer span.End() defer span.End()
query := `insert into shortlinks (id, url, expiration) values ($1, $2, $3);` query := `insert into shortlinks (url, expires_at) values ($1, $2);`
_, err := u.db.ExecContext(ctx, query, dto.Id, dto.Url, dto.Expiration) _, err := u.db.ExecContext(ctx, query, dto.Url, dto.ExpiresAt)
return err return err
} }
@ -44,14 +44,14 @@ func (u *shortlinkRepo) GetShortlink(ctx context.Context, id string) (*Shortlink
_, span := u.tracer.Start(ctx, "postgres::GetShortlink") _, span := u.tracer.Start(ctx, "postgres::GetShortlink")
defer span.End() 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) row := u.db.QueryRowContext(ctx, query, id)
if err := row.Err(); err != nil { if err := row.Err(); err != nil {
return nil, err return nil, err
} }
dto := &ShortlinkDTO{Id: id} dto := &ShortlinkDTO{Id: id}
err := row.Scan(&dto.Url, &dto.Expiration) err := row.Scan(&dto.Url, &dto.ExpiresAt)
if err == nil { if err == nil {
return dto, nil return dto, nil
} }

View File

@ -10,16 +10,10 @@ import (
"go.opentelemetry.io/otel/trace" "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 { type UserRepo interface {
CreateUser(ctx context.Context, dto models.UserDTO) (*models.UserDTO, error) CreateUser(ctx context.Context, dto models.UserDTO) (*models.UserDTO, error)
UpdateUser(ctx context.Context, userId string, dto models.UserUpdateDTO) 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 SetUserEmailVerified(ctx context.Context, userId string) error
GetUserById(ctx context.Context, id string) (*models.UserDTO, error) GetUserById(ctx context.Context, id string) (*models.UserDTO, error)
GetUserByEmail(ctx context.Context, login 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 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 { func (u *userRepo) SetUserEmailVerified(ctx context.Context, userId string) error {
_, span := u.tracer.Start(ctx, "postgres::SetUserEmailVerified") _, span := u.tracer.Start(ctx, "postgres::SetUserEmailVerified")
defer span.End() defer span.End()

View File

@ -48,9 +48,9 @@ func (s *shortlinkService) CreateShortlink(ctx context.Context, url string) (str
expiration := time.Now().Add(7 * 24 * time.Hour) expiration := time.Now().Add(7 * 24 * time.Hour)
dto := repos.ShortlinkDTO{ dto := repos.ShortlinkDTO{
Id: id, Id: id,
Url: url, Url: url,
Expiration: expiration, ExpiresAt: expiration,
} }
if err := s.repo.AddShortlink(ctx, dto); err != nil { if err := s.repo.AddShortlink(ctx, dto); err != nil {
return "", err return "", err
@ -73,7 +73,7 @@ func (s *shortlinkService) GetShortlink(ctx context.Context, id string) (string,
if link == nil { if link == nil {
return "", ErrShortlinkNotexist return "", ErrShortlinkNotexist
} }
if time.Now().After(link.Expiration) { if time.Now().After(link.ExpiresAt) {
return "", ErrShortlinkExpired return "", ErrShortlinkExpired
} }

View File

@ -133,7 +133,7 @@ func (u *userService) AuthenticateUser(ctx context.Context, email, password stri
} }
func (u *userService) VerifyEmail(ctx context.Context, actionToken string) error { 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 { if err != nil {
return err return err
} }
@ -162,7 +162,7 @@ func (u *userService) SendEmailForgotPassword(ctx context.Context, email string)
models.ActionTokenDTO{ models.ActionTokenDTO{
UserId: user.Id, UserId: user.Id,
Value: uuid.New().String(), Value: uuid.New().String(),
Target: models.ActionTokenTargetForgotPassword, Target: models.ActionTokenTargetRestorePassword,
Expiration: time.Now().Add(15 * time.Minute), Expiration: time.Now().Add(15 * time.Minute),
}, },
) )
@ -179,7 +179,7 @@ func (u *userService) sendEmailVerifyUser(ctx context.Context, userId, email str
models.ActionTokenDTO{ models.ActionTokenDTO{
UserId: userId, UserId: userId,
Value: uuid.New().String(), Value: uuid.New().String(),
Target: models.ActionTokenVerifyEmail, Target: models.ActionTokenTargetVerifyEmail,
Expiration: time.Now().Add(1 * time.Hour), 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 { 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 { if err != nil {
return err return err
} }

18
sql/00_common.sql Normal file
View File

@ -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;

View File

@ -4,38 +4,20 @@ create table if not exists users (
secret varchar(256) not null, secret varchar(256) not null,
full_name varchar(256) not null, full_name varchar(256) not null,
email_verified boolean not null default false, email_verified boolean not null default false,
active boolean,
created_at timestamp, created_at timestamp,
updated_at timestamp updated_at timestamp
); );
create index if not exists idx_users_email on users(email); 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 create or replace trigger trg_user_created
before insert on users before insert on users
for each row for each row
execute function set_created_at(); execute function trg_proc_row_created();
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;
create or replace trigger trg_user_updated create or replace trigger trg_user_updated
before update on users before update on users
for each row for each row
when(new is distinct from old) when(new is distinct from old)
execute function set_updated_at(); execute function trg_proc_row_updated();

View File

@ -1,5 +1,17 @@
create table if not exists shortlinks ( create table if not exists shortlinks (
id text primary key, id int generated always as identity,
url text, url text not null,
expiration date 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();

View File

@ -1,9 +1,26 @@
create table if not exists action_tokens ( create table if not exists action_tokens (
id int generated always as identity, id int primary key generated always as identity,
user_id int, user_id int references users(id),
value text, value text not null,
target int, target text not null,
expiration timestamp, expires_at timestamp not null,
created_at timestamp,
updated_at timestamp
primary key(id) 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();