Compare commits

..

12 Commits

30 changed files with 4534 additions and 316 deletions

View File

@@ -16,10 +16,10 @@ headers {
body:json { body:json {
{ {
"name": "Bowling Green 1", "name": "Dayton",
"serverDNS": "USBOW1VS006", "serverDNS": "USDAY1VS006",
"plantToken": "usbow1", "plantToken": "usday1",
"ipAddress": "10.25.0.26", "ipAddress": "10.44.0.26",
"greatPlainsPlantCode": 55, "greatPlainsPlantCode": 55,
"lstServerPort": 4000, "lstServerPort": 4000,
"serverLoc": "E$\\LST" "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 { vars {
url: https://usbet1prod.alpla.net url: http://localhost:5500
session_cookie: session_cookie:
urlv2: http://localhost:3000 urlv2: http://localhost:3000
jwtV2: jwtV2:

View File

@@ -8,6 +8,8 @@ import { createProxyMiddleware, fixRequestBody } from "http-proxy-middleware";
import morgan from "morgan"; import morgan from "morgan";
import os from "os"; import os from "os";
import { dirname, join } from "path"; import { dirname, join } from "path";
import swaggerJsdoc from "swagger-jsdoc";
import swaggerUi from "swagger-ui-express";
import { fileURLToPath } from "url"; import { fileURLToPath } from "url";
import { userMigrate } from "./src/internal/auth/controller/userMigrate.js"; import { userMigrate } from "./src/internal/auth/controller/userMigrate.js";
import { schedulerManager } from "./src/internal/logistics/controller/schedulerManager.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 { setupRoutes } from "./src/internal/routerHandler/routeHandler.js";
import { baseModules } from "./src/internal/system/controller/modules/baseModules.js"; import { baseModules } from "./src/internal/system/controller/modules/baseModules.js";
import { baseSettings } from "./src/internal/system/controller/settings/baseSettings.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 { auth } from "./src/pkg/auth/auth.js";
import { db } from "./src/pkg/db/db.js"; import { db } from "./src/pkg/db/db.js";
import { settings } from "./src/pkg/db/schema/settings.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 main = async () => {
const env = validateEnv(process.env); const env = validateEnv(process.env);
const PORT = Number(env.VITE_PORT) || 4200; const PORT = Number(process.env.VITE_PORT) || 4200;
//create the logger //create the logger
const log = createLogger({ module: "system", subModule: "main start" }); 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 + "/d", express.static(join(__dirname, "../lstDocs/build")));
app.use( app.use(
basePath + "/app", basePath + "/app",
@@ -192,6 +203,7 @@ const main = async () => {
// start up the v1listener // start up the v1listener
v1Listener(); v1Listener();
addListeners();
userMigrate(); userMigrate();
}, 5 * 1000); }, 5 * 1000);

View File

@@ -8,10 +8,63 @@
* add_date * add_date
*/ */
type IncomingForecastData = { import { db } from "../../../../pkg/db/db.js";
CustomerArticleNumber: number; 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) => { export const forecastEdiData = async (data: ForecastData[]) => {
console.log(data); //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 type { Express, Request, Response } from "express";
import dm from "./routes/demandMgt/dmRoutes.js";
import labeling from "./routes/labeling/labelingRoutes.js"; import labeling from "./routes/labeling/labelingRoutes.js";
import schedule from "./routes/scheduler/scheduleRoutes.js"; import schedule from "./routes/scheduler/scheduleRoutes.js";
export const setupLogisticsRoutes = (app: Express, basePath: string) => { export const setupLogisticsRoutes = (app: Express, basePath: string) => {
app.use(basePath + "/api/logistics/schedule", schedule); app.use(basePath + "/api/logistics/schedule", schedule);
app.use(basePath + "/api/logistics/labeling", labeling); app.use(basePath + "/api/logistics/labeling", labeling);
app.use(basePath + "/api/logistics/dm", dm);
// app.use( // app.use(
// basePath + "/api/admin/users", // basePath + "/api/admin/users",

View File

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

View File

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

View File

@@ -40,7 +40,7 @@ export const baseModules = async () => {
} }
if (data) { 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", "description": "What is the port the v1app is on",
"moduleName": "system", "moduleName": "system",
"roles": ["systemAdmin"] "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(); 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) => { router.get("/", async (req, res) => {
const { data, error } = await tryCatch( const { data, error } = await tryCatch(
db.select().from(serverStats).where(eq(serverStats.id, "serverStats")), 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", { export const forecastData = pgTable("forecast_Data", {
id: uuid("id").defaultRandom().primaryKey(), id: uuid("id").defaultRandom().primaryKey(),
customerArticleNumber: text("customer_article_number"), customerArticleNo: text("customer_article_no"),
dateRequested: timestamp("date_requested").defaultNow(), dateRequested: timestamp("date_requested").defaultNow(),
quantity: real("quantity"), quantity: real("quantity"),
requestDate: timestamp("request_date").notNull(), requirementDate: timestamp("requirement_date").notNull(),
article: integer("article"), article: integer("article"),
customerID: integer("customer_id").notNull(),
createdAt: timestamp("created_at").defaultNow(), 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; pendingUpdateFile: string;
lastUpdate: Date; lastUpdate: Date;
memoryUsage: string; memoryUsage: string;
greatPlainsPlantCode: string;
}; };
const updateServerItem = async ( const updateServerItem = async (
@@ -66,6 +67,10 @@ function RouteComponent() {
); );
const [sorting, setSorting] = useState<SortingState>([]); const [sorting, setSorting] = useState<SortingState>([]);
const columnHelper = createColumnHelper<ServerData>(); const columnHelper = createColumnHelper<ServerData>();
const [pagination, setPagination] = useState({
pageIndex: 0, //initial page index
pageSize: 20, //default page size
});
const statsMap = React.useMemo(() => { const statsMap = React.useMemo(() => {
const map = new Map<string, any>(); const map = new Map<string, any>();
@@ -93,7 +98,20 @@ function RouteComponent() {
const columns = [ const columns = [
columnHelper.accessor("name", { 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 }) => { header: ({ column }) => {
return ( return (
<Button <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", { columnHelper.accessor("uptime", {
header: () => { header: () => {
return <span className="flex flex-row gap-2">UpTime</span>; return <span className="flex flex-row gap-2">UpTime</span>;
@@ -215,8 +268,12 @@ function RouteComponent() {
// use Skeleton from shadcn/ui (or any placeholder) // use Skeleton from shadcn/ui (or any placeholder)
return <Skeleton className="h-4 w-16" />; return <Skeleton className="h-4 w-16" />;
} }
console.log(statsMap); //console.log(statsMap);
return <span>{statsRow ? `${statsRow.uptime.toFixed(2)}` : "--"}</span>; return (
<span>
{statsRow ? `${statsRow.uptime.toFixed(2)}` : "Server Offline?"}
</span>
);
}, },
}), }),
columnHelper.accessor("lastUpdate", { columnHelper.accessor("lastUpdate", {
@@ -230,8 +287,10 @@ function RouteComponent() {
// use Skeleton from shadcn/ui (or any placeholder) // use Skeleton from shadcn/ui (or any placeholder)
return <Skeleton className="h-4 w-16" />; return <Skeleton className="h-4 w-16" />;
} }
console.log(statsMap); //console.log(statsMap);
return <span>{statsRow ? `${statsRow.lastUpdate}` : "--"}</span>; return (
<span>{statsRow ? `${statsRow.lastUpdate}` : "Server Offline?"}</span>
);
}, },
}), }),
columnHelper.accessor("build", { columnHelper.accessor("build", {
@@ -245,8 +304,10 @@ function RouteComponent() {
// use Skeleton from shadcn/ui (or any placeholder) // use Skeleton from shadcn/ui (or any placeholder)
return <Skeleton className="h-4 w-16" />; return <Skeleton className="h-4 w-16" />;
} }
console.log(statsMap); //console.log(statsMap);
return <span>{statsRow ? `${statsRow.build}` : "--"}</span>; return (
<span>{statsRow ? `${statsRow.build}` : "Server Offline?"}</span>
);
}, },
}), }),
columnHelper.accessor("pendingUpdateFile", { columnHelper.accessor("pendingUpdateFile", {
@@ -260,8 +321,14 @@ function RouteComponent() {
// use Skeleton from shadcn/ui (or any placeholder) // use Skeleton from shadcn/ui (or any placeholder)
return <Skeleton className="h-4 w-16" />; return <Skeleton className="h-4 w-16" />;
} }
console.log(statsMap); //console.log(statsMap);
return <span>{statsRow ? `${statsRow.pendingUpdateFile}` : "--"}</span>; return (
<span>
{statsRow
? `${statsRow.pendingUpdateFile === null ? "nothing pending" : statsRow.pendingUpdateFile}`
: "Server Offline?"}
</span>
);
}, },
}), }),
columnHelper.accessor("memoryUsage", { columnHelper.accessor("memoryUsage", {
@@ -276,7 +343,11 @@ function RouteComponent() {
return <Skeleton className="h-4 w-16" />; return <Skeleton className="h-4 w-16" />;
} }
//console.log(statsMap); //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(), getPaginationRowModel: getPaginationRowModel(),
onSortingChange: setSorting, onSortingChange: setSorting,
getSortedRowModel: getSortedRowModel(), getSortedRowModel: getSortedRowModel(),
onPaginationChange: setPagination,
//renderSubComponent: ({ row }: { row: any }) => <ExpandedRow row={row} />, //renderSubComponent: ({ row }: { row: any }) => <ExpandedRow row={row} />,
//getRowCanExpand: () => true, //getRowCanExpand: () => true,
state: { state: {
sorting, sorting,
pagination,
}, },
}); });

View File

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

View File

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

View File

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

View File

@@ -1,286 +1,291 @@
import axios from "axios";
import { addDays } from "date-fns";
import XLSX from "xlsx";
import { db } from "../../../../../../../database/dbclient.js"; import { db } from "../../../../../../../database/dbclient.js";
import { settings } from "../../../../../../../database/schema/settings.js"; import { settings } from "../../../../../../../database/schema/settings.js";
import { tryCatch } from "../../../../../../globalUtils/tryCatch.js"; import { tryCatch } from "../../../../../../globalUtils/tryCatch.js";
import XLSX from "xlsx"; import { createLog } from "../../../../../logger/logger.js";
import { excelDateStuff } from "../../../../utils/excelDateStuff.js"; import { sendEmail } from "../../../../../notifications/controller/sendMail.js";
import { postForecast } from "../postForecast.js";
import { query } from "../../../../../sqlServer/prodSqlServer.js"; import { query } from "../../../../../sqlServer/prodSqlServer.js";
import { activeArticle } from "../../../../../sqlServer/querys/dataMart/article.js"; import { activeArticle } from "../../../../../sqlServer/querys/dataMart/article.js";
import { addDays } from "date-fns"; import { excelDateStuff } from "../../../../utils/excelDateStuff.js";
import { sendEmail } from "../../../../../notifications/controller/sendMail.js"; import { postForecast } from "../postForecast.js";
import { createLog } from "../../../../../logger/logger.js";
let customerID = 4; let customerID = 4;
export const lorealForecast = async (data: any, user: any) => { export const lorealForecast = async (data: any, user: any) => {
/** /**
* Post a standard forecast based on the standard template. * Post a standard forecast based on the standard template.
*/ */
const { data: s, error: e } = await tryCatch(db.select().from(settings)); const { data: s, error: e } = await tryCatch(db.select().from(settings));
if (e) { if (e) {
return { return {
sucess: false, sucess: false,
message: `Error getting settings`, message: `Error getting settings`,
data: e, data: e,
}; };
} }
const plantToken = s.filter((s) => s.name === "plantToken"); const plantToken = s.filter((s) => s.name === "plantToken");
const arrayBuffer = await data.arrayBuffer(); const arrayBuffer = await data.arrayBuffer();
const buffer = Buffer.from(arrayBuffer); const buffer = Buffer.from(arrayBuffer);
const workbook = XLSX.read(buffer, { type: "buffer" }); const workbook = XLSX.read(buffer, { type: "buffer" });
const sheet: any = workbook.Sheets["Alpla HDPE"]; const sheet: any = workbook.Sheets["Alpla HDPE"];
const range = XLSX.utils.decode_range(sheet["!ref"]); const range = XLSX.utils.decode_range(sheet["!ref"]);
const psheet: any = workbook.Sheets["Alpla PET"]; const psheet: any = workbook.Sheets["Alpla PET"];
const prange = XLSX.utils.decode_range(psheet["!ref"]); const prange = XLSX.utils.decode_range(psheet["!ref"]);
const headers = []; const headers = [];
for (let C = range.s.c; C <= range.e.c; ++C) { for (let C = range.s.c; C <= range.e.c; ++C) {
const cellAddress = XLSX.utils.encode_cell({ r: 1, c: C }); // row 0 = Excel row 1 const cellAddress = XLSX.utils.encode_cell({ r: 1, c: C }); // row 0 = Excel row 1
const cell = sheet[cellAddress]; const cell = sheet[cellAddress];
headers.push(cell ? cell.v : undefined); headers.push(cell ? cell.v : undefined);
} }
const pheaders = []; const pheaders = [];
for (let C = prange.s.c; C <= prange.e.c; ++C) { for (let C = prange.s.c; C <= prange.e.c; ++C) {
const cellAddress = XLSX.utils.encode_cell({ r: 1, c: C }); // row 0 = Excel row 1 const cellAddress = XLSX.utils.encode_cell({ r: 1, c: C }); // row 0 = Excel row 1
const cell = psheet[cellAddress]; const cell = psheet[cellAddress];
pheaders.push(cell ? cell.v : undefined); pheaders.push(cell ? cell.v : undefined);
} }
const ebmForeCastData: any = XLSX.utils.sheet_to_json(sheet, { const ebmForeCastData: any = XLSX.utils.sheet_to_json(sheet, {
defval: "", defval: "",
header: headers, header: headers,
range: 3, range: 3,
}); });
const petForeCastData: any = XLSX.utils.sheet_to_json(psheet, { const petForeCastData: any = XLSX.utils.sheet_to_json(psheet, {
defval: "", defval: "",
header: pheaders, header: pheaders,
range: 3, range: 3,
}); });
const ebmForecastData: any = []; const ebmForecastData: any = [];
const missingSku: any = []; const missingSku: any = [];
const { data: a, error: ae } = await tryCatch( const { data: a, error: ae } = await tryCatch(
query(activeArticle, "Loreal calling active av") query(activeArticle, "Loreal calling active av"),
); );
if (ae) { if (ae) {
return { return {
success: false, success: false,
message: "Error getting active av", message: "Error getting active av",
data: [], data: [],
}; };
} }
const article: any = a?.data; const article: any = a?.data;
// process the ebm forcast // process the ebm forcast
for (let i = 0; i < ebmForeCastData.length; i++) { for (let i = 0; i < ebmForeCastData.length; i++) {
// bottle code // bottle code
const sku = ebmForeCastData[i]["HDPE Bottle Code"]; const sku = ebmForeCastData[i]["HDPE Bottle Code"];
// ignore the blanks // ignore the blanks
if (sku === "") continue; if (sku === "") continue;
// ignore zero qty // ignore zero qty
// if (ebmForeCastData[i][`Day ${i}`]) continue; // if (ebmForeCastData[i][`Day ${i}`]) continue;
for (let f = 0; f <= 90; f++) { for (let f = 0; f <= 90; f++) {
const day = `Day ${f + 1}`; const day = `Day ${f + 1}`;
// if (ebmForeCastData[i][day] === 0) continue; // if (ebmForeCastData[i][day] === 0) continue;
const forcast = { const forcast = {
customerArticleNo: sku, customerArticleNo: sku,
requirementDate: addDays(new Date(Date.now()), f), //excelDateStuff(parseInt(date)), requirementDate: addDays(new Date(Date.now()), f), //excelDateStuff(parseInt(date)),
quantity: ebmForeCastData[i][day] ?? 0, quantity: ebmForeCastData[i][day] ?? 0,
}; };
if (forcast.quantity === 0) continue; if (forcast.quantity === 0) continue;
// checking to make sure there is a real av to add to. // checking to make sure there is a real av to add to.
const activeAV = article.filter( const activeAV = article.filter(
(c: any) => (c: any) =>
c?.CustomerArticleNumber === c?.CustomerArticleNumber === forcast.customerArticleNo.toString(),
forcast.customerArticleNo.toString() );
);
if (activeAV.length === 0) { if (activeAV.length === 0) {
if (typeof forcast.customerArticleNo === "number") { if (typeof forcast.customerArticleNo === "number") {
missingSku.push(forcast); missingSku.push(forcast);
} }
continue; continue;
} }
ebmForecastData.push(forcast); ebmForecastData.push(forcast);
} }
//console.log(ebmForeCastData.length); //console.log(ebmForeCastData.length);
} }
// petForeCastData.forEach((item: any, index: any) => { // petForeCastData.forEach((item: any, index: any) => {
// //console.log(`Processing item ${index + 1} of ${forecastData.length}`); // //console.log(`Processing item ${index + 1} of ${forecastData.length}`);
// // Extract the customer code // // Extract the customer code
// const customerCode = item["SOUTH PET BOTTLES"]; // const customerCode = item["SOUTH PET BOTTLES"];
// // Process each date in the current object // // Process each date in the current object
// for (const [date, qty] of Object.entries(item)) { // for (const [date, qty] of Object.entries(item)) {
// // Skip metadata fields // // Skip metadata fields
// if (petMetadataFields.includes(date)) continue; // if (petMetadataFields.includes(date)) continue;
// if (qty === 0) continue; // if (qty === 0) continue;
// // Create your transformed record // // Create your transformed record
// const record = { // const record = {
// customerArticleNo: customerCode, // customerArticleNo: customerCode,
// requirementDate: excelDateStuff(parseInt(date)), // requirementDate: excelDateStuff(parseInt(date)),
// quantity: qty, // quantity: qty,
// }; // };
// // Do something with this record // // Do something with this record
// petForecastData.push(record); // petForecastData.push(record);
// } // }
// }); // });
// pet forecast // pet forecast
for (let i = 0; i < petForeCastData.length; i++) { for (let i = 0; i < petForeCastData.length; i++) {
// bottle code // bottle code
const sku = petForeCastData[i]["South PET Bottle Code"]; const sku = petForeCastData[i]["South PET Bottle Code"];
// ignore the blanks // ignore the blanks
if (sku === "") continue; if (sku === "") continue;
// ignore zero qty // ignore zero qty
// if (ebmForeCastData[i][`Day ${i}`]) continue; // if (ebmForeCastData[i][`Day ${i}`]) continue;
for (let f = 0; f <= 90; f++) { for (let f = 0; f <= 90; f++) {
const day = `Day ${f + 1}`; const day = `Day ${f + 1}`;
// if (ebmForeCastData[i][day] === 0) continue; // if (ebmForeCastData[i][day] === 0) continue;
const forcast = { const forcast = {
customerArticleNo: sku, customerArticleNo: sku,
requirementDate: addDays(new Date(Date.now()), f), //excelDateStuff(parseInt(date)), requirementDate: addDays(new Date(Date.now()), f), //excelDateStuff(parseInt(date)),
quantity: petForeCastData[i][day] ?? 0, quantity: petForeCastData[i][day] ?? 0,
}; };
if (forcast.quantity === 0 || forcast.quantity === "") continue; if (forcast.quantity === 0 || forcast.quantity === "") continue;
if (forcast.customerArticleNo < 99999) { if (forcast.customerArticleNo < 99999) {
//console.log(`Sku a normal av ${forcast.customerArticleNo}`); //console.log(`Sku a normal av ${forcast.customerArticleNo}`);
continue; continue;
} }
// checking to make sure there is a real av to add to. // checking to make sure there is a real av to add to.
const activeAV = article.filter( const activeAV = article.filter(
(c: any) => (c: any) =>
c?.CustomerArticleNumber === c?.CustomerArticleNumber === forcast.customerArticleNo.toString(),
forcast.customerArticleNo.toString() );
);
if (activeAV.length === 0) { if (activeAV.length === 0) {
if (typeof forcast.customerArticleNo === "number") { if (typeof forcast.customerArticleNo === "number") {
missingSku.push(forcast); missingSku.push(forcast);
} }
continue; continue;
} }
ebmForecastData.push(forcast); ebmForecastData.push(forcast);
} }
} }
//console.log(comForecast); //console.log(comForecast);
// email the for the missing ones // email the for the missing ones
const missedGrouped = Object.values( const missedGrouped = Object.values(
missingSku.reduce((acc: any, item: any) => { missingSku.reduce((acc: any, item: any) => {
const key = item.customerArticleNo; const key = item.customerArticleNo;
if (!acc[key]) { if (!acc[key]) {
// first time we see this customer // first time we see this customer
acc[key] = item; acc[key] = item;
} else { } else {
// compare dates and keep the earliest // compare dates and keep the earliest
if ( if (
new Date(item.requirementDate) < new Date(item.requirementDate) < new Date(acc[key].requirementDate)
new Date(acc[key].requirementDate) ) {
) { acc[key] = item;
acc[key] = item; }
} }
}
return acc; return acc;
}, {}) }, {}),
); );
const emailSetup = { const emailSetup = {
email: "Blake.matthes@alpla.com; Stuart.Gladney@alpla.com; Harold.Mccalister@alpla.com; Jenn.Osbourn@alpla.com", email:
subject: "Blake.matthes@alpla.com; Stuart.Gladney@alpla.com; Harold.Mccalister@alpla.com; Jenn.Osbourn@alpla.com",
missedGrouped.length > 0 subject:
? `Alert! There are ${missedGrouped.length}, missing skus.` missedGrouped.length > 0
: `Alert! There is a missing SKU.`, ? `Alert! There are ${missedGrouped.length}, missing skus.`
template: "missingLorealSkus", : `Alert! There is a missing SKU.`,
context: { template: "missingLorealSkus",
items: missedGrouped, context: {
}, items: missedGrouped,
}; },
};
const { data: sentEmail, error: sendEmailError } = await tryCatch( const { data: sentEmail, error: sendEmailError } = await tryCatch(
sendEmail(emailSetup) sendEmail(emailSetup),
); );
if (sendEmailError) { if (sendEmailError) {
createLog( createLog(
"error", "error",
"blocking", "blocking",
"notify", "notify",
"Failed to send email, will try again on next interval" "Failed to send email, will try again on next interval",
); );
return { return {
success: false, success: false,
message: "Failed to send email, will try again on next interval", message: "Failed to send email, will try again on next interval",
}; };
} }
// if the customerarticle number is not matching just ignore it // if the customerarticle number is not matching just ignore it
const predefinedObject = { const predefinedObject = {
receivingPlantId: plantToken[0].value, receivingPlantId: plantToken[0].value,
documentName: `ForecastFromLST-${new Date(Date.now()).toLocaleString( documentName: `ForecastFromLST-${new Date(Date.now()).toLocaleString(
"en-US" "en-US",
)}`, )}`,
sender: user.username || "lst-system", sender: user.username || "lst-system",
customerId: customerID, customerId: customerID,
positions: [], positions: [],
}; };
let updatedPredefinedObject = { let updatedPredefinedObject = {
...predefinedObject, ...predefinedObject,
positions: [ positions: [
...predefinedObject.positions, ...predefinedObject.positions,
...ebmForecastData, ...ebmForecastData,
// ...ebmForecastData.filter( // ...ebmForecastData.filter(
// (q: any) => // (q: any) =>
// q.customerArticleNo != "" && q.customerArticleNo != "Total" // q.customerArticleNo != "" && q.customerArticleNo != "Total"
// ), // ),
// ...petForecastData.filter( // ...petForecastData.filter(
// (q: any) => // (q: any) =>
// q.customerArticleNo != "" && q.customerArticleNo != "Total" // q.customerArticleNo != "" && q.customerArticleNo != "Total"
// ), // ),
], ],
}; };
// console.log(updatedPredefinedObject); // console.log(updatedPredefinedObject);
const posting: any = await postForecast(updatedPredefinedObject, user);
return { // posting the data to the new backend so we can store the data.
success: posting.success, axios.post(
message: posting.message, `http://localhost:${process.env.NODE_ENV?.trim() != "production" ? "4200/lst" : "4000"}/api/logistics/dm/forecastData`,
data: posting.data === "" ? ebmForecastData : posting.data, ebmForecastData,
}; );
const posting: any = await postForecast(updatedPredefinedObject, user);
return {
success: posting.success,
message: posting.message,
data: posting.data === "" ? ebmForecastData : posting.data,
};
}; };

View File

@@ -30,20 +30,20 @@ app.UseForwardedHeaders(new ForwardedHeadersOptions
}); });
// Simple file logger // Simple file logger
void LogToFile(string message) // void LogToFile(string message)
{ // {
try // try
{ // {
string logDir = Path.Combine(AppContext.BaseDirectory, "logs"); // string logDir = Path.Combine(AppContext.BaseDirectory, "logs");
Directory.CreateDirectory(logDir); // Directory.CreateDirectory(logDir);
string logFilePath = Path.Combine(logDir, "proxy_log.txt"); // string logFilePath = Path.Combine(logDir, "proxy_log.txt");
File.AppendAllText(logFilePath, $"{DateTime.UtcNow:u}: {message}{Environment.NewLine}"); // File.AppendAllText(logFilePath, $"{DateTime.UtcNow:u}: {message}{Environment.NewLine}");
} // }
catch (Exception ex) // catch (Exception ex)
{ // {
Console.WriteLine($"Logging error: {ex.Message}"); // Console.WriteLine($"Logging error: {ex.Message}");
} // }
} // }
app.Use(async (context, next) => 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, "when": 1761830426770,
"tag": "0025_foamy_mattie_franklin", "tag": "0025_foamy_mattie_franklin",
"breakpoints": true "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", "pino-pretty": "^13.1.2",
"postgres": "^3.4.7", "postgres": "^3.4.7",
"socket.io": "^4.8.1", "socket.io": "^4.8.1",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.1",
"zod": "^4.1.12" "zod": "^4.1.12"
}, },
"devDependencies": { "devDependencies": {
@@ -44,6 +46,8 @@
"@types/nodemailer": "^7.0.2", "@types/nodemailer": "^7.0.2",
"@types/nodemailer-express-handlebars": "^4.0.5", "@types/nodemailer-express-handlebars": "^4.0.5",
"@types/pg": "^8.15.5", "@types/pg": "^8.15.5",
"@types/swagger-jsdoc": "^6.0.4",
"@types/swagger-ui-express": "^4.1.8",
"concurrently": "^9.2.1", "concurrently": "^9.2.1",
"cz-conventional-changelog": "^3.3.0", "cz-conventional-changelog": "^3.3.0",
"standard-version": "^9.5.0", "standard-version": "^9.5.0",
@@ -52,6 +56,50 @@
"typescript": "^5.9.3" "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": { "node_modules/@aws-crypto/sha256-browser": {
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz",
@@ -2446,6 +2494,12 @@
"integrity": "sha512-3zwefSMwHpu8iVUW8YYz227sIv6UFqO31p1Bf1ZH/Vom7CmNyUsXjDBlnNzcuhmOL1XfxZ3nvND42kR23XlbcQ==", "integrity": "sha512-3zwefSMwHpu8iVUW8YYz227sIv6UFqO31p1Bf1ZH/Vom7CmNyUsXjDBlnNzcuhmOL1XfxZ3nvND42kR23XlbcQ==",
"license": "BSD-3-Clause" "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": { "node_modules/@levischuck/tiny-cbor": {
"version": "0.2.11", "version": "0.2.11",
"resolved": "https://registry.npmjs.org/@levischuck/tiny-cbor/-/tiny-cbor-0.2.11.tgz", "resolved": "https://registry.npmjs.org/@levischuck/tiny-cbor/-/tiny-cbor-0.2.11.tgz",
@@ -2556,6 +2610,13 @@
"integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==",
"license": "MIT" "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": { "node_modules/@simplewebauthn/browser": {
"version": "13.1.2", "version": "13.1.2",
"resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-13.1.2.tgz", "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-13.1.2.tgz",
@@ -3344,6 +3405,12 @@
"@types/node": "*" "@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": { "node_modules/@types/mime": {
"version": "1.3.5", "version": "1.3.5",
"dev": true, "dev": true,
@@ -3476,6 +3543,24 @@
"dev": true, "dev": true,
"license": "MIT" "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": { "node_modules/@typespec/ts-http-runtime": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.0.tgz", "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.0.tgz",
@@ -3628,9 +3713,7 @@
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true, "license": "Python-2.0"
"license": "Python-2.0",
"optional": true
}, },
"node_modules/array-ify": { "node_modules/array-ify": {
"version": "1.0.0", "version": "1.0.0",
@@ -3701,7 +3784,6 @@
}, },
"node_modules/balanced-match": { "node_modules/balanced-match": {
"version": "1.0.2", "version": "1.0.2",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/base64-js": { "node_modules/base64-js": {
@@ -3905,7 +3987,6 @@
}, },
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
"version": "1.1.12", "version": "1.1.12",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
@@ -4013,6 +4094,12 @@
"url": "https://github.com/sponsors/ljharb" "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": { "node_modules/callsites": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -4282,7 +4369,6 @@
}, },
"node_modules/concat-map": { "node_modules/concat-map": {
"version": "0.0.1", "version": "0.0.1",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/concat-stream": { "node_modules/concat-stream": {
@@ -5124,6 +5210,18 @@
"node": ">=0.3.1" "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": { "node_modules/dot-prop": {
"version": "5.3.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
@@ -5685,6 +5783,15 @@
"node": ">=0.8.0" "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": { "node_modules/etag": {
"version": "1.8.1", "version": "1.8.1",
"license": "MIT", "license": "MIT",
@@ -5764,6 +5871,7 @@
"node_modules/express": { "node_modules/express": {
"version": "5.1.0", "version": "5.1.0",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"accepts": "^2.0.0", "accepts": "^2.0.0",
"body-parser": "^2.2.0", "body-parser": "^2.2.0",
@@ -6159,7 +6267,6 @@
}, },
"node_modules/fs.realpath": { "node_modules/fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/fsevents": { "node_modules/fsevents": {
@@ -6807,7 +6914,6 @@
}, },
"node_modules/inflight": { "node_modules/inflight": {
"version": "1.0.6", "version": "1.0.6",
"dev": true,
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"once": "^1.3.0", "once": "^1.3.0",
@@ -7228,9 +7334,7 @@
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"argparse": "^2.0.1" "argparse": "^2.0.1"
}, },
@@ -7440,6 +7544,13 @@
"dev": true, "dev": true,
"license": "MIT" "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": { "node_modules/lodash.includes": {
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
@@ -7452,6 +7563,13 @@
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
"license": "MIT" "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": { "node_modules/lodash.isinteger": {
"version": "4.0.4", "version": "4.0.4",
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
@@ -7502,9 +7620,7 @@
"version": "4.6.2", "version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
"integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
"dev": true, "license": "MIT"
"license": "MIT",
"optional": true
}, },
"node_modules/lodash.once": { "node_modules/lodash.once": {
"version": "4.1.1", "version": "4.1.1",
@@ -7933,7 +8049,6 @@
}, },
"node_modules/minimatch": { "node_modules/minimatch": {
"version": "3.1.2", "version": "3.1.2",
"dev": true,
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
@@ -8285,6 +8400,13 @@
"url": "https://github.com/sponsors/sindresorhus" "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": { "node_modules/ora": {
"version": "5.4.1", "version": "5.4.1",
"resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
@@ -8505,7 +8627,6 @@
}, },
"node_modules/path-is-absolute": { "node_modules/path-is-absolute": {
"version": "1.0.1", "version": "1.0.1",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
@@ -10057,6 +10178,92 @@
"url": "https://github.com/sponsors/ljharb" "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": { "node_modules/tar-fs": {
"version": "2.1.4", "version": "2.1.4",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz",
@@ -10495,6 +10702,15 @@
"spdx-expression-parse": "^3.0.0" "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": { "node_modules/vary": {
"version": "1.1.2", "version": "1.1.2",
"license": "MIT", "license": "MIT",
@@ -10712,6 +10928,15 @@
"dev": true, "dev": true,
"license": "ISC" "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": { "node_modules/yargs": {
"version": "16.2.0", "version": "16.2.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
@@ -10762,6 +10987,36 @@
"url": "https://github.com/sponsors/sindresorhus" "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": { "node_modules/zod": {
"version": "4.1.12", "version": "4.1.12",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz",

View File

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