feat: implement system statistics tracking, commit management controllers, and associated database migrations
All checks were successful
Build and Release / release (push) Successful in 1m49s
All checks were successful
Build and Release / release (push) Successful in 1m49s
This commit is contained in:
@@ -22,6 +22,7 @@ type CommitService interface {
|
||||
CreateCommit(ctx context.Context, userID string, projectID string, dto *request.CreateCommitDto) (*response.CommitResponse, *fiber.Error)
|
||||
RestoreCommit(ctx context.Context, userID string, projectID string, dto *request.RestoreCommitDto) *fiber.Error
|
||||
GetProjectCommits(ctx context.Context, projectID string) ([]*response.CommitResponse, *fiber.Error)
|
||||
GetCommitByID(ctx context.Context, commitID string) (*response.CommitResponse, *fiber.Error)
|
||||
}
|
||||
|
||||
type commitService struct {
|
||||
@@ -188,3 +189,17 @@ func (s *commitService) GetProjectCommits(ctx context.Context, projectID string)
|
||||
|
||||
return models.CommitsEntityToResponse(commits), nil
|
||||
}
|
||||
|
||||
func (s *commitService) GetCommitByID(ctx context.Context, commitID string) (*response.CommitResponse, *fiber.Error) {
|
||||
commitUUID, err := convert.StringToUUID(commitID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid commit ID")
|
||||
}
|
||||
|
||||
commit, err := s.commitRepo.GetByID(ctx, commitUUID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Commit not found")
|
||||
}
|
||||
|
||||
return commit.ToResponse(), nil
|
||||
}
|
||||
|
||||
75
internal/services/statisticService.go
Normal file
75
internal/services/statisticService.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"history-api/internal/dtos/request"
|
||||
"history-api/internal/dtos/response"
|
||||
"history-api/internal/gen/sqlc"
|
||||
"history-api/internal/repositories"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
type StatisticService interface {
|
||||
Search(ctx context.Context, dto *request.SearchStatisticDto) ([]*response.StatisticResponse, *fiber.Error)
|
||||
GetByDate(ctx context.Context, dateStr string) (*response.StatisticResponse, *fiber.Error)
|
||||
}
|
||||
|
||||
type statisticService struct {
|
||||
repo repositories.StatisticRepository
|
||||
}
|
||||
|
||||
func NewStatisticService(repo repositories.StatisticRepository) StatisticService {
|
||||
return &statisticService{repo: repo}
|
||||
}
|
||||
|
||||
func (s *statisticService) Search(ctx context.Context, dto *request.SearchStatisticDto) ([]*response.StatisticResponse, *fiber.Error) {
|
||||
params := sqlc.SearchSystemStatisticsParams{}
|
||||
|
||||
if dto.StartDate != "" {
|
||||
parsedDate, err := time.Parse("2006-01-02", dto.StartDate)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid start_date format, expected YYYY-MM-DD")
|
||||
}
|
||||
params.StartDate = pgtype.Date{Time: parsedDate, Valid: true}
|
||||
}
|
||||
|
||||
if dto.EndDate != "" {
|
||||
parsedDate, err := time.Parse("2006-01-02", dto.EndDate)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid end_date format, expected YYYY-MM-DD")
|
||||
}
|
||||
params.EndDate = pgtype.Date{Time: parsedDate, Valid: true}
|
||||
}
|
||||
|
||||
stats, err := s.repo.Search(ctx, params)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to search statistics")
|
||||
}
|
||||
|
||||
responses := make([]*response.StatisticResponse, 0, len(stats))
|
||||
for _, stat := range stats {
|
||||
responses = append(responses, stat.ToResponse())
|
||||
}
|
||||
|
||||
return responses, nil
|
||||
}
|
||||
|
||||
func (s *statisticService) GetByDate(ctx context.Context, dateStr string) (*response.StatisticResponse, *fiber.Error) {
|
||||
parsedDate, err := time.Parse("2006-01-02", dateStr)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid date format, expected YYYY-MM-DD")
|
||||
}
|
||||
|
||||
stat, err := s.repo.GetByDate(ctx, parsedDate)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to get statistics")
|
||||
}
|
||||
if stat == nil {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Statistics not found for the given date")
|
||||
}
|
||||
|
||||
return stat.ToResponse(), nil
|
||||
}
|
||||
@@ -34,6 +34,7 @@ type UserService interface {
|
||||
RestoreUser(ctx context.Context, userId string) (*response.UserResponse, *fiber.Error)
|
||||
GetUserByID(ctx context.Context, userId string) (*response.UserResponse, *fiber.Error)
|
||||
SearchUser(ctx context.Context, dto *request.SearchUserDto) (*response.PaginatedResponse, *fiber.Error)
|
||||
AdminResetPassword(ctx context.Context, userId string, dto *request.ResetPasswordDto) *fiber.Error
|
||||
}
|
||||
|
||||
type userService struct {
|
||||
@@ -121,7 +122,13 @@ func (u *userService) CreateUser(ctx context.Context, dto *request.CreateUserDto
|
||||
if err := tx.Commit(ctx); err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to commit transaction")
|
||||
}
|
||||
|
||||
|
||||
_ = u.c.PublishTask(ctx, constants.StreamEmailName, constants.TaskTypeAdminUserAction, models.AdminUserActionPayload{
|
||||
Email: dto.Email,
|
||||
Password: dto.Password,
|
||||
Action: "create",
|
||||
})
|
||||
|
||||
finalUser, err := u.userRepo.GetByID(ctx, userUUID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch created user")
|
||||
@@ -189,6 +196,63 @@ func (u *userService) ChangePassword(ctx context.Context, userId string, dto *re
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *userService) AdminResetPassword(ctx context.Context, userId string, dto *request.ResetPasswordDto) *fiber.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, "Invalid user ID")
|
||||
}
|
||||
user, err := u.userRepo.GetByID(ctx, pgID)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusNotFound, "Failed to fetch user")
|
||||
}
|
||||
if user == nil {
|
||||
return fiber.NewError(fiber.StatusNotFound, "User not found")
|
||||
}
|
||||
|
||||
hashPassword, err := bcrypt.GenerateFromPassword([]byte(dto.NewPassword), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to hash new password")
|
||||
}
|
||||
|
||||
err = uRepo.UpdatePassword(ctx, sqlc.UpdateUserPasswordParams{
|
||||
ID: pgID,
|
||||
PasswordHash: pgtype.Text{String: string(hashPassword), Valid: true},
|
||||
})
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update password")
|
||||
}
|
||||
|
||||
err = uRepo.UpdateTokenVersion(ctx, sqlc.UpdateTokenVersionParams{
|
||||
ID: pgID,
|
||||
TokenVersion: user.TokenVersion + 1,
|
||||
})
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update token version")
|
||||
}
|
||||
|
||||
if err := tx.Commit(ctx); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to commit transaction")
|
||||
}
|
||||
|
||||
if dto.IsSendEmail {
|
||||
_ = u.c.PublishTask(ctx, constants.StreamEmailName, constants.TaskTypeAdminUserAction, models.AdminUserActionPayload{
|
||||
Email: user.Email,
|
||||
Password: dto.NewPassword,
|
||||
Action: "reset",
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *userService) ChangeRoleUser(ctx context.Context, userId string, claims *response.JWTClaims, dto *request.ChangeRoleDto) (*response.UserResponse, *fiber.Error) {
|
||||
tx, err := u.db.Begin(ctx)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user