Add release management components and update routing logic. Introduce keyboard shortcuts, enhance release data grid, and improve asset handling in Nginx configuration.

This commit is contained in:
Jeppe Bundgaard
2026-05-26 14:14:13 +02:00
parent a952dd2c61
commit 8ab8915429
77 changed files with 8353 additions and 1143 deletions
+29 -8
View File
@@ -18,6 +18,7 @@ import {
releaseManagerControlApiCandidates,
setReleaseManagerControlApiUrl,
} from "@/services/superuserReleases.js";
import { configureReleaseRuntime } from "@/services/releaseTimeline.js";
import { deployCoolifyGatewayRoutes } from "@/services/superuserCoolify.js";
import { __configureRequestQueueForTests, __resetRequestQueueForTests } from "@/services/requestQueue.js";
@@ -27,6 +28,7 @@ describe("superuser release manager service", () => {
localStorage.clear();
sessionStorage.clear();
localStorage.setItem("token", "release-token");
configureReleaseRuntime({ source: "deployment", api_base_url: "https://api.truckwash.io" });
__resetRequestQueueForTests();
__configureRequestQueueForTests({
maxConcurrentGet: 1,
@@ -36,16 +38,17 @@ describe("superuser release manager service", () => {
});
});
it("keeps release manager calls on the explicit control API", async () => {
it("uses the runtime release API before legacy control API overrides", async () => {
configureReleaseRuntime({ api_base_url: "https://api-v2.truckwash.io/master/api" });
setReleaseManagerControlApiUrl("https://control.example.test/");
axiosMock.mockResolvedValueOnce({ status: 200, data: { data: { channels: [] } } });
await getReleaseSummary();
expect(getReleaseManagerControlApiUrl()).toBe("https://control.example.test");
expect(getReleaseManagerControlApiUrl()).toBe("https://api-v2.truckwash.io/master/api");
expect(axiosMock).toHaveBeenCalledWith(
expect.objectContaining({
url: "https://control.example.test/superuser/releases",
url: "https://api-v2.truckwash.io/master/api/superuser/releases",
method: "GET",
headers: expect.objectContaining({ Authorization: "Bearer release-token" }),
})
@@ -61,8 +64,25 @@ describe("superuser release manager service", () => {
await getReleaseSummary();
expect(axiosMock.mock.calls[0][0].url).toBe("https://api.truckwash.io/superuser/releases");
expect(axiosMock.mock.calls[1][0].url).toBe("https://api.truckwash.io:4433/superuser/releases");
expect(releaseManagerControlApiCandidates()[0]).toBe("https://api.truckwash.io:4433");
expect(axiosMock.mock.calls[1][0].url).toBe("https://api-v2.truckwash.io/master/api/superuser/releases");
expect(releaseManagerControlApiCandidates()).toContain("https://api-v2.truckwash.io/master/api");
});
it("uses local runtime API only when the release source is local", async () => {
configureReleaseRuntime({ source: "local", api_base_url: "/api" });
localStorage.setItem(RELEASE_MANAGER_CONTROL_API_STORAGE_KEY, "https://api.truckwash.io");
axiosMock.mockResolvedValueOnce({ status: 200, data: { data: { channels: [] } } });
await getReleaseSummary();
expect(releaseManagerControlApiCandidates()).toEqual(["/api"]);
expect(getReleaseManagerControlApiUrl()).toBe("/api");
expect(axiosMock).toHaveBeenCalledWith(
expect.objectContaining({
url: "/api/superuser/releases",
method: "GET",
})
);
});
it("can clear the explicit control API override", () => {
@@ -70,10 +90,11 @@ describe("superuser release manager service", () => {
clearReleaseManagerControlApiUrl();
expect(localStorage.getItem(RELEASE_MANAGER_CONTROL_API_STORAGE_KEY)).toBeNull();
expect(releaseManagerControlApiCandidates()).toContain("https://api.truckwash.io:4433");
expect(releaseManagerControlApiCandidates()).toContain("https://api-v2.truckwash.io/master/api");
});
it("keeps Coolify route deployment on the release manager control API", async () => {
it("keeps Coolify route deployment on the runtime release API", async () => {
configureReleaseRuntime({ api_base_url: "https://api-v2.truckwash.io/master/api" });
setReleaseManagerControlApiUrl("https://control.example.test");
axiosMock.mockResolvedValueOnce({ status: 200, data: { success: true } });
@@ -81,7 +102,7 @@ describe("superuser release manager service", () => {
expect(axiosMock).toHaveBeenCalledWith(
expect.objectContaining({
url: "https://control.example.test/superuser/coolify/load-balancer/routes/deploy",
url: "https://api-v2.truckwash.io/master/api/superuser/coolify/load-balancer/routes/deploy",
method: "POST",
data: { dry_run: true },
__skipReleaseApiRewrite: true,