ChatWebSocketTicketProperties.java
package com.ecommerce.chat.infrastructure.realtime;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
import java.time.Duration;
@Validated
@ConfigurationProperties("ecommerce.chat.websocket-ticket")
public record ChatWebSocketTicketProperties(
boolean enabled,
@NotBlank
@Pattern(regexp = "[A-Za-z0-9_-]+")
String namespace,
@NotNull Duration ttl,
@NotBlank
@Pattern(regexp = "/[A-Za-z0-9/_-]+")
String targetPath,
@Min(24) @Max(64) int entropyBytes
) {
private static final Duration MINIMUM_TTL = Duration.ofSeconds(5);
private static final Duration MAXIMUM_TTL = Duration.ofMinutes(2);
public ChatWebSocketTicketProperties {
namespace = namespace == null || namespace.isBlank() ? "local" : namespace;
ttl = ttl == null ? Duration.ofSeconds(30) : ttl;
targetPath = targetPath == null || targetPath.isBlank() ? "/ws/chat" : targetPath;
entropyBytes = entropyBytes <= 0 ? 32 : entropyBytes;
if (ttl.compareTo(MINIMUM_TTL) < 0 || ttl.compareTo(MAXIMUM_TTL) > 0) {
throw new IllegalArgumentException(
"websocket ticket ttl must be between 5 seconds and 2 minutes");
}
}
public String redisKeyPrefix() {
return "ecommerce:" + namespace + ":chat:ws-ticket:";
}
}