Compare commits

...

12 Commits

30 changed files with 4534 additions and 316 deletions

View File

@@ -16,10 +16,10 @@ headers {
body:json {
{
"name": "Bowling Green 1",
"serverDNS": "USBOW1VS006",
"plantToken": "usbow1",
"ipAddress": "10.25.0.26",
"name": "Dayton",
"serverDNS": "USDAY1VS006",
"plantToken": "usday1",
"ipAddress": "10.44.0.26",
"greatPlainsPlantCode": 55,
"lstServerPort": 4000,
"serverLoc": "E$\\LST"

View File

@@ -0,0 +1,15 @@
meta {
name: RFID Trigger
type: http
seq: 1
}
post {
url: {{url}}/lst/old/api/rfid/manualtrigger/wrapper1
body: none
auth: inherit
}
settings {
encodeUrl: true
}

View File

@@ -0,0 +1,8 @@
meta {
name: RFID
seq: 3
}
auth {
mode: inherit
}

View File

@@ -0,0 +1,16 @@
meta {
name: ForecastData
type: http
seq: 1
}
post {
url: {{url}}/lst/api/logistics/dm/forecastData
body: none
auth: inherit
}
settings {
encodeUrl: true
timeout: 0
}

View File

@@ -1,5 +1,5 @@
vars {
url: https://usbet1prod.alpla.net
url: http://localhost:5500
session_cookie:
urlv2: http://localhost:3000
jwtV2:

View File

@@ -8,6 +8,8 @@ import { createProxyMiddleware, fixRequestBody } from "http-proxy-middleware";
import morgan from "morgan";
import os from "os";
import { dirname, join } from "path";
import swaggerJsdoc from "swagger-jsdoc";
import swaggerUi from "swagger-ui-express";
import { fileURLToPath } from "url";
import { userMigrate } from "./src/internal/auth/controller/userMigrate.js";
import { schedulerManager } from "./src/internal/logistics/controller/schedulerManager.js";
@@ -15,6 +17,8 @@ import { printers } from "./src/internal/ocp/printers/printers.js";
import { setupRoutes } from "./src/internal/routerHandler/routeHandler.js";
import { baseModules } from "./src/internal/system/controller/modules/baseModules.js";
import { baseSettings } from "./src/internal/system/controller/settings/baseSettings.js";
import { addListeners } from "./src/internal/system/utlis/addListeners.js";
import { swaggerOptions } from "./src/pkg/apiDocs/swaggerOptions.js";
import { auth } from "./src/pkg/auth/auth.js";
import { db } from "./src/pkg/db/db.js";
import { settings } from "./src/pkg/db/schema/settings.js";
@@ -30,7 +34,7 @@ import { setupIoServer } from "./src/ws/server.js";
const main = async () => {
const env = validateEnv(process.env);
const PORT = Number(env.VITE_PORT) || 4200;
const PORT = Number(process.env.VITE_PORT) || 4200;
//create the logger
const log = createLogger({ module: "system", subModule: "main start" });
@@ -159,7 +163,14 @@ const main = async () => {
}),
);
// docs and api stuff
// docs and routes
const openapiSpec: any = swaggerJsdoc(swaggerOptions);
app.use(
basePath + "/api/docs",
swaggerUi.serve,
swaggerUi.setup(openapiSpec),
);
app.use(basePath + "/d", express.static(join(__dirname, "../lstDocs/build")));
app.use(
basePath + "/app",
@@ -192,6 +203,7 @@ const main = async () => {
// start up the v1listener
v1Listener();
addListeners();
userMigrate();
}, 5 * 1000);

View File

@@ -8,10 +8,63 @@
* add_date
*/
type IncomingForecastData = {
CustomerArticleNumber: number;
};
import { db } from "../../../../pkg/db/db.js";
import {
type ForecastData,
forecastData,
forecastDataSchema,
} from "../../../../pkg/db/schema/forecastEDIData.js";
import { prodQuery } from "../../../../pkg/prodSql/prodQuery.js";
import { activeArticle } from "../../../../pkg/prodSql/querys/datamart/article.js";
import { tryCatch } from "../../../../pkg/utils/tryCatch.js";
export const forecastData = async (data: IncomingForecastData) => {
console.log(data);
export const forecastEdiData = async (data: ForecastData[]) => {
//console.log(data);
const { data: article, error } = await tryCatch(
prodQuery(activeArticle, "forecast data active articles"),
);
if (error) {
return {
success: false,
message: "Failed to get active articles",
error: error,
};
}
const forecaseEDIDATA = [];
for (let i = 0; i < data.length; i++) {
const activeAV = article?.data.filter(
(c: any) =>
c?.CustomerArticleNumber === data[i].customerArticleNo?.toString(),
);
const newData = data[i];
//console.log(activeAV[0].IdArtikelvarianten);
forecaseEDIDATA.push({
...newData,
article: activeAV[0].IdArtikelvarianten,
requirementDate: new Date(newData.requirementDate),
});
}
console.log(forecaseEDIDATA[0]);
const { data: f, error: ef } = await tryCatch(
db.insert(forecastData).values(forecaseEDIDATA),
);
if (ef) {
console.log(ef);
return {
success: false,
message: "There was an error posting the new data",
error: ef,
};
}
return {
success: true,
message: "All forecast Data was posted",
data: [],
};
};

View File

@@ -1,10 +1,12 @@
import type { Express, Request, Response } from "express";
import dm from "./routes/demandMgt/dmRoutes.js";
import labeling from "./routes/labeling/labelingRoutes.js";
import schedule from "./routes/scheduler/scheduleRoutes.js";
export const setupLogisticsRoutes = (app: Express, basePath: string) => {
app.use(basePath + "/api/logistics/schedule", schedule);
app.use(basePath + "/api/logistics/labeling", labeling);
app.use(basePath + "/api/logistics/dm", dm);
// app.use(
// basePath + "/api/admin/users",

View File

@@ -4,6 +4,6 @@ import forecastData from "./forecastEDIData.js";
const router = Router();
router.use("/", requireAuth(), forecastData);
router.use("/", forecastData);
export default router;

View File

@@ -1,7 +1,7 @@
import type { Request, Response } from "express";
import { Router } from "express";
import z from "zod";
import { preprintLabels } from "../../controller/labeling/preprint.js";
import { forecastEdiData } from "../../controller/demandManagement/forecastEDIData.js";
export const Preprint = z.object({
scannerId: z.number(),
@@ -15,13 +15,10 @@ export const Preprint = z.object({
const router = Router();
router.post("/preprint", async (req: Request, res: Response) => {
const parsed = Preprint.safeParse(req.body);
const print = await preprintLabels(req.body, req.user?.username || "");
router.post("/forecastData", async (req: Request, res: Response) => {
await forecastEdiData(req.body);
res
.status(200)
.json({ success: print.success, message: print.message, data: print.data });
res.status(200).json({ success: true, message: "Forecast Data", data: [] });
});
export default router;

View File

@@ -40,7 +40,7 @@ export const baseModules = async () => {
}
if (data) {
log.info({ newModulesAdded: data }, "New settings added");
//log.info({ newModulesAdded: data }, "New settings added");
}
}
};

View File

@@ -26,5 +26,12 @@
"description": "What is the port the v1app is on",
"moduleName": "system",
"roles": ["systemAdmin"]
},
{
"name": "dycoPrint",
"value": "0",
"description": "Are we using dyco to print the labels or rfid?",
"moduleName": "ocp",
"roles": ["systemAdmin", "admin", "manager"]
}
]

View File

@@ -11,7 +11,30 @@ import { checkBuildUpdate } from "../utlis/checkForBuild.js";
const router = Router();
// GET /health
/**
* @openapi
* /health:
* get:
* summary: Health check for the API server
* description: Returns basic system stats including memory usage, uptime, and build info.
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* type: object
* properties:
* status:
* type: string
* example: ok
* uptime:
* type: number
* build:
* type: string
* memoryUsage:
* type: string
*/
router.get("/", async (req, res) => {
const { data, error } = await tryCatch(
db.select().from(serverStats).where(eq(serverStats.id, "serverStats")),

View File

@@ -0,0 +1,51 @@
import { Client } from "pg";
import { createLogger } from "../../../pkg/logger/logger.js";
export const addListeners = async () => {
const log = createLogger({ module: "utils", subModule: "listeners" });
const client = new Client({
connectionString: process.env.DATABASE_URL_V1,
});
await client.connect();
try {
log.info({}, "Running the new log listener");
await client.query(`
CREATE OR REPLACE FUNCTION notify_new_log()
RETURNS trigger AS $$
BEGIN
PERFORM pg_notify('logs_channel', row_to_json(NEW)::text);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER logs_notify_trigger
AFTER INSERT ON logs
FOR EACH ROW
EXECUTE FUNCTION notify_new_log();
`);
} catch (e) {
log.info({}, "log listener exist");
}
try {
log.info({}, "Running the new label listener");
await client.query(`
CREATE OR REPLACE FUNCTION notify_new_label()
RETURNS trigger AS $$
BEGIN
PERFORM pg_notify('label_channel', row_to_json(NEW)::text);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER label_notify_trigger
AFTER INSERT ON prodlabels
FOR EACH ROW
EXECUTE FUNCTION notify_new_label();
`);
} catch (e) {
log.info({}, "label listener exists");
}
};

View File

@@ -0,0 +1,11 @@
export const swaggerOptions = {
definition: {
openapi: "3.0.0",
info: {
title: "Logistics Support Tool",
version: "1.0.0",
},
},
// globs where swagger-jsdoc should look for annotations:
apis: ["../../src/**/*.ts"],
};

View File

@@ -13,12 +13,11 @@ import { z } from "zod";
export const forecastData = pgTable("forecast_Data", {
id: uuid("id").defaultRandom().primaryKey(),
customerArticleNumber: text("customer_article_number"),
customerArticleNo: text("customer_article_no"),
dateRequested: timestamp("date_requested").defaultNow(),
quantity: real("quantity"),
requestDate: timestamp("request_date").notNull(),
requirementDate: timestamp("requirement_date").notNull(),
article: integer("article"),
customerID: integer("customer_id").notNull(),
createdAt: timestamp("created_at").defaultNow(),
});

View File

@@ -0,0 +1,195 @@
export const activeArticle = `
use AlplaPROD_test1
SELECT V_Artikel.IdArtikelvarianten,
V_Artikel.Bezeichnung,
V_Artikel.ArtikelvariantenTypBez,
V_Artikel.PreisEinheitBez,
case when sales.price is null then 0 else sales.price end as salesPrice,
TypeOfMaterial=CASE
WHEN
V_Artikel.ArtikelvariantenTypBez LIKE'%Additive'
Then 'AD'
when V_Artikel.ArtikelvariantenTypBez Like '%Masterbatch'
THEN 'MB'
WHEN V_Artikel.ArtikelvariantenTypBez ='Pallet' or
V_Artikel.ArtikelvariantenTypBez ='Top' or
V_Artikel.ArtikelvariantenTypBez ='Bags' or
V_Artikel.ArtikelvariantenTypBez ='Bag' or
V_Artikel.ArtikelvariantenTypBez ='Stretch Wrap' or
V_Artikel.ArtikelvariantenTypBez ='Stretch Film' or
V_Artikel.ArtikelvariantenTypBez ='Banding Materials' or
V_Artikel.ArtikelvariantenTypBez ='Carton' or
V_Artikel.ArtikelvariantenTypBez ='Re-Shipper Box' or
V_Artikel.ArtikelvariantenTypBez ='Label' or
V_Artikel.ArtikelvariantenTypBez ='Pallet Label' or
V_Artikel.ArtikelvariantenTypBez ='Carton Label' or
V_Artikel.ArtikelvariantenTypBez ='Liner' or
V_Artikel.ArtikelvariantenTypBez ='Dose Cup' or
V_Artikel.ArtikelvariantenTypBez ='Metal Cage' or
V_Artikel.ArtikelvariantenTypBez ='Spout' or
V_Artikel.ArtikelvariantenTypBez = 'Slip Sheet' or
V_Artikel.ArtikelvariantenTypBez = 'Palet' or
V_Artikel.ArtikelvariantenTypBez = 'LID' or
V_Artikel.ArtikelvariantenTypBez= 'Metal' or
V_Artikel.ArtikelvariantenTypBez= 'Corner post' or
V_Artikel.ArtikelvariantenTypBez= 'Bottle Label' or
V_Artikel.ArtikelvariantenTypBez = 'Paper label' or
V_Artikel.ArtikelvariantenTypBez = 'Banding' or
V_Artikel.ArtikelvariantenTypBez = 'Glue' or
V_Artikel.ArtikelvariantenTypBez = 'Top Frame' or
V_Artikel.ArtikelvariantenTypBez = 'IML Label' or
V_Artikel.ArtikelvariantenTypBez = 'Purch EBM Bottle' or
V_Artikel.ArtikelvariantenTypBez = 'Purchased Spout' or
V_Artikel.ArtikelvariantenTypBez = 'Gaylord' or
V_Artikel.ArtikelvariantenTypBez = 'Misc. Packaging' or
V_Artikel.ArtikelvariantenTypBez = 'Sleeve' or
V_Artikel.ArtikelvariantenTypBez = 'Plastic Bag' or
V_Artikel.ArtikelvariantenTypBez = 'Purch Spout' or
V_Artikel.ArtikelvariantenTypBez = 'Seal' or
V_Artikel.ArtikelvariantenTypBez = 'Tape' or
V_Artikel.ArtikelvariantenTypBez = 'Box' or
V_Artikel.ArtikelvariantenTypBez = 'Label IML' or
V_Artikel.ArtikelvariantenTypBez = 'Pallet Runner'
THEN 'PKG'
WHEN V_Artikel.ArtikelvariantenTypBez='HD-PE' or
V_Artikel.ArtikelvariantenTypBez='HD-PE PCR' or
V_Artikel.ArtikelvariantenTypBez='HD-PP' or
V_Artikel.ArtikelvariantenTypBez= 'PP' or
V_Artikel.ArtikelvariantenTypBez LIKE '%PCR' or
V_Artikel.ArtikelvariantenTypBez= 'LDPE' or
V_Artikel.ArtikelvariantenTypBez= 'PP' or
V_Artikel.ArtikelvariantenTypBez= 'HDPE' or
V_Artikel.ArtikelvariantenTypBez= 'PET' or
V_Artikel.ArtikelvariantenTypBez= 'PET-P'
THEN 'MM'
WHEN
V_Artikel.ArtikelvariantenTypBez='HDPE-Waste' or
V_Artikel.ArtikelvariantenTypBez='$Waste Container' or
V_Artikel.ArtikelvariantenTypBez='Mixed-Waste' or
V_Artikel.ArtikelvariantenTypBez LIKE'%-Waste%'
THEN 'Waste'
WHEN
V_Artikel.ArtikelvariantenTypBez = 'Bottle' or
V_Artikel.ArtikelvariantenTypBez = 'SBM Bottle' or
V_Artikel.ArtikelvariantenTypBez = 'EBM Bottle' or
V_Artikel.ArtikelvariantenTypBez = 'ISBM Bottle' or
V_Artikel.ArtikelvariantenTypBez = 'Decorated Bottle'
THEN 'Bottle'
WHEN V_Artikel.ArtikelvariantenTypBez = 'Preform'
Then 'Preform'
When
V_Artikel.ArtikelvariantenTypBez = 'Purchased Preform' or
V_Artikel.ArtikelvariantenTypBez = 'Purchased Caps' or
V_Artikel.ArtikelvariantenTypBez = 'Purchased_preform'
THEN 'Purchased_preform'
When
V_Artikel.ArtikelvariantenTypBez = 'Closures' or
V_Artikel.ArtikelvariantenTypBez = 'Cap'
THEN 'Caps'
When
V_Artikel.ArtikelvariantenTypBez = 'Dummy'
THEN 'Not used'
ELSE 'Item not defined' END
,V_Artikel.IdArtikelvariantenTyp,
Round(V_Artikel.ArtikelGewicht, 3) as Article_Weight,
IdAdresse,
AdressBez,
AdressTypBez,
ProdBereichBez,
FG=case when
V_Artikel.ProdBereichBez = 'SBM' or
V_Artikel.ProdBereichBez = 'IM-Caps' or
V_Artikel.ProdBereichBez = 'IM-PET' or
V_Artikel.ProdBereichBez = 'PRINT OFFICE' or
V_Artikel.ProdBereichBez = 'EBM' or
V_Artikel.ProdBereichBez = 'ISBM' or
V_Artikel.ProdBereichBez = 'IM-Finishing'
Then 'FG'
Else 'not Defined Profit Center'
end,
V_Artikel.Umlaeufe as num_of_cycles,
V_FibuKonten_BASIS.FibuKontoNr as CostsCenterId,
V_FibuKonten_BASIS.Bezeichnung as CostCenterDescription,
sales.[KdArtNr] as CustomerArticleNumber,
sales.[KdArtBez] as CustomerArticleDescription,
round(V_Artikel.Zyklus, 2) as CycleTime,
Sypronummer as salesAgreement,
V_Artikel.ProdArtikelBez as ProductFamily
--,REPLACE(pur.UOM,'UOM:','')
,Case when LEFT(
LTRIM(REPLACE(pur.UOM,'UOM:','')),
CHARINDEX(' ', LTRIM(REPLACE(REPLACE(pur.UOM,'UOM:',''), CHAR(13)+CHAR(10), ' ')) + ' ') - 1
) is null then '1' else LEFT(
LTRIM(REPLACE(pur.UOM,'UOM:','')),
CHARINDEX(' ', LTRIM(REPLACE(REPLACE(pur.UOM,'UOM:',''), CHAR(13)+CHAR(10), ' ')) + ' ') - 1
) end AS UOM
--,*
FROM dbo.V_Artikel (nolock)
join
dbo.V_Artikelvarianten (nolock) on dbo.V_Artikel.IdArtikelvarianten =
dbo.V_Artikelvarianten.IdArtikelvarianten
join
dbo.V_FibuKonten_BASIS (nolock) on dbo.V_Artikelvarianten.IdFibuKonto =
dbo.V_FibuKonten_BASIS.IdFibuKonto
-- adding in the sales price
left join
(select * from
(select
ROW_NUMBER() OVER (PARTITION BY IdArtikelvarianten ORDER BY GueltigabDatum DESC) AS RN,
IdArtikelvarianten as av
,GueltigabDatum as validDate
,VKPreis as price
,[KdArtNr]
,[KdArtBez]
--,*
from dbo.T_HistoryVK (nolock)
where
--GueltigabDatum > getDate() - 120
--and
Aktiv = 1
and StandardKunde = 1 -- default address
) a
where RN = 1) as sales
on dbo.V_Artikel.IdArtikelvarianten = sales.av
/* adding the purchase price info */
left join
(select * from
(select
ROW_NUMBER() OVER (PARTITION BY IdArtikelvarianten ORDER BY GueltigabDatum DESC) AS RN,
IdArtikelvarianten as av
,GueltigabDatum as validDate
,EKPreis as price
,LiefArtNr as supplierNr
,CASE
WHEN Bemerkung IS NOT NULL AND Bemerkung LIKE '%UOM:%'
THEN
-- incase there is something funny going on in the remark well jsut check for new lines and what not
LEFT(
REPLACE(REPLACE(Bemerkung, CHAR(13)+CHAR(10), ' '), CHAR(10), ' '),
CASE
WHEN CHARINDEX(' ', REPLACE(REPLACE(Bemerkung, CHAR(13)+CHAR(10), ' '), CHAR(10), ' ')) > 0
THEN CHARINDEX(' ', REPLACE(REPLACE(Bemerkung, CHAR(13)+CHAR(10), ' '), CHAR(10), ' ')) - 1
ELSE LEN(Bemerkung)
END
)
ELSE 'UOM:1'
END AS UOM
,Bemerkung
--,*
from dbo.T_HistoryEK (nolock)
where
StandardLieferant = 1 -- default address
) a
where RN = 1) as pur
on dbo.V_Artikel.IdArtikelvarianten = pur.av
where V_Artikel.aktiv = 1 --and dbo.V_Artikel.IdArtikelvarianten = 1445
order by V_Artikel.IdArtikelvarianten /*, TypeOfMaterial */
`;

View File

@@ -37,6 +37,7 @@ type ServerData = {
pendingUpdateFile: string;
lastUpdate: Date;
memoryUsage: string;
greatPlainsPlantCode: string;
};
const updateServerItem = async (
@@ -66,6 +67,10 @@ function RouteComponent() {
);
const [sorting, setSorting] = useState<SortingState>([]);
const columnHelper = createColumnHelper<ServerData>();
const [pagination, setPagination] = useState({
pageIndex: 0, //initial page index
pageSize: 20, //default page size
});
const statsMap = React.useMemo(() => {
const map = new Map<string, any>();
@@ -93,7 +98,20 @@ function RouteComponent() {
const columns = [
columnHelper.accessor("name", {
cell: (i) => i.getValue(),
cell: ({ row, getValue }) => {
let url = "";
if (row.original.plantToken.includes("test")) {
url = `https://${row.original.serverDNS}.alpla.net/lst/app`;
} else {
url = `https://${row.original.plantToken}prod.alpla.net/lst/app`;
}
return (
<a href={url} target="_blank">
{getValue()}
</a>
);
},
header: ({ column }) => {
return (
<Button
@@ -204,6 +222,41 @@ function RouteComponent() {
},
}),
columnHelper.accessor("greatPlainsPlantCode", {
header: () => {
return <span className="flex flex-row gap-2">GP Code</span>;
},
cell: ({ row, getValue }) => {
const initialValue = String(getValue() ?? "");
const [localValue, setLocalValue] = React.useState(initialValue);
const token = row.original.plantToken;
const field = "greatPlainsPlantCode";
useEffect(() => setLocalValue(initialValue), [initialValue]);
const handleSubmit = (newValue: string) => {
if (newValue !== initialValue) {
setLocalValue(newValue); // keep new value locally immediately
updateServer.mutate({ token, field, value: newValue });
}
};
return (
<Input
value={localValue}
onChange={(e) => setLocalValue(e.currentTarget.value)}
onBlur={(e) => handleSubmit(e.currentTarget.value.trim())}
onKeyDown={(e) => {
if (e.key === "Enter") {
e.preventDefault();
handleSubmit(e.currentTarget.value.trim());
e.currentTarget.blur(); // exit edit mode
}
}}
/>
);
},
}),
columnHelper.accessor("uptime", {
header: () => {
return <span className="flex flex-row gap-2">UpTime</span>;
@@ -215,8 +268,12 @@ function RouteComponent() {
// use Skeleton from shadcn/ui (or any placeholder)
return <Skeleton className="h-4 w-16" />;
}
console.log(statsMap);
return <span>{statsRow ? `${statsRow.uptime.toFixed(2)}` : "--"}</span>;
//console.log(statsMap);
return (
<span>
{statsRow ? `${statsRow.uptime.toFixed(2)}` : "Server Offline?"}
</span>
);
},
}),
columnHelper.accessor("lastUpdate", {
@@ -230,8 +287,10 @@ function RouteComponent() {
// use Skeleton from shadcn/ui (or any placeholder)
return <Skeleton className="h-4 w-16" />;
}
console.log(statsMap);
return <span>{statsRow ? `${statsRow.lastUpdate}` : "--"}</span>;
//console.log(statsMap);
return (
<span>{statsRow ? `${statsRow.lastUpdate}` : "Server Offline?"}</span>
);
},
}),
columnHelper.accessor("build", {
@@ -245,8 +304,10 @@ function RouteComponent() {
// use Skeleton from shadcn/ui (or any placeholder)
return <Skeleton className="h-4 w-16" />;
}
console.log(statsMap);
return <span>{statsRow ? `${statsRow.build}` : "--"}</span>;
//console.log(statsMap);
return (
<span>{statsRow ? `${statsRow.build}` : "Server Offline?"}</span>
);
},
}),
columnHelper.accessor("pendingUpdateFile", {
@@ -260,8 +321,14 @@ function RouteComponent() {
// use Skeleton from shadcn/ui (or any placeholder)
return <Skeleton className="h-4 w-16" />;
}
console.log(statsMap);
return <span>{statsRow ? `${statsRow.pendingUpdateFile}` : "--"}</span>;
//console.log(statsMap);
return (
<span>
{statsRow
? `${statsRow.pendingUpdateFile === null ? "nothing pending" : statsRow.pendingUpdateFile}`
: "Server Offline?"}
</span>
);
},
}),
columnHelper.accessor("memoryUsage", {
@@ -276,7 +343,11 @@ function RouteComponent() {
return <Skeleton className="h-4 w-16" />;
}
//console.log(statsMap);
return <span>{statsRow ? `${statsRow.memoryUsage}` : "--"}</span>;
return (
<span>
{statsRow ? `${statsRow.memoryUsage}` : "Server Offline?"}
</span>
);
},
}),
];
@@ -288,10 +359,12 @@ function RouteComponent() {
getPaginationRowModel: getPaginationRowModel(),
onSortingChange: setSorting,
getSortedRowModel: getSortedRowModel(),
onPaginationChange: setPagination,
//renderSubComponent: ({ row }: { row: any }) => <ExpandedRow row={row} />,
//getRowCanExpand: () => true,
state: {
sorting,
pagination,
},
});

View File

@@ -10,7 +10,7 @@ import {
TableRow,
} from "@/components/ui/table";
import { useAuth, userAccess } from "@/lib/authClient";
import { useSettingStore } from "../../-lib/store/useSettings";
//import { useSettingStore } from "../../-lib/store/useSettings";
import type { LotType } from "../../-types/lots";
import { getlots } from "../../-utils/querys/ocp/lots";
import { LstCard } from "../extendedUi/LstCard";
@@ -67,9 +67,9 @@ export default function Lots() {
const { data, isError, isLoading } = useQuery(getlots());
const { session } = useAuth();
const { settings } = useSettingStore();
//const { settings } = useSettingStore();
const server = settings.filter((n) => n.name === "dbServer")[0]?.value || "";
//const server = settings.filter((n) => n.name === "dbServer")[0]?.value || "";
const lotdata = data ? data : [];
const accessRoles = userAccess("ocp", [
@@ -229,7 +229,8 @@ export default function Lots() {
</TableCell>
{session?.user && accessRoles && (
<>
{server === "usday1vms006" || server === "localhost" ? (
{location.hostname.includes("usday1") ||
location.hostname.includes("localhost") ? (
<>
<TableCell className="flex justify-center">
<ManualPrintForm />

View File

@@ -37,11 +37,10 @@ const printReason = [
];
export default function ManualPrintForm() {
const token = localStorage.getItem("auth_token");
const { settings } = useSettingStore();
const [open, setOpen] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const server = settings.filter((n) => n.name === "server")[0]?.value;
// const serverPort = settings.filter((n) => n.name === "serverPort")[0]?.value;
// const serverUrl = `http://${server}:${serverPort}`;
@@ -61,9 +60,7 @@ export default function ManualPrintForm() {
const logdataUrl = `/lst/old/api/ocp/manuallabellog`;
setIsSubmitting(true);
axios
.post(logdataUrl, logData, {
headers: { Authorization: `Bearer ${token}` },
})
.post(logdataUrl, logData, {})
.then((d) => {
console.log(d);
if (d.data.success) {
@@ -135,7 +132,7 @@ export default function ManualPrintForm() {
If you clicked this in error just click close
</p>
<hr className="mt-2 mb-2" />
{server == "usday1vms006" ? (
{location.hostname.includes("usday1") ? (
<Controller
control={control}
name="printReason"
@@ -206,7 +203,7 @@ export default function ManualPrintForm() {
/>
</div>
<hr />
{dyco[0].value === "0" && (
{dyco[0]?.value === "0" && (
<div>
<p>Enter the missing tag number.</p>
<hr />

View File

@@ -5,6 +5,7 @@ import {
} from "@/components/ui/resizable";
import { useSettingStore } from "../../-lib/store/useSettings";
import WrapperManualTrigger from "../ocme/WrapperCard";
//import WrapperManualTrigger from "../ocme/WrapperCard";
import LabelLog from "./LabelLog";
import LabelRatio from "./LabelRatio";
import Lots from "./Lots";
@@ -15,8 +16,6 @@ export default function OCPPage() {
const { settings } = useSettingStore();
if (settings.length === 0) return;
let token = settings.filter((n) => n.name === "plantToken");
return (
<div className="ml-5 w-11/12 h-9/10">
<ResizablePanelGroup
@@ -62,8 +61,7 @@ export default function OCPPage() {
<ResizableHandle />
<ResizablePanel className="min-h-[200px] min-w-[200px] max-w-[450px]">
<ResizablePanelGroup direction="vertical" autoSaveId="ocpPage_vert">
{token[0].value === "usday1vms006" ||
(location.pathname === "localhost" && (
{location.hostname.includes("usday1") && (
<ResizablePanel
style={{
overflow: "auto",
@@ -74,7 +72,20 @@ export default function OCPPage() {
>
<WrapperManualTrigger />
</ResizablePanel>
))}
)}
{location.hostname.includes("localhost") && (
<ResizablePanel
style={{
overflow: "auto",
scrollbarWidth: "none",
}}
defaultSize={50}
className="min-h-[200px]"
>
<WrapperManualTrigger />
</ResizablePanel>
)}
<ResizableHandle />
<ResizablePanel>

View File

@@ -1,14 +1,15 @@
import axios from "axios";
import { addDays } from "date-fns";
import XLSX from "xlsx";
import { db } from "../../../../../../../database/dbclient.js";
import { settings } from "../../../../../../../database/schema/settings.js";
import { tryCatch } from "../../../../../../globalUtils/tryCatch.js";
import XLSX from "xlsx";
import { excelDateStuff } from "../../../../utils/excelDateStuff.js";
import { postForecast } from "../postForecast.js";
import { createLog } from "../../../../../logger/logger.js";
import { sendEmail } from "../../../../../notifications/controller/sendMail.js";
import { query } from "../../../../../sqlServer/prodSqlServer.js";
import { activeArticle } from "../../../../../sqlServer/querys/dataMart/article.js";
import { addDays } from "date-fns";
import { sendEmail } from "../../../../../notifications/controller/sendMail.js";
import { createLog } from "../../../../../logger/logger.js";
import { excelDateStuff } from "../../../../utils/excelDateStuff.js";
import { postForecast } from "../postForecast.js";
let customerID = 4;
export const lorealForecast = async (data: any, user: any) => {
@@ -69,7 +70,7 @@ export const lorealForecast = async (data: any, user: any) => {
const missingSku: any = [];
const { data: a, error: ae } = await tryCatch(
query(activeArticle, "Loreal calling active av")
query(activeArticle, "Loreal calling active av"),
);
if (ae) {
@@ -108,8 +109,7 @@ export const lorealForecast = async (data: any, user: any) => {
// checking to make sure there is a real av to add to.
const activeAV = article.filter(
(c: any) =>
c?.CustomerArticleNumber ===
forcast.customerArticleNo.toString()
c?.CustomerArticleNumber === forcast.customerArticleNo.toString(),
);
if (activeAV.length === 0) {
@@ -181,8 +181,7 @@ export const lorealForecast = async (data: any, user: any) => {
// checking to make sure there is a real av to add to.
const activeAV = article.filter(
(c: any) =>
c?.CustomerArticleNumber ===
forcast.customerArticleNo.toString()
c?.CustomerArticleNumber === forcast.customerArticleNo.toString(),
);
if (activeAV.length === 0) {
@@ -209,19 +208,19 @@ export const lorealForecast = async (data: any, user: any) => {
} else {
// compare dates and keep the earliest
if (
new Date(item.requirementDate) <
new Date(acc[key].requirementDate)
new Date(item.requirementDate) < new Date(acc[key].requirementDate)
) {
acc[key] = item;
}
}
return acc;
}, {})
}, {}),
);
const emailSetup = {
email: "Blake.matthes@alpla.com; Stuart.Gladney@alpla.com; Harold.Mccalister@alpla.com; Jenn.Osbourn@alpla.com",
email:
"Blake.matthes@alpla.com; Stuart.Gladney@alpla.com; Harold.Mccalister@alpla.com; Jenn.Osbourn@alpla.com",
subject:
missedGrouped.length > 0
? `Alert! There are ${missedGrouped.length}, missing skus.`
@@ -233,14 +232,14 @@ export const lorealForecast = async (data: any, user: any) => {
};
const { data: sentEmail, error: sendEmailError } = await tryCatch(
sendEmail(emailSetup)
sendEmail(emailSetup),
);
if (sendEmailError) {
createLog(
"error",
"blocking",
"notify",
"Failed to send email, will try again on next interval"
"Failed to send email, will try again on next interval",
);
return {
success: false,
@@ -252,7 +251,7 @@ export const lorealForecast = async (data: any, user: any) => {
const predefinedObject = {
receivingPlantId: plantToken[0].value,
documentName: `ForecastFromLST-${new Date(Date.now()).toLocaleString(
"en-US"
"en-US",
)}`,
sender: user.username || "lst-system",
customerId: customerID,
@@ -276,6 +275,12 @@ export const lorealForecast = async (data: any, user: any) => {
],
};
// console.log(updatedPredefinedObject);
// posting the data to the new backend so we can store the data.
axios.post(
`http://localhost:${process.env.NODE_ENV?.trim() != "production" ? "4200/lst" : "4000"}/api/logistics/dm/forecastData`,
ebmForecastData,
);
const posting: any = await postForecast(updatedPredefinedObject, user);
return {

View File

@@ -30,20 +30,20 @@ app.UseForwardedHeaders(new ForwardedHeadersOptions
});
// Simple file logger
void LogToFile(string message)
{
try
{
string logDir = Path.Combine(AppContext.BaseDirectory, "logs");
Directory.CreateDirectory(logDir);
string logFilePath = Path.Combine(logDir, "proxy_log.txt");
File.AppendAllText(logFilePath, $"{DateTime.UtcNow:u}: {message}{Environment.NewLine}");
}
catch (Exception ex)
{
Console.WriteLine($"Logging error: {ex.Message}");
}
}
// void LogToFile(string message)
// {
// try
// {
// string logDir = Path.Combine(AppContext.BaseDirectory, "logs");
// Directory.CreateDirectory(logDir);
// string logFilePath = Path.Combine(logDir, "proxy_log.txt");
// File.AppendAllText(logFilePath, $"{DateTime.UtcNow:u}: {message}{Environment.NewLine}");
// }
// catch (Exception ex)
// {
// Console.WriteLine($"Logging error: {ex.Message}");
// }
// }
app.Use(async (context, next) =>
{

View File

@@ -0,0 +1,2 @@
ALTER TABLE "forecast_Data" RENAME COLUMN "customer_article_number" TO "customer_article_no";--> statement-breakpoint
ALTER TABLE "forecast_Data" DROP COLUMN "customer_id";

View File

@@ -0,0 +1 @@
ALTER TABLE "forecast_Data" RENAME COLUMN "request_date" TO "requirement_date";

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -183,6 +183,20 @@
"when": 1761830426770,
"tag": "0025_foamy_mattie_franklin",
"breakpoints": true
},
{
"idx": 26,
"version": "7",
"when": 1761938410811,
"tag": "0026_heavy_robin_chapel",
"breakpoints": true
},
{
"idx": 27,
"version": "7",
"when": 1761938877395,
"tag": "0027_special_harpoon",
"breakpoints": true
}
]
}

285
package-lock.json generated
View File

@@ -33,6 +33,8 @@
"pino-pretty": "^13.1.2",
"postgres": "^3.4.7",
"socket.io": "^4.8.1",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.1",
"zod": "^4.1.12"
},
"devDependencies": {
@@ -44,6 +46,8 @@
"@types/nodemailer": "^7.0.2",
"@types/nodemailer-express-handlebars": "^4.0.5",
"@types/pg": "^8.15.5",
"@types/swagger-jsdoc": "^6.0.4",
"@types/swagger-ui-express": "^4.1.8",
"concurrently": "^9.2.1",
"cz-conventional-changelog": "^3.3.0",
"standard-version": "^9.5.0",
@@ -52,6 +56,50 @@
"typescript": "^5.9.3"
}
},
"node_modules/@apidevtools/json-schema-ref-parser": {
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz",
"integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==",
"license": "MIT",
"dependencies": {
"@jsdevtools/ono": "^7.1.3",
"@types/json-schema": "^7.0.6",
"call-me-maybe": "^1.0.1",
"js-yaml": "^4.1.0"
}
},
"node_modules/@apidevtools/openapi-schemas": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz",
"integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/@apidevtools/swagger-methods": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz",
"integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==",
"license": "MIT"
},
"node_modules/@apidevtools/swagger-parser": {
"version": "10.0.3",
"resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz",
"integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==",
"license": "MIT",
"dependencies": {
"@apidevtools/json-schema-ref-parser": "^9.0.6",
"@apidevtools/openapi-schemas": "^2.0.4",
"@apidevtools/swagger-methods": "^3.0.2",
"@jsdevtools/ono": "^7.1.3",
"call-me-maybe": "^1.0.1",
"z-schema": "^5.0.1"
},
"peerDependencies": {
"openapi-types": ">=7"
}
},
"node_modules/@aws-crypto/sha256-browser": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz",
@@ -2446,6 +2494,12 @@
"integrity": "sha512-3zwefSMwHpu8iVUW8YYz227sIv6UFqO31p1Bf1ZH/Vom7CmNyUsXjDBlnNzcuhmOL1XfxZ3nvND42kR23XlbcQ==",
"license": "BSD-3-Clause"
},
"node_modules/@jsdevtools/ono": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
"license": "MIT"
},
"node_modules/@levischuck/tiny-cbor": {
"version": "0.2.11",
"resolved": "https://registry.npmjs.org/@levischuck/tiny-cbor/-/tiny-cbor-0.2.11.tgz",
@@ -2556,6 +2610,13 @@
"integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==",
"license": "MIT"
},
"node_modules/@scarf/scarf": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz",
"integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==",
"hasInstallScript": true,
"license": "Apache-2.0"
},
"node_modules/@simplewebauthn/browser": {
"version": "13.1.2",
"resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-13.1.2.tgz",
@@ -3344,6 +3405,12 @@
"@types/node": "*"
}
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
"license": "MIT"
},
"node_modules/@types/mime": {
"version": "1.3.5",
"dev": true,
@@ -3476,6 +3543,24 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/swagger-jsdoc": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/@types/swagger-jsdoc/-/swagger-jsdoc-6.0.4.tgz",
"integrity": "sha512-W+Xw5epcOZrF/AooUM/PccNMSAFOKWZA5dasNyMujTwsBkU74njSJBpvCCJhHAJ95XRMzQrrW844Btu0uoetwQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/swagger-ui-express": {
"version": "4.1.8",
"resolved": "https://registry.npmjs.org/@types/swagger-ui-express/-/swagger-ui-express-4.1.8.tgz",
"integrity": "sha512-AhZV8/EIreHFmBV5wAs0gzJUNq9JbbSXgJLQubCC0jtIo6prnI9MIRRxnU4MZX9RB9yXxF1V4R7jtLl/Wcj31g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/express": "*",
"@types/serve-static": "*"
}
},
"node_modules/@typespec/ts-http-runtime": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.0.tgz",
@@ -3628,9 +3713,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
"license": "Python-2.0",
"optional": true
"license": "Python-2.0"
},
"node_modules/array-ify": {
"version": "1.0.0",
@@ -3701,7 +3784,6 @@
},
"node_modules/balanced-match": {
"version": "1.0.2",
"dev": true,
"license": "MIT"
},
"node_modules/base64-js": {
@@ -3905,7 +3987,6 @@
},
"node_modules/brace-expansion": {
"version": "1.1.12",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
@@ -4013,6 +4094,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/call-me-maybe": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz",
"integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==",
"license": "MIT"
},
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -4282,7 +4369,6 @@
},
"node_modules/concat-map": {
"version": "0.0.1",
"dev": true,
"license": "MIT"
},
"node_modules/concat-stream": {
@@ -5124,6 +5210,18 @@
"node": ">=0.3.1"
}
},
"node_modules/doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
"integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
"license": "Apache-2.0",
"dependencies": {
"esutils": "^2.0.2"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/dot-prop": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
@@ -5685,6 +5783,15 @@
"node": ">=0.8.0"
}
},
"node_modules/esutils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/etag": {
"version": "1.8.1",
"license": "MIT",
@@ -5764,6 +5871,7 @@
"node_modules/express": {
"version": "5.1.0",
"license": "MIT",
"peer": true,
"dependencies": {
"accepts": "^2.0.0",
"body-parser": "^2.2.0",
@@ -6159,7 +6267,6 @@
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"dev": true,
"license": "ISC"
},
"node_modules/fsevents": {
@@ -6807,7 +6914,6 @@
},
"node_modules/inflight": {
"version": "1.0.6",
"dev": true,
"license": "ISC",
"dependencies": {
"once": "^1.3.0",
@@ -7228,9 +7334,7 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"argparse": "^2.0.1"
},
@@ -7440,6 +7544,13 @@
"dev": true,
"license": "MIT"
},
"node_modules/lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
"integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
"deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.",
"license": "MIT"
},
"node_modules/lodash.includes": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
@@ -7452,6 +7563,13 @@
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
"license": "MIT"
},
"node_modules/lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
"deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.",
"license": "MIT"
},
"node_modules/lodash.isinteger": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
@@ -7502,9 +7620,7 @@
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
"integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
"dev": true,
"license": "MIT",
"optional": true
"license": "MIT"
},
"node_modules/lodash.once": {
"version": "4.1.1",
@@ -7933,7 +8049,6 @@
},
"node_modules/minimatch": {
"version": "3.1.2",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
@@ -8285,6 +8400,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/openapi-types": {
"version": "12.1.3",
"resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz",
"integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==",
"license": "MIT",
"peer": true
},
"node_modules/ora": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
@@ -8505,7 +8627,6 @@
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -10057,6 +10178,92 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/swagger-jsdoc": {
"version": "6.2.8",
"resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz",
"integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==",
"license": "MIT",
"dependencies": {
"commander": "6.2.0",
"doctrine": "3.0.0",
"glob": "7.1.6",
"lodash.mergewith": "^4.6.2",
"swagger-parser": "^10.0.3",
"yaml": "2.0.0-1"
},
"bin": {
"swagger-jsdoc": "bin/swagger-jsdoc.js"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/swagger-jsdoc/node_modules/commander": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz",
"integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==",
"license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/swagger-jsdoc/node_modules/glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"license": "ISC",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/swagger-parser": {
"version": "10.0.3",
"resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz",
"integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==",
"license": "MIT",
"dependencies": {
"@apidevtools/swagger-parser": "10.0.3"
},
"engines": {
"node": ">=10"
}
},
"node_modules/swagger-ui-dist": {
"version": "5.30.1",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.30.1.tgz",
"integrity": "sha512-4mNAUM31sr52K3JcK9qiGbfsFKNh/dm3PkEe+F9FAM31YY/NoRYUgsR/L6d7LLFn6PgZXtBG2ygp8+7UnpUIPg==",
"license": "Apache-2.0",
"dependencies": {
"@scarf/scarf": "=1.4.0"
}
},
"node_modules/swagger-ui-express": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz",
"integrity": "sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==",
"license": "MIT",
"dependencies": {
"swagger-ui-dist": ">=5.0.0"
},
"engines": {
"node": ">= v0.10.32"
},
"peerDependencies": {
"express": ">=4.0.0 || >=5.0.0-beta"
}
},
"node_modules/tar-fs": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz",
@@ -10495,6 +10702,15 @@
"spdx-expression-parse": "^3.0.0"
}
},
"node_modules/validator": {
"version": "13.15.20",
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.20.tgz",
"integrity": "sha512-KxPOq3V2LmfQPP4eqf3Mq/zrT0Dqp2Vmx2Bn285LwVahLc+CsxOM0crBHczm8ijlcjZ0Q5Xd6LW3z3odTPnlrw==",
"license": "MIT",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/vary": {
"version": "1.1.2",
"license": "MIT",
@@ -10712,6 +10928,15 @@
"dev": true,
"license": "ISC"
},
"node_modules/yaml": {
"version": "2.0.0-1",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz",
"integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==",
"license": "ISC",
"engines": {
"node": ">= 6"
}
},
"node_modules/yargs": {
"version": "16.2.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
@@ -10762,6 +10987,36 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/z-schema": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz",
"integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==",
"license": "MIT",
"dependencies": {
"lodash.get": "^4.4.2",
"lodash.isequal": "^4.5.0",
"validator": "^13.7.0"
},
"bin": {
"z-schema": "bin/z-schema"
},
"engines": {
"node": ">=8.0.0"
},
"optionalDependencies": {
"commander": "^9.4.1"
}
},
"node_modules/z-schema/node_modules/commander": {
"version": "9.5.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
"integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
"license": "MIT",
"optional": true,
"engines": {
"node": "^12.20.0 || >=14"
}
},
"node_modules/zod": {
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz",

View File

@@ -67,6 +67,8 @@
"pino-pretty": "^13.1.2",
"postgres": "^3.4.7",
"socket.io": "^4.8.1",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.1",
"zod": "^4.1.12"
},
"devDependencies": {
@@ -78,6 +80,8 @@
"@types/nodemailer": "^7.0.2",
"@types/nodemailer-express-handlebars": "^4.0.5",
"@types/pg": "^8.15.5",
"@types/swagger-jsdoc": "^6.0.4",
"@types/swagger-ui-express": "^4.1.8",
"concurrently": "^9.2.1",
"cz-conventional-changelog": "^3.3.0",
"standard-version": "^9.5.0",