Migrate backend from Python to Java Spring Boot

- Full Java 21 + Spring Boot 3.3 backend with Virtual Threads
- HikariCP connection pool for Oracle ADB
- JWT auth, Redis caching, OCI GenAI integration
- YouTube transcript extraction via API + Playwright browser fallback
- SSE streaming for bulk operations
- Scheduled daemon for channel scanning/video processing
- Mobile UI: collapse restaurant list to single row on selection
- Switch PM2 ecosystem config to Java backend

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
joungmin
2026-03-09 20:26:32 +09:00
parent 161b1383be
commit 6d05be2331
50 changed files with 4644 additions and 23 deletions

View File

@@ -0,0 +1,69 @@
package com.tasteby.controller;
import com.tasteby.repository.ChannelRepository;
import com.tasteby.security.AuthUtil;
import com.tasteby.service.CacheService;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/channels")
public class ChannelController {
private final ChannelRepository repo;
private final CacheService cache;
public ChannelController(ChannelRepository repo, CacheService cache) {
this.repo = repo;
this.cache = cache;
}
@GetMapping
public List<Map<String, Object>> list() {
String key = cache.makeKey("channels");
String cached = cache.getRaw(key);
if (cached != null) {
try {
var mapper = new com.fasterxml.jackson.databind.ObjectMapper();
return mapper.readValue(cached,
new com.fasterxml.jackson.core.type.TypeReference<>() {});
} catch (Exception ignored) {}
}
var result = repo.findAllActive();
cache.set(key, result);
return result;
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Map<String, Object> create(@RequestBody Map<String, String> body) {
AuthUtil.requireAdmin();
String channelId = body.get("channel_id");
String channelName = body.get("channel_name");
String titleFilter = body.get("title_filter");
try {
String id = repo.create(channelId, channelName, titleFilter);
cache.flush();
return Map.of("id", id, "channel_id", channelId);
} catch (Exception e) {
if (e.getMessage() != null && e.getMessage().toUpperCase().contains("UQ_CHANNELS_CID")) {
throw new ResponseStatusException(HttpStatus.CONFLICT, "Channel already exists");
}
throw e;
}
}
@DeleteMapping("/{channelId}")
public Map<String, Object> delete(@PathVariable String channelId) {
AuthUtil.requireAdmin();
if (!repo.deactivate(channelId)) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Channel not found");
}
cache.flush();
return Map.of("ok", true);
}
}