Added stuff
This commit is contained in:
parent
55eb95e08a
commit
af3435834f
|
@ -62,7 +62,8 @@ public class SecurityConfig {
|
||||||
"/auth/token",
|
"/auth/token",
|
||||||
"/auth/register",
|
"/auth/register",
|
||||||
"/auth/activate",
|
"/auth/activate",
|
||||||
"/auth/reset-password"
|
"/auth/reset-password",
|
||||||
|
"/submissions/video-processing-complete"
|
||||||
).permitAll()
|
).permitAll()
|
||||||
// Everything else must be authenticated, just to be safe.
|
// Everything else must be authenticated, just to be safe.
|
||||||
.anyRequest().authenticated();
|
.anyRequest().authenticated();
|
||||||
|
|
|
@ -5,6 +5,7 @@ import nl.andrewlalis.gymboard_api.domains.auth.model.User;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
import org.springframework.data.jpa.repository.Modifying;
|
import org.springframework.data.jpa.repository.Modifying;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -14,5 +15,8 @@ public interface SubmissionRepository extends JpaRepository<Submission, String>,
|
||||||
@Modifying
|
@Modifying
|
||||||
void deleteAllByUser(User user);
|
void deleteAllByUser(User user);
|
||||||
|
|
||||||
List<Submission> findAllByVideoProcessingTaskId(long taskId);
|
@Query("SELECT s FROM Submission s " +
|
||||||
|
"WHERE s.videoProcessingTaskId = :taskId AND " +
|
||||||
|
"(s.videoFileId IS NULL OR s.thumbnailFileId IS NULL)")
|
||||||
|
List<Submission> findUnprocessedByTaskId(long taskId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,7 +157,9 @@ public class ExerciseSubmissionService {
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void handleVideoProcessingComplete(VideoProcessingCompletePayload payload) {
|
public void handleVideoProcessingComplete(VideoProcessingCompletePayload payload) {
|
||||||
for (var submission : submissionRepository.findAllByVideoProcessingTaskId(payload.taskId())) {
|
var submissionsToUpdate = submissionRepository.findUnprocessedByTaskId(payload.taskId());
|
||||||
|
log.info("Received video processing complete message from CDN: {}, affecting {} submissions.", payload, submissionsToUpdate.size());
|
||||||
|
for (var submission : submissionsToUpdate) {
|
||||||
if (payload.status().equalsIgnoreCase("COMPLETE")) {
|
if (payload.status().equalsIgnoreCase("COMPLETE")) {
|
||||||
submission.setVideoFileId(payload.videoFileId());
|
submission.setVideoFileId(payload.videoFileId());
|
||||||
submission.setThumbnailFileId(payload.thumbnailFileId());
|
submission.setThumbnailFileId(payload.thumbnailFileId());
|
||||||
|
|
|
@ -116,10 +116,15 @@ public class TokenService {
|
||||||
|
|
||||||
public Jws<Claims> getToken(String token) {
|
public Jws<Claims> getToken(String token) {
|
||||||
if (token == null) return null;
|
if (token == null) return null;
|
||||||
var builder = Jwts.parserBuilder()
|
try {
|
||||||
.setSigningKey(this.getPrivateKey())
|
var builder = Jwts.parserBuilder()
|
||||||
.requireIssuer(ISSUER);
|
.setSigningKey(this.getPrivateKey())
|
||||||
return builder.build().parseClaimsJws(token);
|
.requireIssuer(ISSUER);
|
||||||
|
return builder.build().parseClaimsJws(token);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Error parsing JWT.", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private PrivateKey getPrivateKey() {
|
private PrivateKey getPrivateKey() {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package nl.andrewlalis.gymboardcdn.uploads.service;
|
package nl.andrewlalis.gymboardcdn.uploads.service;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import nl.andrewlalis.gymboardcdn.files.FileMetadata;
|
import nl.andrewlalis.gymboardcdn.files.FileMetadata;
|
||||||
|
@ -10,6 +11,7 @@ import nl.andrewlalis.gymboardcdn.uploads.service.process.ThumbnailGenerator;
|
||||||
import nl.andrewlalis.gymboardcdn.uploads.service.process.VideoProcessor;
|
import nl.andrewlalis.gymboardcdn.uploads.service.process.VideoProcessor;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@ -20,6 +22,7 @@ import java.net.http.HttpRequest;
|
||||||
import java.net.http.HttpResponse;
|
import java.net.http.HttpResponse;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.time.Duration;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
@ -36,6 +39,9 @@ public class VideoProcessingService {
|
||||||
private final ThumbnailGenerator thumbnailGenerator;
|
private final ThumbnailGenerator thumbnailGenerator;
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
@Value("${app.api-origin}")
|
||||||
|
private String apiOrigin;
|
||||||
|
|
||||||
public VideoProcessingService(Executor videoProcessingExecutor,
|
public VideoProcessingService(Executor videoProcessingExecutor,
|
||||||
VideoProcessingTaskRepository taskRepo,
|
VideoProcessingTaskRepository taskRepo,
|
||||||
FileStorageService fileStorageService,
|
FileStorageService fileStorageService,
|
||||||
|
@ -52,6 +58,9 @@ public class VideoProcessingService {
|
||||||
private void updateTask(VideoProcessingTask task, VideoProcessingTask.Status status) {
|
private void updateTask(VideoProcessingTask task, VideoProcessingTask.Status status) {
|
||||||
task.setStatus(status);
|
task.setStatus(status);
|
||||||
taskRepo.saveAndFlush(task);
|
taskRepo.saveAndFlush(task);
|
||||||
|
if (status == VideoProcessingTask.Status.COMPLETED || status == VideoProcessingTask.Status.FAILED) {
|
||||||
|
sendTaskCompleteToApi(task);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Scheduled(fixedDelay = 5, timeUnit = TimeUnit.SECONDS)
|
@Scheduled(fixedDelay = 5, timeUnit = TimeUnit.SECONDS)
|
||||||
|
@ -143,9 +152,6 @@ public class VideoProcessingService {
|
||||||
task.setThumbnailFileId(thumbnailFileId);
|
task.setThumbnailFileId(thumbnailFileId);
|
||||||
updateTask(task, VideoProcessingTask.Status.COMPLETED);
|
updateTask(task, VideoProcessingTask.Status.COMPLETED);
|
||||||
log.info("Finished processing task {}.", task.getId());
|
log.info("Finished processing task {}.", task.getId());
|
||||||
|
|
||||||
// Send HTTP POST to API, with video id and thumbnail id.
|
|
||||||
sendProcessedDataToApi(videoFileId, thumbnailFileId);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Failed to copy processed video to final storage location.", e);
|
log.error("Failed to copy processed video to final storage location.", e);
|
||||||
updateTask(task, VideoProcessingTask.Status.FAILED);
|
updateTask(task, VideoProcessingTask.Status.FAILED);
|
||||||
|
@ -162,24 +168,37 @@ public class VideoProcessingService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendProcessedDataToApi(String videoId, String thumbnailId) throws IOException {
|
/**
|
||||||
|
* Sends an update message to the Gymboard API when a task finishes its
|
||||||
|
* processing.
|
||||||
|
* @param task The task to send.
|
||||||
|
*/
|
||||||
|
private void sendTaskCompleteToApi(VideoProcessingTask task) {
|
||||||
ObjectNode obj = objectMapper.createObjectNode();
|
ObjectNode obj = objectMapper.createObjectNode();
|
||||||
obj.put("videoFileId", videoId);
|
obj.put("taskId", task.getId());
|
||||||
obj.put("thumbnailFileId", thumbnailId);
|
obj.put("status", task.getStatus().name());
|
||||||
String json = objectMapper.writeValueAsString(obj);
|
obj.put("videoFileId", task.getVideoFileId());
|
||||||
|
obj.put("thumbnailFileId", task.getThumbnailFileId());
|
||||||
|
String json;
|
||||||
|
try {
|
||||||
|
json = objectMapper.writeValueAsString(obj);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
log.error("JSON error while sending task data to API for task " + task.getId(), e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
HttpClient httpClient = HttpClient.newBuilder().build();
|
HttpClient httpClient = HttpClient.newBuilder().build();
|
||||||
HttpRequest request = HttpRequest.newBuilder(URI.create("http://localhost:8080/submissions/123/processed-data"))
|
HttpRequest request = HttpRequest.newBuilder(URI.create(apiOrigin + "/submissions/video-processing-complete"))
|
||||||
.header("Authorization", "Bearer bullshit")
|
.header("Content-Type", "application/json")
|
||||||
|
.timeout(Duration.ofSeconds(3))
|
||||||
.POST(HttpRequest.BodyPublishers.ofString(json))
|
.POST(HttpRequest.BodyPublishers.ofString(json))
|
||||||
.build();
|
.build();
|
||||||
try {
|
try {
|
||||||
HttpResponse<Void> response = httpClient.send(request, HttpResponse.BodyHandlers.discarding());
|
HttpResponse<Void> response = httpClient.send(request, HttpResponse.BodyHandlers.discarding());
|
||||||
if (response.statusCode() == 200) {
|
if (response.statusCode() >= 400) {
|
||||||
// We can now delete the task.
|
log.error("API returned not-ok response {}", response.statusCode());
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (Exception e) {
|
||||||
// TODO: Retry!
|
log.error("Failed to send HTTP request to API.", e);
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue