5 Commits

Author SHA1 Message Date
4ff10dfcb2 test(dm): repost test
All checks were successful
Build and Push LST Docker Image / docker (push) Successful in 1m39s
Checking why comments do not post correctly

ref #31
2026-06-17 01:50:25 -05:00
9a0bb18c5b feat(dm): added in a repost incase we wanted do this instead of reuploading the file
All checks were successful
Build and Push LST Docker Image / docker (push) Successful in 2m34s
ref #31
2026-06-17 01:44:15 -05:00
1838c6f1e9 docs(readme): updated readme with actaul install 2026-06-17 01:39:00 -05:00
3a24d62957 refactor(api docks): added api docks back into the front end and prep for docusorus
All checks were successful
Build and Push LST Docker Image / docker (push) Successful in 2m15s
2026-06-16 18:53:44 -05:00
6a14bab30c refactor(psi): rebuilt the delivery and planning data to 2.0 data 2026-06-16 18:52:58 -05:00
17 changed files with 501 additions and 433 deletions

View File

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

View File

@@ -9,8 +9,6 @@ import os from "node:os";
import { apiReference } from "@scalar/express-api-reference"; import { apiReference } from "@scalar/express-api-reference";
// const port = 3000; // const port = 3000;
import type { OpenAPIV3_1 } from "openapi-types"; import type { OpenAPIV3_1 } from "openapi-types";
import { cronerActiveJobs } from "../scaler/cronerActiveJobs.spec.js";
import { cronerStatusChange } from "../scaler/cronerStatusChange.spec.js";
import { prodLoginSpec } from "../scaler/login.spec.js"; import { prodLoginSpec } from "../scaler/login.spec.js";
import { openDockApt } from "../scaler/opendockGetRelease.spec.js"; import { openDockApt } from "../scaler/opendockGetRelease.spec.js";
import { prodRestartSpec } from "../scaler/prodSqlRestart.spec.js"; import { prodRestartSpec } from "../scaler/prodSqlRestart.spec.js";
@@ -125,8 +123,6 @@ export const setupApiDocsRoutes = (baseUrl: string, app: Express) => {
...prodLoginSpec, ...prodLoginSpec,
...prodRegisterSpec, ...prodRegisterSpec,
//...mergedDatamart, //...mergedDatamart,
...cronerActiveJobs,
...cronerStatusChange,
...openDockApt, ...openDockApt,
// Add more specs here as you build features // Add more specs here as you build features

View File

@@ -275,10 +275,10 @@ export const runDatamartQuery = async (data: Data) => {
.replace("[startDate]", `${data.options.startDate}`) .replace("[startDate]", `${data.options.startDate}`)
.replace("[endDate]", `${data.options.endDate}`) .replace("[endDate]", `${data.options.endDate}`)
.replace( .replace(
"and p.IdArtikelvarianten in ([articles])", "and pl.ArticleHumanReadableId IN ([articles]) ",
data.options.articles data.options.articles
? `and p.IdArtikelvarianten in (${data.options.articles})` ? `and pl.ArticleHumanReadableId IN (${data.options.articles})`
: "--and p.IdArtikelvarianten in ([articles])", : "--and pl.ArticleHumanReadableId IN ([articles])",
); );
break; break;
default: default:

View File

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

View File

@@ -1,74 +1,73 @@
use [test1_AlplaPROD2.0_Read] use [test1_AlplaPROD2.0_Read]
DECLARE @StartDate DATE = '[startDate]' -- 2025-1-1 DECLARE @StartDate DATE = '[startDate]'
DECLARE @EndDate DATE = '[endDate]' -- 2025-1-31 DECLARE @EndDate DATE = '[endDate]'
SELECT
r.[ArticleHumanReadableId] ;WITH bol_20 AS ( -- 2.0 BOL, one per release (newest doc wins)
,[ReleaseNumber] SELECT pos.ReleaseId,
,h.CustomerOrderNumber dd.JournalNumber,
,x.CustomerLineItemNumber ROW_NUMBER() OVER (PARTITION BY pos.ReleaseId
,[CustomerReleaseNumber] ORDER BY dd.ShippingDate DESC) AS rn
,[ReleaseState] FROM [outboundDelivery].[DeliveryDocumentPosition] (nolock) pos
,[DeliveryState] JOIN [outboundDelivery].[DeliveryDocument] (nolock) dd
,ea.JournalNummer as BOL_Number ON dd.Id = pos.DeliveryDocumentId
,[ReleaseConfirmationState] -- WHERE dd.DocumentType = <BOL value> -- see note below
,[PlanningState] )
SELECT
r.[ArticleHumanReadableId]
,[ReleaseNumber]
,h.CustomerOrderNumber
,x.CustomerLineItemNumber
,[CustomerReleaseNumber]
,[ReleaseState]
,[DeliveryState]
,COALESCE(ea.JournalNummer, bol_20.JournalNumber) AS BOL_Number -- 1.0 or 2.0
,[ReleaseConfirmationState]
,[PlanningState]
,format(r.[OrderDate], 'yyyy-MM-dd HH:mm') as OrderDate ,format(r.[OrderDate], 'yyyy-MM-dd HH:mm') as OrderDate
--,r.[OrderDate] --,r.[OrderDate]
,FORMAT(r.[DeliveryDate], 'yyyy-MM-dd HH:mm') as DeliveryDate ,FORMAT(r.[DeliveryDate], 'yyyy-MM-dd HH:mm') as DeliveryDate
--,r.[DeliveryDate] --,r.[DeliveryDate]
,FORMAT(r.[LoadingDate], 'yyyy-MM-dd HH:mm') as LoadingDate ,FORMAT(r.[LoadingDate], 'yyyy-MM-dd HH:mm') as LoadingDate
--,r.[LoadingDate] --,r.[LoadingDate]
,[Quantity] ,[Quantity]
,[DeliveredQuantity] ,[DeliveredQuantity]
,r.[AdditionalInformation1] ,r.[AdditionalInformation1]
,r.[AdditionalInformation2] ,r.[AdditionalInformation2]
,[TradeUnits] ,[TradeUnits]
,[LoadingUnits] ,[LoadingUnits]
,[Trucks] ,[Trucks]
,[LoadingToleranceType] ,[LoadingToleranceType]
,[SalesPrice] ,[SalesPrice]
,[Currency] ,[Currency]
,[QuantityUnit] ,[QuantityUnit]
,[SalesPriceRemark] ,[SalesPriceRemark]
,r.[Remark] ,r.[Remark]
,[Irradiated] ,[Irradiated]
,r.[CreatedByEdi] ,r.[CreatedByEdi]
,[DeliveryAddressHumanReadableId] ,[DeliveryAddressHumanReadableId]
,DeliveryAddressDescription ,DeliveryAddressDescription
,[CustomerArtNo] ,[CustomerArtNo]
,[TotalPrice] ,[TotalPrice]
,r.[ArticleAlias] ,r.[ArticleAlias]
FROM [order].[Release] (nolock) AS r
LEFT JOIN [order].LineItem AS x ON r.LineItemId = x.id
LEFT JOIN [order].Header AS h ON x.HeaderId = h.id
FROM [order].[Release] (nolock) as r -- 1.0 BOL (legacy) — unchanged
LEFT JOIN AlplaPROD_test1.dbo.V_LadePlanungenLadeAuftragAbruf (nolock) AS zz
ON zz.AbrufIdAuftragsAbruf = r.ReleaseNumber
LEFT JOIN (
SELECT * FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY IdJournal ORDER BY add_date DESC) AS RowNum, *
FROM [AlplaPROD_test1].[dbo].[T_Lieferungen] (nolock)
) t WHERE RowNum = 1
) AS ea ON zz.IdLieferschein = ea.IdJournal
left join -- 2.0 BOL (new)
[order].LineItem as x on LEFT JOIN bol_20 ON bol_20.ReleaseId = r.Id AND bol_20.rn = 1
r.LineItemId = x.id WHERE r.DeliveryDate BETWEEN @StartDate AND @EndDate
left join
[order].Header as h on
x.HeaderId = h.id
--bol stuff
left join
AlplaPROD_test1.dbo.V_LadePlanungenLadeAuftragAbruf (nolock) as zz
on zz.AbrufIdAuftragsAbruf = r.ReleaseNumber
left join
(select * from (SELECT
ROW_NUMBER() OVER (PARTITION BY IdJournal ORDER BY add_date DESC) AS RowNum
,*
FROM [AlplaPROD_test1].[dbo].[T_Lieferungen] (nolock)) x
where RowNum = 1) as ea on
zz.IdLieferschein = ea.IdJournal
where
--r.ReleaseNumber = 1452
r.DeliveryDate between @StartDate AND @EndDate
and DeliveredQuantity > 0 and DeliveredQuantity > 0
--and r.ArticleHumanReadableId in ([articles]) --and r.ArticleHumanReadableId in ([articles])
--and Journalnummer = 169386 --and Journalnummer = 169386

View File

@@ -1,79 +0,0 @@
use AlplaPROD_test1
/**
move this over to the delivery date range query once we have the shift data mapped over correctly.
update the psi stuff on this as well.
**/
DECLARE @StartDate DATE = '[startDate]' -- 2025-1-1
DECLARE @EndDate DATE = '[endDate]' -- 2025-1-31
SELECT
r.[ArticleHumanReadableId]
,[ReleaseNumber]
,h.CustomerOrderNumber
,x.CustomerLineItemNumber
,[CustomerReleaseNumber]
,[ReleaseState]
,[DeliveryState]
,ea.JournalNummer as BOL_Number
,[ReleaseConfirmationState]
,[PlanningState]
--,format(r.[OrderDate], 'yyyy-MM-dd HH:mm') as OrderDate
,r.[OrderDate]
--,FORMAT(r.[DeliveryDate], 'yyyy-MM-dd HH:mm') as DeliveryDate
,r.[DeliveryDate]
--,FORMAT(r.[LoadingDate], 'yyyy-MM-dd HH:mm') as LoadingDate
,r.[LoadingDate]
,[Quantity]
,[DeliveredQuantity]
,r.[AdditionalInformation1]
,r.[AdditionalInformation2]
,[TradeUnits]
,[LoadingUnits]
,[Trucks]
,[LoadingToleranceType]
,[SalesPrice]
,[Currency]
,[QuantityUnit]
,[SalesPriceRemark]
,r.[Remark]
,[Irradiated]
,r.[CreatedByEdi]
,[DeliveryAddressHumanReadableId]
,DeliveryAddressDescription
,[CustomerArtNo]
,[TotalPrice]
,r.[ArticleAlias]
FROM [order].[Release] (nolock) as r
left join
[order].LineItem as x on
r.LineItemId = x.id
left join
[order].Header as h on
x.HeaderId = h.id
--bol stuff
left join
AlplaPROD_test1.dbo.V_LadePlanungenLadeAuftragAbruf (nolock) as zz
on zz.AbrufIdAuftragsAbruf = r.ReleaseNumber
left join
(select * from (SELECT
ROW_NUMBER() OVER (PARTITION BY IdJournal ORDER BY add_date DESC) AS RowNum
,*
FROM [AlplaPROD_test1].[dbo].[T_Lieferungen] (nolock)) x
where RowNum = 1) as ea on
zz.IdLieferschein = ea.IdJournal
where
r.ArticleHumanReadableId in ([articles])
--r.ReleaseNumber = 1452
and r.DeliveryDate between @StartDate AND @EndDate
--and DeliveredQuantity > 0
--and Journalnummer = 169386

View File

@@ -1,32 +1,72 @@
use AlplaPROD_test1 use [test1_AlplaPROD2.0_Read]
declare @start_date nvarchar(30) = '[startDate]' --'2025-01-01'
declare @end_date nvarchar(30) = '[endDate]' --'2025-08-09'
/*
articles will need to be passed over as well as the date structure we want to see
*/
select x.IdArtikelvarianten As Article, DECLARE @start_date date = '[startDate]'; --'2025-01-01'
ProduktionAlias as Description, DECLARE @end_date date = '[endDate]'; --'2025-08-09'
standort as MachineId, DECLARE @tz sysname = 'Eastern Standard Time'; -- usday1; use 'Central Standard Time' for usksc1
MaschinenBezeichnung as MachineName, DECLARE @shiftSeconds int = 7*3600; -- 07:00 production-day anchor
--MaschZyklus as PlanningCycleTime, --TODO: add in the proper time zone based on the env, get correcr shift time as well
x.IdProdPlanung as LotNumber, ;WITH src AS (
FORMAT(ProdTag, 'MM/dd/yyyy') as ProductionDay, SELECT
x.planMenge as TotalPlanned, pl.RunningNumber, pl.ArticleHumanReadableId, pl.ArticleAlias, pl.ArticleDescription,
ProduktionMenge as QTYPerDay, pl.MachineLocation, pl.MachineDescription, pl.ProductionLotState, pl.ProductionInterrupt,
round(ProduktionMengeVPK, 2) PalDay, pl.PlanQuantityPieces, pl.PlanQuantityLoadingUnit,
Status as finished CAST(pl.ProdStart AT TIME ZONE @tz AS datetime2(0)) AS StartLocal,
--MaschStdAuslastung as nee CAST(pl.PlanEnd AT TIME ZONE @tz AS datetime2(0)) AS EndLocal
FROM productionScheduling.ProductionLot AS pl
from dbo.V_ProdLosProduktionJeProdTag_PLANNING (nolock) as x WHERE pl.PlanEnd > pl.ProdStart
and pl.publishState = 1
left join and pl.ArticleHumanReadableId IN ([articles]) -- <-- article filter (was IdArtikelvarianten)
dbo.V_ProdPlanung (nolock) as p on --AND pl.RunningNumber = 28094 -- <-- lot filter (was IdProdPlanung); comment out for all
x.IdProdPlanung = p.IdProdPlanung ),
calc AS (
where ProdTag between @start_date and @end_date SELECT *,
and p.IdArtikelvarianten in ([articles]) DATEADD(SECOND, @shiftSeconds,
--and V_ProdLosProduktionJeProdTag_PLANNING.IdKunde = 10 CAST(CASE WHEN DATEDIFF(SECOND, CAST(StartLocal AS date), StartLocal) >= @shiftSeconds
--and IdProdPlanung = 18442 THEN CAST(StartLocal AS date)
ELSE DATEADD(DAY,-1, CAST(StartLocal AS date)) END AS datetime2(0))) AS FirstBoundary
order by ProdTag desc FROM src
),
days AS ( -- one row per production day the lot touches
SELECT RunningNumber, ArticleHumanReadableId, ArticleAlias, ArticleDescription, MachineLocation,
MachineDescription, ProductionLotState, PlanQuantityPieces, PlanQuantityLoadingUnit,
StartLocal, EndLocal, FirstBoundary AS DayStart
FROM calc
UNION ALL
SELECT RunningNumber, ArticleHumanReadableId, ArticleAlias, ArticleDescription, MachineLocation,
MachineDescription, ProductionLotState, PlanQuantityPieces, PlanQuantityLoadingUnit,
StartLocal, EndLocal, DATEADD(DAY,1,DayStart)
FROM days
WHERE DATEADD(DAY,1,DayStart) < EndLocal
),
seg AS ( -- working seconds inside each production day
SELECT *,
DATEDIFF(SECOND,
CASE WHEN StartLocal > DayStart THEN StartLocal ELSE DayStart END,
CASE WHEN EndLocal < DATEADD(DAY,1,DayStart) THEN EndLocal ELSE DATEADD(DAY,1,DayStart) END
) AS SegSec
FROM days
),
cum AS ( -- cumulative seconds for telescoping round
SELECT *,
SUM(SegSec) OVER (PARTITION BY RunningNumber ORDER BY DayStart ROWS UNBOUNDED PRECEDING) AS CumEnd,
SUM(SegSec) OVER (PARTITION BY RunningNumber ORDER BY DayStart ROWS UNBOUNDED PRECEDING) - SegSec AS CumStart,
SUM(SegSec) OVER (PARTITION BY RunningNumber) AS DenomSec
FROM seg
)
SELECT
ArticleHumanReadableId AS Article,
ArticleAlias AS Description,
MachineLocation AS MachineId,
MachineDescription AS MachineName,
RunningNumber AS LotNumber,
FORMAT(DayStart, 'MM/dd/yyyy') AS ProductionDay,
PlanQuantityPieces AS TotalPlanned,
ROUND(PlanQuantityPieces * 1.0 * CumEnd / DenomSec, 0)
- ROUND(PlanQuantityPieces * 1.0 * CumStart / DenomSec, 0) AS QTYPerDay,
ROUND(PlanQuantityLoadingUnit * CumEnd / DenomSec, 2)
- ROUND(PlanQuantityLoadingUnit * CumStart / DenomSec, 2) AS PalDay,
ProductionLotState AS finished
FROM cum
WHERE CAST(DayStart AS date) BETWEEN @start_date AND @end_date -- filter AFTER cumulative calc
ORDER BY RunningNumber, DayStart DESC
OPTION (MAXRECURSION 366);

View File

@@ -1,43 +0,0 @@
import type { OpenAPIV3_1 } from "openapi-types";
export const cronerActiveJobs: OpenAPIV3_1.PathsObject = {
"/api/utils/croner": {
get: {
summary: "Cron jobs",
description: "Returns all jobs on the server.",
tags: ["Utils"],
responses: {
"200": {
description: "Jobs returned",
content: {
"application/json": {
schema: {
type: "object",
properties: {
status: {
type: "boolean",
format: "boolean",
example: true,
},
uptime: {
type: "number",
format: "3454.34",
example: 3454.34,
},
memoryUsage: {
type: "string",
format: "Heap: 11.62 MB / RSS: 86.31 MB",
},
sqlServerStats: {
type: "number",
format: "442127",
},
},
},
},
},
},
},
},
},
};

View File

@@ -1,94 +0,0 @@
import type { OpenAPIV3_1 } from "openapi-types";
export const cronerStatusChange: OpenAPIV3_1.PathsObject = {
"/api/utils/croner/{status}": {
patch: {
summary: "Pauses or Resume the Job",
description:
"When sending start or stop with job name it will resume or stop the job",
tags: ["Utils"],
parameters: [
{
name: "status",
in: "path",
required: true,
description: "Status change",
schema: {
type: "string",
},
example: "start",
},
{
name: "limit",
in: "query",
required: false, // 👈 optional
description: "Maximum number of records to return",
schema: {
type: "integer",
minimum: 1,
maximum: 100,
},
example: 10,
},
],
requestBody: {
required: true,
content: {
"application/json": {
schema: {
type: "object",
required: ["name"],
properties: {
name: {
type: "string",
example: "start",
},
},
},
},
},
},
responses: {
"200": {
description: "Successful response",
content: {
"application/json": {
schema: {
type: "object",
properties: {
success: { type: "boolean", example: true },
data: {
type: "object",
example: {
name: "exampleName",
value: "some value",
},
},
},
},
},
},
},
"400": {
description: "Bad request",
content: {
"application/json": {
schema: {
type: "object",
properties: {
success: { type: "boolean", example: false },
message: {
type: "string",
example: "Invalid name parameter",
},
},
},
},
},
},
},
},
},
};

View File

@@ -1,10 +1,14 @@
import { useQuery, useSuspenseQuery } from "@tanstack/react-query"; import { useQuery, useSuspenseQuery } from "@tanstack/react-query";
import { Link } from "@tanstack/react-router";
import { LaptopMinimal } from "lucide-react";
import { import {
Sidebar, Sidebar,
SidebarContent, SidebarContent,
SidebarHeader, SidebarFooter,
SidebarMenu, SidebarMenu,
SidebarMenuButton,
SidebarMenuItem, SidebarMenuItem,
useSidebar,
} from "@/components/ui/sidebar"; } from "@/components/ui/sidebar";
import { useSession } from "@/lib/auth-client"; import { useSession } from "@/lib/auth-client";
import { getSettings } from "../../lib/queries/getSettings"; import { getSettings } from "../../lib/queries/getSettings";
@@ -23,6 +27,7 @@ export function AppSidebar() {
openDock: ["read"], openDock: ["read"],
}), }),
); );
const { setOpen } = useSidebar();
// const { data: canReadWarehouse = false } = useQuery( // const { data: canReadWarehouse = false } = useQuery(
// permissionQuery({ // permissionQuery({
@@ -36,7 +41,7 @@ export function AppSidebar() {
collapsible="offcanvas" collapsible="offcanvas"
className="top-(--header-height) h-[calc(100svh-var(--header-height))]!" className="top-(--header-height) h-[calc(100svh-var(--header-height))]!"
> >
<SidebarHeader> <SidebarContent>
<SidebarMenu> <SidebarMenu>
<SidebarMenuItem> <SidebarMenuItem>
<SidebarContent> <SidebarContent>
@@ -64,7 +69,24 @@ export function AppSidebar() {
</SidebarContent> </SidebarContent>
</SidebarMenuItem> </SidebarMenuItem>
</SidebarMenu> </SidebarMenu>
</SidebarHeader> </SidebarContent>
{session &&
(session.user.role === "admin" ||
session.user.role === "systemAdmin" ||
session.user.role === "manager") && (
<SidebarFooter>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton asChild>
<Link to={"/apidocs"} onClick={() => setOpen(false)}>
<LaptopMinimal />
<span>Api docs</span>
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarFooter>
)}
</Sidebar> </Sidebar>
); );
} }

View File

@@ -11,9 +11,11 @@
import { Route as rootRouteImport } from './routes/__root' import { Route as rootRouteImport } from './routes/__root'
import { Route as ForbiddenRouteImport } from './routes/forbidden' import { Route as ForbiddenRouteImport } from './routes/forbidden'
import { Route as AppDownRouteImport } from './routes/app-down' import { Route as AppDownRouteImport } from './routes/app-down'
import { Route as ApidocsRouteImport } from './routes/apidocs'
import { Route as AboutRouteImport } from './routes/about' import { Route as AboutRouteImport } from './routes/about'
import { Route as IndexRouteImport } from './routes/index' import { Route as IndexRouteImport } from './routes/index'
import { Route as DocsIndexRouteImport } from './routes/docs/index' import { Route as DocsIndexRouteImport } from './routes/docs/index'
import { Route as DocsDatamartRouteImport } from './routes/docs/datamart'
import { Route as DocsSplatRouteImport } from './routes/docs/$' import { Route as DocsSplatRouteImport } from './routes/docs/$'
import { Route as AdminUsersRouteImport } from './routes/admin/users' import { Route as AdminUsersRouteImport } from './routes/admin/users'
import { Route as AdminSettingsRouteImport } from './routes/admin/settings' import { Route as AdminSettingsRouteImport } from './routes/admin/settings'
@@ -41,6 +43,11 @@ const AppDownRoute = AppDownRouteImport.update({
path: '/app-down', path: '/app-down',
getParentRoute: () => rootRouteImport, getParentRoute: () => rootRouteImport,
} as any) } as any)
const ApidocsRoute = ApidocsRouteImport.update({
id: '/apidocs',
path: '/apidocs',
getParentRoute: () => rootRouteImport,
} as any)
const AboutRoute = AboutRouteImport.update({ const AboutRoute = AboutRouteImport.update({
id: '/about', id: '/about',
path: '/about', path: '/about',
@@ -56,6 +63,11 @@ const DocsIndexRoute = DocsIndexRouteImport.update({
path: '/docs/', path: '/docs/',
getParentRoute: () => rootRouteImport, getParentRoute: () => rootRouteImport,
} as any) } as any)
const DocsDatamartRoute = DocsDatamartRouteImport.update({
id: '/docs/datamart',
path: '/docs/datamart',
getParentRoute: () => rootRouteImport,
} as any)
const DocsSplatRoute = DocsSplatRouteImport.update({ const DocsSplatRoute = DocsSplatRouteImport.update({
id: '/docs/$', id: '/docs/$',
path: '/docs/$', path: '/docs/$',
@@ -145,6 +157,7 @@ const WarehouseDockdoorscanningScansDockScansRoute =
export interface FileRoutesByFullPath { export interface FileRoutesByFullPath {
'/': typeof IndexRoute '/': typeof IndexRoute
'/about': typeof AboutRoute '/about': typeof AboutRoute
'/apidocs': typeof ApidocsRoute
'/app-down': typeof AppDownRoute '/app-down': typeof AppDownRoute
'/forbidden': typeof ForbiddenRoute '/forbidden': typeof ForbiddenRoute
'/login': typeof authLoginRoute '/login': typeof authLoginRoute
@@ -155,6 +168,7 @@ export interface FileRoutesByFullPath {
'/admin/settings': typeof AdminSettingsRoute '/admin/settings': typeof AdminSettingsRoute
'/admin/users': typeof AdminUsersRoute '/admin/users': typeof AdminUsersRoute
'/docs/$': typeof DocsSplatRoute '/docs/$': typeof DocsSplatRoute
'/docs/datamart': typeof DocsDatamartRoute
'/docs/': typeof DocsIndexRoute '/docs/': typeof DocsIndexRoute
'/user/profile': typeof authUserProfileRoute '/user/profile': typeof authUserProfileRoute
'/user/resetpassword': typeof authUserResetpasswordRoute '/user/resetpassword': typeof authUserResetpasswordRoute
@@ -168,6 +182,7 @@ export interface FileRoutesByFullPath {
export interface FileRoutesByTo { export interface FileRoutesByTo {
'/': typeof IndexRoute '/': typeof IndexRoute
'/about': typeof AboutRoute '/about': typeof AboutRoute
'/apidocs': typeof ApidocsRoute
'/app-down': typeof AppDownRoute '/app-down': typeof AppDownRoute
'/forbidden': typeof ForbiddenRoute '/forbidden': typeof ForbiddenRoute
'/login': typeof authLoginRoute '/login': typeof authLoginRoute
@@ -178,6 +193,7 @@ export interface FileRoutesByTo {
'/admin/settings': typeof AdminSettingsRoute '/admin/settings': typeof AdminSettingsRoute
'/admin/users': typeof AdminUsersRoute '/admin/users': typeof AdminUsersRoute
'/docs/$': typeof DocsSplatRoute '/docs/$': typeof DocsSplatRoute
'/docs/datamart': typeof DocsDatamartRoute
'/docs': typeof DocsIndexRoute '/docs': typeof DocsIndexRoute
'/user/profile': typeof authUserProfileRoute '/user/profile': typeof authUserProfileRoute
'/user/resetpassword': typeof authUserResetpasswordRoute '/user/resetpassword': typeof authUserResetpasswordRoute
@@ -192,6 +208,7 @@ export interface FileRoutesById {
__root__: typeof rootRouteImport __root__: typeof rootRouteImport
'/': typeof IndexRoute '/': typeof IndexRoute
'/about': typeof AboutRoute '/about': typeof AboutRoute
'/apidocs': typeof ApidocsRoute
'/app-down': typeof AppDownRoute '/app-down': typeof AppDownRoute
'/forbidden': typeof ForbiddenRoute '/forbidden': typeof ForbiddenRoute
'/(auth)/login': typeof authLoginRoute '/(auth)/login': typeof authLoginRoute
@@ -202,6 +219,7 @@ export interface FileRoutesById {
'/admin/settings': typeof AdminSettingsRoute '/admin/settings': typeof AdminSettingsRoute
'/admin/users': typeof AdminUsersRoute '/admin/users': typeof AdminUsersRoute
'/docs/$': typeof DocsSplatRoute '/docs/$': typeof DocsSplatRoute
'/docs/datamart': typeof DocsDatamartRoute
'/docs/': typeof DocsIndexRoute '/docs/': typeof DocsIndexRoute
'/(auth)/user/profile': typeof authUserProfileRoute '/(auth)/user/profile': typeof authUserProfileRoute
'/(auth)/user/resetpassword': typeof authUserResetpasswordRoute '/(auth)/user/resetpassword': typeof authUserResetpasswordRoute
@@ -217,6 +235,7 @@ export interface FileRouteTypes {
fullPaths: fullPaths:
| '/' | '/'
| '/about' | '/about'
| '/apidocs'
| '/app-down' | '/app-down'
| '/forbidden' | '/forbidden'
| '/login' | '/login'
@@ -227,6 +246,7 @@ export interface FileRouteTypes {
| '/admin/settings' | '/admin/settings'
| '/admin/users' | '/admin/users'
| '/docs/$' | '/docs/$'
| '/docs/datamart'
| '/docs/' | '/docs/'
| '/user/profile' | '/user/profile'
| '/user/resetpassword' | '/user/resetpassword'
@@ -240,6 +260,7 @@ export interface FileRouteTypes {
to: to:
| '/' | '/'
| '/about' | '/about'
| '/apidocs'
| '/app-down' | '/app-down'
| '/forbidden' | '/forbidden'
| '/login' | '/login'
@@ -250,6 +271,7 @@ export interface FileRouteTypes {
| '/admin/settings' | '/admin/settings'
| '/admin/users' | '/admin/users'
| '/docs/$' | '/docs/$'
| '/docs/datamart'
| '/docs' | '/docs'
| '/user/profile' | '/user/profile'
| '/user/resetpassword' | '/user/resetpassword'
@@ -263,6 +285,7 @@ export interface FileRouteTypes {
| '__root__' | '__root__'
| '/' | '/'
| '/about' | '/about'
| '/apidocs'
| '/app-down' | '/app-down'
| '/forbidden' | '/forbidden'
| '/(auth)/login' | '/(auth)/login'
@@ -273,6 +296,7 @@ export interface FileRouteTypes {
| '/admin/settings' | '/admin/settings'
| '/admin/users' | '/admin/users'
| '/docs/$' | '/docs/$'
| '/docs/datamart'
| '/docs/' | '/docs/'
| '/(auth)/user/profile' | '/(auth)/user/profile'
| '/(auth)/user/resetpassword' | '/(auth)/user/resetpassword'
@@ -287,6 +311,7 @@ export interface FileRouteTypes {
export interface RootRouteChildren { export interface RootRouteChildren {
IndexRoute: typeof IndexRoute IndexRoute: typeof IndexRoute
AboutRoute: typeof AboutRoute AboutRoute: typeof AboutRoute
ApidocsRoute: typeof ApidocsRoute
AppDownRoute: typeof AppDownRoute AppDownRoute: typeof AppDownRoute
ForbiddenRoute: typeof ForbiddenRoute ForbiddenRoute: typeof ForbiddenRoute
authLoginRoute: typeof authLoginRoute authLoginRoute: typeof authLoginRoute
@@ -297,6 +322,7 @@ export interface RootRouteChildren {
AdminSettingsRoute: typeof AdminSettingsRoute AdminSettingsRoute: typeof AdminSettingsRoute
AdminUsersRoute: typeof AdminUsersRoute AdminUsersRoute: typeof AdminUsersRoute
DocsSplatRoute: typeof DocsSplatRoute DocsSplatRoute: typeof DocsSplatRoute
DocsDatamartRoute: typeof DocsDatamartRoute
DocsIndexRoute: typeof DocsIndexRoute DocsIndexRoute: typeof DocsIndexRoute
authUserProfileRoute: typeof authUserProfileRoute authUserProfileRoute: typeof authUserProfileRoute
authUserResetpasswordRoute: typeof authUserResetpasswordRoute authUserResetpasswordRoute: typeof authUserResetpasswordRoute
@@ -324,6 +350,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AppDownRouteImport preLoaderRoute: typeof AppDownRouteImport
parentRoute: typeof rootRouteImport parentRoute: typeof rootRouteImport
} }
'/apidocs': {
id: '/apidocs'
path: '/apidocs'
fullPath: '/apidocs'
preLoaderRoute: typeof ApidocsRouteImport
parentRoute: typeof rootRouteImport
}
'/about': { '/about': {
id: '/about' id: '/about'
path: '/about' path: '/about'
@@ -345,6 +378,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof DocsIndexRouteImport preLoaderRoute: typeof DocsIndexRouteImport
parentRoute: typeof rootRouteImport parentRoute: typeof rootRouteImport
} }
'/docs/datamart': {
id: '/docs/datamart'
path: '/docs/datamart'
fullPath: '/docs/datamart'
preLoaderRoute: typeof DocsDatamartRouteImport
parentRoute: typeof rootRouteImport
}
'/docs/$': { '/docs/$': {
id: '/docs/$' id: '/docs/$'
path: '/docs/$' path: '/docs/$'
@@ -463,6 +503,7 @@ declare module '@tanstack/react-router' {
const rootRouteChildren: RootRouteChildren = { const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute, IndexRoute: IndexRoute,
AboutRoute: AboutRoute, AboutRoute: AboutRoute,
ApidocsRoute: ApidocsRoute,
AppDownRoute: AppDownRoute, AppDownRoute: AppDownRoute,
ForbiddenRoute: ForbiddenRoute, ForbiddenRoute: ForbiddenRoute,
authLoginRoute: authLoginRoute, authLoginRoute: authLoginRoute,
@@ -473,6 +514,7 @@ const rootRouteChildren: RootRouteChildren = {
AdminSettingsRoute: AdminSettingsRoute, AdminSettingsRoute: AdminSettingsRoute,
AdminUsersRoute: AdminUsersRoute, AdminUsersRoute: AdminUsersRoute,
DocsSplatRoute: DocsSplatRoute, DocsSplatRoute: DocsSplatRoute,
DocsDatamartRoute: DocsDatamartRoute,
DocsIndexRoute: DocsIndexRoute, DocsIndexRoute: DocsIndexRoute,
authUserProfileRoute: authUserProfileRoute, authUserProfileRoute: authUserProfileRoute,
authUserResetpasswordRoute: authUserResetpasswordRoute, authUserResetpasswordRoute: authUserResetpasswordRoute,

View File

@@ -19,8 +19,10 @@ const RootLayout = () => {
<div className="relative min-h-[calc(100svh-var(--header-height))]"> <div className="relative min-h-[calc(100svh-var(--header-height))]">
<AppSidebar /> <AppSidebar />
<main className="w-full p-4"> <main className="w-full">
<div className="mx-auto w-full max-w-7xl"> <div className="mx-auto w-full flex justify-center">
{" "}
{/* className="mx-auto w-full max-w-7xl" use this for dashboards and stuff*/}
<Outlet /> <Outlet />
</div> </div>
</main> </main>
@@ -31,7 +33,7 @@ const RootLayout = () => {
</SidebarProvider> </SidebarProvider>
</ThemeProvider> </ThemeProvider>
{session && session.user.role === "systemAdmin" && ( {session && session.user.role === "systemAdmin" && (
<TanStackRouterDevtools /> <TanStackRouterDevtools position="bottom-right" />
)} )}
</div> </div>
); );

View File

@@ -248,7 +248,7 @@ function RouteComponent() {
}; };
//console.log(logs); //console.log(logs);
return ( return (
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1 max-w-7xl">
<div className="flex gap-1 justify-end"> <div className="flex gap-1 justify-end">
<Button onClick={triggerBuild}>Trigger Build</Button> <Button onClick={triggerBuild}>Trigger Build</Button>
<Button onClick={() => clearRoom()}>Clear Logs</Button> <Button onClick={() => clearRoom()}>Clear Logs</Button>

View File

@@ -0,0 +1,15 @@
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/apidocs")({
component: RouteComponent,
});
function RouteComponent() {
return (
<iframe
src="/lst/api/docs"
className="h-[calc(100vh-64px)] w-full border-0"
title="LST API Docs"
/>
);
}

View File

@@ -1,18 +1,18 @@
import { useSuspenseQuery } from "@tanstack/react-query"; import { useSuspenseQuery } from "@tanstack/react-query";
import { createFileRoute, redirect } from "@tanstack/react-router"; import { createFileRoute, redirect } from "@tanstack/react-router";
import { createColumnHelper } from "@tanstack/react-table"; import { createColumnHelper } from "@tanstack/react-table";
import { Trash } from "lucide-react";
import { Suspense, useState } from "react"; import { Suspense, useState } from "react";
import { toast } from "sonner";
import { Button } from "../../../components/ui/button";
import { Spinner } from "../../../components/ui/spinner";
import { api } from "../../../lib/apiHelper";
import { authClient } from "../../../lib/auth-client"; import { authClient } from "../../../lib/auth-client";
import { getArticleLinks } from "../../../lib/queries/getArticleLinks"; import { getArticleLinks } from "../../../lib/queries/getArticleLinks";
import LstTable from "../../../lib/tableStuff/LstTable"; import LstTable from "../../../lib/tableStuff/LstTable";
import SearchableHeader from "../../../lib/tableStuff/SearchableHeader"; import SearchableHeader from "../../../lib/tableStuff/SearchableHeader";
import SkellyTable from "../../../lib/tableStuff/SkellyTable"; import SkellyTable from "../../../lib/tableStuff/SkellyTable";
import NewArticleLink from "./-components/NewArticleLink"; import NewArticleLink from "./-components/NewArticleLink";
import { api } from "../../../lib/apiHelper";
import { toast } from "sonner";
import { Button } from "../../../components/ui/button";
import { Spinner } from "../../../components/ui/spinner";
import { Trash } from "lucide-react";
export const Route = createFileRoute("/transportation/opendock/")({ export const Route = createFileRoute("/transportation/opendock/")({
beforeLoad: async ({ location }) => { beforeLoad: async ({ location }) => {
@@ -97,77 +97,77 @@ const ArticleLinkTable = () => {
cell: (i) => i.getValue(), cell: (i) => i.getValue(),
}), }),
columnHelper.accessor("deleteUser", { columnHelper.accessor("deleteUser", {
header: ({ column }) => ( header: ({ column }) => (
<SearchableHeader <SearchableHeader
column={column} column={column}
title="Delete Link" title="Delete Link"
searchable={false} searchable={false}
/> />
), ),
filterFn: "includesString", filterFn: "includesString",
cell: (i) => { cell: (i) => {
// biome-ignore lint: just removing the lint for now to get this going will maybe fix later // biome-ignore lint: just removing the lint for now to get this going will maybe fix later
const [activeToggle, setActiveToggle] = useState(false); const [activeToggle, setActiveToggle] = useState(false);
const onTrigger = async () => { const onTrigger = async () => {
setActiveToggle(true); setActiveToggle(true);
try { try {
const res = await api.delete( const res = await api.delete(
`/opendock/articleCheck/${i.row.original.id}`, `/opendock/articleCheck/${i.row.original.id}`,
{ {
withCredentials: true, withCredentials: true,
timeout: 5000, timeout: 5000,
validateStatus: () => true, validateStatus: () => true,
}, },
);
if (res.data.success) {
toast.success(`AV: ${i.row.original.av} was deleted.`);
refetch();
setActiveToggle(false);
}
if (!res.data.success) {
toast.error(
`AV: ${i.row.original.av} encountered an error when trying to delete: ${res.data.message}`,
);
refetch();
setActiveToggle(false);
}
} catch (error) {
setActiveToggle(false);
console.error(error);
}
};
return (
<div>
<div className="flex items-center space-x-2">
<Button
variant="destructive"
disabled={activeToggle}
onClick={onTrigger}
>
{activeToggle ? (
<span>
<Spinner />
</span>
) : (
<span>
<Trash />
</span>
)}
</Button>
</div>
</div>
); );
},
}), if (res.data.success) {
toast.success(`AV: ${i.row.original.av} was deleted.`);
refetch();
setActiveToggle(false);
}
if (!res.data.success) {
toast.error(
`AV: ${i.row.original.av} encountered an error when trying to delete: ${res.data.message}`,
);
refetch();
setActiveToggle(false);
}
} catch (error) {
setActiveToggle(false);
console.error(error);
}
};
return (
<div>
<div className="flex items-center space-x-2">
<Button
variant="destructive"
disabled={activeToggle}
onClick={onTrigger}
>
{activeToggle ? (
<span>
<Spinner />
</span>
) : (
<span>
<Trash />
</span>
)}
</Button>
</div>
</div>
);
},
}),
]; ];
return ( return (
<div> <div className="">
<div> <div>
<div className="flex justify-end m-2"> <div className="flex justify-end m-2">
<Suspense <Suspense

90
package-lock.json generated
View File

@@ -10,7 +10,7 @@
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@dotenvx/dotenvx": "^1.57.0", "@dotenvx/dotenvx": "^1.57.0",
"@scalar/express-api-reference": "^0.9.4", "@scalar/express-api-reference": "^0.9.20",
"@socket.io/admin-ui": "^0.5.1", "@socket.io/admin-ui": "^0.5.1",
"archiver": "^7.0.1", "archiver": "^7.0.1",
"axios": "^1.13.6", "axios": "^1.13.6",
@@ -44,7 +44,8 @@
"socket.io": "^4.8.3", "socket.io": "^4.8.3",
"socket.io-client": "^4.8.3", "socket.io-client": "^4.8.3",
"xlsx": "^0.18.5", "xlsx": "^0.18.5",
"zod": "^4.3.6" "zod": "^4.3.6",
"zod-openapi": "^6.0.0"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "2.4.8", "@biomejs/biome": "2.4.8",
@@ -2424,46 +2425,61 @@
"devOptional": true, "devOptional": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@scalar/core": { "node_modules/@scalar/client-side-rendering": {
"version": "0.4.4", "version": "0.1.13",
"resolved": "https://registry.npmjs.org/@scalar/core/-/core-0.4.4.tgz", "resolved": "https://registry.npmjs.org/@scalar/client-side-rendering/-/client-side-rendering-0.1.13.tgz",
"integrity": "sha512-eXIG0opyQn45FzpTp0dAWFP1Vjcx+helgUAsa0uN36tyUR7DSmz2kRwHqqedzvPWryeRCKPz7/vwzKpETZp5lg==", "integrity": "sha512-p8V4HgEWjaCpqsnhclg1pTfjE9JA0AWRr0ocBQHexoHo+pqnSs1d83Mv9rjH7R0FZJrlCSandZZeY3DMX2gYXQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@scalar/types": "0.7.4" "@scalar/schemas": "0.3.3",
"@scalar/types": "0.12.3",
"@scalar/validation": "0.6.0"
}, },
"engines": { "engines": {
"node": ">=22" "node": ">=22"
} }
}, },
"node_modules/@scalar/express-api-reference": { "node_modules/@scalar/express-api-reference": {
"version": "0.9.4", "version": "0.9.20",
"resolved": "https://registry.npmjs.org/@scalar/express-api-reference/-/express-api-reference-0.9.4.tgz", "resolved": "https://registry.npmjs.org/@scalar/express-api-reference/-/express-api-reference-0.9.20.tgz",
"integrity": "sha512-KXG+VaMArCGcWhzDV2rfkHd+UF1HYevIFbO6cqFpd+az7QHvVT99BU8Yh60T1dmtCp504s0Pl/vcTyJ91fK1Ug==", "integrity": "sha512-J0P6qpYoL0kXvs/A/vuAwCqQFCYnErbXSB5/3lEGTbARuK0oGyMvl55dQyW5Ucq3CX1npuRejlTX6bxEprSvJA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@scalar/core": "0.4.4" "@scalar/client-side-rendering": "0.1.13"
}, },
"engines": { "engines": {
"node": ">=22" "node": ">=22"
} }
}, },
"node_modules/@scalar/helpers": { "node_modules/@scalar/helpers": {
"version": "0.4.2", "version": "0.8.1",
"resolved": "https://registry.npmjs.org/@scalar/helpers/-/helpers-0.4.2.tgz", "resolved": "https://registry.npmjs.org/@scalar/helpers/-/helpers-0.8.1.tgz",
"integrity": "sha512-IrgrGVSahCfYDNWITazz4Q1BOndp5eEzlimRkfxiYn++KqeWyLfALyym1omqcdKGYtiSx1KIbKaUJL9vkjaN7w==", "integrity": "sha512-yuiuBCadP5bjAnIv23QvifVN/NaMi9xBF6b8Wdk4QOzwzLPJmp699MAdf33J0A5i2qKcvnu32iz/VkEJmQRe5g==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=22" "node": ">=22"
} }
}, },
"node_modules/@scalar/types": { "node_modules/@scalar/schemas": {
"version": "0.7.4", "version": "0.3.3",
"resolved": "https://registry.npmjs.org/@scalar/types/-/types-0.7.4.tgz", "resolved": "https://registry.npmjs.org/@scalar/schemas/-/schemas-0.3.3.tgz",
"integrity": "sha512-1o9uf42lZ9YD0XP/HMWrwXN0unx6vFTTgtduA1F28Yloea9Pfv9N2R/t0wO91iSIzw4+NubEFolunbdb2QcgHA==", "integrity": "sha512-qDcgFu6ta5Z90L9D2P6DFKzYesU+FW5+m55SGmdI4iRMRCwj5umHpec2Y2W/SJcCF6bbUZawMxuOH2Ja6rUNpQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@scalar/helpers": "0.4.2", "@scalar/helpers": "0.8.1",
"@scalar/validation": "0.6.0"
},
"engines": {
"node": ">=22"
}
},
"node_modules/@scalar/types": {
"version": "0.12.3",
"resolved": "https://registry.npmjs.org/@scalar/types/-/types-0.12.3.tgz",
"integrity": "sha512-7zaXafbgTFmsJ/9AwYeExUWzXoZNyKOL0SEVAUWRaOndcjxpFCtwzuPrc1elMEWdHopWbY1Qe5pWKbE2aqG2HA==",
"license": "MIT",
"dependencies": {
"@scalar/helpers": "0.8.1",
"nanoid": "^5.1.6", "nanoid": "^5.1.6",
"type-fest": "^5.3.1", "type-fest": "^5.3.1",
"zod": "^4.3.5" "zod": "^4.3.5"
@@ -2472,6 +2488,15 @@
"node": ">=22" "node": ">=22"
} }
}, },
"node_modules/@scalar/validation": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@scalar/validation/-/validation-0.6.0.tgz",
"integrity": "sha512-tpmmG+/xRE2Kn9RpflU3AIyZv08v10+E1ZrJCx7z6+/91zHVxy0M73kC1LT4/8PbYNt85ywyC8+n+D99JdMcGA==",
"license": "MIT",
"engines": {
"node": ">=20"
}
},
"node_modules/@serialport/binding-mock": { "node_modules/@serialport/binding-mock": {
"version": "10.2.2", "version": "10.2.2",
"resolved": "https://registry.npmjs.org/@serialport/binding-mock/-/binding-mock-10.2.2.tgz", "resolved": "https://registry.npmjs.org/@serialport/binding-mock/-/binding-mock-10.2.2.tgz",
@@ -10566,9 +10591,9 @@
"license": "ISC" "license": "ISC"
}, },
"node_modules/nanoid": { "node_modules/nanoid": {
"version": "5.1.7", "version": "5.1.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.7.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.11.tgz",
"integrity": "sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ==", "integrity": "sha512-v+KEsUv2ps74PaSKv0gHTxTCgMXOIfBEbaqa6w6ISIGC7ZsvHN4N9oJ8d4cmf0n5oTzQz2SLmThbQWhjd/8eKg==",
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@@ -13252,9 +13277,9 @@
} }
}, },
"node_modules/type-fest": { "node_modules/type-fest": {
"version": "5.5.0", "version": "5.7.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.5.0.tgz", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.7.0.tgz",
"integrity": "sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g==", "integrity": "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==",
"license": "(MIT OR CC0-1.0)", "license": "(MIT OR CC0-1.0)",
"dependencies": { "dependencies": {
"tagged-tag": "^1.0.0" "tagged-tag": "^1.0.0"
@@ -13937,6 +13962,21 @@
"funding": { "funding": {
"url": "https://github.com/sponsors/colinhacks" "url": "https://github.com/sponsors/colinhacks"
} }
},
"node_modules/zod-openapi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/zod-openapi/-/zod-openapi-6.0.0.tgz",
"integrity": "sha512-mS4eRJ4DGCPrg6elRbJqc/3nLe4EPVi8KiHRKZ7dcTR5m5orPy8EfoWmceAyGZAq71MAWuyrTTOag7W5N61ZPQ==",
"license": "MIT",
"engines": {
"node": ">=22.14.0"
},
"funding": {
"url": "https://github.com/samchungy/zod-openapi?sponsor=1"
},
"peerDependencies": {
"zod": "^4.0.0"
}
} }
} }
} }

View File

@@ -68,7 +68,7 @@
}, },
"dependencies": { "dependencies": {
"@dotenvx/dotenvx": "^1.57.0", "@dotenvx/dotenvx": "^1.57.0",
"@scalar/express-api-reference": "^0.9.4", "@scalar/express-api-reference": "^0.9.20",
"@socket.io/admin-ui": "^0.5.1", "@socket.io/admin-ui": "^0.5.1",
"archiver": "^7.0.1", "archiver": "^7.0.1",
"axios": "^1.13.6", "axios": "^1.13.6",
@@ -102,7 +102,8 @@
"socket.io": "^4.8.3", "socket.io": "^4.8.3",
"socket.io-client": "^4.8.3", "socket.io-client": "^4.8.3",
"xlsx": "^0.18.5", "xlsx": "^0.18.5",
"zod": "^4.3.6" "zod": "^4.3.6",
"zod-openapi": "^6.0.0"
}, },
"config": { "config": {
"commitizen": { "commitizen": {