ApiKeyApplicationService.java

package com.yumu.noveltranslator.application.service;

import com.yumu.noveltranslator.domain.model.ApiKey;
import com.yumu.noveltranslator.port.dto.common.PageResponse;
import com.yumu.noveltranslator.port.in.ApiKeyPort;
import com.yumu.noveltranslator.port.out.ApiKeyCachePort;
import com.yumu.noveltranslator.port.out.UserRepositoryPort;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.util.List;

@Service
@RequiredArgsConstructor
public class ApiKeyApplicationService implements ApiKeyPort {

    private final UserRepositoryPort userRepositoryPort;
    private final ApiKeyCachePort apiKeyCachePort;

    private static final SecureRandom SECURE_RANDOM = new SecureRandom();
    private static final String API_KEY_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789";

    @Override
    public ApiKey createApiKey(Long userId, String name) {
        ApiKey apiKey = new ApiKey();
        apiKey.setUserId(userId);
        apiKey.setApiKey(generateApiKey());
        apiKey.setName(name);
        apiKey.setActive(true);
        apiKey.setTotalUsage(0L);
        apiKey.setCreatedAt(LocalDateTime.now());

        userRepositoryPort.saveApiKey(apiKey);
        return apiKey;
    }

    @Override
    public PageResponse<ApiKey> listApiKeys(Long userId, int page, int pageSize) {
        List<ApiKey> allKeys = userRepositoryPort.findApiKeysByUserId(userId);
        int start = (page - 1) * pageSize;
        int end = Math.min(start + pageSize, allKeys.size());
        List<ApiKey> pagedKeys = allKeys.subList(Math.max(0, start), Math.max(0, end));
        return PageResponse.of(page, pageSize, (long) allKeys.size(), pagedKeys);
    }

    @Override
    public boolean deleteApiKey(Long id, Long userId) {
        return userRepositoryPort.findApiKeyById(id)
                .filter(key -> key.getUserId().equals(userId))
                .map(key -> {
                    apiKeyCachePort.invalidate(key.getApiKey());
                    userRepositoryPort.deleteApiKey(id);
                    return true;
                }).orElse(false);
    }

    @Override
    public ApiKey resetApiKey(Long id, Long userId) {
        return userRepositoryPort.findApiKeyById(id)
                .filter(key -> key.getUserId().equals(userId))
                .map(key -> {
                    apiKeyCachePort.invalidate(key.getApiKey());
                    key.setApiKey(generateApiKey());
                    key.setTotalUsage(0L);
                    userRepositoryPort.updateApiKey(key);
                    return key;
                }).orElse(null);
    }

    @Override
    public ApiKey revealApiKey(Long id, Long userId) {
        return userRepositoryPort.findApiKeyById(id)
                .filter(key -> key.getUserId().equals(userId))
                .orElse(null);
    }

    private String generateApiKey() {
        StringBuilder sb = new StringBuilder("nt_sk_");
        for (int i = 0; i < 32; i++) {
            sb.append(API_KEY_CHARS.charAt(SECURE_RANDOM.nextInt(API_KEY_CHARS.length())));
        }
        return sb.toString();
    }
}