Module project, commit, submission
All checks were successful
Build and Release / release (push) Successful in 1m15s
All checks were successful
Build and Release / release (push) Successful in 1m15s
This commit is contained in:
@@ -27,6 +27,7 @@ import (
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
@@ -46,6 +47,7 @@ type authService struct {
|
||||
roleRepo repositories.RoleRepository
|
||||
tokenRepo repositories.TokenRepository
|
||||
c cache.Cache
|
||||
db *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewAuthService(
|
||||
@@ -53,12 +55,14 @@ func NewAuthService(
|
||||
roleRepo repositories.RoleRepository,
|
||||
tokenRepo repositories.TokenRepository,
|
||||
c cache.Cache,
|
||||
db *pgxpool.Pool,
|
||||
) AuthService {
|
||||
return &authService{
|
||||
userRepo: userRepo,
|
||||
roleRepo: roleRepo,
|
||||
tokenRepo: tokenRepo,
|
||||
c: c,
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,14 +117,6 @@ func (a *authService) genToken(user *models.UserEntity) (*response.AuthResponse,
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (a *authService) saveNewRefreshToken(ctx context.Context, params sqlc.UpdateUserRefreshTokenParams) error {
|
||||
err := a.userRepo.UpdateRefreshToken(ctx, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *authService) Signin(ctx context.Context, dto *request.SignInDto) (*response.AuthResponse, error) {
|
||||
if !constants.EMAIL_REGEX.MatchString(dto.Email) {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid email")
|
||||
@@ -153,7 +149,7 @@ func (a *authService) Signin(ctx context.Context, dto *request.SignInDto) (*resp
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
err = a.saveNewRefreshToken(
|
||||
err = a.userRepo.UpdateRefreshToken(
|
||||
ctx,
|
||||
sqlc.UpdateUserRefreshTokenParams{
|
||||
ID: pgID,
|
||||
@@ -172,24 +168,32 @@ func (a *authService) Signin(ctx context.Context, dto *request.SignInDto) (*resp
|
||||
}
|
||||
|
||||
func (a *authService) Logout(ctx context.Context, userId string) error {
|
||||
tx, err := a.db.Begin(ctx)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to start transaction")
|
||||
}
|
||||
defer tx.Rollback(ctx)
|
||||
|
||||
uRepoTx := a.userRepo.WithTx(tx)
|
||||
|
||||
pgID, err := convert.StringToUUID(userId)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
user , err := a.userRepo.GetByID(ctx, pgID)
|
||||
user, err := a.userRepo.GetByID(ctx, pgID)
|
||||
if err != nil || user == nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Invalid user data")
|
||||
}
|
||||
|
||||
err = a.userRepo.UpdateTokenVersion(ctx, sqlc.UpdateTokenVersionParams{
|
||||
ID: pgID,
|
||||
|
||||
err = uRepoTx.UpdateTokenVersion(ctx, sqlc.UpdateTokenVersionParams{
|
||||
ID: pgID,
|
||||
TokenVersion: user.TokenVersion + 1,
|
||||
})
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
err = a.userRepo.UpdateRefreshToken(ctx, sqlc.UpdateUserRefreshTokenParams{
|
||||
err = uRepoTx.UpdateRefreshToken(ctx, sqlc.UpdateUserRefreshTokenParams{
|
||||
ID: pgID,
|
||||
RefreshToken: pgtype.Text{
|
||||
String: "",
|
||||
@@ -199,6 +203,10 @@ func (a *authService) Logout(ctx context.Context, userId string) error {
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
err = tx.Commit(ctx)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -228,7 +236,7 @@ func (a *authService) RefreshToken(ctx context.Context, id string, refreshToken
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
err = a.saveNewRefreshToken(
|
||||
err = a.userRepo.UpdateRefreshToken(
|
||||
ctx,
|
||||
sqlc.UpdateUserRefreshTokenParams{
|
||||
ID: pgID,
|
||||
@@ -246,10 +254,19 @@ func (a *authService) RefreshToken(ctx context.Context, id string, refreshToken
|
||||
}
|
||||
|
||||
func (a *authService) Signup(ctx context.Context, dto *request.SignUpDto) (*response.AuthResponse, error) {
|
||||
tx, err := a.db.Begin(ctx)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to start transaction")
|
||||
}
|
||||
defer tx.Rollback(ctx)
|
||||
|
||||
uRepoTx := a.userRepo.WithTx(tx)
|
||||
rRepoTx := a.roleRepo.WithTx(tx)
|
||||
|
||||
if !constants.EMAIL_REGEX.MatchString(dto.Email) {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid email")
|
||||
}
|
||||
err := constants.ValidatePassword(dto.Password)
|
||||
err = constants.ValidatePassword(dto.Password)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
@@ -276,7 +293,7 @@ func (a *authService) Signup(ctx context.Context, dto *request.SignUpDto) (*resp
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
user, err = a.userRepo.UpsertUser(
|
||||
user, err = uRepoTx.UpsertUser(
|
||||
ctx,
|
||||
sqlc.UpsertUserParams{
|
||||
Email: dto.Email,
|
||||
@@ -295,7 +312,7 @@ func (a *authService) Signup(ctx context.Context, dto *request.SignUpDto) (*resp
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
_, err = a.userRepo.CreateProfile(
|
||||
_, err = uRepoTx.CreateProfile(
|
||||
ctx,
|
||||
sqlc.CreateUserProfileParams{
|
||||
UserID: userId,
|
||||
@@ -308,7 +325,7 @@ func (a *authService) Signup(ctx context.Context, dto *request.SignUpDto) (*resp
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
role, err := a.roleRepo.GetByname(ctx, constants.RoleTypeUser.String())
|
||||
role, err := a.roleRepo.GetByName(ctx, constants.RoleTypeUser.String())
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@@ -318,7 +335,7 @@ func (a *authService) Signup(ctx context.Context, dto *request.SignUpDto) (*resp
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
err = a.roleRepo.CreateUserRole(
|
||||
err = rRepoTx.CreateUserRole(
|
||||
ctx,
|
||||
sqlc.CreateUserRoleParams{
|
||||
UserID: userId,
|
||||
@@ -334,7 +351,7 @@ func (a *authService) Signup(ctx context.Context, dto *request.SignUpDto) (*resp
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
err = a.saveNewRefreshToken(
|
||||
err = uRepoTx.UpdateRefreshToken(
|
||||
ctx,
|
||||
sqlc.UpdateUserRefreshTokenParams{
|
||||
ID: userId,
|
||||
@@ -348,6 +365,11 @@ func (a *authService) Signup(ctx context.Context, dto *request.SignUpDto) (*resp
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
err = tx.Commit(ctx)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
@@ -389,6 +411,15 @@ func (a *authService) ForgotPassword(ctx context.Context, dto *request.ForgotPas
|
||||
}
|
||||
|
||||
func (a *authService) SigninWithGoogle(ctx context.Context, dto *request.SigninWithGoogleDto) (*response.AuthResponse, error) {
|
||||
tx, err := a.db.Begin(ctx)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to start transaction")
|
||||
}
|
||||
defer tx.Rollback(ctx)
|
||||
|
||||
uRepoTx := a.userRepo.WithTx(tx)
|
||||
rRepoTx := a.roleRepo.WithTx(tx)
|
||||
|
||||
user, err := a.userRepo.GetByEmail(ctx, dto.Email)
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
@@ -403,7 +434,7 @@ func (a *authService) SigninWithGoogle(ctx context.Context, dto *request.SigninW
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
err = a.saveNewRefreshToken(
|
||||
err = uRepoTx.UpdateRefreshToken(
|
||||
ctx,
|
||||
sqlc.UpdateUserRefreshTokenParams{
|
||||
ID: userId,
|
||||
@@ -419,7 +450,7 @@ func (a *authService) SigninWithGoogle(ctx context.Context, dto *request.SigninW
|
||||
return data, nil
|
||||
}
|
||||
|
||||
user, err = a.userRepo.UpsertUser(
|
||||
user, err = uRepoTx.UpsertUser(
|
||||
ctx,
|
||||
sqlc.UpsertUserParams{
|
||||
Email: dto.Email,
|
||||
@@ -437,7 +468,7 @@ func (a *authService) SigninWithGoogle(ctx context.Context, dto *request.SigninW
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
_, err = a.userRepo.CreateProfile(
|
||||
_, err = uRepoTx.CreateProfile(
|
||||
ctx,
|
||||
sqlc.CreateUserProfileParams{
|
||||
UserID: userId,
|
||||
@@ -454,7 +485,7 @@ func (a *authService) SigninWithGoogle(ctx context.Context, dto *request.SigninW
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
role, err := a.roleRepo.GetByname(ctx, constants.RoleTypeUser.String())
|
||||
role, err := a.roleRepo.GetByName(ctx, constants.RoleTypeUser.String())
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@@ -464,7 +495,7 @@ func (a *authService) SigninWithGoogle(ctx context.Context, dto *request.SigninW
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
err = a.roleRepo.CreateUserRole(
|
||||
err = rRepoTx.CreateUserRole(
|
||||
ctx,
|
||||
sqlc.CreateUserRoleParams{
|
||||
UserID: userId,
|
||||
@@ -479,7 +510,7 @@ func (a *authService) SigninWithGoogle(ctx context.Context, dto *request.SigninW
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
err = a.saveNewRefreshToken(
|
||||
err = uRepoTx.UpdateRefreshToken(
|
||||
ctx,
|
||||
sqlc.UpdateUserRefreshTokenParams{
|
||||
ID: userId,
|
||||
@@ -492,6 +523,10 @@ func (a *authService) SigninWithGoogle(ctx context.Context, dto *request.SigninW
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
err = tx.Commit(ctx)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
|
||||
168
internal/services/commitService.go
Normal file
168
internal/services/commitService.go
Normal file
@@ -0,0 +1,168 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"history-api/internal/dtos/request"
|
||||
"history-api/internal/dtos/response"
|
||||
"history-api/internal/gen/sqlc"
|
||||
"history-api/internal/models"
|
||||
"history-api/internal/repositories"
|
||||
"history-api/pkg/constants"
|
||||
"history-api/pkg/convert"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type CommitService interface {
|
||||
CreateCommit(ctx context.Context, userID string, projectID string, dto *request.CreateCommitDto) (*response.CommitResponse, error)
|
||||
RestoreCommit(ctx context.Context, userID string, projectID string, dto *request.RestoreCommitDto) error
|
||||
GetProjectCommits(ctx context.Context, projectID string) ([]*response.CommitResponse, error)
|
||||
}
|
||||
|
||||
type commitService struct {
|
||||
db *pgxpool.Pool
|
||||
commitRepo repositories.CommitRepository
|
||||
projectRepo repositories.ProjectRepository
|
||||
}
|
||||
|
||||
func NewCommitService(
|
||||
db *pgxpool.Pool,
|
||||
commitRepo repositories.CommitRepository,
|
||||
projectRepo repositories.ProjectRepository,
|
||||
) CommitService {
|
||||
return &commitService{
|
||||
db: db,
|
||||
commitRepo: commitRepo,
|
||||
projectRepo: projectRepo,
|
||||
}
|
||||
}
|
||||
func (s *commitService) checkWritePermission(ctx context.Context, userID string, projectUUID pgtype.UUID) error {
|
||||
project, err := s.projectRepo.GetByID(ctx, projectUUID)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusNotFound, "Project not found")
|
||||
}
|
||||
|
||||
if project.UserID == userID {
|
||||
return nil
|
||||
}
|
||||
|
||||
userUUID, _ := convert.StringToUUID(userID)
|
||||
roleVal, err := s.projectRepo.CheckPermission(ctx, sqlc.CheckProjectPermissionParams{
|
||||
ProjectID: projectUUID,
|
||||
UserID: userUUID,
|
||||
})
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusForbidden, "You do not have permission to write to this project")
|
||||
}
|
||||
|
||||
role := constants.ParseProjectMemberRole(roleVal)
|
||||
if !role.CanWrite() {
|
||||
return fiber.NewError(fiber.StatusForbidden, "You do not have permission to write to this project")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *commitService) CreateCommit(ctx context.Context, userID string, projectID string, dto *request.CreateCommitDto) (*response.CommitResponse, error) {
|
||||
tx, err := s.db.Begin(ctx)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to start transaction")
|
||||
}
|
||||
defer tx.Rollback(ctx)
|
||||
|
||||
cRepoTx := s.commitRepo.WithTx(tx)
|
||||
pRepoTx := s.projectRepo.WithTx(tx)
|
||||
|
||||
projectUUID, err := convert.StringToUUID(projectID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project ID")
|
||||
}
|
||||
|
||||
if err := s.checkWritePermission(ctx, userID, projectUUID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userUUID, err := convert.StringToUUID(userID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid user ID")
|
||||
}
|
||||
|
||||
commit, err := cRepoTx.Create(ctx, sqlc.CreateCommitParams{
|
||||
ProjectID: projectUUID,
|
||||
SnapshotJson: dto.SnapshotJson,
|
||||
UserID: userUUID,
|
||||
EditSummary: convert.StringToText(dto.EditSummary),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create commit")
|
||||
}
|
||||
|
||||
commitUUID, err := convert.StringToUUID(commit.ID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid commit ID")
|
||||
}
|
||||
|
||||
err = pRepoTx.UpdateLatestCommit(ctx, sqlc.UpdateLatestCommitParams{
|
||||
ID: projectUUID,
|
||||
LatestCommitID: commitUUID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update project latest commit")
|
||||
}
|
||||
|
||||
if err := tx.Commit(ctx); err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to commit transaction")
|
||||
}
|
||||
|
||||
return commit.ToResponse(), nil
|
||||
}
|
||||
|
||||
func (s *commitService) RestoreCommit(ctx context.Context, userID string, projectID string, dto *request.RestoreCommitDto) error {
|
||||
projectUUID, err := convert.StringToUUID(projectID)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid project ID")
|
||||
}
|
||||
|
||||
if err := s.checkWritePermission(ctx, userID, projectUUID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
commitUUID, err := convert.StringToUUID(dto.CommitID)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid commit ID")
|
||||
}
|
||||
|
||||
commit, err := s.commitRepo.GetByID(ctx, commitUUID)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusNotFound, "Commit not found")
|
||||
}
|
||||
|
||||
if commit.ProjectID != projectID {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Commit does not belong to this project")
|
||||
}
|
||||
|
||||
err = s.projectRepo.UpdateLatestCommit(ctx, sqlc.UpdateLatestCommitParams{
|
||||
ID: projectUUID,
|
||||
LatestCommitID: commitUUID,
|
||||
})
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to restore commit")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *commitService) GetProjectCommits(ctx context.Context, projectID string) ([]*response.CommitResponse, error) {
|
||||
projectUUID, err := convert.StringToUUID(projectID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project ID")
|
||||
}
|
||||
|
||||
commits, err := s.commitRepo.GetByProjectID(ctx, projectUUID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch commits")
|
||||
}
|
||||
|
||||
return models.CommitsEntityToResponse(commits), nil
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
@@ -24,6 +23,10 @@ type ProjectService interface {
|
||||
DeleteProject(ctx context.Context, id string) error
|
||||
CreateProject(ctx context.Context, userID string, dto *request.CreateProjectDto) (*response.ProjectResponse, error)
|
||||
UpdateProject(ctx context.Context, id string, dto *request.UpdateProjectDto) (*response.ProjectResponse, error)
|
||||
AddMember(ctx context.Context, callerID string, projectID string, dto *request.AddProjectMemberDto) (*response.ProjectResponse, error)
|
||||
UpdateMemberRole(ctx context.Context, callerID string, projectID string, memberUserID string, dto *request.UpdateProjectMemberDto) (*response.ProjectResponse, error)
|
||||
RemoveMember(ctx context.Context, callerID string, projectID string, memberUserID string) error
|
||||
ChangeOwner(ctx context.Context, callerID string, projectID string, newOwnerID string) (*response.ProjectResponse, error)
|
||||
}
|
||||
|
||||
type projectService struct {
|
||||
@@ -36,6 +39,25 @@ func NewProjectService(projectRepo repositories.ProjectRepository) ProjectServic
|
||||
}
|
||||
}
|
||||
|
||||
func (s *projectService) checkCallerIsOwner(ctx context.Context, callerID string, projectUUID pgtype.UUID) error {
|
||||
project, err := s.projectRepo.GetByID(ctx, projectUUID)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusNotFound, "Project not found")
|
||||
}
|
||||
if project.UserID == callerID {
|
||||
return nil
|
||||
}
|
||||
callerUUID, _ := convert.StringToUUID(callerID)
|
||||
role, err := s.projectRepo.CheckPermission(ctx, sqlc.CheckProjectPermissionParams{
|
||||
ProjectID: projectUUID,
|
||||
UserID: callerUUID,
|
||||
})
|
||||
if err != nil || constants.ParseProjectMemberRole(role) != constants.ProjectMemberRoleOwner {
|
||||
return fiber.NewError(fiber.StatusForbidden, "Only project owner can perform this action")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *projectService) GetProjectByID(ctx context.Context, id string) (*response.ProjectResponse, error) {
|
||||
projectUUID, err := convert.StringToUUID(id)
|
||||
if err != nil {
|
||||
@@ -96,7 +118,7 @@ func (s *projectService) fillSearchArgs(arg *sqlc.SearchProjectsParams, dto *req
|
||||
for _, statusStr := range dto.Statuses {
|
||||
statusType := constants.ParseProjectStatusTypeText(statusStr)
|
||||
if statusType != constants.ProjectStatusTypeUnknow {
|
||||
arg.Statuses = append(arg.Statuses, fmt.Sprintf("%d", statusType.Int16()))
|
||||
arg.Statuses = append(arg.Statuses, statusType.Int16())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,15 +233,11 @@ func (s *projectService) UpdateProject(ctx context.Context, id string, dto *requ
|
||||
}
|
||||
|
||||
arg := sqlc.UpdateProjectParams{
|
||||
ID: projectUUID,
|
||||
ID: projectUUID,
|
||||
Title: convert.PtrToText(dto.Title),
|
||||
Description: convert.PtrToText(dto.Description),
|
||||
}
|
||||
|
||||
if dto.Title != nil {
|
||||
arg.Title = convert.PtrToText(dto.Title)
|
||||
}
|
||||
if dto.Description != nil {
|
||||
arg.Description = convert.PtrToText(dto.Description)
|
||||
}
|
||||
if dto.Status != nil {
|
||||
statusType := constants.ParseProjectStatusTypeText(*dto.Status)
|
||||
if statusType == constants.ProjectStatusTypeUnknow {
|
||||
@@ -254,3 +272,143 @@ func (s *projectService) DeleteProject(ctx context.Context, id string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *projectService) AddMember(ctx context.Context, callerID string, projectID string, dto *request.AddProjectMemberDto) (*response.ProjectResponse, error) {
|
||||
projectUUID, err := convert.StringToUUID(projectID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project ID format")
|
||||
}
|
||||
|
||||
if err := s.checkCallerIsOwner(ctx, callerID, projectUUID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
memberUUID, err := convert.StringToUUID(dto.UserID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid member user ID format")
|
||||
}
|
||||
|
||||
callerUUID, _ := convert.StringToUUID(callerID)
|
||||
|
||||
if dto.UserID == callerID {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Cannot add yourself as a member")
|
||||
}
|
||||
|
||||
role := constants.ParseProjectMemberRoleText(dto.Role)
|
||||
|
||||
err = s.projectRepo.AddMember(ctx, sqlc.AddProjectMemberParams{
|
||||
ProjectID: projectUUID,
|
||||
UserID: memberUUID,
|
||||
Role: role.Int16(),
|
||||
InvitedBy: callerUUID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusConflict, "Member already exists or user not found")
|
||||
}
|
||||
|
||||
project, err := s.projectRepo.GetByID(ctx, projectUUID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch updated project")
|
||||
}
|
||||
|
||||
return project.ToResponse(), nil
|
||||
}
|
||||
|
||||
func (s *projectService) UpdateMemberRole(ctx context.Context, callerID string, projectID string, memberUserID string, dto *request.UpdateProjectMemberDto) (*response.ProjectResponse, error) {
|
||||
projectUUID, err := convert.StringToUUID(projectID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project ID format")
|
||||
}
|
||||
|
||||
if err := s.checkCallerIsOwner(ctx, callerID, projectUUID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
memberUUID, err := convert.StringToUUID(memberUserID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid member user ID format")
|
||||
}
|
||||
|
||||
role := constants.ParseProjectMemberRoleText(dto.Role)
|
||||
|
||||
err = s.projectRepo.UpdateMemberRole(ctx, sqlc.UpdateProjectMemberRoleParams{
|
||||
ProjectID: projectUUID,
|
||||
UserID: memberUUID,
|
||||
Role: role.Int16(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Member not found")
|
||||
}
|
||||
|
||||
project, err := s.projectRepo.GetByID(ctx, projectUUID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch updated project")
|
||||
}
|
||||
|
||||
return project.ToResponse(), nil
|
||||
}
|
||||
|
||||
func (s *projectService) RemoveMember(ctx context.Context, callerID string, projectID string, memberUserID string) error {
|
||||
projectUUID, err := convert.StringToUUID(projectID)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid project ID format")
|
||||
}
|
||||
|
||||
if err := s.checkCallerIsOwner(ctx, callerID, projectUUID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
memberUUID, err := convert.StringToUUID(memberUserID)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid member user ID format")
|
||||
}
|
||||
|
||||
if callerID == memberUserID {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Cannot remove yourself from the project")
|
||||
}
|
||||
|
||||
err = s.projectRepo.RemoveMember(ctx, sqlc.RemoveProjectMemberParams{
|
||||
ProjectID: projectUUID,
|
||||
UserID: memberUUID,
|
||||
})
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusNotFound, "Member not found")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *projectService) ChangeOwner(ctx context.Context, callerID string, projectID string, newOwnerID string) (*response.ProjectResponse, error) {
|
||||
projectUUID, err := convert.StringToUUID(projectID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project ID format")
|
||||
}
|
||||
|
||||
if err := s.checkCallerIsOwner(ctx, callerID, projectUUID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if callerID == newOwnerID {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "You are already the owner")
|
||||
}
|
||||
|
||||
newOwnerUUID, err := convert.StringToUUID(newOwnerID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid new owner ID format")
|
||||
}
|
||||
|
||||
err = s.projectRepo.ChangeOwner(ctx, sqlc.ChangeProjectOwnerParams{
|
||||
ID: projectUUID,
|
||||
UserID: newOwnerUUID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to change owner")
|
||||
}
|
||||
|
||||
project, err := s.projectRepo.GetByID(ctx, projectUUID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch updated project")
|
||||
}
|
||||
|
||||
return project.ToResponse(), nil
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ type roleService struct {
|
||||
roleRepo repositories.RoleRepository
|
||||
}
|
||||
|
||||
|
||||
func NewRoleService(
|
||||
roleRepo repositories.RoleRepository,
|
||||
) RoleService {
|
||||
@@ -48,4 +47,4 @@ func (r *roleService) GetRoleByID(ctx context.Context, id string) (*response.Rol
|
||||
}
|
||||
|
||||
return role.ToResponse(), nil
|
||||
}
|
||||
}
|
||||
|
||||
282
internal/services/submissionService.go
Normal file
282
internal/services/submissionService.go
Normal file
@@ -0,0 +1,282 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"history-api/internal/dtos/request"
|
||||
"history-api/internal/dtos/response"
|
||||
"history-api/internal/gen/sqlc"
|
||||
"history-api/internal/models"
|
||||
"history-api/internal/repositories"
|
||||
"history-api/pkg/cache"
|
||||
"history-api/pkg/constants"
|
||||
"history-api/pkg/convert"
|
||||
"slices"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type SubmissionService interface {
|
||||
CreateSubmission(ctx context.Context, userID string, dto *request.CreateSubmissionDto) (*response.SubmissionResponse, error)
|
||||
UpdateSubmissionStatus(ctx context.Context, reviewerID string, submissionID string, dto *request.UpdateSubmissionStatusDto) (*response.SubmissionResponse, error)
|
||||
GetSubmissionByID(ctx context.Context, id string) (*response.SubmissionResponse, error)
|
||||
SearchSubmissions(ctx context.Context, dto *request.SearchSubmissionDto) (*response.PaginatedResponse, error)
|
||||
DeleteSubmission(ctx context.Context, userID string, id string, claims *response.JWTClaims) error
|
||||
}
|
||||
|
||||
type submissionService struct {
|
||||
submissionRepo repositories.SubmissionRepository
|
||||
projectRepo repositories.ProjectRepository
|
||||
commitRepo repositories.CommitRepository
|
||||
userRepo repositories.UserRepository
|
||||
db *pgxpool.Pool
|
||||
c cache.Cache
|
||||
}
|
||||
|
||||
func NewSubmissionService(
|
||||
submissionRepo repositories.SubmissionRepository,
|
||||
projectRepo repositories.ProjectRepository,
|
||||
commitRepo repositories.CommitRepository,
|
||||
userRepo repositories.UserRepository,
|
||||
db *pgxpool.Pool,
|
||||
c cache.Cache,
|
||||
) SubmissionService {
|
||||
return &submissionService{
|
||||
submissionRepo: submissionRepo,
|
||||
projectRepo: projectRepo,
|
||||
commitRepo: commitRepo,
|
||||
userRepo: userRepo,
|
||||
db: db,
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *submissionService) CreateSubmission(ctx context.Context, userID string, dto *request.CreateSubmissionDto) (*response.SubmissionResponse, error) {
|
||||
projectUUID, err := convert.StringToUUID(dto.ProjectID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project ID")
|
||||
}
|
||||
|
||||
commitUUID, err := convert.StringToUUID(dto.CommitID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid commit ID")
|
||||
}
|
||||
|
||||
userUUID, err := convert.StringToUUID(userID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid user ID")
|
||||
}
|
||||
|
||||
commit, err := s.commitRepo.GetByID(ctx, commitUUID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Commit not found")
|
||||
}
|
||||
if commit.ProjectID != dto.ProjectID {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Commit does not belong to project")
|
||||
}
|
||||
|
||||
arg := sqlc.CreateSubmissionParams{
|
||||
ProjectID: projectUUID,
|
||||
CommitID: commitUUID,
|
||||
UserID: userUUID,
|
||||
Status: constants.StatusTypePending.Int16(),
|
||||
Content: convert.StringToText(dto.Content),
|
||||
}
|
||||
|
||||
submission, err := s.submissionRepo.Create(ctx, arg)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create submission")
|
||||
}
|
||||
|
||||
return submission.ToResponse(), nil
|
||||
}
|
||||
|
||||
func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewerID string, submissionID string, dto *request.UpdateSubmissionStatusDto) (*response.SubmissionResponse, error) {
|
||||
submissionUUID, err := convert.StringToUUID(submissionID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid submission ID")
|
||||
}
|
||||
|
||||
reviewerUUID, err := convert.StringToUUID(reviewerID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid reviewer ID")
|
||||
}
|
||||
|
||||
status := constants.ParseStatusTypeText(dto.Status)
|
||||
if status == constants.StatusTypeUnknown {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid status")
|
||||
}
|
||||
|
||||
|
||||
submission, err := s.submissionRepo.GetByID(ctx, submissionUUID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Submission not found")
|
||||
}
|
||||
|
||||
if submission.Status != constants.StatusTypePending {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Submission already processed")
|
||||
}
|
||||
|
||||
arg := sqlc.UpdateSubmissionParams{
|
||||
ID: submissionUUID,
|
||||
Status: pgtype.Int2{Int16: status.Int16(), Valid: true},
|
||||
ReviewedBy: reviewerUUID,
|
||||
ReviewNote: convert.StringToText(dto.ReviewNote),
|
||||
}
|
||||
|
||||
updatedSubmission, err := s.submissionRepo.Update(ctx, arg)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update submission status")
|
||||
}
|
||||
|
||||
_ = s.c.Del(ctx, fmt.Sprintf("proejct:id:%s", submission.ProjectID))
|
||||
|
||||
|
||||
return updatedSubmission.ToResponse(), nil
|
||||
}
|
||||
|
||||
func (m *submissionService) fillSearchArgs(arg *sqlc.SearchSubmissionsParams, dto *request.SearchSubmissionDto) {
|
||||
if dto.Sort != "" {
|
||||
arg.Sort = pgtype.Text{String: dto.Sort, Valid: true}
|
||||
} else {
|
||||
arg.Sort = pgtype.Text{String: "id", Valid: true}
|
||||
}
|
||||
|
||||
arg.Order = pgtype.Text{String: "asc", Valid: true}
|
||||
if dto.Order == "desc" {
|
||||
arg.Order = pgtype.Text{String: "desc", Valid: true}
|
||||
}
|
||||
|
||||
if len(dto.Statuses) > 0 {
|
||||
for _, id := range dto.Statuses {
|
||||
if u := constants.ParseStatusTypeText(id); u != constants.StatusTypeUnknown {
|
||||
arg.Statuses = append(arg.Statuses, u.Int16())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(dto.UserIDs) > 0 {
|
||||
for _, id := range dto.UserIDs {
|
||||
if u, err := convert.StringToUUID(id); err == nil {
|
||||
arg.UserIds = append(arg.UserIds, u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if dto.ReviewedBy != nil {
|
||||
if rvID, err := convert.StringToUUID(*dto.ReviewedBy); err == nil {
|
||||
arg.ReviewedBy = rvID
|
||||
}
|
||||
}
|
||||
|
||||
if dto.CreatedFrom != nil {
|
||||
arg.CreatedFrom = pgtype.Timestamptz{Time: *dto.CreatedFrom, Valid: true}
|
||||
}
|
||||
|
||||
if dto.CreatedTo != nil {
|
||||
arg.CreatedTo = pgtype.Timestamptz{Time: *dto.CreatedTo, Valid: true}
|
||||
}
|
||||
|
||||
if dto.Search != "" {
|
||||
arg.SearchText = pgtype.Text{String: dto.Search, Valid: true}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *submissionService) GetSubmissionByID(ctx context.Context, id string) (*response.SubmissionResponse, error) {
|
||||
submissionUUID, err := convert.StringToUUID(id)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid submission ID")
|
||||
}
|
||||
|
||||
submission, err := s.submissionRepo.GetByID(ctx, submissionUUID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Submission not found")
|
||||
}
|
||||
|
||||
return submission.ToResponse(), nil
|
||||
}
|
||||
|
||||
func (s *submissionService) SearchSubmissions(ctx context.Context, dto *request.SearchSubmissionDto) (*response.PaginatedResponse, error) {
|
||||
if dto.Page < 1 {
|
||||
dto.Page = 1
|
||||
}
|
||||
if dto.Limit == 0 {
|
||||
dto.Limit = 20
|
||||
}
|
||||
offset := (dto.Page - 1) * dto.Limit
|
||||
|
||||
arg := sqlc.SearchSubmissionsParams{
|
||||
Limit: int32(dto.Limit),
|
||||
Offset: int32(offset),
|
||||
}
|
||||
|
||||
s.fillSearchArgs(&arg, dto)
|
||||
|
||||
var rows []*models.SubmissionEntity
|
||||
var totalRecords int64
|
||||
|
||||
g, gCtx := errgroup.WithContext(ctx)
|
||||
|
||||
g.Go(func() error {
|
||||
var err error
|
||||
rows, err = s.submissionRepo.Search(gCtx, arg)
|
||||
return err
|
||||
})
|
||||
|
||||
g.Go(func() error {
|
||||
countArg := sqlc.CountSubmissionsParams{
|
||||
UserIds: arg.UserIds,
|
||||
Statuses: arg.Statuses,
|
||||
ReviewedBy: arg.ReviewedBy,
|
||||
CreatedFrom: arg.CreatedFrom,
|
||||
CreatedTo: arg.CreatedTo,
|
||||
SearchText: arg.SearchText,
|
||||
}
|
||||
var err error
|
||||
totalRecords, err = s.submissionRepo.Count(gCtx, countArg)
|
||||
return err
|
||||
})
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
submissions := models.SubmissionsEntityToResponse(rows)
|
||||
|
||||
return response.BuildPaginatedResponse(submissions, totalRecords, dto.Page, dto.Limit), nil
|
||||
}
|
||||
|
||||
func (s *submissionService) DeleteSubmission(ctx context.Context, userID string, id string, claims *response.JWTClaims) error {
|
||||
submissionUUID, err := convert.StringToUUID(id)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid submission ID")
|
||||
}
|
||||
|
||||
submission, err := s.submissionRepo.GetByID(ctx, submissionUUID)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusNotFound, "Submission not found")
|
||||
}
|
||||
|
||||
shoudDelete := false
|
||||
if slices.Contains(claims.Roles, constants.RoleTypeAdmin) || slices.Contains(claims.Roles, constants.RoleTypeMod) {
|
||||
shoudDelete = true
|
||||
}
|
||||
|
||||
if submission.UserID == claims.UId && submission.Status == constants.StatusTypePending {
|
||||
shoudDelete = true
|
||||
}
|
||||
|
||||
if !shoudDelete {
|
||||
return fiber.NewError(fiber.StatusForbidden, "You don't have permission to delete this submission")
|
||||
}
|
||||
|
||||
err = s.submissionRepo.Delete(ctx, submissionUUID)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete submission")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -15,13 +15,13 @@ import (
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type UserService interface {
|
||||
//user
|
||||
GetUserCurrent(ctx context.Context, userId string) (*response.UserResponse, error)
|
||||
UpdateProfile(ctx context.Context, userId string, dto *request.UpdateProfileDto) (*response.UserResponse, error)
|
||||
ChangePassword(ctx context.Context, userId string, dto *request.ChangePasswordDto) error
|
||||
|
||||
@@ -37,21 +37,33 @@ type userService struct {
|
||||
userRepo repositories.UserRepository
|
||||
roleRepo repositories.RoleRepository
|
||||
c cache.Cache
|
||||
db *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewUserService(
|
||||
userRepo repositories.UserRepository,
|
||||
roleRepo repositories.RoleRepository,
|
||||
c cache.Cache,
|
||||
db *pgxpool.Pool,
|
||||
|
||||
) UserService {
|
||||
return &userService{
|
||||
userRepo: userRepo,
|
||||
roleRepo: roleRepo,
|
||||
c: c,
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
func (u *userService) ChangePassword(ctx context.Context, userId string, dto *request.ChangePasswordDto) error {
|
||||
tx, err := u.db.Begin(ctx)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to start transaction")
|
||||
}
|
||||
defer tx.Rollback(ctx)
|
||||
|
||||
uRepo := u.userRepo.WithTx(tx)
|
||||
|
||||
pgID, err := convert.StringToUUID(userId)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
@@ -68,7 +80,6 @@ func (u *userService) ChangePassword(ctx context.Context, userId string, dto *re
|
||||
if dto.OldPassword == "" {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Old password required")
|
||||
}
|
||||
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(dto.OldPassword)); err != nil {
|
||||
return fiber.NewError(fiber.StatusUnauthorized, "Invalid password!")
|
||||
}
|
||||
@@ -81,7 +92,7 @@ func (u *userService) ChangePassword(ctx context.Context, userId string, dto *re
|
||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
err = u.userRepo.UpdatePassword(ctx, sqlc.UpdateUserPasswordParams{
|
||||
err = uRepo.UpdatePassword(ctx, sqlc.UpdateUserPasswordParams{
|
||||
ID: pgID,
|
||||
PasswordHash: pgtype.Text{String: string(hashPassword), Valid: true},
|
||||
})
|
||||
@@ -89,10 +100,30 @@ func (u *userService) ChangePassword(ctx context.Context, userId string, dto *re
|
||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
err = uRepo.UpdateTokenVersion(ctx, sqlc.UpdateTokenVersionParams{
|
||||
ID: pgID,
|
||||
TokenVersion: user.TokenVersion + 1,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tx.Commit(ctx); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to commit transaction")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *userService) ChangeRoleUser(ctx context.Context, userId string, claims *response.JWTClaims, dto *request.ChangeRoleDto) (*response.UserResponse, error) {
|
||||
tx, err := u.db.Begin(ctx)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to start transaction")
|
||||
}
|
||||
defer tx.Rollback(ctx)
|
||||
|
||||
uRepo := u.userRepo.WithTx(tx)
|
||||
rRepo := u.roleRepo.WithTx(tx)
|
||||
|
||||
userUUID, err := convert.StringToUUID(userId)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
@@ -180,12 +211,12 @@ func (u *userService) ChangeRoleUser(ctx context.Context, userId string, claims
|
||||
user.Roles = append(user.Roles, role.ToRoleSimple())
|
||||
}
|
||||
|
||||
err = u.roleRepo.BulkDeleteRolesFromUser(ctx, userUUID)
|
||||
err = rRepo.BulkDeleteRolesFromUser(ctx, userUUID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
err = u.roleRepo.CreateUserRole(ctx, sqlc.CreateUserRoleParams{
|
||||
err = rRepo.CreateUserRole(ctx, sqlc.CreateUserRoleParams{
|
||||
UserID: userUUID,
|
||||
Column2: roleIdList,
|
||||
})
|
||||
@@ -193,7 +224,7 @@ func (u *userService) ChangeRoleUser(ctx context.Context, userId string, claims
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
err = u.userRepo.UpdateTokenVersion(ctx, sqlc.UpdateTokenVersionParams{
|
||||
err = uRepo.UpdateTokenVersion(ctx, sqlc.UpdateTokenVersionParams{
|
||||
ID: userUUID,
|
||||
TokenVersion: user.TokenVersion + 1,
|
||||
})
|
||||
@@ -202,6 +233,11 @@ func (u *userService) ChangeRoleUser(ctx context.Context, userId string, claims
|
||||
}
|
||||
user.TokenVersion += 1
|
||||
|
||||
err = tx.Commit(ctx)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
mapCache := map[string]any{
|
||||
fmt.Sprintf("user:email:%s", user.Email): user,
|
||||
fmt.Sprintf("user:id:%s", user.ID): user,
|
||||
@@ -209,7 +245,6 @@ func (u *userService) ChangeRoleUser(ctx context.Context, userId string, claims
|
||||
_ = u.c.MSet(ctx, mapCache, constants.NormalCacheDuration)
|
||||
|
||||
return user.ToResponse(), nil
|
||||
|
||||
}
|
||||
|
||||
func (u *userService) DeleteUser(ctx context.Context, userId string) error {
|
||||
@@ -264,18 +299,6 @@ func (u *userService) UpdateProfile(ctx context.Context, userId string, dto *req
|
||||
return newUser.ToResponse(), nil
|
||||
}
|
||||
|
||||
func (u *userService) GetUserCurrent(ctx context.Context, userId string) (*response.UserResponse, error) {
|
||||
pgID, err := convert.StringToUUID(userId)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
user, err := u.userRepo.GetByID(ctx, pgID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||
}
|
||||
return user.ToResponse(), nil
|
||||
}
|
||||
|
||||
func (u *userService) RestoreUser(ctx context.Context, userId string) (*response.UserResponse, error) {
|
||||
pgID, err := convert.StringToUUID(userId)
|
||||
if err != nil {
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
@@ -33,6 +34,7 @@ type verificationService struct {
|
||||
userRepo repositories.UserRepository
|
||||
roleRepo repositories.RoleRepository
|
||||
c cache.Cache
|
||||
db *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewVerificationService(
|
||||
@@ -41,6 +43,7 @@ func NewVerificationService(
|
||||
userRepo repositories.UserRepository,
|
||||
roleRepo repositories.RoleRepository,
|
||||
c cache.Cache,
|
||||
db *pgxpool.Pool,
|
||||
) VerificationService {
|
||||
return &verificationService{
|
||||
verificationRepo: verificationRepo,
|
||||
@@ -48,6 +51,7 @@ func NewVerificationService(
|
||||
userRepo: userRepo,
|
||||
roleRepo: roleRepo,
|
||||
c: c,
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,6 +279,12 @@ func (v *verificationService) SearchVerification(ctx context.Context, dto *reque
|
||||
}
|
||||
|
||||
func (v *verificationService) UpdateStatusVerification(ctx context.Context, userId string, verificationId string, dto *request.UpdateVerificationStatusDto) (*response.UserVerificationResponse, error) {
|
||||
tx, err := v.db.Begin(ctx)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to start transaction")
|
||||
}
|
||||
defer tx.Rollback(ctx)
|
||||
|
||||
statusType := constants.ParseStatusTypeText(dto.Status)
|
||||
if statusType == constants.StatusTypeUnknown {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Unknown status type!")
|
||||
@@ -289,7 +299,7 @@ func (v *verificationService) UpdateStatusVerification(ctx context.Context, user
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
historianRole, err := v.roleRepo.GetByname(ctx, constants.RoleTypeHistorian.String())
|
||||
historianRole, err := v.roleRepo.GetByName(ctx, constants.RoleTypeHistorian.String())
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@@ -318,7 +328,11 @@ func (v *verificationService) UpdateStatusVerification(ctx context.Context, user
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
err = v.verificationRepo.UpdateStatus(
|
||||
vRepoTx := v.verificationRepo.WithTx(tx)
|
||||
rRepoTx := v.roleRepo.WithTx(tx)
|
||||
uRepoTx := v.userRepo.WithTx(tx)
|
||||
|
||||
err = vRepoTx.UpdateStatus(
|
||||
ctx,
|
||||
sqlc.UpdateUserVerificationStatusParams{
|
||||
ID: verificationUUID,
|
||||
@@ -354,12 +368,12 @@ func (v *verificationService) UpdateStatusVerification(ctx context.Context, user
|
||||
roleIdList = append(roleIdList, roleID)
|
||||
}
|
||||
|
||||
err = v.roleRepo.BulkDeleteRolesFromUser(ctx, userVerificationUUID)
|
||||
err = rRepoTx.BulkDeleteRolesFromUser(ctx, userVerificationUUID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
err = v.roleRepo.CreateUserRole(ctx, sqlc.CreateUserRoleParams{
|
||||
err = rRepoTx.CreateUserRole(ctx, sqlc.CreateUserRoleParams{
|
||||
UserID: userVerificationUUID,
|
||||
Column2: roleIdList,
|
||||
})
|
||||
@@ -367,7 +381,7 @@ func (v *verificationService) UpdateStatusVerification(ctx context.Context, user
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
err = v.userRepo.UpdateTokenVersion(ctx, sqlc.UpdateTokenVersionParams{
|
||||
err = uRepoTx.UpdateTokenVersion(ctx, sqlc.UpdateTokenVersionParams{
|
||||
ID: userVerificationUUID,
|
||||
TokenVersion: userVerification.TokenVersion + 1,
|
||||
})
|
||||
@@ -376,11 +390,19 @@ func (v *verificationService) UpdateStatusVerification(ctx context.Context, user
|
||||
}
|
||||
userVerification.TokenVersion += 1
|
||||
|
||||
if err := tx.Commit(ctx); err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to commit transaction")
|
||||
}
|
||||
|
||||
mapCache := map[string]any{
|
||||
fmt.Sprintf("user:email:%s", userVerification.Email): userVerification,
|
||||
fmt.Sprintf("user:id:%s", userVerification.ID): userVerification,
|
||||
}
|
||||
_ = v.c.MSet(ctx, mapCache, constants.NormalCacheDuration)
|
||||
} else {
|
||||
if err := tx.Commit(ctx); err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to commit transaction")
|
||||
}
|
||||
}
|
||||
|
||||
v.c.PublishTask(ctx, constants.StreamEmailName, constants.TaskTypeNotifyHistorianReview, data)
|
||||
|
||||
Reference in New Issue
Block a user