5 Commits

Author SHA1 Message Date
3e8417dcff refactor(builds): refactored the build process to hold in the build info plus version
All checks were successful
Build and Push LST Docker Image / docker (push) Successful in 3m54s
2026-06-18 13:51:17 -05:00
1b1918dcd0 refactor(builds): refactored how the builds works to include the build number now
this will be for the new lst - ccc
2026-06-17 10:34:53 -05:00
4ff10dfcb2 test(dm): repost test
All checks were successful
Build and Push LST Docker Image / docker (push) Successful in 1m39s
Checking why comments do not post correctly

ref #31
2026-06-17 01:50:25 -05:00
9a0bb18c5b feat(dm): added in a repost incase we wanted do this instead of reuploading the file
All checks were successful
Build and Push LST Docker Image / docker (push) Successful in 2m34s
ref #31
2026-06-17 01:44:15 -05:00
1838c6f1e9 docs(readme): updated readme with actaul install 2026-06-17 01:39:00 -05:00
14 changed files with 361 additions and 164 deletions

View File

@@ -7,7 +7,7 @@
Quick summary of current rewrite/migration goal. Quick summary of current rewrite/migration goal.
- **Phase:** Backend rewrite - **Phase:** Backend rewrite
- **Last updated:** 2026-05-27 - **Last updated:** 2026-06-17
--- ---
@@ -39,21 +39,91 @@ _Status legend:_
--- ---
## Setup / Installation # Install
How to run the current version of the app. ## Files needed to be downloaded before install.
### To run the server
- [PostgresSQL](https://www.postgresql.org/download/windows/) - current version using is 17
- [NodeJS](https://nodejs.org)
- [NSSM](https://nssm.cc/)
### To manage the server
- [VSCODE](https://code.visualstudio.com/)
## Creating directories needed
- Create a new folder where we will host the server files.
- Copy the nssm.exe into this folder
- Copy the get the build from the releases and extract.
- This will house all the compiles and minified files needed to start the server up, this includes the frontend.
- Save the nssm.exe into this folder as well, this will be used to control the service.
## Do the initial install
### DB instal setup
1. Install postgres
2. Open pgAdmin
3. create a new Database named lst_db_v3. this can also be to your liking
### Initial server setup
1. Open VSCode and navigate to the folder where you extracted the files.
2. Click trusted when it pops up.
3. Open a terminal window inside vscode.
4. Run the install script this will install all dependence's needed as well as do all the database migrations
### Create the .env file
In the root of the folder create a new .env file by renaming .env-example to .env
change all the parameters to your desired server
```bash ```bash
git clone https://git.tuffraid.net/cowch/lst_v3.git npm run install --omit=dev
cd lst_v3
npm install
``` ```
Rename the .env-example to .env Next we want to do an initial db
Update all the fields
```bash ```bash
npm run dev:db:migrate npm run dev:db:migrate
npm run dev ```
### Run the start command to get all the basic settings and modules installed
1. Run the below
```bash
npm start
```
### Creating first user.
Open http://[SERVER]:[PORT]/api/docs or postman and create a user.
- Please do not try to manually enter a new user this is due to how the password is hashed, as well as setting systemAdmin for the first user.
- Change the server and port to what you changed in the DB.
### Running as a serivice.
You want to CD into the scripts folder.
```bash
cd .\scripts\
```
Next use the example command below to get the service up and running.
- Options legend
- serviceName = not recommended to change to reduce issues with the update process
- option = use install for the install, but you can use this script later to stop, start, restart the service.
- appPath = where did you extract the server files
- description = no need to change this unless you want it to be something else
- command = do not change this unless you know what your doing and really need to change this.
```powershell
.\services.ps1 -serviceName "LSTV3_app" -option "install" -appPath "D:\LS_V3T" -description "Logistics Support Tool V3" -command "run start"
``` ```

View File

@@ -9,6 +9,7 @@ import { routeHitMiddleware } from "./middleware/routeHit.middleware.js";
import { setupRoutes } from "./routeHandler.routes.js"; import { setupRoutes } from "./routeHandler.routes.js";
import { auth } from "./utils/auth.utils.js"; import { auth } from "./utils/auth.utils.js";
import { lstCors } from "./utils/cors.utils.js"; import { lstCors } from "./utils/cors.utils.js";
import { getAppVersion } from "./utils/version.utils.js";
const createApp = async () => { const createApp = async () => {
const log = createLogger({ module: "system", subModule: "main start" }); const log = createLogger({ module: "system", subModule: "main start" });
@@ -35,6 +36,7 @@ const createApp = async () => {
app.all(`${baseUrl}/api/auth/*splat`, toNodeHandler(auth)); app.all(`${baseUrl}/api/auth/*splat`, toNodeHandler(auth));
app.use(express.json()); app.use(express.json());
const version = await getAppVersion();
app.get(`${baseUrl}/api/lst-config.js`, (_, res) => { app.get(`${baseUrl}/api/lst-config.js`, (_, res) => {
res.type("application/javascript"); res.type("application/javascript");
res.setHeader("Cache-Control", "no-store"); res.setHeader("Cache-Control", "no-store");
@@ -47,7 +49,9 @@ const createApp = async () => {
appVersion: ${JSON.stringify(umamiConfig.appVersion ?? "dev")}, appVersion: ${JSON.stringify(umamiConfig.appVersion ?? "dev")},
umamiHost: ${JSON.stringify(umamiConfig.umamiHost ?? "")}, umamiHost: ${JSON.stringify(umamiConfig.umamiHost ?? "")},
umamiWebsiteId: ${JSON.stringify(umamiConfig.umamiWebsiteId ?? "")}, 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)}
}; };
`); `);
}); });

View File

@@ -17,12 +17,13 @@ import { prodStopSpec } from "../scaler/prodSqlStop.spec.js";
import { prodRegisterSpec } from "../scaler/register.spec.js"; import { prodRegisterSpec } from "../scaler/register.spec.js";
// all the specs // all the specs
import { statusSpec } from "../scaler/stats.spec.js"; import { statusSpec } from "../scaler/stats.spec.js";
import { getAppVersion } from "../utils/version.utils.js";
export const openApiBase: OpenAPIV3_1.Document = { export const openApiBase: OpenAPIV3_1.Document = {
openapi: "3.1.0", openapi: "3.1.0",
info: { info: {
title: "LST API", title: "LST API",
version: "3.0.0", version: (await getAppVersion()).version ?? "",
description: "Label System Tracking API", description: "Label System Tracking API",
}, },
servers: [ servers: [

View File

@@ -0,0 +1,57 @@
import { Router } from "express";
import { requireAuth } from "../middleware/auth.middleware.js";
import { apiReturn } from "../utils/returnHelper.utils.js";
import { postData } from "./logistics.dm.postData.js";
const r = Router();
r.post("/", requireAuth, async (req, res) => {
let posting: any;
if (req.body.type !== "forecast" || req.body.type !== "orders") {
return apiReturn(res, {
success: false,
level: "error",
module: "dm",
subModule: "repost",
message: "You must pass over a proper type.",
data: [],
status: 500,
});
}
if (req.body.type === "forecast") {
posting = await postData(
{
type: "forecast",
endpoint: "/public/v1.0/DemandManagement/DELFOR",
data: req.body.data as any,
},
req.user,
);
}
if (req.body.type === "orders") {
posting = await postData(
{
type: "orders",
endpoint: "/public/v1.0/DemandManagement/ORDERS",
data: req.body.data as any,
},
req.user,
);
}
return apiReturn(res, {
success: posting.success,
level: posting.success ? "info" : "error",
module: "dm",
subModule: "repost",
message: posting.message,
data: [],
status: 200,
});
});
export default r;

View File

@@ -1,5 +1,6 @@
import { createServer } from "node:http"; import { createServer } from "node:http";
import os from "node:os"; import os from "node:os";
import createApp from "./app.js"; import createApp from "./app.js";
import { db } from "./db/db.controller.js"; import { db } from "./db/db.controller.js";
import { startDbNotificationListener } from "./db/db.listener.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; const port = Number(process.env.PORT) || 3000;
export let systemSettings: Setting[] = []; export let systemSettings: Setting[] = [];
const start = async () => { const start = async () => {
const { app, baseUrl } = await createApp(); const { app, baseUrl } = await createApp();

View File

@@ -6,12 +6,15 @@ import {
type SqlQuery, type SqlQuery,
sqlQuerySelector, sqlQuerySelector,
} from "../prodSql/prodSqlQuerySelector.utils.js"; } from "../prodSql/prodSqlQuerySelector.utils.js";
import { isServerRunning } from "../tcpServer/tcp.server.js"; import { isServerRunning } from "../tcpServer/tcp.server.js";
import { getAppVersion } from "../utils/version.utils.js";
const router = Router(); const router = Router();
router.get("/", async (_, res) => { router.get("/", async (_, res) => {
const used = process.memoryUsage(); const used = process.memoryUsage();
const version = await getAppVersion();
const query = sqlQuerySelector("prodSqlStats") as SqlQuery; const query = sqlQuerySelector("prodSqlStats") as SqlQuery;
@@ -20,6 +23,8 @@ router.get("/", async (_, res) => {
status: "ok", status: "ok",
uptime: process.uptime(), uptime: process.uptime(),
nodeVersion: process.version, nodeVersion: process.version,
appVersion: version.version ?? "",
lastBuildDate: version.lastBuildTime ?? "",
memoryUsage: `Heap: ${(used.heapUsed / 1024 / 1024).toFixed(2)} MB / RSS: ${( memoryUsage: `Heap: ${(used.heapUsed / 1024 / 1024).toFixed(2)} MB / RSS: ${(
used.rss / 1024 / 1024 used.rss / 1024 / 1024
).toFixed(2)} MB`, ).toFixed(2)} MB`,

View File

@@ -2,6 +2,7 @@ import { spawn } from "node:child_process";
import { createLogger } from "../logger/logger.controller.js"; import { createLogger } from "../logger/logger.controller.js";
import { emitToRoom } from "../socket.io/roomEmitter.socket.js"; import { emitToRoom } from "../socket.io/roomEmitter.socket.js";
import { updateAppStats } from "./updateAppStats.utils.js"; import { updateAppStats } from "./updateAppStats.utils.js";
import { getAppVersion } from "./version.utils.js";
import { zipBuild } from "./zipper.utils.js"; import { zipBuild } from "./zipper.utils.js";
export const emitBuildLog = (message: string, level = "info") => { export const emitBuildLog = (message: string, level = "info") => {
@@ -28,6 +29,8 @@ export let building = false;
const log = createLogger({ module: "utils", subModule: "builds" }); const log = createLogger({ module: "utils", subModule: "builds" });
export const build = async () => { export const build = async () => {
const appDir = process.env.DEV_DIR ?? ""; const appDir = process.env.DEV_DIR ?? "";
const build = await getAppVersion();
return new Promise((resolve) => { return new Promise((resolve) => {
building = true; building = true;
@@ -72,6 +75,7 @@ export const build = async () => {
updateAppStats({ updateAppStats({
lastUpdated: new Date(), lastUpdated: new Date(),
building: false, building: false,
currentBuild: build.build,
}); });
emitBuildLog(`Build failed with code ${code}`, "error"); emitBuildLog(`Build failed with code ${code}`, "error");
//reject(new Error(`Build failed with code ${code}`)); //reject(new Error(`Build failed with code ${code}`));

View File

@@ -4,6 +4,7 @@ import { appStats } from "../db/schema/stats.schema.js";
export const updateAppStats = async ( export const updateAppStats = async (
data: Partial<typeof appStats.$inferInsert>, data: Partial<typeof appStats.$inferInsert>,
) => { ) => {
console.log(data);
await db await db
.insert(appStats) .insert(appStats)
.values({ .values({

View File

@@ -0,0 +1,17 @@
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}`,
build: parseInt((config.build as string) ?? "1", 0),
lastBuildTime: config.lastBuildDate,
};
};

View File

@@ -2,6 +2,7 @@ import fs from "node:fs";
import fsp from "node:fs/promises"; import fsp from "node:fs/promises";
import path from "node:path"; import path from "node:path";
import archiver from "archiver"; import archiver from "archiver";
import { format } from "date-fns";
import { createLogger } from "../logger/logger.controller.js"; import { createLogger } from "../logger/logger.controller.js";
import { emitBuildLog } from "./build.utils.js"; import { emitBuildLog } from "./build.utils.js";
import { updateAppStats } from "./updateAppStats.utils.js"; import { updateAppStats } from "./updateAppStats.utils.js";
@@ -17,33 +18,31 @@ const exists = async (target: string) => {
} }
}; };
const getNextBuildNumber = async (buildNumberFile: string) => { const getNextBuildNumber = async (versionPath: string) => {
if (!(await exists(buildNumberFile))) { const raw = await fsp.readFile(versionPath, "utf8");
await fsp.writeFile(buildNumberFile, "1", "utf8"); const config = JSON.parse(raw);
return 1; const current = Number.parseInt(config.build.trim(), 10);
}
const raw = await fsp.readFile(buildNumberFile, "utf8");
const current = Number.parseInt(raw.trim(), 10);
let nextBuild: string;
if (Number.isNaN(current) || current < 1) { if (Number.isNaN(current) || current < 1) {
await fsp.writeFile(buildNumberFile, "1", "utf8"); nextBuild = "1";
return 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 return { version: config.version, build: config.build };
await updateAppStats({
currentBuild: next,
lastBuildAt: new Date(),
building: true,
});
return next;
}; };
const cleanupOldBuilds = async (buildFolder: string, maxBuilds: number) => { const cleanupOldBuilds = async (buildFolder: string, maxBuilds: number) => {
@@ -53,7 +52,8 @@ const cleanupOldBuilds = async (buildFolder: string, maxBuilds: number) => {
for (const entry of entries) { for (const entry of entries) {
if (!entry.isFile()) continue; 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 fullPath = path.join(buildFolder, entry.name);
const stat = await fsp.stat(fullPath); const stat = await fsp.stat(fullPath);
@@ -85,7 +85,7 @@ export const zipBuild = async () => {
} }
const includesFile = path.join(appDir, ".includes"); 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 buildFolder = path.join(appDir, "builds");
const tempFolder = path.join(appDir, "temp", "zip-temp"); const tempFolder = path.join(appDir, "temp", "zip-temp");
if (!(await exists(includesFile))) { if (!(await exists(includesFile))) {
@@ -95,11 +95,13 @@ export const zipBuild = async () => {
await fsp.mkdir(buildFolder, { recursive: true }); await fsp.mkdir(buildFolder, { recursive: true });
const buildNumber = await getNextBuildNumber(buildNumberFile); const lstVersion = await getNextBuildNumber(version);
const zipFileName = `LSTV3-${buildNumber}.zip`; const zipFileName = `LSTV3-${lstVersion.version}.${lstVersion.build}.zip`;
const zipFile = path.join(buildFolder, zipFileName); const zipFile = path.join(buildFolder, zipFileName);
// make the folders in case they are not created already // 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)) { if (await exists(tempFolder)) {
await fsp.rm(tempFolder, { recursive: true, force: true }); await fsp.rm(tempFolder, { recursive: true, force: true });
@@ -166,11 +168,12 @@ export const zipBuild = async () => {
await updateAppStats({ await updateAppStats({
lastUpdated: new Date(), lastUpdated: new Date(),
building: false, building: false,
currentBuild: lstVersion.build,
}); });
return { return {
success: true, success: true,
buildNumber, build: lstVersion.build,
zipFile, zipFile,
zipFileName, zipFileName,
}; };

View File

@@ -0,0 +1,17 @@
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/docs/datamart")({
component: RouteComponent,
});
function RouteComponent() {
return (
<div className="w-full h-[calc(100vh-48px)] overflow-hidden mr-2 ml-2">
<iframe
className="w-full h-full border-0"
title="datamart"
src="https://docs.tuffraid.net/share/40lshswjqq/p/logistics-support-tool-docs-g3nuKAZ7Pw"
/>
</div>
);
}

View File

@@ -4,4 +4,10 @@
{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" } { "path": "./tsconfig.node.json" }
], ],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
} }

View File

@@ -1,6 +1,8 @@
{ {
"name": "lst_v3", "name": "lst_v3",
"version": "0.1.0-alpha.3", "version": "0.1.0-alpha.3",
"build": "179",
"lastBuildDate": "6/17/2026 11:03",
"description": "The tool that supports us in our everyday alplaprod", "description": "The tool that supports us in our everyday alplaprod",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@@ -31,24 +31,32 @@ function Update-Server {
[string]$Server, [string]$Server,
[string]$Token [string]$Token
) )
$buildFile = Join-Path $AppDir ".buildNumber"
$buildFile = Join-Path $AppDir "package.json"
$BuildNumber = 1 $BuildNumber = 1
$BuildFolder = Join-Path $AppDir "builds" $BuildFolder = Join-Path $AppDir "builds"
if (Test-Path $BuildFile) { $packageJson = Get-Content $buildFile -Raw | ConvertFrom-Json
$content = Get-Content $BuildFile | Select-Object -First 1
$num = $content.Trim() -as [int] # safe cast
if ($num) { # Extract the .build property and cast it safely to an integer
$BuildNumber = $num + 1 $num = "$($packageJson.version).$(([int]$packageJson.build) -1)"
} # if (Test-Path $BuildFile) {
else { # # Parse the entire JSON file into a PowerShell object
$BuildNumber = 1 # $packageJson = Get-Content $buildFile -Raw | ConvertFrom-Json
}
} # # Extract the .build property and cast it safely to an integer
# $num = "($packageJson.version).($packageJson.build)"
# if ($null -ne $num) {
# $BuildNumber = $num + 1
# }
# else {
# $BuildNumber = 1
# }
# }
# Get The current Build we have zipped up # Get The current Build we have zipped up
$BuildNumber = ([int]$BuildNumber - 1).ToString() $BuildNumber = $num
# copy the latest build over # copy the latest build over