This commit is contained in:
NathanErvin 2025-12-18 15:47:57 +00:00 committed by GitHub
commit c93774b02f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 108 additions and 0 deletions

View File

@ -50,6 +50,7 @@ For full installation options (including desktop and Kubernetes), see our [Docum
- [**Homepage**](https://stirling.com)
- [**API Docs**](https://registry.scalar.com/@stirlingpdf/apis/stirling-pdf-processing-api/)
- [**Server Plan & Enterprise**](https://docs.stirlingpdf.com/Paid-Offerings)
- [**Docker Guide**](https://docs.stirlingpdf.com/Installation/Docker%20Guide/)
## Support

View File

@ -0,0 +1,41 @@
package stirling.software.SPDF.controller.api.misc;
import lombok.RequiredArgsConstructor;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import stirling.software.SPDF.service.ScalePagesService;
import stirling.software.SPDF.service.UsageMetricsService;
import java.io.IOException;
import java.security.Principal;
@RestController
@RequestMapping("/api/v1/misc")
@RequiredArgsConstructor
public class ScalePagesController {
private final ScalePagesService scalePagesService;
private final UsageMetricsService usageMetricsService;
@PostMapping("/scale-pages")
public ResponseEntity<byte[]> scalePages(
@RequestPart("file") MultipartFile file,
@RequestParam(defaultValue = "A4") String targetSize,
@RequestParam(defaultValue = "true") boolean keepAspectRatio,
Principal principal) throws IOException {
usageMetricsService.recordUsage(
"scale-pages",
principal != null ? principal.getName() : null
);
byte[] output = scalePagesService.scalePages(
file.getBytes(), targetSize, keepAspectRatio);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=scaled.pdf")
.contentType(MediaType.APPLICATION_PDF)
.body(output);
}
}

View File

@ -0,0 +1,66 @@
package stirling.software.SPDF.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.LongAdder;
import java.util.stream.Collectors;
@Slf4j
@Service
public class UsageMetricsService {
private final Map<String, LongAdder> globalCounts = new ConcurrentHashMap<>();
// toolName -> userName -> count
private final Map<String, Map<String, LongAdder>> perUserCounts = new ConcurrentHashMap<>();
private final ObjectMapper mapper = new ObjectMapper();
private final File statsFile = new File("configs/usage-stats.json");
public void recordUsage(String toolName, String username) {
// global
globalCounts.computeIfAbsent(toolName, k -> new LongAdder()).increment();
// peruser
if (username != null && !username.isBlank()) {
perUserCounts
.computeIfAbsent(toolName, k -> new ConcurrentHashMap<>())
.computeIfAbsent(username, k -> new LongAdder())
.increment();
}
}
// optional getters if you want to expose stats later
public Map<String, Long> getGlobalCounts() {
return globalCounts.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().sum()));
}
@Scheduled(fixedDelay = 60_000)
public void persistStats() {
try {
Map<String, Object> dto = Map.of(
"global", getGlobalCounts(),
"perUser", perUserCounts.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> e.getValue().entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey, x -> x.getValue().sum()
))
))
);
statsFile.getParentFile().mkdirs();
mapper.writerWithDefaultPrettyPrinter().writeValue(statsFile, dto);
} catch (IOException e) {
log.warn("Failed to persist usage stats", e);
}
}
}