API REST para gestión de turnos de una barbería, construida con Spring Boot, Spring Security + JWT, Spring Data JPA e Hibernate, contenerizada con Docker.
| Tecnología | Versión |
|---|---|
| Java | 17 |
| Spring Boot | 4.0.3 |
| Spring Security | 7.x |
| JWT (jjwt) | 0.11.5 |
| MySQL | 8.4 |
| Lombok | latest |
| Docker / Docker Compose | latest |
| Springdoc OpenAPI (Swagger) | 2.8.6 |
- En producción asegurate de proveer una clave JWT (
jwt.secret) con al menos 32 bytes (256 bits). El proyecto contiene una medida temporal para tests que deriva SHA-256 del secreto cuando es muy corto, pero en producción debe usarse una clave segura y larga.
La primera vez que se levanta el sistema, se crea automáticamente un usuario administrador con las siguientes credenciales:
| Campo | Valor |
|---|---|
| Username | Admin |
admin@admin.com |
|
| Contraseña | Admin123 |
| Rol | ADMIN |
⚠️ Importante: Cambiá la contraseña del administrador en cuanto el sistema esté en producción.
Con este usuario podés:
- Crear usuarios con rol
BARBER(actualizando el rol desdePUT /api/user/{id}) - Gestionar todos los turnos y usuarios del sistema
- Acceder a todos los endpoints protegidos con rol
ADMIN
- Docker instalado y corriendo
- (Opcional para desarrollo local) JDK 17 + Maven 3.9+
git clone https://github.com/CiroDiPaolo/shift-cut.git
cd shift-cutCopiá el archivo de ejemplo y completá los valores:
cp .env.example .envEditá .env con tus propios valores:
DB_NAME=cuts
DB_USERNAME=root
DB_PASSWORD=tu_password_seguro
JWT_SECRET=tu_secreto_jwt_muy_largo_y_seguro
SERVER_PORT=8080Importante: El archivo
.env.exampleque está en el repositorio es SOLO UN EJEMPLO y NO contiene secretos reales; el archivo.envcon valores reales no debe subirse al repositorio (está incluido en.gitignore). Asegurate de no commitear.envni tus secretos.
# Primera vez o cuando haya cambios en el código
docker compose up --build
# Veces siguientes
docker compose up
# Detener los contenedores
docker compose downLa API quedará disponible en: http://localhost:8080
Este proyecto incluye documentación interactiva de la API generada automáticamente con Swagger/OpenAPI.
- Accede a la documentación en: http://localhost:8080/swagger-ui.html
- Puedes probar los endpoints directamente desde la interfaz web.
- La documentación se actualiza automáticamente con cada cambio en los controladores y modelos.
- Todos los endpoints REST principales (usuarios, turnos, autenticación).
- Modelos de datos y ejemplos de request/response.
- Códigos de respuesta y descripciones.
Nota: Los tests unitarios y de integración no aparecen en Swagger, pero garantizan que la API funciona correctamente y cumple con los requisitos de negocio.
| Método | Endpoint | Descripción |
|---|---|---|
| POST | /auth/register |
Registro de nuevo usuario |
| POST | /auth/login |
Login, devuelve JWT |
{
"username": "juanperez",
"email": "juan@example.com",
"password": "123456"
}{
"email": "juan@example.com",
"password": "123456"
}Respuesta (ejemplo):
{
"token": "eyJhbGciOiJIUzI1NiJ9..."
}| Método | Endpoint | Rol requerido | Descripción |
|---|---|---|---|
| GET | /api/user/me |
USER / ADMIN | Obtener usuario autenticado |
| GET | /api/user/{id} |
ADMIN | Obtener usuario por ID |
| GET | /api/user |
ADMIN | Listar todos los usuarios |
| GET | /api/user/email/{email} |
ADMIN | Buscar usuario por email |
| PUT | /api/user/{id} |
ADMIN o propio USER | Actualizar usuario |
| DELETE | /api/user/{id} |
ADMIN | Eliminar usuario |
{
"username": "barbero1",
"email": "barbero1@barberia.com",
"password": "nuevoPassword123",
"role": "BARBER",
"status": true
}{
"username": "juanperez",
"email": "juan_nuevo@example.com",
"password": "nuevoPassword456"
}Nota: Si se envía role o status en la petición como USER, serán ignorados por el controlador.
{
"id": 3,
"username": "juanperez",
"email": "juan_nuevo@example.com",
"role": "USER",
"status": true
}- 403 Forbidden: Si un usuario intenta modificar a otro usuario o cambiar su rol/status sin ser ADMIN.
- 409 Conflict: Si el email o username ya existen en otro usuario.
- 404 Not Found: Si el usuario no existe.
Nota: La contraseña siempre se almacena encriptada. El campo "password" es opcional en la actualización: si no se envía, no se modifica.
| Método | Endpoint | Rol requerido | Descripción |
|---|---|---|---|
| GET | /api/appointment |
ADMIN | Listar todos los turnos |
| GET | /api/appointment/{id} |
ADMIN | Obtener turno por ID |
| GET | /api/appointment/user/{userId} |
ADMIN o propio USER | Turnos de un usuario |
| POST | /api/appointment |
USER / ADMIN | Crear turno |
| PUT | /api/appointment/{id} |
ADMIN | Actualizar turno |
| DELETE | /api/appointment/{id} |
ADMIN | Eliminar turno |
| Rol | Descripción |
|---|---|
USER |
Cliente de la barbería |
ADMIN |
Administrador del sistema |
BARBER |
Barbero (extensible) |
| Enum | Descripción |
|---|---|
HAIR_CUT |
Corte de cabello |
HAIR_CUT_AND_BEARD |
Corte de cabello y barba |
- Asegurate de tener MySQL corriendo en
localhost:3306con una base de datos llamadacuts - Configurá las variables de entorno o usá los valores de fallback de
application.properties - Ejecutá:
./mvnw spring-boot:runPara ejecutar la suite de tests localmente (se puede setear JWT_SECRET temporalmente para pruebas):
$env:JWT_SECRET='tu_secreto_largo_32_plus_bytes'; .\mvnw testEn CI/producción, exportá JWT_SECRET como variable de entorno segura antes de ejecutar la aplicación.
src/main/java/com/shift_cut/
├── Config/
│ ├── Auth/ # Login, Register, AuthService, AuthController
│ └── Security/ # JWT Filter, JWT Service, Security Config
├── Control/ # Controllers REST
├── Exceptions/ # Excepciones personalizadas + GlobalExceptionHandler
├── Model/
│ ├── DTO/ # Data Transfer Objects
│ └── Enum/ # Role, ServiceType
├── Repository/ # Interfaces JPA
└── Service/ # Lógica de negocio