Fixes to NodeJS version.

This commit is contained in:
2026-04-19 20:47:47 +02:00
parent aca4998da7
commit 176fa5ead2
28 changed files with 686 additions and 134 deletions

View File

@@ -32,15 +32,28 @@ export class AzureImageService {
}
});
const payload = (await response.json()) as { value?: Array<{ name?: string; displayName?: string; metadata?: { regionType?: string } }> };
if (!response.ok) {
throw new Error(`Failed to fetch Azure locations: ${response.status} ${response.statusText}`);
}
const locations = (payload.value ?? [])
.filter((loc) => loc.metadata?.regionType === "Physical" && Boolean(loc.name))
const payload = (await response.json()) as {
value?: Array<{ name?: string; displayName?: string; metadata?: { regionType?: string } }>;
};
const allLocations = (payload.value ?? [])
.filter((loc) => Boolean(loc.name))
.map((loc) => ({
name: loc.name as string,
displayName: loc.displayName ?? (loc.name as string)
displayName: loc.displayName ?? (loc.name as string),
regionType: loc.metadata?.regionType
}));
const physical = allLocations.filter((loc) => loc.regionType?.toLowerCase() === "physical");
const locations = (physical.length > 0 ? physical : allLocations).map((loc) => ({
name: loc.name,
displayName: loc.displayName
}));
locations.sort((a, b) => a.name.localeCompare(b.name));
this.cache.set(cacheKey, locations, CACHE_TTL_MS);
return locations;
@@ -54,7 +67,7 @@ export class AzureImageService {
}
const response = await this.computeClient.virtualMachineImages.listPublishers(location);
const publishers = this.extractNames(response);
const publishers = this.extractNames(response).sort((a, b) => a.localeCompare(b));
this.cache.set(cacheKey, publishers, CACHE_TTL_MS);
return publishers;
@@ -68,7 +81,7 @@ export class AzureImageService {
}
const response = await this.computeClient.virtualMachineImages.listOffers(location, publisher);
const offers = this.extractNames(response);
const offers = this.extractNames(response).sort((a, b) => a.localeCompare(b));
this.cache.set(cacheKey, offers, CACHE_TTL_MS);
return offers;
@@ -82,7 +95,7 @@ export class AzureImageService {
}
const response = await this.computeClient.virtualMachineImages.listSkus(location, publisher, offer);
const skus = this.extractNames(response);
const skus = this.extractNames(response).sort((a, b) => a.localeCompare(b));
this.cache.set(cacheKey, skus, CACHE_TTL_MS);
return skus;

View File

@@ -1,12 +1,23 @@
import { existsSync } from "node:fs";
import { join } from "node:path";
import cors from "cors";
import "dotenv/config";
import express from "express";
import { z } from "zod";
import { AzureImageService } from "./azure-service";
import { TemplateService } from "./template-service";
const findAppNewRoot = (): string => {
const candidates = [join(__dirname, "../../.."), join(__dirname, "../..")];
for (const candidate of candidates) {
if (existsSync(join(candidate, "templates.json"))) {
return candidate;
}
}
throw new Error("Unable to resolve app-new root");
};
const queryLocation = z.object({ location: z.string().min(1) });
const queryOffer = z.object({ location: z.string().min(1), publisher: z.string().min(1) });
const querySku = z.object({ location: z.string().min(1), publisher: z.string().min(1), offer: z.string().min(1) });
@@ -127,7 +138,7 @@ const makeApp = () => {
res.status(500).json({ message });
});
const frontendRoot = join(process.cwd(), "dist/frontend");
const frontendRoot = join(findAppNewRoot(), "dist/frontend");
if (existsSync(frontendRoot)) {
app.use(express.static(frontendRoot));
app.get(/^(?!\/api).*/, (_req, res) => {

View File

@@ -1,30 +1,30 @@
import { readFileSync } from "node:fs";
import { existsSync, readFileSync } from "node:fs";
import { join } from "node:path";
import nunjucks from "nunjucks";
import type { ImageSelection, UsageTemplate } from "./types";
const findAppRoot = (): string => {
// Supports running from src/backend (dev) and dist/backend (compiled).
const devPath = join(__dirname, "../../../app");
const buildPath = join(__dirname, "../../app");
try {
readFileSync(join(devPath, "templates.json"), "utf8");
return devPath;
} catch {
return buildPath;
const findAppNewRoot = (): string => {
const candidates = [join(__dirname, "../../.."), join(__dirname, "../..")];
for (const candidate of candidates) {
if (existsSync(join(candidate, "templates.json"))) {
return candidate;
}
}
throw new Error("Unable to resolve app-new template root");
};
export class TemplateService {
private readonly appRoot = findAppRoot();
private readonly appNewRoot = findAppNewRoot();
private readonly env = nunjucks.configure(join(this.appRoot, "templates"), {
private readonly env = nunjucks.configure(join(this.appNewRoot, "templates"), {
autoescape: false,
noCache: true
});
private readonly templates: UsageTemplate[] = JSON.parse(
readFileSync(join(this.appRoot, "templates.json"), "utf8")
readFileSync(join(this.appNewRoot, "templates.json"), "utf8")
) as UsageTemplate[];
public getTemplates(): UsageTemplate[] {

View File

@@ -4,7 +4,7 @@ const semverSortKey = (value: string): number[] => value.split(".").map((part) =
export const sortImageVersionsIfSemantic = (versions: string[]): string[] => {
if (!versions.every((value) => SEMVER_PATTERN.test(value))) {
return versions;
return [...versions].sort((a, b) => a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }));
}
return [...versions].sort((a, b) => {