mirror of
https://github.com/imgfloat/server.git
synced 2026-02-05 11:49:25 +00:00
Add listing
This commit is contained in:
@@ -2,12 +2,12 @@ package com.imgfloat.app.controller;
|
||||
|
||||
import com.imgfloat.app.model.AdminRequest;
|
||||
import com.imgfloat.app.model.Asset;
|
||||
import com.imgfloat.app.model.AssetRequest;
|
||||
import com.imgfloat.app.model.TransformRequest;
|
||||
import com.imgfloat.app.model.VisibilityRequest;
|
||||
import com.imgfloat.app.service.ChannelDirectoryService;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@@ -18,11 +18,14 @@ import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.springframework.http.HttpStatus.FORBIDDEN;
|
||||
import static org.springframework.http.HttpStatus.NOT_FOUND;
|
||||
import static org.springframework.http.HttpStatus.BAD_REQUEST;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/channels/{broadcaster}")
|
||||
@@ -82,15 +85,22 @@ public class ChannelApiController {
|
||||
return channelDirectoryService.getVisibleAssets(broadcaster);
|
||||
}
|
||||
|
||||
@PostMapping("/assets")
|
||||
@PostMapping(value = "/assets", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
public ResponseEntity<Asset> createAsset(@PathVariable("broadcaster") String broadcaster,
|
||||
@Valid @RequestBody AssetRequest request,
|
||||
@org.springframework.web.bind.annotation.RequestPart("file") MultipartFile file,
|
||||
OAuth2AuthenticationToken authentication) {
|
||||
String login = TwitchUser.from(authentication).login();
|
||||
ensureAuthorized(broadcaster, login);
|
||||
return channelDirectoryService.createAsset(broadcaster, request)
|
||||
.map(ResponseEntity::ok)
|
||||
.orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Channel not found"));
|
||||
if (file == null || file.isEmpty()) {
|
||||
throw new ResponseStatusException(BAD_REQUEST, "Asset file is required");
|
||||
}
|
||||
try {
|
||||
return channelDirectoryService.createAsset(broadcaster, file)
|
||||
.map(ResponseEntity::ok)
|
||||
.orElseThrow(() -> new ResponseStatusException(BAD_REQUEST, "Unable to read image"));
|
||||
} catch (IOException e) {
|
||||
throw new ResponseStatusException(BAD_REQUEST, "Failed to process image", e);
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/assets/{assetId}/transform")
|
||||
|
||||
@@ -22,6 +22,7 @@ public class ViewController {
|
||||
String login = TwitchUser.from(authentication).login();
|
||||
model.addAttribute("username", login);
|
||||
model.addAttribute("channel", login);
|
||||
model.addAttribute("adminChannels", channelDirectoryService.adminChannelsFor(login));
|
||||
return "dashboard";
|
||||
}
|
||||
return "index";
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
package com.imgfloat.app.model;
|
||||
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
public class AssetRequest {
|
||||
@NotBlank
|
||||
private String url;
|
||||
|
||||
@Min(1)
|
||||
private double width;
|
||||
|
||||
@Min(1)
|
||||
private double height;
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public double getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public void setWidth(double width) {
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
public double getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public void setHeight(double height) {
|
||||
this.height = height;
|
||||
}
|
||||
}
|
||||
@@ -2,17 +2,23 @@ package com.imgfloat.app.service;
|
||||
|
||||
import com.imgfloat.app.model.Asset;
|
||||
import com.imgfloat.app.model.AssetEvent;
|
||||
import com.imgfloat.app.model.AssetRequest;
|
||||
import com.imgfloat.app.model.Channel;
|
||||
import com.imgfloat.app.model.TransformRequest;
|
||||
import com.imgfloat.app.model.VisibilityRequest;
|
||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
@Service
|
||||
public class ChannelDirectoryService {
|
||||
@@ -55,9 +61,16 @@ public class ChannelDirectoryService {
|
||||
.toList();
|
||||
}
|
||||
|
||||
public Optional<Asset> createAsset(String broadcaster, AssetRequest request) {
|
||||
public Optional<Asset> createAsset(String broadcaster, MultipartFile file) throws IOException {
|
||||
Channel channel = getOrCreateChannel(broadcaster);
|
||||
Asset asset = new Asset(request.getUrl(), request.getWidth(), request.getHeight());
|
||||
byte[] bytes = file.getBytes();
|
||||
BufferedImage image = ImageIO.read(new ByteArrayInputStream(bytes));
|
||||
if (image == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
String contentType = Optional.ofNullable(file.getContentType()).orElse("application/octet-stream");
|
||||
String dataUrl = "data:" + contentType + ";base64," + Base64.getEncoder().encodeToString(bytes);
|
||||
Asset asset = new Asset(dataUrl, image.getWidth(), image.getHeight());
|
||||
channel.getAssets().put(asset.getId(), asset);
|
||||
messagingTemplate.convertAndSend(topicFor(broadcaster), AssetEvent.created(broadcaster, asset));
|
||||
return Optional.of(asset);
|
||||
@@ -108,6 +121,17 @@ public class ChannelDirectoryService {
|
||||
return channel != null && channel.getAdmins().contains(username.toLowerCase());
|
||||
}
|
||||
|
||||
public Collection<String> adminChannelsFor(String username) {
|
||||
if (username == null) {
|
||||
return List.of();
|
||||
}
|
||||
String login = username.toLowerCase();
|
||||
return channels.values().stream()
|
||||
.filter(channel -> channel.getAdmins().contains(login))
|
||||
.map(Channel::getBroadcaster)
|
||||
.toList();
|
||||
}
|
||||
|
||||
private String topicFor(String broadcaster) {
|
||||
return "/topic/channel/" + broadcaster.toLowerCase();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user