From e886a3600be435b488ce61b4ecfe65edb1d4ea56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Kr=C3=BChlmann?= Date: Tue, 6 Jan 2026 03:21:49 +0100 Subject: [PATCH] Use environment for download link --- Makefile | 4 ++ README.md | 2 + .../config/SystemEnvironmentValidator.java | 10 ++++ .../imgfloat/controller/ViewController.java | 5 ++ .../service/GithubReleaseService.java | 45 ++++++++++++++ .../imgfloat/service/VersionService.java | 59 +++++++++++++++++-- .../templates/fragments/downloads.html | 12 +--- 7 files changed, 123 insertions(+), 14 deletions(-) create mode 100644 src/main/java/dev/kruhlmann/imgfloat/service/GithubReleaseService.java diff --git a/Makefile b/Makefile index 07e6a71..39756e2 100644 --- a/Makefile +++ b/Makefile @@ -4,12 +4,16 @@ .DEFAULT_GOAL := build IMGFLOAT_DB_PATH ?= ./imgfloat.db +IMGFLOAT_GITHUB_OWNER ?= Kruhlmann +IMGFLOAT_GITHUB_REPO ?= imgfloat-j IMGFLOAT_ASSETS_PATH ?= ./assets IMGFLOAT_PREVIEWS_PATH ?= ./previews SPRING_SERVLET_MULTIPART_MAX_REQUEST_SIZE ?= 10MB SPRING_SERVLET_MULTIPART_MAX_FILE_SIZE ?= 10MB RUNTIME_ENV = IMGFLOAT_ASSETS_PATH=$(IMGFLOAT_ASSETS_PATH) \ IMGFLOAT_PREVIEWS_PATH=$(IMGFLOAT_PREVIEWS_PATH) \ + IMGFLOAT_GITHUB_OWNER=$(IMGFLOAT_GITHUB_OWNER) \ + IMGFLOAT_GITHUB_REPO=$(IMGFLOAT_GITHUB_REPO) \ IMGFLOAT_DB_PATH=$(IMGFLOAT_DB_PATH) \ SPRING_SERVLET_MULTIPART_MAX_FILE_SIZE=$(SPRING_SERVLET_MULTIPART_MAX_FILE_SIZE) \ SPRING_SERVLET_MULTIPART_MAX_REQUEST_SIZE=$(SPRING_SERVLET_MULTIPART_MAX_REQUEST_SIZE) diff --git a/README.md b/README.md index 4393834..46a5968 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ Define the following required environment variables: | `IMGFLOAT_INITIAL_TWITCH_USERNAME_SYSADMIN` | Twitch username of the initial sysadmin user | example_broadcaster | | `SPRING_SERVLET_MULTIPART_MAX_FILE_SIZE` | Maximum upload file size | 10MB | | `SPRING_SERVLET_MULTIPART_MAX_REQUEST_SIZE` | Maximum upload request size | 10MB | +| `IMGFLOAT_GITHUB_OWNER` | GitHub owner used to build desktop download links | Kruhlmann | +| `IMGFLOAT_GITHUB_REPO` | GitHub repo used to build desktop download links | imgfloat-j | | `TWITCH_CLIENT_ID` | Oauth2 client id | i1bjnh4whieht5kzn307nvu3rn5pqi | | `TWITCH_CLIENT_SECRET` | Oauth2 client secret | vpkn8cp7ona65l121j6q78l9gkmed3 | diff --git a/src/main/java/dev/kruhlmann/imgfloat/config/SystemEnvironmentValidator.java b/src/main/java/dev/kruhlmann/imgfloat/config/SystemEnvironmentValidator.java index faffbcd..2fdd98c 100644 --- a/src/main/java/dev/kruhlmann/imgfloat/config/SystemEnvironmentValidator.java +++ b/src/main/java/dev/kruhlmann/imgfloat/config/SystemEnvironmentValidator.java @@ -40,6 +40,12 @@ public class SystemEnvironmentValidator { @Value("${IMGFLOAT_INITIAL_TWITCH_USERNAME_SYSADMIN:#{null}}") private String initialSysadmin; + @Value("${IMGFLOAT_GITHUB_OWNER:#{null}}") + private String githubOwner; + + @Value("${IMGFLOAT_GITHUB_REPO:#{null}}") + private String githubRepo; + private long maxUploadBytes; private long maxRequestBytes; @@ -70,6 +76,8 @@ public class SystemEnvironmentValidator { checkString(twitchClientSecret, "TWITCH_CLIENT_SECRET", missing); checkString(assetsPath, "IMGFLOAT_ASSETS_PATH", missing); checkString(previewsPath, "IMGFLOAT_PREVIEWS_PATH", missing); + checkString(githubOwner, "IMGFLOAT_GITHUB_OWNER", missing); + checkString(githubRepo, "IMGFLOAT_GITHUB_REPO", missing); if (!missing.isEmpty()) { throw new IllegalStateException("Missing or invalid environment variables:\n" + missing); @@ -84,6 +92,8 @@ public class SystemEnvironmentValidator { log.info(" - IMGFLOAT_INITIAL_TWITCH_USERNAME_SYSADMIN: {}", initialSysadmin); log.info(" - IMGFLOAT_ASSETS_PATH: {}", assetsPath); log.info(" - IMGFLOAT_PREVIEWS_PATH: {}", previewsPath); + log.info(" - IMGFLOAT_GITHUB_OWNER: {}", githubOwner); + log.info(" - IMGFLOAT_GITHUB_REPO: {}", githubRepo); } private void checkString(String value, String name, StringBuilder missing) { diff --git a/src/main/java/dev/kruhlmann/imgfloat/controller/ViewController.java b/src/main/java/dev/kruhlmann/imgfloat/controller/ViewController.java index e169efb..404797f 100644 --- a/src/main/java/dev/kruhlmann/imgfloat/controller/ViewController.java +++ b/src/main/java/dev/kruhlmann/imgfloat/controller/ViewController.java @@ -9,6 +9,7 @@ import dev.kruhlmann.imgfloat.model.Settings; import dev.kruhlmann.imgfloat.service.AuthorizationService; import dev.kruhlmann.imgfloat.service.ChannelDirectoryService; import dev.kruhlmann.imgfloat.service.GitInfoService; +import dev.kruhlmann.imgfloat.service.GithubReleaseService; import dev.kruhlmann.imgfloat.service.SettingsService; import dev.kruhlmann.imgfloat.service.VersionService; import dev.kruhlmann.imgfloat.util.LogSanitizer; @@ -29,6 +30,7 @@ public class ViewController { private final GitInfoService gitInfoService; private final ObjectMapper objectMapper; private final AuthorizationService authorizationService; + private final GithubReleaseService githubReleaseService; private final long uploadLimitBytes; public ViewController( @@ -38,6 +40,7 @@ public class ViewController { GitInfoService gitInfoService, ObjectMapper objectMapper, AuthorizationService authorizationService, + GithubReleaseService githubReleaseService, long uploadLimitBytes ) { this.channelDirectoryService = channelDirectoryService; @@ -46,6 +49,7 @@ public class ViewController { this.gitInfoService = gitInfoService; this.objectMapper = objectMapper; this.authorizationService = authorizationService; + this.githubReleaseService = githubReleaseService; this.uploadLimitBytes = uploadLimitBytes; } @@ -129,6 +133,7 @@ public class ViewController { private void addVersionAttributes(Model model) { model.addAttribute("version", versionService.getVersion()); model.addAttribute("releaseVersion", versionService.getReleaseVersion()); + model.addAttribute("downloadBaseUrl", githubReleaseService.getDownloadBaseUrl()); model.addAttribute("buildCommitShort", gitInfoService.getShortCommitSha()); model.addAttribute("buildCommitUrl", gitInfoService.getCommitUrl()); } diff --git a/src/main/java/dev/kruhlmann/imgfloat/service/GithubReleaseService.java b/src/main/java/dev/kruhlmann/imgfloat/service/GithubReleaseService.java new file mode 100644 index 0000000..ff327eb --- /dev/null +++ b/src/main/java/dev/kruhlmann/imgfloat/service/GithubReleaseService.java @@ -0,0 +1,45 @@ +package dev.kruhlmann.imgfloat.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +@Component +public class GithubReleaseService { + + private static final Logger LOG = LoggerFactory.getLogger(GithubReleaseService.class); + + private final VersionService versionService; + private final String githubOwner; + private final String githubRepo; + + public GithubReleaseService( + VersionService versionService, + @Value("${IMGFLOAT_GITHUB_OWNER:#{null}}") String githubOwner, + @Value("${IMGFLOAT_GITHUB_REPO:#{null}}") String githubRepo + ) { + this.versionService = versionService; + this.githubOwner = githubOwner; + this.githubRepo = githubRepo; + } + + public String getDownloadBaseUrl() { + validateConfiguration(); + String releaseTag = versionService.getReleaseTag(); + return String.format( + "https://github.com/%s/%s/releases/download/%s/", + githubOwner.trim(), + githubRepo.trim(), + releaseTag + ); + } + + private void validateConfiguration() { + if (!StringUtils.hasText(githubOwner) || !StringUtils.hasText(githubRepo)) { + LOG.error("GitHub download configuration is missing (owner={}, repo={})", githubOwner, githubRepo); + throw new IllegalStateException("Missing GitHub owner or repo configuration for download links"); + } + } +} diff --git a/src/main/java/dev/kruhlmann/imgfloat/service/VersionService.java b/src/main/java/dev/kruhlmann/imgfloat/service/VersionService.java index c900ff8..9880306 100644 --- a/src/main/java/dev/kruhlmann/imgfloat/service/VersionService.java +++ b/src/main/java/dev/kruhlmann/imgfloat/service/VersionService.java @@ -2,9 +2,12 @@ package dev.kruhlmann.imgfloat.service; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @@ -13,23 +16,38 @@ import org.springframework.stereotype.Component; public class VersionService { private static final Logger LOG = LoggerFactory.getLogger(VersionService.class); - private final String version; + private static final Pattern PACKAGE_VERSION_PATTERN = Pattern.compile("\"version\"\\s*:\\s*\"([^\"]+)\""); + + private final String serverVersion; + private final String clientVersion; private final String releaseVersion; public VersionService() { - this.version = resolveVersion(); - this.releaseVersion = normalizeReleaseVersion(this.version); + this.serverVersion = resolveServerVersion(); + this.clientVersion = resolveClientVersion(); + this.releaseVersion = normalizeReleaseVersion(this.clientVersion); } public String getVersion() { - return version; + return serverVersion; } public String getReleaseVersion() { return releaseVersion; } - private String resolveVersion() { + public String getReleaseTag() { + if (releaseVersion == null || releaseVersion.isBlank()) { + return "latest"; + } + if ("latest".equalsIgnoreCase(releaseVersion)) { + return "latest"; + } + String normalized = releaseVersion.startsWith("v") ? releaseVersion.substring(1) : releaseVersion; + return "v" + normalized; + } + + private String resolveServerVersion() { String pomVersion = getPomVersion(); if (pomVersion != null && !pomVersion.isBlank()) { return pomVersion; @@ -43,6 +61,15 @@ public class VersionService { return "unknown"; } + private String resolveClientVersion() { + String packageJsonVersion = getPackageJsonVersion(); + if (packageJsonVersion != null && !packageJsonVersion.isBlank()) { + return packageJsonVersion; + } + + return serverVersion; + } + private String normalizeReleaseVersion(String baseVersion) { if (baseVersion == null || baseVersion.isBlank()) { return "latest"; @@ -131,4 +158,26 @@ public class VersionService { } return null; } + + private String getPackageJsonVersion() { + Path packageJsonPath = Paths.get("package.json"); + if (!Files.exists(packageJsonPath) || !Files.isRegularFile(packageJsonPath)) { + return null; + } + + try { + String packageJson = Files.readString(packageJsonPath, StandardCharsets.UTF_8); + Matcher matcher = PACKAGE_VERSION_PATTERN.matcher(packageJson); + if (matcher.find()) { + String version = matcher.group(1); + if (version != null && !version.isBlank()) { + return version.trim(); + } + } + } catch (IOException e) { + LOG.warn("Unable to read version from package.json", e); + } + + return null; + } } diff --git a/src/main/resources/templates/fragments/downloads.html b/src/main/resources/templates/fragments/downloads.html index 153fbb6..70c51ef 100644 --- a/src/main/resources/templates/fragments/downloads.html +++ b/src/main/resources/templates/fragments/downloads.html @@ -10,9 +10,7 @@

Apple Silicon build (ARM64)

- Download DMG @@ -22,9 +20,7 @@

Installer for Windows 10 and 11

- Download EXE @@ -34,9 +30,7 @@

AppImage for most distributions

- Download AppImage