refactor(modules): moved modules to app to control everything based on there active setting

This commit is contained in:
2025-10-29 21:57:11 -05:00
parent 6493e0398a
commit 99b2d762d6
35 changed files with 5807 additions and 121 deletions

View File

@@ -1,32 +1,34 @@
import { Router } from "express";
import { requireAuth } from "../../../../pkg/middleware/authMiddleware.js";
import { restrictToHosts } from "../../../../pkg/middleware/restrictToHosts.js";
import addServer from "./addServer.js";
import getServers from "./getServers.js";
import updateServer from "./updateServer.js";
import { restrictToHosts } from "../../../../pkg/middleware/restrictToHosts.js";
import { requireAuth } from "../../../../pkg/middleware/authMiddleware.js";
const router = Router();
router.use("/", getServers);
router.use(
"/",
requireAuth("user", ["systemAdmin", "admin"]),
restrictToHosts([
"usmcd1vms036.alpla.net",
"USMCD1VMS036.alpla.net",
"https://usmcd1vms036.alpla.net",
]),
addServer
"/",
requireAuth("user", ["systemAdmin", "admin"]),
restrictToHosts([
"localhost:5500",
"usmcd1vms036.alpla.net",
"USMCD1VMS036.alpla.net",
"https://usmcd1vms036.alpla.net",
]),
addServer,
);
router.use(
"/",
requireAuth("user", ["systemAdmin", "admin"]),
restrictToHosts([
"usmcd1vms036.alpla.net",
"USMCD1VMS036.alpla.net",
"https://usmcd1vms036.alpla.net",
]),
updateServer
"/",
requireAuth("user", ["systemAdmin", "admin"]),
restrictToHosts([
"localhost:5500",
"usmcd1vms036.alpla.net",
"USMCD1VMS036.alpla.net",
"https://usmcd1vms036.alpla.net",
]),
updateServer,
);
export default router;

View File

@@ -42,8 +42,12 @@ router.post("/", async (req: Request, res: Response) => {
.update(user)
.set({ lastLogin: sql`NOW()` })
.where(eq(user.id, newUser.user.id));
return res.status(201).json(user);
return res
.status(201)
.json({ success: true, message: "User created", data: newUser });
} catch (err) {
console.log(err);
if (err instanceof z.ZodError) {
const flattened = z.flattenError(err);
return res.status(400).json({
@@ -59,6 +63,12 @@ router.post("/", async (req: Request, res: Response) => {
error: err.status,
});
}
return res.status(200).json({
success: false,
message: "There was an error creating your user.",
error: err,
});
}
});

View File

@@ -1,10 +1,46 @@
import { sql } from "drizzle-orm";
import { readFileSync } from "fs";
import path from "path";
import { fileURLToPath } from "url";
import { db } from "../../../../pkg/db/db.js";
import { modules } from "../../../../pkg/db/schema/modules.js";
import { createLogger } from "../../../../pkg/logger/logger.js";
import { tryCatch } from "../../../../pkg/utils/tryCatch.js";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export const baseModules = async () => {
const log = createLogger({ module: "system", subModule: "base modules" });
const modulePath = path.resolve(__dirname, "./modules.json");
const newModules = JSON.parse(readFileSync(modulePath, "utf-8"));
for (const m of newModules) {
const { data, error } = await tryCatch(
db
.insert(modules)
.values(m)
.onConflictDoUpdate({
target: modules.name,
set: {
category: m.category,
link: m.link,
icon: m.icon,
roles: m.roles,
upd_date: sql`NOW()`,
upd_user: "LST-user",
},
})
.returning({ name: modules.name }),
);
if (error) {
console.log(error);
log.error({ error }, "There was an error adding new settings");
}
if (data) {
log.info({ newModulesAdded: data }, "New settings added");
}
}
};

View File

@@ -1,30 +1,122 @@
[
{
"name": "plantToken",
"value": "test3",
"description": "The plant token for the plant IE: test3 or usday1",
"moduleName": "system",
"roles": ["systemAdmin"]
"name": "users",
"category": "admin",
"icon": "User",
"link": "/lst/app/admin/users",
"active": true,
"roles": ["systemAdmin", "admin"]
},
{
"name": "dbServer",
"value": "usmcd1vms036",
"description": "What is the db server",
"moduleName": "system",
"roles": ["systemAdmin"]
"name": "system",
"category": "admin",
"icon": "MonitorCog",
"link": "/lst/app/system",
"active": true,
"roles": ["systemAdmin", "admin"]
},
{
"name": "v1Server",
"value": "localhost",
"description": "What is the port the v1app is on",
"moduleName": "system",
"roles": ["systemAdmin"]
"name": "ocp",
"category": "production",
"icon": "Printer",
"link": "/lst/app/old/ocp",
"active": true,
"roles": ["viewer", "manager", "tester", "systemAdmin", "admin"]
},
{
"name": "v1Port",
"value": "3000",
"description": "What is the port the v1app is on",
"moduleName": "system",
"roles": ["systemAdmin"]
"name": "rfidReaders",
"category": "production",
"icon": "Tags",
"link": "/lst/app/old/rfid",
"active": false,
"roles": ["viewer", "manager", "tester", "systemAdmin", "admin"]
},
{
"name": "ocmeCycleCounts",
"category": "ocme",
"icon": "Package",
"link": "/lst/app/old/ocme/cyclecount",
"active": false,
"roles": ["manager", "tester", "systemAdmin", "admin"]
},
{
"name": "siloAdjustments",
"category": "logistics",
"icon": "Database",
"link": "/lst/app/old/siloAdjustments",
"active": true,
"roles": ["manager", "admin", "tester", "systemAdmin"]
},
{
"name": "demandManagement",
"category": "logistics",
"icon": "Truck",
"link": "/lst/app/old/dm",
"active": true,
"roles": ["manager", "admin", "tester", "systemAdmin"]
},
{
"name": "logistics",
"category": "logistics",
"icon": "",
"link": "/lst/app/old",
"active": false,
"roles": ["admin", "systemAdmin", "manager", "viewer", "tester"]
},
{
"name": "helperCommands",
"category": "logistics",
"icon": "Package",
"link": "/lst/app/old/helperCommands",
"active": true,
"roles": ["admin", "systemAdmin", "manager", "viewer", "tester"]
},
{
"name": "materialHelper",
"category": "logistics",
"active": true,
"icon": "Package",
"link": "/lst/app/old/materialHelper/consumption",
"roles": ["admin", "systemAdmin", "manager", "viewer", "tester"]
},
{
"name": "openOrders",
"category": "logistics",
"active": true,
"icon": "Truck",
"link": "/lst/app/old/openOrders",
"roles": ["admin", "systemAdmin", "manager", "viewer", "tester"]
},
{
"name": "production",
"category": "production",
"active": false,
"icon": "",
"link": "",
"roles": ["admin", "systemAdmin", "manager", "viewer", "tester"]
},
{
"name": "quality",
"category": "quality",
"active": false,
"icon": "",
"link": "",
"roles": ["admin", "systemAdmin", "manager", "viewer", "tester"]
},
{
"name": "eom",
"category": "logistics",
"active": true,
"icon": "",
"link": "/lst/app/old/eom",
"roles": ["admin", "systemAdmin", "manager", "tester"]
},
{
"name": "forklifts",
"category": "logistics",
"active": false,
"icon": "",
"link": "/lst/app/old/forklifts",
"roles": ["admin", "systemAdmin", "manager", "tester"]
}
]

View File

@@ -1,4 +1,5 @@
import type { Express, Request, Response } from "express";
import modules from "./routes/modules/moduleRoutes.js";
import settings from "./routes/settings/settingRoutes.js";
import stats from "./routes/stats.js";
@@ -9,4 +10,8 @@ export const setupSystemRoutes = (app: Express, basePath: string) => {
basePath + "/api/system/settings", // will pass bc system admin but this is just telling us we need this
settings,
);
app.use(
basePath + "/api/system/modules", // will pass bc system admin but this is just telling us we need this
modules,
);
};

View File

@@ -0,0 +1,36 @@
import { and, asc, eq } from "drizzle-orm";
import type { Request, Response } from "express";
import { Router } from "express";
import { db } from "../../../../pkg/db/db.js";
import { modules } from "../../../../pkg/db/schema/modules.js";
import { serverData } from "../../../../pkg/db/schema/servers.js";
import { tryCatch } from "../../../../pkg/utils/tryCatch.js";
const router = Router();
router.get("/", async (req: Request, res: Response) => {
// const token = req.query.token;
// const conditions = [];
// if (token !== undefined) {
// conditions.push(eq(serverData.plantToken, `${token}`));
// }
//conditions.push(eq(serverData.active, true));
const { data, error } = await tryCatch(
db
.select()
.from(modules)
//.where(and(...conditions))
.orderBy(asc(modules.name)),
);
if (error) {
return res.status(400).json({ error: error });
}
res.status(200).json({ message: "Current modules", data: data });
});
export default router;

View File

@@ -0,0 +1,15 @@
import { Router } from "express";
import { requireAuth } from "../../../../pkg/middleware/authMiddleware.js";
import getModules from "./getModules.js";
import updateModules from "./updateModules.js";
const router = Router();
router.use("/", getModules);
router.use(
"/update",
requireAuth("system", ["systemAdmin", "admin"]),
updateModules,
);
export default router;

View File

@@ -0,0 +1,131 @@
import axios from "axios";
import { eq, sql } from "drizzle-orm";
import type { Request, Response } from "express";
import { Router } from "express";
import https from "https";
import { db } from "../../../../pkg/db/db.js";
import { modules } from "../../../../pkg/db/schema/modules.js";
import { serverData } from "../../../../pkg/db/schema/servers.js";
import { createLogger } from "../../../../pkg/logger/logger.js";
import { tryCatch } from "../../../../pkg/utils/tryCatch.js";
const router = Router();
router.patch("/:module", async (req: Request, res: Response) => {
const log = createLogger({ module: "admin", subModule: "update module" });
// when a server is updated and is posted from localhost or 127.0.0.1 we also want to post it to the test server so we can see it from there, we want to insert with update on conflict.
const module = req.params.module;
const updates: Record<string, any> = {};
if (req.body?.active !== undefined) {
updates.active = req.body.active;
}
if (req.body?.icon !== undefined) {
updates.icon = req.body.icon;
}
if (req.body?.link !== undefined) {
updates.link = req.body.link;
}
updates.upd_user = req.user!.username || "lst_user";
updates.upd_date = sql`NOW()`;
try {
if (Object.keys(updates).length > 0) {
await db.update(modules).set(updates).where(eq(modules.name, module));
}
// if we pass updateAll:true then we want to update all servers this will help kill all bad stuff if we have it.
if (req.body?.updateAll) {
const { data: serverInfo, error: errorData } = await tryCatch(
db.select().from(serverData),
);
if (errorData) {
log.error(
{ error: errorData },
"There was an error getting the serverData",
);
return;
}
for (const s of serverInfo) {
try {
const url = s.plantToken.includes("test")
? `https://${s.serverDNS}.alpla.net`
: `https://${s.plantToken}prod.alpla.net`;
const axiosInstance = axios.create({
httpsAgent: new https.Agent({ rejectUnauthorized: false }),
withCredentials: true,
});
const loginRes = (await axiosInstance.post(
`${url}/lst/api/auth/sign-in/username`,
{
username: process.env.MAIN_SERVER_USERNAME,
password: process.env.MAIN_SERVER_PASSWORD,
},
{
headers: { "Content-Type": "application/json" },
},
)) as any;
const setCookie = loginRes?.headers["set-cookie"][0];
//console.log(setCookie.split(";")[0].replace("__Secure-", ""));
if (!setCookie) {
throw new Error("Did not receive a Set-Cookie header from login");
}
const { data, error } = await tryCatch(
axios.patch(`${url}/lst/api/system/modules/${module}`, updates, {
headers: {
"Content-Type": "application/json",
Cookie: setCookie.split(";")[0],
},
withCredentials: true,
}),
);
if (error) {
//console.log(error);
log.error(
{ stack: error },
"There was an error updating the system",
);
return res.status(400).json({
message: `${module} encountered an error updating on the servers`,
});
}
log.info({ stack: data?.data }, "module was just updated.");
return res
.status(200)
.json({ message: `${module} was just updated on all servers` });
} catch (e) {
log.error(
{ error: e },
`There was an error updating the module setting on ${s.name}`,
);
return res.status(400).json({
message: `${module} encountered an error updating on the servers`,
});
}
}
} else {
return res
.status(200)
.json({ message: `${module}, was just updated to this server only` });
}
} catch (error) {
console.log(error);
res.status(400).json({ message: "Error Server updated", error });
}
});
export default router;

View File

@@ -0,0 +1,42 @@
import {
boolean,
jsonb,
pgTable,
text,
timestamp,
uniqueIndex,
uuid,
} from "drizzle-orm/pg-core";
import { createSelectSchema } from "drizzle-zod";
//import {z} from "zod";
export const modules = pgTable(
"modules",
{
module_id: uuid("module_id").defaultRandom().primaryKey(),
name: text("name").notNull(),
active: boolean("active").default(false),
category: text("category"),
icon: text("icon"),
link: text("link"),
//roles: text("roles").notNull().default(`["view", "systemAdmin"]`), // ["view", "technician", "supervisor","manager", "admin","systemAdmin"]
roles: jsonb("roles").notNull().default(["view", "systemAdmin"]), // ["view", "technician", "supervisor","manager", "admin","systemAdmin"]
add_User: text("add_User").default("LST_System").notNull(),
add_Date: timestamp("add_Date").defaultNow(),
upd_user: text("upd_User").default("LST_System").notNull(),
upd_date: timestamp("upd_date").defaultNow(),
},
(table) => [
// uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
uniqueIndex("module_name").on(table.name),
],
);
// Schema for inserting a user - can be used to validate API requests
// export const insertModuleSchema = createInsertSchema(modules, {
// name: z.string().min(3, {message: "Module name should be longer than 3 letters"}),
// });
// Schema for selecting a Expenses - can be used to validate API responses
export const selectModuleSchema = createSelectSchema(modules);
export type Modules = typeof modules;