fix: resolve problemas de cadastro, seletor de localização e swagger
- Corrige violação de restrição de role no Registro de Candidato (usa 'candidate' em minúsculo) - Corrige erro de chave duplicada para slug da empresa adicionando timestamp ao workspace do candidato - Corrige crash no LocationPicker tratando respostas nulas no frontend e retornando arrays vazios no backend - Corrige documentação do Swagger para o endpoint de Login e adiciona definição de segurança BearerAuth
This commit is contained in:
parent
1b9bd81687
commit
1f9b54d719
8 changed files with 355 additions and 164 deletions
|
|
@ -19,6 +19,9 @@ import (
|
|||
// @version 1.0
|
||||
// @description API for GoHorseJobs recruitment platform.
|
||||
// @BasePath /
|
||||
// @securityDefinitions.apikey BearerAuth
|
||||
// @in header
|
||||
// @name Authorization
|
||||
func main() {
|
||||
// Load .env file
|
||||
if err := godotenv.Load(); err != nil {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,41 @@ const docTemplate = `{
|
|||
"host": "{{.Host}}",
|
||||
"basePath": "{{.BasePath}}",
|
||||
"paths": {
|
||||
"/admin/storage/test-connection": {
|
||||
"post": {
|
||||
"description": "Checks if the current configuration (DB or Env) allows access to the S3 bucket.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"admin"
|
||||
],
|
||||
"summary": "Test Object Storage Connection",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/applications": {
|
||||
"get": {
|
||||
"description": "List all applications for a job",
|
||||
|
|
@ -262,7 +297,7 @@ const docTemplate = `{
|
|||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "object"
|
||||
"$ref": "#/definitions/github_com_rede5_gohorsejobs_backend_internal_core_dto.LoginRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
@ -288,6 +323,23 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/auth/logout": {
|
||||
"post": {
|
||||
"description": "Clears the httpOnly JWT cookie, effectively logging the user out.",
|
||||
"tags": [
|
||||
"Auth"
|
||||
],
|
||||
"summary": "User Logout",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/companies": {
|
||||
"get": {
|
||||
"description": "Returns a list of all companies.",
|
||||
|
|
@ -1636,63 +1688,6 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/system/credentials": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Saves encrypted credentials payload (e.g. Stripe key encrypted by Backoffice)",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"System"
|
||||
],
|
||||
"summary": "Save Credentials",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Credentials Payload",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid Request",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/tokens": {
|
||||
"post": {
|
||||
"security": [
|
||||
|
|
@ -1954,6 +1949,63 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/users/me/password": {
|
||||
"patch": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Updates the current user's password.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"summary": "Update My Password",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Password Details",
|
||||
"name": "password",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "No Content",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid Request",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/users/me/profile": {
|
||||
"patch": {
|
||||
"security": [
|
||||
|
|
@ -2112,6 +2164,17 @@ const docTemplate = `{
|
|||
}
|
||||
},
|
||||
"definitions": {
|
||||
"github_com_rede5_gohorsejobs_backend_internal_core_dto.LoginRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"github_com_rede5_gohorsejobs_backend_internal_core_dto.SaveFCMTokenRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -2766,6 +2829,13 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
"BearerAuth": {
|
||||
"type": "apiKey",
|
||||
"name": "Authorization",
|
||||
"in": "header"
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,41 @@
|
|||
},
|
||||
"basePath": "/",
|
||||
"paths": {
|
||||
"/admin/storage/test-connection": {
|
||||
"post": {
|
||||
"description": "Checks if the current configuration (DB or Env) allows access to the S3 bucket.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"admin"
|
||||
],
|
||||
"summary": "Test Object Storage Connection",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/applications": {
|
||||
"get": {
|
||||
"description": "List all applications for a job",
|
||||
|
|
@ -255,7 +290,7 @@
|
|||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "object"
|
||||
"$ref": "#/definitions/github_com_rede5_gohorsejobs_backend_internal_core_dto.LoginRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
@ -281,6 +316,23 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/auth/logout": {
|
||||
"post": {
|
||||
"description": "Clears the httpOnly JWT cookie, effectively logging the user out.",
|
||||
"tags": [
|
||||
"Auth"
|
||||
],
|
||||
"summary": "User Logout",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/companies": {
|
||||
"get": {
|
||||
"description": "Returns a list of all companies.",
|
||||
|
|
@ -1629,63 +1681,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/system/credentials": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Saves encrypted credentials payload (e.g. Stripe key encrypted by Backoffice)",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"System"
|
||||
],
|
||||
"summary": "Save Credentials",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Credentials Payload",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid Request",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/tokens": {
|
||||
"post": {
|
||||
"security": [
|
||||
|
|
@ -1947,6 +1942,63 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/users/me/password": {
|
||||
"patch": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Updates the current user's password.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"summary": "Update My Password",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Password Details",
|
||||
"name": "password",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "No Content",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid Request",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/users/me/profile": {
|
||||
"patch": {
|
||||
"security": [
|
||||
|
|
@ -2105,6 +2157,17 @@
|
|||
}
|
||||
},
|
||||
"definitions": {
|
||||
"github_com_rede5_gohorsejobs_backend_internal_core_dto.LoginRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"github_com_rede5_gohorsejobs_backend_internal_core_dto.SaveFCMTokenRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -2759,5 +2822,12 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
"BearerAuth": {
|
||||
"type": "apiKey",
|
||||
"name": "Authorization",
|
||||
"in": "header"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,12 @@
|
|||
basePath: /
|
||||
definitions:
|
||||
github_com_rede5_gohorsejobs_backend_internal_core_dto.LoginRequest:
|
||||
properties:
|
||||
email:
|
||||
type: string
|
||||
password:
|
||||
type: string
|
||||
type: object
|
||||
github_com_rede5_gohorsejobs_backend_internal_core_dto.SaveFCMTokenRequest:
|
||||
properties:
|
||||
platform:
|
||||
|
|
@ -468,6 +475,30 @@ info:
|
|||
title: GoHorseJobs API
|
||||
version: "1.0"
|
||||
paths:
|
||||
/admin/storage/test-connection:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Checks if the current configuration (DB or Env) allows access to
|
||||
the S3 bucket.
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
summary: Test Object Storage Connection
|
||||
tags:
|
||||
- admin
|
||||
/api/v1/applications:
|
||||
get:
|
||||
consumes:
|
||||
|
|
@ -630,7 +661,7 @@ paths:
|
|||
name: login
|
||||
required: true
|
||||
schema:
|
||||
type: object
|
||||
$ref: '#/definitions/github_com_rede5_gohorsejobs_backend_internal_core_dto.LoginRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
|
|
@ -649,6 +680,17 @@ paths:
|
|||
summary: User Login
|
||||
tags:
|
||||
- Auth
|
||||
/api/v1/auth/logout:
|
||||
post:
|
||||
description: Clears the httpOnly JWT cookie, effectively logging the user out.
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
type: object
|
||||
summary: User Logout
|
||||
tags:
|
||||
- Auth
|
||||
/api/v1/companies:
|
||||
get:
|
||||
consumes:
|
||||
|
|
@ -1512,43 +1554,6 @@ paths:
|
|||
summary: List All Tickets (Admin)
|
||||
tags:
|
||||
- Support
|
||||
/api/v1/system/credentials:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Saves encrypted credentials payload (e.g. Stripe key encrypted
|
||||
by Backoffice)
|
||||
parameters:
|
||||
- description: Credentials Payload
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
"400":
|
||||
description: Invalid Request
|
||||
schema:
|
||||
type: string
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
type: string
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Save Credentials
|
||||
tags:
|
||||
- System
|
||||
/api/v1/tokens:
|
||||
post:
|
||||
consumes:
|
||||
|
|
@ -1783,6 +1788,42 @@ paths:
|
|||
summary: Upload Avatar
|
||||
tags:
|
||||
- Users
|
||||
/api/v1/users/me/password:
|
||||
patch:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Updates the current user's password.
|
||||
parameters:
|
||||
- description: Password Details
|
||||
in: body
|
||||
name: password
|
||||
required: true
|
||||
schema:
|
||||
type: object
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"204":
|
||||
description: No Content
|
||||
schema:
|
||||
type: string
|
||||
"400":
|
||||
description: Invalid Request
|
||||
schema:
|
||||
type: string
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
type: string
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
type: string
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Update My Password
|
||||
tags:
|
||||
- Users
|
||||
/api/v1/users/me/profile:
|
||||
patch:
|
||||
consumes:
|
||||
|
|
@ -1815,4 +1856,9 @@ paths:
|
|||
summary: Update My Profile
|
||||
tags:
|
||||
- Users
|
||||
securityDefinitions:
|
||||
BearerAuth:
|
||||
in: header
|
||||
name: Authorization
|
||||
type: apiKey
|
||||
swagger: "2.0"
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ func NewCoreHandlers(l *auth.LoginUseCase, reg *auth.RegisterCandidateUseCase, c
|
|||
// @Tags Auth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param login body object true "Login Credentials"
|
||||
// @Param login body dto.LoginRequest true "Login Credentials"
|
||||
// @Success 200 {object} object
|
||||
// @Failure 400 {string} string "Invalid Request"
|
||||
// @Failure 401 {string} string "Unauthorized"
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/rede5/gohorsejobs/backend/internal/core/domain/entity"
|
||||
"github.com/rede5/gohorsejobs/backend/internal/core/dto"
|
||||
|
|
@ -44,7 +45,7 @@ func (uc *RegisterCandidateUseCase) Execute(ctx context.Context, input dto.Regis
|
|||
// NOTE: ID is empty - will be auto-generated by DB (SERIAL)
|
||||
candidateCompany := entity.NewCompany(
|
||||
"", // DB generates SERIAL id
|
||||
fmt.Sprintf("Candidate - %s", input.Name),
|
||||
fmt.Sprintf("Candidate - %s (%d)", input.Name, time.Now().Unix()),
|
||||
nil, // No document for candidates
|
||||
nil, // No contact - will use user's contact info
|
||||
)
|
||||
|
|
@ -65,7 +66,7 @@ func (uc *RegisterCandidateUseCase) Execute(ctx context.Context, input dto.Regis
|
|||
}
|
||||
|
||||
// Assign Role
|
||||
user.AssignRole(entity.Role{Name: "CANDIDATE"})
|
||||
user.AssignRole(entity.Role{Name: entity.RoleCandidate})
|
||||
|
||||
saved, err := uc.userRepo.Save(ctx, user)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ func (r *LocationRepository) ListCountries(ctx context.Context) ([]*entity.Count
|
|||
}
|
||||
defer rows.Close()
|
||||
|
||||
var countries []*entity.Country
|
||||
countries := []*entity.Country{}
|
||||
for rows.Next() {
|
||||
c := &entity.Country{}
|
||||
// Scan columns. Be careful with NULL handling if fields are nullable, but schema says NOT NULL mostly.
|
||||
|
|
@ -61,7 +61,7 @@ func (r *LocationRepository) ListStates(ctx context.Context, countryID int64) ([
|
|||
}
|
||||
defer rows.Close()
|
||||
|
||||
var states []*entity.State
|
||||
states := []*entity.State{}
|
||||
for rows.Next() {
|
||||
s := &entity.State{}
|
||||
var iso2, typeStr sql.NullString
|
||||
|
|
@ -88,7 +88,7 @@ func (r *LocationRepository) ListCities(ctx context.Context, stateID int64) ([]*
|
|||
}
|
||||
defer rows.Close()
|
||||
|
||||
var cities []*entity.City
|
||||
cities := []*entity.City{}
|
||||
for rows.Next() {
|
||||
c := &entity.City{}
|
||||
// schema: latitude NOT NULL, longitude NOT NULL.
|
||||
|
|
@ -123,7 +123,7 @@ func (r *LocationRepository) Search(ctx context.Context, query string, countryID
|
|||
}
|
||||
defer rows.Close()
|
||||
|
||||
var results []*entity.LocationSearchResult
|
||||
results := []*entity.LocationSearchResult{}
|
||||
for rows.Next() {
|
||||
res := &entity.LocationSearchResult{}
|
||||
var stateID sql.NullInt64
|
||||
|
|
|
|||
|
|
@ -27,9 +27,10 @@ export function LocationPicker({ value, onChange }: LocationPickerProps) {
|
|||
// Initial Load
|
||||
useEffect(() => {
|
||||
locationsApi.listCountries().then(res => {
|
||||
setCountries(res);
|
||||
const data = res || [];
|
||||
setCountries(data);
|
||||
// Default to Brazil if available (User is Brazilian based on context)
|
||||
const br = res.find(c => c.iso2 === "BR");
|
||||
const br = data.find(c => c.iso2 === "BR");
|
||||
if (br) setSelectedCountry(br.id.toString());
|
||||
}).catch(console.error);
|
||||
}, []);
|
||||
|
|
@ -156,7 +157,7 @@ export function LocationPicker({ value, onChange }: LocationPickerProps) {
|
|||
<SelectValue placeholder="Selecione" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{countries.map((c) => (
|
||||
{Array.isArray(countries) && countries.map((c) => (
|
||||
<SelectItem key={c.id} value={c.id.toString()}>
|
||||
<span className="flex items-center gap-2">
|
||||
<span>{c.emoji}</span>
|
||||
|
|
|
|||
Loading…
Reference in a new issue