docs(bruno): overhaul bruno to match new API

This commit is contained in:
2026-06-22 22:08:56 +01:00
parent da6bf16d07
commit dc5af8f868
27 changed files with 46 additions and 223 deletions

View File

@@ -1,30 +0,0 @@
meta {
name: Get Google Token
type: http
seq: 3
}
get {
url:
body: none
auth: oauth2
}
auth:oauth2 {
grant_type: authorization_code
callback_url: http://localhost:8080
authorization_url: https://accounts.google.com/o/oauth2/v2/auth
access_token_url: https://oauth2.googleapis.com/token
refresh_token_url:
client_id: {{google_client_id}}
client_secret: {{google_secret}}
scope: profile email openid
state:
pkce: true
credentials_placement: body
credentials_id: credentials
token_placement: header
token_header_prefix: Bearer
auto_fetch_token: true
auto_refresh_token: false
}

View File

@@ -1,11 +0,0 @@
meta {
name: Get User
type: http
seq: 1
}
get {
url: {{base_url}}/auth/user
body: none
auth: inherit
}

View File

@@ -1,21 +0,0 @@
meta {
name: Login
type: http
seq: 2
}
post {
url: {{base_url}}/auth/login
body: json
auth: none
}
body:json {
{
"token": "{{$oauth2.credentials.id_token}}"
}
}
script:post-response {
bru.setEnvVar("token", res.getBody().data.token);
}

View File

@@ -1,7 +1,7 @@
meta { meta {
name: Get all clips name: Get all clips
type: http type: http
seq: 1 seq: 2
} }
get { get {

View File

@@ -1,7 +1,7 @@
meta { meta {
name: Update Clip name: Update Clip
type: http type: http
seq: 2 seq: 3
} }
patch { patch {

View File

@@ -1,5 +1,6 @@
meta { meta {
name: Clips name: Clips
seq: 3
} }
auth { auth {

View File

@@ -1,11 +0,0 @@
meta {
name: Converted Download
type: http
seq: 2
}
get {
url: {{base_url}}/download/output/{{uuid}}
body: none
auth: inherit
}

View File

@@ -1,11 +0,0 @@
meta {
name: Original Download
type: http
seq: 1
}
get {
url: {{base_url}}/download/input/{{uuid}}
body: none
auth: inherit
}

View File

@@ -1,3 +0,0 @@
meta {
name: Download
}

View File

@@ -1,17 +0,0 @@
meta {
name: Edit
type: http
seq: 1
}
post {
url: {{base_url}}/edit/{{uuid}}
body: formUrlEncoded
auth: inherit
}
body:form-urlencoded {
startPoint: 10
duration: 40
title: best possible title
}

View File

@@ -1,11 +0,0 @@
meta {
name: Process
type: http
seq: 2
}
get {
url: {{base_url}}/process/{{uuid}}
body: none
auth: inherit
}

View File

@@ -1,11 +0,0 @@
meta {
name: Progress
type: http
seq: 3
}
get {
url: {{base_url}}/progress/{{uuid}}
body: none
auth: inherit
}

View File

@@ -1,3 +0,0 @@
meta {
name: Edit
}

View File

@@ -1,11 +0,0 @@
meta {
name: Converted Metadata
type: http
seq: 2
}
get {
url: {{base_url}}/metadata/converted/{{uuid}}
body: none
auth: inherit
}

View File

@@ -1,11 +0,0 @@
meta {
name: Original Metadata
type: http
seq: 1
}
get {
url: {{base_url}}/metadata/original/{{uuid}}
body: none
auth: inherit
}

View File

@@ -1,3 +0,0 @@
meta {
name: Metadata
}

View File

@@ -1,22 +0,0 @@
meta {
name: Save Clip
type: http
seq: 3
}
post {
url: {{base_url}}/stream/clip
body: json
auth: inherit
}
body:json {
{
"duration": 60
}
}
settings {
encodeUrl: true
timeout: 0
}

View File

@@ -1,23 +0,0 @@
meta {
name: Save Section
type: http
seq: 2
}
post {
url: {{base_url}}/stream/save
body: json
auth: inherit
}
body:json {
{
"startTime": "2026-06-20T15:00:00Z",
"endTime": "2026-06-20T15:01:00Z"
}
}
settings {
encodeUrl: true
timeout: 0
}

View File

@@ -1,6 +1,6 @@
meta { meta {
name: Streams name: Streams
seq: 7 seq: 6
} }
auth { auth {

View File

@@ -1,7 +1,7 @@
meta { meta {
name: Upload name: Upload
type: http type: http
seq: 1 seq: 2
} }
post { post {

View File

@@ -30,11 +30,9 @@ public class SecurityConfig {
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class) .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeHttpRequests(auth -> auth .authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.OPTIONS, "/api/v1/**").permitAll() .requestMatchers(HttpMethod.OPTIONS, "/api/v1/**").permitAll()
.requestMatchers("/api/v1/download/clip/**","/api/v1/clips/", "/api/v1/clips/**").authenticated() .requestMatchers("/api/v1/media/**").authenticated()
.requestMatchers("/api/v1/auth/login", "/api/v1/auth/user").permitAll() .requestMatchers("/api/v1/jobs/**").authenticated()
.requestMatchers("/api/v1/upload", "/api/v1/download/**").permitAll() .requestMatchers("/api/v1/users/me", "/api/v1/users/logout").authenticated()
.requestMatchers("/api/v1/edit/**", "/api/v1/process/**", "/api/v1/progress/**", "/api/v1/convert/**").permitAll()
.requestMatchers("/api/v1/metadata/**").permitAll()
.requestMatchers("/api/v1/stream/start", "/api/v1/stream/stop", "/api/v1/stream/heartbeat").permitAll() .requestMatchers("/api/v1/stream/start", "/api/v1/stream/stop", "/api/v1/stream/heartbeat").permitAll()
.anyRequest().authenticated() .anyRequest().authenticated()
) )

View File

@@ -3,6 +3,7 @@ package com.ddf.vodsystem.controllers;
import com.ddf.vodsystem.controllers.dto.JobResponse; import com.ddf.vodsystem.controllers.dto.JobResponse;
import com.ddf.vodsystem.dto.APIResponse; import com.ddf.vodsystem.dto.APIResponse;
import com.ddf.vodsystem.dto.Job; import com.ddf.vodsystem.dto.Job;
import com.ddf.vodsystem.exceptions.ClipNotFound;
import com.ddf.vodsystem.exceptions.NotReadyException; import com.ddf.vodsystem.exceptions.NotReadyException;
import com.ddf.vodsystem.services.JobRegistryService; import com.ddf.vodsystem.services.JobRegistryService;
import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.FileSystemResource;
@@ -17,7 +18,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@RestController @RestController
@RequestMapping("api/v1/job") @RequestMapping("api/v1/jobs")
public class JobController { public class JobController {
private final JobRegistryService jobRegistryService; private final JobRegistryService jobRegistryService;
private static final String SUCCESS = "success"; private static final String SUCCESS = "success";
@@ -57,7 +58,7 @@ public class JobController {
Resource resource = new FileSystemResource(job.getDownload()); Resource resource = new FileSystemResource(job.getDownload());
if (!resource.exists()) { if (!resource.exists()) {
return ResponseEntity.notFound().build(); throw new ClipNotFound("Job download file " + job.getDownload() + " does not exist.");
} }
return ResponseEntity.ok() return ResponseEntity.ok()

View File

@@ -1,16 +1,19 @@
package com.ddf.vodsystem.controllers; package com.ddf.vodsystem.controllers;
import com.ddf.vodsystem.controllers.dto.ClipSectionRequest;
import com.ddf.vodsystem.controllers.dto.SaveSectionRequest;
import com.ddf.vodsystem.controllers.dto.UUIDResponse; import com.ddf.vodsystem.controllers.dto.UUIDResponse;
import com.ddf.vodsystem.dto.Job; import com.ddf.vodsystem.dto.Job;
import com.ddf.vodsystem.dto.ClipOptions; import com.ddf.vodsystem.dto.ClipOptions;
import com.ddf.vodsystem.dto.APIResponse; import com.ddf.vodsystem.dto.APIResponse;
import com.ddf.vodsystem.services.MediaService; import com.ddf.vodsystem.services.MediaService;
import jakarta.validation.Valid;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.io.IOException; import java.io.IOException;
import java.time.Instant;
@RestController @RestController
@RequestMapping("/api/v1/media") @RequestMapping("/api/v1/media")
@@ -22,10 +25,10 @@ public class MediaController {
this.mediaService = mediaService; this.mediaService = mediaService;
} }
@PostMapping("/compress") @PostMapping(value = "/compress", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<APIResponse<UUIDResponse>> compress( public ResponseEntity<APIResponse<UUIDResponse>> compress(
@RequestParam("file")MultipartFile file, @RequestParam("file")MultipartFile file,
@RequestParam("options") ClipOptions options @Valid @ModelAttribute ClipOptions options
) throws IOException, InterruptedException { ) throws IOException, InterruptedException {
Job job = mediaService.compress(file, options); Job job = mediaService.compress(file, options);
return ResponseEntity.ok(new APIResponse<>( return ResponseEntity.ok(new APIResponse<>(
@@ -38,9 +41,8 @@ public class MediaController {
@PostMapping("/save") @PostMapping("/save")
public ResponseEntity<APIResponse<UUIDResponse>> save( public ResponseEntity<APIResponse<UUIDResponse>> save(
@RequestParam("start") Instant startTime, @RequestBody SaveSectionRequest saveSectionRequest) throws IOException {
@RequestParam("end") Instant endTime) throws IOException { Job job = mediaService.saveSection(saveSectionRequest.startTime(), saveSectionRequest.endTime());
Job job = mediaService.saveSection(startTime, endTime);
return ResponseEntity.ok(new APIResponse<>( return ResponseEntity.ok(new APIResponse<>(
SUCCESS, SUCCESS,
"Section saving successfully started", "Section saving successfully started",
@@ -50,8 +52,8 @@ public class MediaController {
@PostMapping("/clip") @PostMapping("/clip")
public ResponseEntity<APIResponse<UUIDResponse>> clip( public ResponseEntity<APIResponse<UUIDResponse>> clip(
@RequestParam("duration") float duration) throws IOException { @RequestBody ClipSectionRequest clipSectionRequest) throws IOException {
Job job = mediaService.clip(duration); Job job = mediaService.clip(clipSectionRequest.duration());
return ResponseEntity.ok(new APIResponse<>( return ResponseEntity.ok(new APIResponse<>(
SUCCESS, SUCCESS,
"Clipping successfully started", "Clipping successfully started",

View File

@@ -14,7 +14,7 @@ import org.springframework.web.bind.annotation.*;
import java.util.Optional; import java.util.Optional;
@RestController @RestController
@RequestMapping("/api/v1/user/") @RequestMapping("/api/v1/users")
public class UserController { public class UserController {
private final UserService userService; private final UserService userService;

View File

@@ -1,15 +1,31 @@
package com.ddf.vodsystem.dto; package com.ddf.vodsystem.dto;
import jakarta.validation.constraints.*;
import lombok.Data; import lombok.Data;
@Data @Data
public class ClipOptions { public class ClipOptions {
@NotBlank
private String title; private String title;
private String description; private String description;
@NotNull
@Positive
private Float startPoint; private Float startPoint;
@NotNull @Positive
private Float duration; private Float duration;
@DecimalMin("1.0") @DecimalMax("120.0")
private Float fps; private Float fps;
@Positive
private Integer width; private Integer width;
@Positive
private Integer height; private Integer height;
@Positive
private Float fileSize; private Float fileSize;
} }

View File

@@ -12,6 +12,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.filter.OncePerRequestFilter;
@@ -60,9 +61,12 @@ public class JwtFilter extends OncePerRequestFilter {
} }
Optional<Authentication> authentication = getAuthentication(jwt); Optional<Authentication> authentication = getAuthentication(jwt);
authentication.ifPresent(value -> authentication.ifPresent(auth -> {
SecurityContextHolder.getContext().setAuthentication(value) SecurityContext context = SecurityContextHolder.createEmptyContext();
); context.setAuthentication(auth);
SecurityContextHolder.setContext(context);
});
filterChain.doFilter(request, response); filterChain.doFilter(request, response);
} }

View File

@@ -229,7 +229,7 @@ public class ClipService {
try { try {
newClipFile = directoryService.getUserClipsFile(user.getId(), fileName); newClipFile = directoryService.getUserClipsFile(user.getId(), fileName);
thumbnailFile = directoryService.getUserThumbnailsFile(user.getId(), fileName + ".png"); thumbnailFile = directoryService.getUserThumbnailsFile(user.getId(), fileName + ".png");
directoryService.cutFile(clipFile, newClipFile); directoryService.copyFile(clipFile, newClipFile);
} catch (IOException e) { } catch (IOException e) {
throw new StorageException("Failed to move clip from temporary directory to output directory", e); throw new StorageException("Failed to move clip from temporary directory to output directory", e);
} }