shortlink fixes and app properties

This commit is contained in:
2026-01-06 05:56:19 -05:00
parent 6c8205b90c
commit 887ccc01d5
4 changed files with 65 additions and 16 deletions

View File

@@ -11,13 +11,21 @@ public class ShortLinksProperties {
private boolean enabled = true; private boolean enabled = true;
/** /**
* The public base URL used when generating links (differs in dev vs prod). * The public base URL used when generating short links (differs in dev vs prod).
* Examples: * Examples:
* - http://localhost:8080 * - http://localhost:8080
* - https://bb.ooo * - https://bb.ooo
*/ */
private String publicBaseUrl = "http://localhost:8080"; private String publicBaseUrl = "http://localhost:8080";
/**
* The frontend base URL used when redirecting BUILD short links.
* Examples:
* - http://localhost:3000
* - https://battlbuilder.com
*/
private String frontendBaseUrl = "http://localhost:3000";
public boolean isEnabled() { public boolean isEnabled() {
return enabled; return enabled;
} }
@@ -33,4 +41,12 @@ public class ShortLinksProperties {
public void setPublicBaseUrl(String publicBaseUrl) { public void setPublicBaseUrl(String publicBaseUrl) {
this.publicBaseUrl = publicBaseUrl; this.publicBaseUrl = publicBaseUrl;
} }
public String getFrontendBaseUrl() {
return frontendBaseUrl;
}
public void setFrontendBaseUrl(String frontendBaseUrl) {
this.frontendBaseUrl = frontendBaseUrl;
}
} }

View File

@@ -1,6 +1,8 @@
package group.goforward.battlbuilder.controllers; package group.goforward.battlbuilder.controllers;
import group.goforward.battlbuilder.config.ShortLinksProperties;
import group.goforward.battlbuilder.repos.ShortLinkRepository; import group.goforward.battlbuilder.repos.ShortLinkRepository;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@@ -14,36 +16,64 @@ import java.net.URI;
public class ShortLinkRedirectController { public class ShortLinkRedirectController {
private final ShortLinkRepository repo; private final ShortLinkRepository repo;
private final ShortLinksProperties props;
public ShortLinkRedirectController(ShortLinkRepository repo) { public ShortLinkRedirectController(ShortLinkRepository repo, ShortLinksProperties props) {
this.repo = repo; this.repo = repo;
this.props = props;
} }
/**
* Single canonical short-link entry point.
*
* We intentionally keep ONE route (/go/{code}) for all short links
* (BUY affiliate redirects + BUILD share links) to avoid duplicated controllers
* and ambiguous Spring mappings.
*
* BUY -> redirect to destination_url (stored from offer.buy_url / AvantLink)
* BUILD-> redirect to frontendBaseUrl + /builds/{buildUuid}
*/
@GetMapping("/go/{code}") @GetMapping("/go/{code}")
public ResponseEntity<Void> go(@PathVariable String code) { public ResponseEntity<Void> go(@PathVariable String code) {
// Lookup only active links; 404 for missing/disabled
var link = repo.findByCodeAndIsActiveTrue(code) var link = repo.findByCodeAndIsActiveTrue(code)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
String dest; final String dest;
// ----------------------------
// BUY: redirect to affiliate URL
// ----------------------------
if ("BUY".equalsIgnoreCase(link.getType())) { if ("BUY".equalsIgnoreCase(link.getType())) {
dest = link.getDestinationUrl(); String url = link.getDestinationUrl();
if (dest == null || dest.isBlank()) { if (url == null || url.isBlank()) throw new ResponseStatusException(HttpStatus.NOT_FOUND);
throw new ResponseStatusException(HttpStatus.NOT_FOUND); dest = url;
}
// ----------------------------
// BUILD: redirect to Next.js build page
// ----------------------------
} else if ("BUILD".equalsIgnoreCase(link.getType())) { } else if ("BUILD".equalsIgnoreCase(link.getType())) {
if (link.getBuildUuid() == null) { if (link.getBuildUuid() == null) throw new ResponseStatusException(HttpStatus.NOT_FOUND);
throw new ResponseStatusException(HttpStatus.NOT_FOUND);
}
// ✅ Adjust to your actual frontend route + base URL if needed (dev vs prod) // Use env-specific frontend host (dev vs prod)
dest = "/builds/" + link.getBuildUuid(); String base = props.getFrontendBaseUrl();
if (base == null || base.isBlank()) throw new ResponseStatusException(HttpStatus.NOT_FOUND);
// Normalize trailing slash so we don't end up with //builds/...
base = base.endsWith("/") ? base.substring(0, base.length() - 1) : base;
// NOTE: Adjust path if your Next.js route changes
dest = base + "/builds/" + link.getBuildUuid();
// ----------------------------
// Unknown type: treat as missing
// ----------------------------
} else { } else {
throw new ResponseStatusException(HttpStatus.NOT_FOUND); throw new ResponseStatusException(HttpStatus.NOT_FOUND);
} }
// 302 (FOUND) redirect
return ResponseEntity.status(HttpStatus.FOUND) return ResponseEntity.status(HttpStatus.FOUND)
.location(URI.create(dest)) .location(URI.create(dest))
.build(); .build();

View File

@@ -1,3 +1,4 @@
shortlinks: shortlinks:
enabled: true enabled: true
publicBaseUrl: "http://localhost:8080" publicBaseUrl: http://localhost:8080
frontendBaseUrl: http://localhost:3000

View File

@@ -1,3 +1,5 @@
shortlinks: shortlinks:
enabled: true enabled: true
publicBaseUrl: "https://battl.build" publicBaseUrl: https://battl.build
frontendBaseUrl: https://battlbuilders.com