package research.loghunter.service; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import research.loghunter.dto.PatternDto; import research.loghunter.entity.Pattern; import research.loghunter.repository.PatternRepository; import java.util.List; import java.util.regex.PatternSyntaxException; import java.util.stream.Collectors; @Service @RequiredArgsConstructor @Transactional(readOnly = true) public class PatternService { private final PatternRepository patternRepository; public List findAll() { return patternRepository.findAll().stream() .map(this::toDto) .collect(Collectors.toList()); } public List findAllActive() { return patternRepository.findByActiveTrueOrderBySeverityAscNameAsc().stream() .map(this::toDto) .collect(Collectors.toList()); } public PatternDto findById(Long id) { return patternRepository.findById(id) .map(this::toDto) .orElseThrow(() -> new RuntimeException("Pattern not found: " + id)); } @Transactional public PatternDto create(PatternDto dto) { validateRegex(dto.getRegex()); if (dto.getExcludeRegex() != null && !dto.getExcludeRegex().isBlank()) { validateRegex(dto.getExcludeRegex()); } Pattern pattern = Pattern.builder() .name(dto.getName()) .regex(dto.getRegex()) .excludeRegex(dto.getExcludeRegex()) .severity(dto.getSeverity() != null ? dto.getSeverity() : "ERROR") .contextLines(dto.getContextLines() != null ? dto.getContextLines() : 5) .description(dto.getDescription()) .active(dto.getActive() != null ? dto.getActive() : true) .build(); pattern = patternRepository.save(pattern); return toDto(pattern); } @Transactional public PatternDto update(Long id, PatternDto dto) { validateRegex(dto.getRegex()); if (dto.getExcludeRegex() != null && !dto.getExcludeRegex().isBlank()) { validateRegex(dto.getExcludeRegex()); } Pattern pattern = patternRepository.findById(id) .orElseThrow(() -> new RuntimeException("Pattern not found: " + id)); pattern.setName(dto.getName()); pattern.setRegex(dto.getRegex()); pattern.setExcludeRegex(dto.getExcludeRegex()); pattern.setSeverity(dto.getSeverity()); pattern.setContextLines(dto.getContextLines()); pattern.setDescription(dto.getDescription()); pattern.setActive(dto.getActive()); return toDto(patternRepository.save(pattern)); } @Transactional public void delete(Long id) { patternRepository.deleteById(id); } /** * 정규식 패턴 테스트 */ public PatternTestResult testPattern(String regex, String sampleText) { try { java.util.regex.Pattern compiledPattern = java.util.regex.Pattern.compile(regex); java.util.regex.Matcher matcher = compiledPattern.matcher(sampleText); boolean found = matcher.find(); String matchedText = found ? matcher.group() : null; int matchStart = found ? matcher.start() : -1; int matchEnd = found ? matcher.end() : -1; return new PatternTestResult(true, found, matchedText, matchStart, matchEnd, null); } catch (PatternSyntaxException e) { return new PatternTestResult(false, false, null, -1, -1, e.getMessage()); } } private void validateRegex(String regex) { try { java.util.regex.Pattern.compile(regex); } catch (PatternSyntaxException e) { throw new RuntimeException("Invalid regex pattern: " + e.getMessage()); } } private PatternDto toDto(Pattern pattern) { return PatternDto.builder() .id(pattern.getId()) .name(pattern.getName()) .regex(pattern.getRegex()) .excludeRegex(pattern.getExcludeRegex()) .severity(pattern.getSeverity()) .contextLines(pattern.getContextLines()) .description(pattern.getDescription()) .active(pattern.getActive()) .createdAt(pattern.getCreatedAt()) .build(); } public record PatternTestResult( boolean validRegex, boolean matched, String matchedText, int matchStart, int matchEnd, String errorMessage ) {} }