package com.trafficaudit.system.controller;
|
|
import com.trafficaudit.common.Result;
|
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RestController;
|
|
import java.io.InputStream;
|
import java.net.URI;
|
import java.nio.file.Files;
|
import java.nio.file.Path;
|
import java.nio.file.Paths;
|
import java.util.LinkedHashMap;
|
import java.util.Map;
|
import java.util.Properties;
|
|
/**
|
* Exposes the product version so the UI (and operators) can confirm which build
|
* is actually running on a server. Kept public on purpose: the login page shows
|
* the version before any token exists.
|
*/
|
@RestController
|
@RequestMapping("/api")
|
public class VersionController {
|
|
private static final String VERSION_FILE = "app-version.properties";
|
|
@GetMapping("/version")
|
public Result<Map<String, Object>> version() {
|
Map<String, Object> info = new LinkedHashMap<>();
|
info.put("version", readVersion());
|
info.put("buildTime", resolveBuildTime());
|
return Result.ok(info);
|
}
|
|
private String readVersion() {
|
try (InputStream in = new ClassPathResource(VERSION_FILE).getInputStream()) {
|
Properties props = new Properties();
|
props.load(in);
|
String value = props.getProperty("version");
|
return value == null || value.trim().isEmpty() ? "unknown" : value.trim();
|
} catch (Exception e) {
|
return "unknown";
|
}
|
}
|
|
/**
|
* Build time of the running artifact. In a packaged run this is the jar file's
|
* last-modified time; when running from exploded classes (mvn spring-boot:run)
|
* there is no jar, so the value is null.
|
*/
|
private String resolveBuildTime() {
|
try {
|
URI location = VersionController.class.getProtectionDomain()
|
.getCodeSource().getLocation().toURI();
|
Path path = resolveArtifactPath(location);
|
if (path != null && Files.isRegularFile(path)) {
|
return Files.getLastModifiedTime(path).toInstant().toString();
|
}
|
} catch (Exception ignored) {
|
// Fall through: build time is optional information.
|
}
|
return null;
|
}
|
|
/**
|
* Spring Boot executable jars expose a nested classpath such as
|
* "jar:file:/app.jar!/BOOT-INF/classes!/". Strip the nested part before
|
* asking the filesystem for the jar timestamp.
|
*/
|
private Path resolveArtifactPath(URI location) {
|
String value = location.toString();
|
int nested = value.indexOf("!/");
|
if (nested >= 0) {
|
value = value.substring(0, nested);
|
}
|
if (value.startsWith("jar:")) {
|
value = value.substring(4);
|
}
|
return Paths.get(URI.create(value));
|
}
|
}
|