diff --git a/src/main/java/dev/kruhlmann/imgfloat/config/OAuthTokenCipher.java b/src/main/java/dev/kruhlmann/imgfloat/config/OAuthTokenCipher.java index b82d774..9e23d6a 100644 --- a/src/main/java/dev/kruhlmann/imgfloat/config/OAuthTokenCipher.java +++ b/src/main/java/dev/kruhlmann/imgfloat/config/OAuthTokenCipher.java @@ -32,46 +32,28 @@ public class OAuthTokenCipher { } public static OAuthTokenCipher fromEnvironment() { - String base64Key = System.getenv(KEY_ENV); - if (base64Key == null || base64Key.isBlank()) { - throw new IllegalStateException(KEY_ENV + " is required to encrypt OAuth tokens"); - } - SecretKey primaryKey = decodeKey(base64Key, KEY_ENV); - List keys = new ArrayList<>(); - keys.add(primaryKey); - - String previousKeys = System.getenv(PREVIOUS_KEYS_ENV); - if (previousKeys != null && !previousKeys.isBlank()) { - for (String value : previousKeys.split(",")) { - String trimmed = value.trim(); - if (!trimmed.isEmpty()) { - keys.add(decodeKey(trimmed, PREVIOUS_KEYS_ENV)); - } - } - } - - return new OAuthTokenCipher(primaryKey, keys); + return buildFromKeys(System.getenv(KEY_ENV), System.getenv(PREVIOUS_KEYS_ENV)); } public static OAuthTokenCipher fromEnvironment(Environment environment) { - String base64Key = environment.getProperty(KEY_ENV); + return buildFromKeys(environment.getProperty(KEY_ENV), environment.getProperty(PREVIOUS_KEYS_ENV)); + } + + private static OAuthTokenCipher buildFromKeys(String base64Key, String previousKeysRaw) { if (base64Key == null || base64Key.isBlank()) { throw new IllegalStateException(KEY_ENV + " is required to encrypt OAuth tokens"); } SecretKey primaryKey = decodeKey(base64Key, KEY_ENV); List keys = new ArrayList<>(); keys.add(primaryKey); - - String previousKeys = environment.getProperty(PREVIOUS_KEYS_ENV); - if (previousKeys != null && !previousKeys.isBlank()) { - for (String value : previousKeys.split(",")) { + if (previousKeysRaw != null && !previousKeysRaw.isBlank()) { + for (String value : previousKeysRaw.split(",")) { String trimmed = value.trim(); if (!trimmed.isEmpty()) { keys.add(decodeKey(trimmed, PREVIOUS_KEYS_ENV)); } } } - return new OAuthTokenCipher(primaryKey, keys); } diff --git a/src/main/java/dev/kruhlmann/imgfloat/config/SecurityConfig.java b/src/main/java/dev/kruhlmann/imgfloat/config/SecurityConfig.java index e1f387f..82d38b0 100644 --- a/src/main/java/dev/kruhlmann/imgfloat/config/SecurityConfig.java +++ b/src/main/java/dev/kruhlmann/imgfloat/config/SecurityConfig.java @@ -76,7 +76,7 @@ public class SecurityConfig { .permitAll() .requestMatchers(HttpMethod.GET, "/api/channels/*/canvas") .permitAll() - .requestMatchers(HttpMethod.GET, "/api/channels/gasolinebased/script-assets/*/attachments/*/content") + .requestMatchers(HttpMethod.GET, "/api/channels/*/script-assets/*/attachments/*/content") .permitAll() .requestMatchers(HttpMethod.GET, "/api/channels/*/assets/*/content") .permitAll() diff --git a/src/main/java/dev/kruhlmann/imgfloat/model/api/response/AssetPatch.java b/src/main/java/dev/kruhlmann/imgfloat/model/api/response/AssetPatch.java index ab99fad..bebb4b1 100644 --- a/src/main/java/dev/kruhlmann/imgfloat/model/api/response/AssetPatch.java +++ b/src/main/java/dev/kruhlmann/imgfloat/model/api/response/AssetPatch.java @@ -76,24 +76,15 @@ public record AssetPatch( ); } + /** + * Produces a patch carrying only a display-order update. + */ + public static AssetPatch forOrder(String assetId, int order) { + return new AssetPatch(assetId, null, null, null, null, null, null, null, order, null, null, null, null, null, null); + } + public static AssetPatch fromVisibility(String assetId, boolean hidden) { - return new AssetPatch( - assetId, - null, - null, - null, - null, - null, - null, - null, - null, - hidden, - null, - null, - null, - null, - null - ); + return new AssetPatch(assetId, null, null, null, null, null, null, null, null, hidden, null, null, null, null, null); } private static Double changed(double before, double after) { diff --git a/src/main/java/dev/kruhlmann/imgfloat/service/ChannelDirectoryService.java b/src/main/java/dev/kruhlmann/imgfloat/service/ChannelDirectoryService.java index 0fcd689..c98aa1b 100644 --- a/src/main/java/dev/kruhlmann/imgfloat/service/ChannelDirectoryService.java +++ b/src/main/java/dev/kruhlmann/imgfloat/service/ChannelDirectoryService.java @@ -37,6 +37,7 @@ import dev.kruhlmann.imgfloat.service.media.MediaOptimizationService; import dev.kruhlmann.imgfloat.service.media.OptimizedAsset; import dev.kruhlmann.imgfloat.service.media.MediaTypeRegistry; import dev.kruhlmann.imgfloat.util.AllowedDomainNormalizer; +import dev.kruhlmann.imgfloat.util.StringNormalizer; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -198,16 +199,7 @@ public class ChannelDirectoryService { @Transactional(rollbackFor = IOException.class) public Optional createAsset(String broadcaster, MultipartFile file, String actor) throws IOException { long fileSize = file.getSize(); - if (fileSize > uploadLimitBytes) { - throw new ResponseStatusException( - PAYLOAD_TOO_LARGE, - String.format( - "Uploaded file is too large (%d bytes). Maximum allowed is %d bytes.", - fileSize, - uploadLimitBytes - ) - ); - } + enforceUploadLimit(fileSize); Channel channel = getOrCreateChannel(broadcaster); byte[] bytes = file.getBytes(); String mediaType = mediaDetectionService @@ -746,23 +738,7 @@ public class ChannelDirectoryService { EnumSet.of(AssetType.SCRIPT) ); if (!Objects.equals(beforeOrder, asset.getDisplayOrder())) { - AssetPatch patch = new AssetPatch( - asset.getId(), - null, - null, - null, - null, - null, - null, - null, - asset.getDisplayOrder(), - null, - null, - null, - null, - null, - null - ); + AssetPatch patch = AssetPatch.forOrder(asset.getId(), asset.getDisplayOrder()); messagingTemplate.convertAndSend(topicFor(broadcaster), AssetEvent.updated(broadcaster, patch)); auditLogService.recordEntry( asset.getBroadcaster(), @@ -1268,17 +1244,7 @@ public class ChannelDirectoryService { MultipartFile file, String actor ) throws IOException { - long fileSize = file.getSize(); - if (fileSize > uploadLimitBytes) { - throw new ResponseStatusException( - PAYLOAD_TOO_LARGE, - String.format( - "Uploaded file is too large (%d bytes). Maximum allowed is %d bytes.", - fileSize, - uploadLimitBytes - ) - ); - } + enforceUploadLimit(file.getSize()); Asset asset = requireScriptAssetForBroadcaster(broadcaster, scriptAssetId); byte[] bytes = file.getBytes(); @@ -1434,7 +1400,7 @@ public class ChannelDirectoryService { } private String normalize(String value) { - return value == null ? null : value.toLowerCase(Locale.ROOT); + return StringNormalizer.toLowerCaseRoot(value); } private boolean isCodeMediaType(String mediaType) { @@ -1640,23 +1606,7 @@ public class ChannelDirectoryService { .filter((asset) -> asset.getDisplayOrder() != null) .filter((asset) -> !asset.getId().equals(targetAssetId)) .forEach((asset) -> { - AssetPatch patch = new AssetPatch( - asset.getId(), - null, - null, - null, - null, - null, - null, - null, - asset.getDisplayOrder(), - null, - null, - null, - null, - null, - null - ); + AssetPatch patch = AssetPatch.forOrder(asset.getId(), asset.getDisplayOrder()); messagingTemplate.convertAndSend(topicFor(broadcaster), AssetEvent.updated(broadcaster, patch)); }); } diff --git a/src/main/java/dev/kruhlmann/imgfloat/service/ChannelSettingsService.java b/src/main/java/dev/kruhlmann/imgfloat/service/ChannelSettingsService.java index 2bfa297..cf08c3a 100644 --- a/src/main/java/dev/kruhlmann/imgfloat/service/ChannelSettingsService.java +++ b/src/main/java/dev/kruhlmann/imgfloat/service/ChannelSettingsService.java @@ -16,6 +16,8 @@ import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Service; import org.springframework.web.server.ResponseStatusException; +import dev.kruhlmann.imgfloat.util.StringNormalizer; + /** * Manages per-channel canvas and script-overlay settings. * Extracted from {@link ChannelDirectoryService} to give it a focused responsibility. @@ -198,6 +200,6 @@ public class ChannelSettingsService { } private String normalize(String value) { - return value == null ? null : value.toLowerCase(Locale.ROOT); + return StringNormalizer.toLowerCaseRoot(value); } } diff --git a/src/main/java/dev/kruhlmann/imgfloat/util/StringNormalizer.java b/src/main/java/dev/kruhlmann/imgfloat/util/StringNormalizer.java new file mode 100644 index 0000000..3e3434b --- /dev/null +++ b/src/main/java/dev/kruhlmann/imgfloat/util/StringNormalizer.java @@ -0,0 +1,19 @@ +package dev.kruhlmann.imgfloat.util; + +import java.util.Locale; + +/** + * Shared string normalization utilities. Centralises {@link Locale#ROOT} lowercase + * conversions so that individual classes do not duplicate the same one-liner. + */ +public final class StringNormalizer { + + private StringNormalizer() {} + + /** + * Returns {@code value.toLowerCase(Locale.ROOT)}, or {@code null} if {@code value} is {@code null}. + */ + public static String toLowerCaseRoot(String value) { + return value == null ? null : value.toLowerCase(Locale.ROOT); + } +}