Merge pull request #13 from rede5/back-task-5
feat(auth): melhora registro e login com vinculo profissional e status ativo aprimora o fluxo de autenticação, permitindo que o processo de registro já capture dados básicos do profissional (Nome, Telefone) e vincule automaticamente a um perfil na tabela cadastro_profissionais. Também implementa a política de segurança onde novos usuários nascem Inativos por padrão. Principais Mudanças: Registro (/auth/register): Novos campos obrigatórios/opcionais: nome, telefone. Vínculo Automático: Cria registro na tabela usuarios e cadastro_profissionais numa única transação lógica. Default Inativo: Usuários agora são criados com ativo = false (alterado na query e no schema), exigindo aprovação posterior. Login (/auth/login): Separação da strutura de Request ( loginRequest vs registerRequest ) para evitar erros de validação. Resposta agora inclui o status ativo: boolean para que o frontend possa tratar usuários pendentes. Database: Ajuste na constraint default da coluna ativo em usuarios. Impacto: O frontend agora deve tratar o caso de ativo: false no login (ex: mostrar mensagem "Aguardando aprovação") e enviar nome/telefone no registro.
This commit is contained in:
commit
7a300de997
8 changed files with 125 additions and 32 deletions
|
|
@ -1587,7 +1587,7 @@ const docTemplate = `{
|
|||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/auth.authRequest"
|
||||
"$ref": "#/definitions/auth.loginRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
@ -1700,7 +1700,7 @@ const docTemplate = `{
|
|||
},
|
||||
"/auth/register": {
|
||||
"post": {
|
||||
"description": "Register a new user (defaults to 'profissional' role) with email and password",
|
||||
"description": "Register a new user (defaults to 'profissional' role) with email, password, name and phone",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
|
|
@ -1718,7 +1718,7 @@ const docTemplate = `{
|
|||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/auth.authRequest"
|
||||
"$ref": "#/definitions/auth.registerRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
@ -1778,7 +1778,7 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"auth.authRequest": {
|
||||
"auth.loginRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"email",
|
||||
|
|
@ -1809,9 +1809,35 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"auth.registerRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"email",
|
||||
"nome",
|
||||
"senha"
|
||||
],
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"nome": {
|
||||
"type": "string"
|
||||
},
|
||||
"senha": {
|
||||
"type": "string",
|
||||
"minLength": 6
|
||||
},
|
||||
"telefone": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"auth.userResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ativo": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1581,7 +1581,7 @@
|
|||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/auth.authRequest"
|
||||
"$ref": "#/definitions/auth.loginRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
@ -1694,7 +1694,7 @@
|
|||
},
|
||||
"/auth/register": {
|
||||
"post": {
|
||||
"description": "Register a new user (defaults to 'profissional' role) with email and password",
|
||||
"description": "Register a new user (defaults to 'profissional' role) with email, password, name and phone",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
|
|
@ -1712,7 +1712,7 @@
|
|||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/auth.authRequest"
|
||||
"$ref": "#/definitions/auth.registerRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
@ -1772,7 +1772,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"auth.authRequest": {
|
||||
"auth.loginRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"email",
|
||||
|
|
@ -1803,9 +1803,35 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"auth.registerRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"email",
|
||||
"nome",
|
||||
"senha"
|
||||
],
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"nome": {
|
||||
"type": "string"
|
||||
},
|
||||
"senha": {
|
||||
"type": "string",
|
||||
"minLength": 6
|
||||
},
|
||||
"telefone": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"auth.userResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ativo": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ definitions:
|
|||
required:
|
||||
- ano_semestre
|
||||
type: object
|
||||
auth.authRequest:
|
||||
auth.loginRequest:
|
||||
properties:
|
||||
email:
|
||||
type: string
|
||||
|
|
@ -36,8 +36,26 @@ definitions:
|
|||
user:
|
||||
$ref: '#/definitions/auth.userResponse'
|
||||
type: object
|
||||
auth.registerRequest:
|
||||
properties:
|
||||
email:
|
||||
type: string
|
||||
nome:
|
||||
type: string
|
||||
senha:
|
||||
minLength: 6
|
||||
type: string
|
||||
telefone:
|
||||
type: string
|
||||
required:
|
||||
- email
|
||||
- nome
|
||||
- senha
|
||||
type: object
|
||||
auth.userResponse:
|
||||
properties:
|
||||
ativo:
|
||||
type: boolean
|
||||
email:
|
||||
type: string
|
||||
id:
|
||||
|
|
@ -1338,7 +1356,7 @@ paths:
|
|||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/auth.authRequest'
|
||||
$ref: '#/definitions/auth.loginRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
|
|
@ -1416,15 +1434,15 @@ paths:
|
|||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Register a new user (defaults to 'profissional' role) with email
|
||||
and password
|
||||
description: Register a new user (defaults to 'profissional' role) with email,
|
||||
password, name and phone
|
||||
parameters:
|
||||
- description: Register Request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/auth.authRequest'
|
||||
$ref: '#/definitions/auth.registerRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
|
|
|
|||
|
|
@ -18,32 +18,40 @@ func NewHandler(service *Service) *Handler {
|
|||
return &Handler{service: service}
|
||||
}
|
||||
|
||||
type authRequest struct {
|
||||
Email string `json:"email" binding:"required,email"`
|
||||
Senha string `json:"senha" binding:"required,min=6"`
|
||||
type registerRequest struct {
|
||||
Email string `json:"email" binding:"required,email"`
|
||||
Senha string `json:"senha" binding:"required,min=6"`
|
||||
Nome string `json:"nome" binding:"required"`
|
||||
Telefone string `json:"telefone"`
|
||||
}
|
||||
|
||||
// Register godoc
|
||||
// @Summary Register a new user
|
||||
// @Description Register a new user (defaults to 'profissional' role) with email and password
|
||||
// @Description Register a new user (defaults to 'profissional' role) with email, password, name and phone
|
||||
// @Tags auth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body authRequest true "Register Request"
|
||||
// @Param request body registerRequest true "Register Request"
|
||||
// @Success 201 {object} map[string]string
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Failure 500 {object} map[string]string
|
||||
// @Router /auth/register [post]
|
||||
func (h *Handler) Register(c *gin.Context) {
|
||||
var req authRequest
|
||||
var req registerRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Default simplified registration: Role="profissional", No professional data yet
|
||||
// Default simplified registration: Role="profissional"
|
||||
role := "profissional"
|
||||
var profData *profissionais.CreateProfissionalInput = nil
|
||||
|
||||
// Create professional data from input
|
||||
profData := &profissionais.CreateProfissionalInput{
|
||||
Nome: req.Nome,
|
||||
Whatsapp: &req.Telefone, // Map Telefone to Whatsapp
|
||||
// FuncaoProfissionalID is left empty intentionally
|
||||
}
|
||||
|
||||
_, err := h.service.Register(c.Request.Context(), req.Email, req.Senha, role, profData)
|
||||
if err != nil {
|
||||
|
|
@ -58,6 +66,11 @@ func (h *Handler) Register(c *gin.Context) {
|
|||
c.JSON(http.StatusCreated, gin.H{"message": "user created"})
|
||||
}
|
||||
|
||||
type loginRequest struct {
|
||||
Email string `json:"email" binding:"required,email"`
|
||||
Senha string `json:"senha" binding:"required,min=6"`
|
||||
}
|
||||
|
||||
type loginResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
ExpiresAt string `json:"expires_at"`
|
||||
|
|
@ -69,6 +82,7 @@ type userResponse struct {
|
|||
ID string `json:"id"`
|
||||
Email string `json:"email"`
|
||||
Role string `json:"role"`
|
||||
Ativo bool `json:"ativo"`
|
||||
}
|
||||
|
||||
// Login godoc
|
||||
|
|
@ -77,13 +91,13 @@ type userResponse struct {
|
|||
// @Tags auth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body authRequest true "Login Request"
|
||||
// @Param request body loginRequest true "Login Request"
|
||||
// @Success 200 {object} loginResponse
|
||||
// @Failure 401 {object} map[string]string
|
||||
// @Failure 500 {object} map[string]string
|
||||
// @Router /auth/login [post]
|
||||
func (h *Handler) Login(c *gin.Context) {
|
||||
var req authRequest
|
||||
var req loginRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
|
|
@ -112,6 +126,7 @@ func (h *Handler) Login(c *gin.Context) {
|
|||
ID: uuid.UUID(user.ID.Bytes).String(),
|
||||
Email: user.Email,
|
||||
Role: user.Role,
|
||||
Ativo: user.Ativo,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ import (
|
|||
)
|
||||
|
||||
const createUsuario = `-- name: CreateUsuario :one
|
||||
INSERT INTO usuarios (email, senha_hash, role)
|
||||
VALUES ($1, $2, $3)
|
||||
INSERT INTO usuarios (email, senha_hash, role, ativo)
|
||||
VALUES ($1, $2, $3, false)
|
||||
RETURNING id, email, senha_hash, role, ativo, criado_em, atualizado_em
|
||||
`
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
-- name: CreateUsuario :one
|
||||
INSERT INTO usuarios (email, senha_hash, role)
|
||||
VALUES ($1, $2, $3)
|
||||
INSERT INTO usuarios (email, senha_hash, role, ativo)
|
||||
VALUES ($1, $2, $3, false)
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetUsuarioByEmail :one
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ CREATE TABLE IF NOT EXISTS usuarios (
|
|||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
senha_hash VARCHAR(255) NOT NULL,
|
||||
role VARCHAR(50) NOT NULL DEFAULT 'profissional',
|
||||
ativo BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
ativo BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
criado_em TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
atualizado_em TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
|
|
|||
|
|
@ -50,15 +50,23 @@ func (s *Service) Create(ctx context.Context, userID string, input CreateProfiss
|
|||
return nil, errors.New("invalid usuario_id from context")
|
||||
}
|
||||
|
||||
funcaoUUID, err := uuid.Parse(input.FuncaoProfissionalID)
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid funcao_profissional_id")
|
||||
var funcaoUUID uuid.UUID
|
||||
var funcaoValid bool
|
||||
if input.FuncaoProfissionalID != "" {
|
||||
parsed, err := uuid.Parse(input.FuncaoProfissionalID)
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid funcao_profissional_id")
|
||||
}
|
||||
funcaoUUID = parsed
|
||||
funcaoValid = true
|
||||
} else {
|
||||
funcaoValid = false
|
||||
}
|
||||
|
||||
params := generated.CreateProfissionalParams{
|
||||
UsuarioID: pgtype.UUID{Bytes: usuarioUUID, Valid: true},
|
||||
Nome: input.Nome,
|
||||
FuncaoProfissionalID: pgtype.UUID{Bytes: funcaoUUID, Valid: true},
|
||||
FuncaoProfissionalID: pgtype.UUID{Bytes: funcaoUUID, Valid: funcaoValid},
|
||||
Endereco: toPgText(input.Endereco),
|
||||
Cidade: toPgText(input.Cidade),
|
||||
Uf: toPgText(input.Uf),
|
||||
|
|
|
|||
Loading…
Reference in a new issue