This commit is contained in:
@@ -1 +1,294 @@
|
||||
package services
|
||||
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/config"
|
||||
"history-api/pkg/constant"
|
||||
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type AuthService interface {
|
||||
Signin(ctx context.Context, dto *request.SignInDto) (*response.AuthResponse, error)
|
||||
Signup(ctx context.Context, dto *request.SignUpDto) (*response.AuthResponse, error)
|
||||
ForgotPassword(ctx context.Context) error
|
||||
VerifyToken(ctx context.Context) error
|
||||
CreateToken(ctx context.Context) error
|
||||
SigninWith3rd(ctx context.Context) error
|
||||
RefreshToken(ctx context.Context, id string) (*response.AuthResponse, error)
|
||||
}
|
||||
|
||||
type authService struct {
|
||||
userRepo repositories.UserRepository
|
||||
roleRepo repositories.RoleRepository
|
||||
}
|
||||
|
||||
func NewAuthService(
|
||||
userRepo repositories.UserRepository,
|
||||
roleRepo repositories.RoleRepository,
|
||||
) AuthService {
|
||||
return &authService{
|
||||
userRepo: userRepo,
|
||||
roleRepo: roleRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *authService) genToken(Uid string, role []constant.Role) (*response.AuthResponse, error) {
|
||||
jwtSecret, err := config.GetConfig("JWT_SECRET")
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "missing JWT_SECRET in environment")
|
||||
}
|
||||
jwtRefreshSecret, err := config.GetConfig("JWT_REFRESH_SECRET")
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "missing JWT_REFRESH_SECRET in environment")
|
||||
}
|
||||
|
||||
if jwtSecret == "" || jwtRefreshSecret == "" {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "missing JWT secrets in environment")
|
||||
}
|
||||
|
||||
claimsAccess := &response.JWTClaims{
|
||||
UId: Uid,
|
||||
Roles: role,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(30 * time.Minute)),
|
||||
},
|
||||
}
|
||||
|
||||
claimsRefresh := &response.JWTClaims{
|
||||
UId: Uid,
|
||||
Roles: role,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(15 * 24 * time.Hour)),
|
||||
},
|
||||
}
|
||||
|
||||
accessToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claimsAccess)
|
||||
at, err := accessToken.SignedString([]byte(jwtSecret))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
refreshToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claimsRefresh)
|
||||
rt, err := refreshToken.SignedString([]byte(jwtRefreshSecret))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := response.AuthResponse{
|
||||
AccessToken: at,
|
||||
RefreshToken: rt,
|
||||
}
|
||||
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 !constant.EMAIL_REGEX.MatchString(dto.Email) {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid email")
|
||||
}
|
||||
|
||||
err := constant.ValidatePassword(dto.Password)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
user, err := a.userRepo.GetByEmail(ctx, dto.Email)
|
||||
if err != nil || user == nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(dto.Password)); err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusUnauthorized, "Invalid identity or password!")
|
||||
}
|
||||
|
||||
data, err := a.genToken(user.ID, models.RolesEntityToRoleConstant(user.Roles))
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
|
||||
}
|
||||
var pgID pgtype.UUID
|
||||
err = pgID.Scan(user.ID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
err = a.saveNewRefreshToken(
|
||||
ctx,
|
||||
sqlc.UpdateUserRefreshTokenParams{
|
||||
ID: pgID,
|
||||
RefreshToken: pgtype.Text{
|
||||
String: data.RefreshToken,
|
||||
Valid: data.RefreshToken != "",
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return data, nil
|
||||
|
||||
}
|
||||
|
||||
func (a *authService) RefreshToken(ctx context.Context, id string) (*response.AuthResponse, error) {
|
||||
var pgID pgtype.UUID
|
||||
err := pgID.Scan(id)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
user, err := a.userRepo.GetByID(ctx, pgID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Invalid user data")
|
||||
}
|
||||
roles := models.RolesEntityToRoleConstant(user.Roles)
|
||||
|
||||
if slices.Contains(roles, constant.BANNED) {
|
||||
return nil, fiber.NewError(fiber.StatusUnauthorized, "User is banned!")
|
||||
}
|
||||
|
||||
data, err := a.genToken(id, roles)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
err = a.saveNewRefreshToken(
|
||||
ctx,
|
||||
sqlc.UpdateUserRefreshTokenParams{
|
||||
ID: pgID,
|
||||
RefreshToken: pgtype.Text{
|
||||
String: data.RefreshToken,
|
||||
Valid: data.RefreshToken != "",
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (a *authService) Signup(ctx context.Context, dto *request.SignUpDto) (*response.AuthResponse, error) {
|
||||
if !constant.EMAIL_REGEX.MatchString(dto.Email) {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid email")
|
||||
}
|
||||
err := constant.ValidatePassword(dto.Password)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
user, err := a.userRepo.GetByEmail(ctx, dto.Email)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if user != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "User already exists")
|
||||
}
|
||||
hashed, err := bcrypt.GenerateFromPassword([]byte(dto.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
user, err = a.userRepo.UpsertUser(
|
||||
ctx,
|
||||
sqlc.UpsertUserParams{
|
||||
Email: dto.Email,
|
||||
PasswordHash: pgtype.Text{
|
||||
String: string(hashed),
|
||||
Valid: len(hashed) != 0,
|
||||
},
|
||||
IsVerified: true,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
var userId pgtype.UUID
|
||||
err = userId.Scan(user.ID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
_, err = a.userRepo.CreateProfile(
|
||||
ctx,
|
||||
sqlc.CreateUserProfileParams{
|
||||
UserID: userId,
|
||||
DisplayName: pgtype.Text{
|
||||
String: dto.DisplayName,
|
||||
Valid: dto.DisplayName != "",
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
err = a.roleRepo.AddUserRole(
|
||||
ctx,
|
||||
sqlc.AddUserRoleParams{
|
||||
UserID: userId,
|
||||
Name: constant.USER.String(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
data, err := a.genToken(user.ID, constant.USER.ToSlice())
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
err = a.saveNewRefreshToken(
|
||||
ctx,
|
||||
sqlc.UpdateUserRefreshTokenParams{
|
||||
ID: userId,
|
||||
RefreshToken: pgtype.Text{
|
||||
String: data.RefreshToken,
|
||||
Valid: data.RefreshToken != "",
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// ForgotPassword implements [AuthService].
|
||||
func (a *authService) ForgotPassword(ctx context.Context) error {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// SigninWith3rd implements [AuthService].
|
||||
func (a *authService) SigninWith3rd(ctx context.Context) error {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// CreateToken implements [AuthService].
|
||||
func (a *authService) CreateToken(ctx context.Context) error {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Verify implements [AuthService].
|
||||
func (a *authService) VerifyToken(ctx context.Context) error {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
57
internal/services/tileService.go
Normal file
57
internal/services/tileService.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"history-api/internal/repositories"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
)
|
||||
|
||||
type TileService interface {
|
||||
GetMetadata(ctx context.Context) (map[string]string, error)
|
||||
GetTile(ctx context.Context, z, x, y int) ([]byte, map[string]string, error)
|
||||
}
|
||||
|
||||
type tileService struct {
|
||||
tileRepo repositories.TileRepository
|
||||
}
|
||||
|
||||
func NewTileService(
|
||||
TileRepo repositories.TileRepository,
|
||||
) TileService {
|
||||
return &tileService{
|
||||
tileRepo: TileRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tileService) GetMetadata(ctx context.Context) (map[string]string, error) {
|
||||
metaData, err := t.tileRepo.GetMetadata(ctx)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return metaData, nil
|
||||
}
|
||||
|
||||
|
||||
func (t *tileService) GetTile(ctx context.Context, z, x, y int) ([]byte, map[string]string, error) {
|
||||
contentType := make(map[string]string)
|
||||
|
||||
data, format, isPBF, err := t.tileRepo.GetTile(ctx, z, x, y)
|
||||
if err != nil {
|
||||
return nil, contentType, fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
contentType["Content-Type"] = "image/png"
|
||||
if format == "jpg" {
|
||||
contentType["Content-Type"] = "image/jpeg"
|
||||
}
|
||||
if format == "pbf" {
|
||||
contentType["Content-Type"] = "application/x-protobuf"
|
||||
}
|
||||
|
||||
if isPBF {
|
||||
contentType["Content-Encoding"] = "gzip"
|
||||
}
|
||||
|
||||
return data, contentType, nil
|
||||
|
||||
}
|
||||
83
internal/services/userService.go
Normal file
83
internal/services/userService.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"history-api/internal/dtos/request"
|
||||
"history-api/internal/dtos/response"
|
||||
"history-api/internal/repositories"
|
||||
)
|
||||
|
||||
type UserService interface {
|
||||
//user
|
||||
GetUserCurrent(ctx context.Context, dto *request.SignInDto) (*response.AuthResponse, error)
|
||||
UpdateProfile(ctx context.Context, id string) (*response.UserResponse, error)
|
||||
ChangePassword(ctx context.Context, id string) (*response.UserResponse, error)
|
||||
|
||||
//admin
|
||||
DeleteUser(ctx context.Context, id string) (*response.UserResponse, error)
|
||||
ChangeRoleUser(ctx context.Context, id string) (*response.UserResponse, error)
|
||||
RestoreUser(ctx context.Context, id string) (*response.UserResponse, error)
|
||||
GetUserByID(ctx context.Context, id string) (*response.UserResponse, error)
|
||||
Search(ctx context.Context, id string) ([]*response.UserResponse, error)
|
||||
GetAllUser(ctx context.Context, id string) ([]*response.UserResponse, error)
|
||||
}
|
||||
|
||||
type userService struct {
|
||||
userRepo repositories.UserRepository
|
||||
roleRepo repositories.RoleRepository
|
||||
}
|
||||
|
||||
func NewUserService(
|
||||
userRepo repositories.UserRepository,
|
||||
roleRepo repositories.RoleRepository,
|
||||
) UserService {
|
||||
return &userService{
|
||||
userRepo: userRepo,
|
||||
roleRepo: roleRepo,
|
||||
}
|
||||
}
|
||||
|
||||
// ChangePassword implements [UserService].
|
||||
func (u *userService) ChangePassword(ctx context.Context, id string) (*response.UserResponse, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// ChangeRoleUser implements [UserService].
|
||||
func (u *userService) ChangeRoleUser(ctx context.Context, id string) (*response.UserResponse, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// DeleteUser implements [UserService].
|
||||
func (u *userService) DeleteUser(ctx context.Context, id string) (*response.UserResponse, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// GetAllUser implements [UserService].
|
||||
func (u *userService) GetAllUser(ctx context.Context, id string) ([]*response.UserResponse, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// GetUserByID implements [UserService].
|
||||
func (u *userService) GetUserByID(ctx context.Context, id string) (*response.UserResponse, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// GetUserCurrent implements [UserService].
|
||||
func (u *userService) GetUserCurrent(ctx context.Context, dto *request.SignInDto) (*response.AuthResponse, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// RestoreUser implements [UserService].
|
||||
func (u *userService) RestoreUser(ctx context.Context, id string) (*response.UserResponse, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Search implements [UserService].
|
||||
func (u *userService) Search(ctx context.Context, id string) ([]*response.UserResponse, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// UpdateProfile implements [UserService].
|
||||
func (u *userService) UpdateProfile(ctx context.Context, id string) (*response.UserResponse, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
Reference in New Issue
Block a user