diff --git a/backend/app.ts b/backend/app.ts index a868fad..3591010 100644 --- a/backend/app.ts +++ b/backend/app.ts @@ -9,6 +9,7 @@ import { routeHitMiddleware } from "./middleware/routeHit.middleware.js"; import { setupRoutes } from "./routeHandler.routes.js"; import { auth } from "./utils/auth.utils.js"; import { lstCors } from "./utils/cors.utils.js"; +import { getAppVersion } from "./utils/version.utils.js"; const createApp = async () => { const log = createLogger({ module: "system", subModule: "main start" }); @@ -35,6 +36,7 @@ const createApp = async () => { app.all(`${baseUrl}/api/auth/*splat`, toNodeHandler(auth)); app.use(express.json()); + const version = await getAppVersion(); app.get(`${baseUrl}/api/lst-config.js`, (_, res) => { res.type("application/javascript"); res.setHeader("Cache-Control", "no-store"); @@ -47,7 +49,9 @@ const createApp = async () => { appVersion: ${JSON.stringify(umamiConfig.appVersion ?? "dev")}, umamiHost: ${JSON.stringify(umamiConfig.umamiHost ?? "")}, umamiWebsiteId: ${JSON.stringify(umamiConfig.umamiWebsiteId ?? "")}, - timezone: ${JSON.stringify(process.env.TIMEZONE ?? "America/Chicago")} + timezone: ${JSON.stringify(process.env.TIMEZONE ?? "America/Chicago")}, + version: ${JSON.stringify(version.version)}, + lastBuildTIme: ${JSON.stringify(version.lastBuildTime)} }; `); }); diff --git a/backend/configs/scaler.config.ts b/backend/configs/scaler.config.ts index bba0474..634fb95 100644 --- a/backend/configs/scaler.config.ts +++ b/backend/configs/scaler.config.ts @@ -17,12 +17,13 @@ import { prodStopSpec } from "../scaler/prodSqlStop.spec.js"; import { prodRegisterSpec } from "../scaler/register.spec.js"; // all the specs import { statusSpec } from "../scaler/stats.spec.js"; +import { getAppVersion } from "../utils/version.utils.js"; export const openApiBase: OpenAPIV3_1.Document = { openapi: "3.1.0", info: { title: "LST API", - version: "3.0.0", + version: (await getAppVersion()).version ?? "", description: "Label System Tracking API", }, servers: [ diff --git a/backend/server.ts b/backend/server.ts index 1b86555..56c4bbb 100644 --- a/backend/server.ts +++ b/backend/server.ts @@ -1,5 +1,6 @@ import { createServer } from "node:http"; import os from "node:os"; + import createApp from "./app.js"; import { db } from "./db/db.controller.js"; import { startDbNotificationListener } from "./db/db.listener.js"; @@ -32,6 +33,7 @@ import { ppooMonitoring } from "./warehousing/warehousing.ppooMonitor.js"; const port = Number(process.env.PORT) || 3000; export let systemSettings: Setting[] = []; + const start = async () => { const { app, baseUrl } = await createApp(); diff --git a/backend/system/stats.route.ts b/backend/system/stats.route.ts index 6b26b60..f1acf84 100644 --- a/backend/system/stats.route.ts +++ b/backend/system/stats.route.ts @@ -6,12 +6,15 @@ import { type SqlQuery, sqlQuerySelector, } from "../prodSql/prodSqlQuerySelector.utils.js"; + import { isServerRunning } from "../tcpServer/tcp.server.js"; +import { getAppVersion } from "../utils/version.utils.js"; const router = Router(); router.get("/", async (_, res) => { const used = process.memoryUsage(); + const version = await getAppVersion(); const query = sqlQuerySelector("prodSqlStats") as SqlQuery; @@ -20,6 +23,8 @@ router.get("/", async (_, res) => { status: "ok", uptime: process.uptime(), nodeVersion: process.version, + appVersion: version.version ?? "", + lastBuildDate: version.lastBuildTime ?? "", memoryUsage: `Heap: ${(used.heapUsed / 1024 / 1024).toFixed(2)} MB / RSS: ${( used.rss / 1024 / 1024 ).toFixed(2)} MB`, diff --git a/backend/utils/version.utils.ts b/backend/utils/version.utils.ts new file mode 100644 index 0000000..02a7357 --- /dev/null +++ b/backend/utils/version.utils.ts @@ -0,0 +1,16 @@ +import fsp from "node:fs/promises"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +export const getAppVersion = async () => { + const __filename = fileURLToPath(import.meta.url); + const __dirname = path.dirname(__filename); + const version = path.join(__dirname, "../../package.json"); + const raw = await fsp.readFile(`${version}`, "utf8"); + const config = JSON.parse(raw); + + return { + version: `${config.version}.${parseInt((config.build as string) ?? "1", 0) - 1}`, + lastBuildTime: config.lastBuildDate, + }; +}; diff --git a/backend/utils/zipper.utils.ts b/backend/utils/zipper.utils.ts index 4b6eac3..f6eda46 100644 --- a/backend/utils/zipper.utils.ts +++ b/backend/utils/zipper.utils.ts @@ -2,6 +2,7 @@ import fs from "node:fs"; import fsp from "node:fs/promises"; import path from "node:path"; import archiver from "archiver"; +import { format } from "date-fns"; import { createLogger } from "../logger/logger.controller.js"; import { emitBuildLog } from "./build.utils.js"; import { updateAppStats } from "./updateAppStats.utils.js"; @@ -17,33 +18,31 @@ const exists = async (target: string) => { } }; -const getNextBuildNumber = async (buildNumberFile: string) => { - if (!(await exists(buildNumberFile))) { - await fsp.writeFile(buildNumberFile, "1", "utf8"); - return 1; - } - - const raw = await fsp.readFile(buildNumberFile, "utf8"); - const current = Number.parseInt(raw.trim(), 10); +const getNextBuildNumber = async (versionPath: string) => { + const raw = await fsp.readFile(versionPath, "utf8"); + const config = JSON.parse(raw); + const current = Number.parseInt(config.build.trim(), 10); + let nextBuild: string; if (Number.isNaN(current) || current < 1) { - await fsp.writeFile(buildNumberFile, "1", "utf8"); - return 1; + nextBuild = "1"; + } else { + nextBuild = String(current + 1); // Incrementing the build number } - const next = current + 1; + const updatedConfig = { + ...config, + build: nextBuild, + lastBuildDate: format(new Date(Date.now()), "M/d/yyyy HH:mm"), + }; - await fsp.writeFile(buildNumberFile, String(next), "utf8"); + await fsp.writeFile( + versionPath, + JSON.stringify(updatedConfig, null, 4) + "\n", + "utf8", + ); - // update the server with the next build number - - await updateAppStats({ - currentBuild: next, - lastBuildAt: new Date(), - building: true, - }); - - return next; + return { version: config.version, build: config.build }; }; const cleanupOldBuilds = async (buildFolder: string, maxBuilds: number) => { @@ -53,7 +52,8 @@ const cleanupOldBuilds = async (buildFolder: string, maxBuilds: number) => { for (const entry of entries) { if (!entry.isFile()) continue; - if (!/^LSTV3-\d+\.zip$/i.test(entry.name)) continue; + //if (!/^LSTV3-\d+\.zip$/i.test(entry.name)) continue; + if (!entry.name.includes("LSTV3")) continue; const fullPath = path.join(buildFolder, entry.name); const stat = await fsp.stat(fullPath); @@ -85,7 +85,7 @@ export const zipBuild = async () => { } const includesFile = path.join(appDir, ".includes"); - const buildNumberFile = path.join(appDir, ".buildNumber"); + const version = path.join(appDir, "package.json"); const buildFolder = path.join(appDir, "builds"); const tempFolder = path.join(appDir, "temp", "zip-temp"); if (!(await exists(includesFile))) { @@ -95,11 +95,13 @@ export const zipBuild = async () => { await fsp.mkdir(buildFolder, { recursive: true }); - const buildNumber = await getNextBuildNumber(buildNumberFile); - const zipFileName = `LSTV3-${buildNumber}.zip`; + const lstVersion = await getNextBuildNumber(version); + const zipFileName = `LSTV3-${lstVersion.version}.${lstVersion.build}.zip`; const zipFile = path.join(buildFolder, zipFileName); // make the folders in case they are not created already - emitBuildLog(`Using build number: ${buildNumber}`); + emitBuildLog( + `Using version, build number: ${lstVersion.version}.${lstVersion.build}`, + ); if (await exists(tempFolder)) { await fsp.rm(tempFolder, { recursive: true, force: true }); @@ -170,7 +172,7 @@ export const zipBuild = async () => { return { success: true, - buildNumber, + build: lstVersion.build, zipFile, zipFileName, }; diff --git a/package.json b/package.json index 154aa65..e29762e 100644 --- a/package.json +++ b/package.json @@ -1,113 +1,115 @@ { - "name": "lst_v3", - "version": "0.1.0-alpha.3", - "description": "The tool that supports us in our everyday alplaprod", - "main": "index.js", - "scripts": { - "test": "dotenvx run -f .env -- vitest", - "test:run": "dotenvx run -f .env -- vitest run", - "dev": "concurrently -n \"server,frontend\" -c \"#007755, #1F73D1\" \"npm run dev:app\" \"npm run dev:frontend\"", - "dev:app": "dotenvx run -f .env -- tsx watch backend/server.ts", - "dev:frontend": "cd frontend && npm run dev", - "dev:db:migrate": "npx drizzle-kit push", - "dev:db:generate": "tsc && npx drizzle-kit generate --config=drizzle.config.ts", - "build": "rimraf dist && npm run dev:db:generate && npm run dev:db:migrate && npm run build:app && npm run build:copySql && npm run build:copyGpSql && npm run build:emailTemplate && cd frontend && npm run build", - "build:app": "tsc", - "agent": "powershell -ExecutionPolicy Bypass -File scripts/agentController.ps1", - "build:docker": "rimraf dist && npm run build:app && npm run build:copySql && npm run build:copyGpSql && npm run build:emailTemplate", - "build:emailTemplate": "cpy \"backend/utils/mailViews/**/*\" dist/utils/mailViews --parents", - "build:copyGpSql": "cpy \"backend/gpSql/queries/**/*\" dist/gpSql/queries --parents", - "build:copySql": "cpy \"backend/prodSql/queries/**/*\" dist/prodSql/queries --parents", - "lint": "tsc && biome lint", - "start": "npm run start:server", - "start:server": "dotenvx run -f .env -- node dist/server.js", - "start:docker": "node dist/server.js", - "version": "changeset version", - "specCheck": "node scripts/check-route-specs.mjs", - "commit": "cz", - "release": "npm run build && commit-and-tag-version", - "build:apk": "cd lstMobile && expo prebuild --clean && cd android && gradlew.bat assembleRelease " - }, - "repository": { - "type": "git", - "url": "https://git.tuffraid.net/cowch/lst_v3.git" - }, - "keywords": [], - "author": "", - "license": "ISC", - "type": "module", - "devDependencies": { - "@biomejs/biome": "2.4.8", - "@commitlint/cli": "^20.5.0", - "@commitlint/config-conventional": "^20.5.0", - "@types/archiver": "^7.0.0", - "@types/cors": "^2.8.19", - "@types/express": "^5.0.6", - "@types/morgan": "^1.9.10", - "@types/mssql": "^9.1.9", - "@types/multer": "^2.1.0", - "@types/net-snmp": "^3.23.0", - "@types/node": "^25.5.0", - "@types/nodemailer": "^7.0.11", - "@types/nodemailer-express-handlebars": "^4.0.6", - "@types/pg": "^8.18.0", - "@types/supertest": "^7.2.0", - "@types/swagger-jsdoc": "^6.0.4", - "@types/swagger-ui-express": "^4.1.8", - "commit-and-tag-version": "^12.7.1", - "commitizen": "^4.3.1", - "cpy-cli": "^7.0.0", - "cz-conventional-changelog": "^3.3.0", - "npm-check-updates": "^19.6.5", - "openapi-types": "^12.1.3", - "supertest": "^7.2.2", - "ts-node-dev": "^2.0.0", - "tsx": "^4.21.0", - "typescript": "^5.9.3", - "vitest": "^4.1.8" - }, - "dependencies": { - "@dotenvx/dotenvx": "^1.57.0", - "@scalar/express-api-reference": "^0.9.20", - "@socket.io/admin-ui": "^0.5.1", - "archiver": "^7.0.1", - "axios": "^1.13.6", - "bcryptjs": "^3.0.3", - "better-auth": "^1.5.5", - "chokidar": "^5.0.0", - "concurrently": "^9.2.1", - "cors": "^2.8.6", - "croner": "^10.0.1", - "date-fns": "^4.1.0", - "date-fns-tz": "^3.2.0", - "drizzle-kit": "^0.31.10", - "drizzle-orm": "^0.45.1", - "drizzle-zod": "^0.8.3", - "excel-date-to-js": "^1.1.5", - "express": "^5.2.1", - "husky": "^9.1.7", - "ldapts": "^8.1.7", - "modbus-serial": "^8.0.25", - "morgan": "^1.10.1", - "mssql": "^12.2.1", - "multer": "^2.1.1", - "net-snmp": "^3.26.1", - "nodemailer": "^8.0.3", - "nodemailer-express-handlebars": "^7.0.0", - "pg": "^8.20.0", - "pino": "^10.3.1", - "pino-pretty": "^13.1.3", - "postgres": "^3.4.8", - "powershell": "^2.3.3", - "socket.io": "^4.8.3", - "socket.io-client": "^4.8.3", - "xlsx": "^0.18.5", - "zod": "^4.3.6", - "zod-openapi": "^6.0.0" - }, - "config": { - "commitizen": { - "path": "cz-conventional-changelog" - } - } + "name": "lst_v3", + "version": "0.1.0-alpha.3", + "build": "175", + "lastBuildDate": "6/17/2026 10:26", + "description": "The tool that supports us in our everyday alplaprod", + "main": "index.js", + "scripts": { + "test": "dotenvx run -f .env -- vitest", + "test:run": "dotenvx run -f .env -- vitest run", + "dev": "concurrently -n \"server,frontend\" -c \"#007755, #1F73D1\" \"npm run dev:app\" \"npm run dev:frontend\"", + "dev:app": "dotenvx run -f .env -- tsx watch backend/server.ts", + "dev:frontend": "cd frontend && npm run dev", + "dev:db:migrate": "npx drizzle-kit push", + "dev:db:generate": "tsc && npx drizzle-kit generate --config=drizzle.config.ts", + "build": "rimraf dist && npm run dev:db:generate && npm run dev:db:migrate && npm run build:app && npm run build:copySql && npm run build:copyGpSql && npm run build:emailTemplate && cd frontend && npm run build", + "build:app": "tsc", + "agent": "powershell -ExecutionPolicy Bypass -File scripts/agentController.ps1", + "build:docker": "rimraf dist && npm run build:app && npm run build:copySql && npm run build:copyGpSql && npm run build:emailTemplate", + "build:emailTemplate": "cpy \"backend/utils/mailViews/**/*\" dist/utils/mailViews --parents", + "build:copyGpSql": "cpy \"backend/gpSql/queries/**/*\" dist/gpSql/queries --parents", + "build:copySql": "cpy \"backend/prodSql/queries/**/*\" dist/prodSql/queries --parents", + "lint": "tsc && biome lint", + "start": "npm run start:server", + "start:server": "dotenvx run -f .env -- node dist/server.js", + "start:docker": "node dist/server.js", + "version": "changeset version", + "specCheck": "node scripts/check-route-specs.mjs", + "commit": "cz", + "release": "npm run build && commit-and-tag-version", + "build:apk": "cd lstMobile && expo prebuild --clean && cd android && gradlew.bat assembleRelease " + }, + "repository": { + "type": "git", + "url": "https://git.tuffraid.net/cowch/lst_v3.git" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "module", + "devDependencies": { + "@biomejs/biome": "2.4.8", + "@commitlint/cli": "^20.5.0", + "@commitlint/config-conventional": "^20.5.0", + "@types/archiver": "^7.0.0", + "@types/cors": "^2.8.19", + "@types/express": "^5.0.6", + "@types/morgan": "^1.9.10", + "@types/mssql": "^9.1.9", + "@types/multer": "^2.1.0", + "@types/net-snmp": "^3.23.0", + "@types/node": "^25.5.0", + "@types/nodemailer": "^7.0.11", + "@types/nodemailer-express-handlebars": "^4.0.6", + "@types/pg": "^8.18.0", + "@types/supertest": "^7.2.0", + "@types/swagger-jsdoc": "^6.0.4", + "@types/swagger-ui-express": "^4.1.8", + "commit-and-tag-version": "^12.7.1", + "commitizen": "^4.3.1", + "cpy-cli": "^7.0.0", + "cz-conventional-changelog": "^3.3.0", + "npm-check-updates": "^19.6.5", + "openapi-types": "^12.1.3", + "supertest": "^7.2.2", + "ts-node-dev": "^2.0.0", + "tsx": "^4.21.0", + "typescript": "^5.9.3", + "vitest": "^4.1.8" + }, + "dependencies": { + "@dotenvx/dotenvx": "^1.57.0", + "@scalar/express-api-reference": "^0.9.20", + "@socket.io/admin-ui": "^0.5.1", + "archiver": "^7.0.1", + "axios": "^1.13.6", + "bcryptjs": "^3.0.3", + "better-auth": "^1.5.5", + "chokidar": "^5.0.0", + "concurrently": "^9.2.1", + "cors": "^2.8.6", + "croner": "^10.0.1", + "date-fns": "^4.1.0", + "date-fns-tz": "^3.2.0", + "drizzle-kit": "^0.31.10", + "drizzle-orm": "^0.45.1", + "drizzle-zod": "^0.8.3", + "excel-date-to-js": "^1.1.5", + "express": "^5.2.1", + "husky": "^9.1.7", + "ldapts": "^8.1.7", + "modbus-serial": "^8.0.25", + "morgan": "^1.10.1", + "mssql": "^12.2.1", + "multer": "^2.1.1", + "net-snmp": "^3.26.1", + "nodemailer": "^8.0.3", + "nodemailer-express-handlebars": "^7.0.0", + "pg": "^8.20.0", + "pino": "^10.3.1", + "pino-pretty": "^13.1.3", + "postgres": "^3.4.8", + "powershell": "^2.3.3", + "socket.io": "^4.8.3", + "socket.io-client": "^4.8.3", + "xlsx": "^0.18.5", + "zod": "^4.3.6", + "zod-openapi": "^6.0.0" + }, + "config": { + "commitizen": { + "path": "cz-conventional-changelog" + } + } } diff --git a/scripts/updateServer.ps1 b/scripts/updateServer.ps1 index 9787733..9d7204e 100644 --- a/scripts/updateServer.ps1 +++ b/scripts/updateServer.ps1 @@ -31,24 +31,32 @@ function Update-Server { [string]$Server, [string]$Token ) - $buildFile = Join-Path $AppDir ".buildNumber" + + $buildFile = Join-Path $AppDir "package.json" $BuildNumber = 1 $BuildFolder = Join-Path $AppDir "builds" - if (Test-Path $BuildFile) { - $content = Get-Content $BuildFile | Select-Object -First 1 - $num = $content.Trim() -as [int] # safe cast + $packageJson = Get-Content $buildFile -Raw | ConvertFrom-Json + + # Extract the .build property and cast it safely to an integer + $num = "$($packageJson.version).$(([int]$packageJson.build) -1)" + # if (Test-Path $BuildFile) { + # # Parse the entire JSON file into a PowerShell object + # $packageJson = Get-Content $buildFile -Raw | ConvertFrom-Json + + # # Extract the .build property and cast it safely to an integer + # $num = "($packageJson.version).($packageJson.build)" - if ($num) { - $BuildNumber = $num + 1 - } - else { - $BuildNumber = 1 - } - } + # if ($null -ne $num) { + # $BuildNumber = $num + 1 + # } + # else { + # $BuildNumber = 1 + # } + # } # Get The current Build we have zipped up - $BuildNumber = ([int]$BuildNumber - 1).ToString() + $BuildNumber = $num # copy the latest build over