package ru.soune.nocopy.controller.demo; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import ru.soune.nocopy.dto.demo.*; import ru.soune.nocopy.service.demo.DemoSessionService; @Slf4j @RestController @RequestMapping("/api/demo") @RequiredArgsConstructor public class DemoController { private final DemoSessionService demoSessionService; private final HttpServletRequest httpServletRequest; /** * Загрузка файла для демонстрации * Требуется файл в формате multipart/form-data * * @param file файл для загрузки * @return DemoUploadResponse с информацией о сессии */ @PostMapping("/upload") public ResponseEntity uploadFile(@RequestParam("file") MultipartFile file) { try { if (file.isEmpty()) { return ResponseEntity.badRequest() .body(new DemoUploadResponse( null, null, null, 0L, "FAILED", "File is empty", 0L, 0 )); } String clientIp = getClientIp(); log.info("Demo upload request from IP: {} for file: {}", clientIp, file.getOriginalFilename()); DemoUploadResponse response = demoSessionService.uploadFile(file, clientIp); return ResponseEntity.ok(response); } catch (RuntimeException e) { log.warn("Demo upload validation failed: {}", e.getMessage()); return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body(new DemoUploadResponse( null, null, null, 0L, "FAILED", e.getMessage(), 0L, 0 )); } catch (Exception e) { log.error("Error during demo upload", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(new DemoUploadResponse( null, null, null, 0L, "FAILED", "Upload failed: " + e.getMessage(), 0L, 0 )); } } /** * Получение информации о загруженном файле в демо-сессии * * @param sessionId ID демо-сессии * @return DemoSessionInfo с деталями файла */ @GetMapping("/file/{sessionId}") public ResponseEntity getFileInfo(@PathVariable String sessionId) { try { String clientIp = getClientIp(); log.info("Getting file info for session: {} from IP: {}", sessionId, clientIp); DemoSessionInfo info = demoSessionService.getSessionInfo(sessionId, clientIp); return ResponseEntity.ok(info); } catch (RuntimeException e) { log.warn("Error getting session info: {}", e.getMessage()); return ResponseEntity.status(HttpStatus.NOT_FOUND) .body(new DemoSessionInfo( null, null, null, 0L, "ERROR", null, null, 0, null )); } } /** * Запуск поиска похожих файлов для демо * Асинхронная операция - результаты могут загружаться в реальном времени * * @param sessionId ID демо-сессии * @return DemoSearchResponse с результатами поиска */ @PostMapping("/search/{sessionId}") public ResponseEntity startSearch(@PathVariable String sessionId) { try { String clientIp = getClientIp(); log.info("Starting demo search for session: {} from IP: {}", sessionId, clientIp); DemoSearchResponse response = demoSessionService.startSearch(sessionId, clientIp); return ResponseEntity.ok(response); } catch (RuntimeException e) { log.warn("Error starting demo search: {}", e.getMessage()); return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body(new DemoSearchResponse( sessionId, "FAILED", 0, 0.0, null, null, 0L, null, e.getMessage() )); } } /** * Очистка демо-сессии и удаление файлов * Должна быть вызвана пользователем или автоматически при закрытии демо * * @param sessionId ID демо-сессии * @return успешное подтверждение удаления */ @DeleteMapping("/cleanup/{sessionId}") public ResponseEntity cleanup(@PathVariable String sessionId) { try { String clientIp = getClientIp(); log.info("Demo cleanup request for session: {} from IP: {}", sessionId, clientIp); demoSessionService.deleteSession(sessionId, clientIp); return ResponseEntity.ok(new DemoUploadResponse( sessionId, null, null, 0L, "CLEANED", "Session cleaned successfully", 0L, 100 )); } catch (RuntimeException e) { log.warn("Error cleaning up session: {}", e.getMessage()); return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body(new DemoUploadResponse( sessionId, null, null, 0L, "ERROR", e.getMessage(), 0L, 0 )); } } /** * Health check эндпоинт для демо API * Проверяет доступность и готовность к приёму файлов */ @GetMapping("/health") public ResponseEntity health() { return ResponseEntity.ok(new DemoUploadResponse( null, null, null, 0L, "HEALTHY", "Demo service is ready", 0L, 100 )); } /** * Извлечение IP адреса клиента из запроса * Учитывает различные прокси и заголовки */ private String getClientIp() { String xForwardedFor = httpServletRequest.getHeader("X-Forwarded-For"); if (xForwardedFor != null && !xForwardedFor.isEmpty()) { return xForwardedFor.split(",")[0].trim(); } String xRealIp = httpServletRequest.getHeader("X-Real-IP"); if (xRealIp != null && !xRealIp.isEmpty()) { return xRealIp; } return httpServletRequest.getRemoteAddr(); } }