refactor(old app): login migration to new app

This commit is contained in:
2025-10-21 20:22:21 -05:00
parent a2a8e0ef9f
commit eb3fa4dd52
28 changed files with 2273 additions and 2140 deletions

View File

@@ -1,28 +1,26 @@
import { getUsers } from "@/utils/querys/admin/users";
import { useQuery } from "@tanstack/react-query";
import UserCard from "./components/UserCard";
export default function UserPage() { export default function UserPage() {
const { data, isError, error, isLoading } = useQuery(getUsers()); //const { data, isError, error, isLoading } = useQuery(getUsers());
if (isLoading) return <div className="m-auto">Loading users...</div>; // if (isLoading) return <div className="m-auto">Loading users...</div>;
if (isError) // if (isError)
return ( // return (
<div className="m-auto"> // <div className="m-auto">
There was an error getting the users.... {JSON.stringify(error)} // There was an error getting the users.... {JSON.stringify(error)}
</div> // </div>
); // );
return ( return (
<div className="m-2 w-dvw"> <div className="m-2 w-dvw">
{data.map((u: any) => { <span>This has been moved to the new system</span>
return ( {/* {data.map((u: any) => {
<div> return (
<UserCard user={u} /> <div>
</div> <UserCard user={u} />
);
})} </div>
</div> );
); })} */}
</div>
);
} }

View File

@@ -1,172 +1,177 @@
import { useSessionStore } from "../../lib/store/sessionStore"; import { zodResolver } from "@hookform/resolvers/zod";
import { LstCard } from "../extendedUI/LstCard";
import { CardHeader } from "../ui/card";
import { toast } from "sonner";
import { z } from "zod";
import { useRouter, useSearch } from "@tanstack/react-router"; import { useRouter, useSearch } from "@tanstack/react-router";
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod"; import { toast } from "sonner";
import { Label } from "../ui/label"; import { z } from "zod";
import { Input } from "../ui/input"; import { useAuthStore } from "@/lib/store/useAuthStore";
import { Checkbox } from "../ui/checkbox"; import { useSessionStore } from "../../lib/store/sessionStore";
import { LstCard } from "../extendedUI/LstCard";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
import { CardHeader } from "../ui/card";
import { Checkbox } from "../ui/checkbox";
import { Input } from "../ui/input";
import { Label } from "../ui/label";
const FormSchema = z.object({ const FormSchema = z.object({
username: z.string().min(1, "You must enter a valid username"), username: z.string().min(1, "You must enter a valid username"),
password: z.string().min(4, "You must enter a valid password"), password: z.string().min(4, "You must enter a valid password"),
rememberMe: z.boolean(), rememberMe: z.boolean(),
}); });
const LoginForm = () => { const LoginForm = () => {
const { setSession } = useSessionStore(); const { setSession } = useSessionStore();
const rememeberMe = localStorage.getItem("rememberMe") === "true"; const { setUserInfo } = useAuthStore();
const username = localStorage.getItem("username") || ""; const rememeberMe = localStorage.getItem("rememberMe") === "true";
const router = useRouter(); const username = localStorage.getItem("username") || "";
const search = useSearch({ from: "/login" }); const router = useRouter();
const search = useSearch({ from: "/login" });
const { const {
register, register,
handleSubmit, handleSubmit,
control, control,
formState: { errors }, formState: { errors },
} = useForm<z.infer<typeof FormSchema>>({ } = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema), resolver: zodResolver(FormSchema),
defaultValues: { defaultValues: {
username: username || "", username: username || "",
password: "", password: "",
rememberMe: rememeberMe, rememberMe: rememeberMe,
}, },
}); });
const onSubmitLogin = async (value: z.infer<typeof FormSchema>) => { const onSubmitLogin = async (value: z.infer<typeof FormSchema>) => {
// Do something with form data // Do something with form data
// first update the rememberMe incase it was selected // first update the rememberMe incase it was selected
if (value.rememberMe) { if (value.rememberMe) {
localStorage.setItem("rememberMe", value.rememberMe.toString()); localStorage.setItem("rememberMe", value.rememberMe.toString());
localStorage.setItem("username", value.username); localStorage.setItem("username", value.username);
} else { } else {
localStorage.removeItem("rememberMe"); localStorage.removeItem("rememberMe");
localStorage.removeItem("username"); localStorage.removeItem("username");
} }
try { try {
const response = await fetch("/api/auth/login", { const response = await fetch("/api/auth/login", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
username: value.username, username: value.username,
password: value.password, password: value.password,
}), }),
}); });
const data = await response.json(); const data = await response.json();
// Store token in localStorage // Store token in localStorage
// localStorage.setItem("auth_token", data.data.token); // localStorage.setItem("auth_token", data.data.token);
if (data.success) { if (data.success) {
const prod = btoa( const prod = btoa(`${value.username.toLowerCase()}:${value.password}`);
`${value.username.toLowerCase()}:${value.password}` const prodUser = { ...data.user, prod: prod };
); setUserInfo(value.username.toLowerCase(), value.password);
const prodUser = { ...data.user, prod: prod }; setSession(prodUser, data.data.token);
toast.success(`You are logged in as ${data.data.user.username}`);
setSession(prodUser, data.token); //console.log(search.redirect ? search.redirect : "oops");
toast.success(`You are logged in as ${data.user.username}`); router.history.push(search.redirect ? search.redirect : "/");
}
console.log(search.redirect ? search.redirect : "oops"); if (!data.success) {
router.history.push(search.redirect ? search.redirect : "/"); toast.error(`${data.message}`);
} }
if (!data.success) { //console.log(data);
toast.error(`${data.message}`); } catch (err) {
} // @ts-ignore
if (!err.response.success) {
// @ts-ignore
toast.error(err.response.data.message);
} else {
// @ts-ignore
toast.error(err?.message);
}
}
};
//console.log(data); return (
} catch (err) { <div className="ml-[25%]">
toast.error("Invalid credentials"); <LstCard className="p-3 w-96">
} <CardHeader>
}; <div>
<p className="text-2xl">Login to LST</p>
</div>
</CardHeader>
<hr className="rounded"></hr>
<form onSubmit={handleSubmit(onSubmitLogin)}>
<div>
<Label htmlFor="username" className="m-1">
Username
</Label>
<Input
placeholder="smith001"
{...register("username")}
className={errors.username ? "border-red-500" : ""}
aria-invalid={!!errors.username}
/>
{errors.username && (
<p className="text-red-500 text-sm mt-1">
{errors.username.message}
</p>
)}
</div>
<div>
<>
<Label htmlFor={"password"} className="m-1">
Password
</Label>
<Input
type="password"
{...register("password")}
className={errors.password ? "border-red-500" : ""}
aria-invalid={!!errors.password}
/>
{errors.password && (
<p className="text-red-500 text-sm mt-1">
{errors.password.message}
</p>
)}
</>
</div>
<div className="flex justify-between pt-2">
<div className="flex">
<Controller
render={({ field }) => (
<>
<Checkbox
id="remember"
checked={field.value}
onCheckedChange={field.onChange}
/>
<label
htmlFor="remember"
className="pl-2 text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
remember me
</label>
</>
)}
control={control}
name="rememberMe"
defaultValue={rememeberMe}
/>
</div>
return ( <div className="flex justify-end">
<div className="ml-[25%]"> <Button type="submit">Submit</Button>
<LstCard className="p-3 w-96"> </div>
<CardHeader> </div>
<div> </form>
<p className="text-2xl">Login to LST</p> </LstCard>
</div> </div>
</CardHeader> );
<hr className="rounded"></hr>
<form onSubmit={handleSubmit(onSubmitLogin)}>
<div>
<Label htmlFor="username" className="m-1">
Username
</Label>
<Input
placeholder="smith001"
{...register("username")}
className={errors.username ? "border-red-500" : ""}
aria-invalid={!!errors.username}
/>
{errors.username && (
<p className="text-red-500 text-sm mt-1">
{errors.username.message}
</p>
)}
</div>
<div>
<>
<Label htmlFor={"password"} className="m-1">
Password
</Label>
<Input
type="password"
{...register("password")}
className={
errors.password ? "border-red-500" : ""
}
aria-invalid={!!errors.password}
/>
{errors.password && (
<p className="text-red-500 text-sm mt-1">
{errors.password.message}
</p>
)}
</>
</div>
<div className="flex justify-between pt-2">
<div className="flex">
<Controller
render={({ field }) => (
<>
<Checkbox
id="remember"
checked={field.value}
onCheckedChange={field.onChange}
/>
<label
htmlFor="remember"
className="pl-2 text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
remember me
</label>
</>
)}
control={control}
name="rememberMe"
defaultValue={rememeberMe}
/>
</div>
<div className="flex justify-end">
<Button type="submit">Submit</Button>
</div>
</div>
</form>
</LstCard>
</div>
);
}; };
export default LoginForm; export default LoginForm;

View File

@@ -1,61 +1,55 @@
import { useSessionStore } from "../../lib/store/sessionStore";
import { useModuleStore } from "../../lib/store/useModuleStore";
import { moduleActive } from "../../utils/moduleActive";
import { hasAccess } from "../../utils/userAccess";
import { import {
Sidebar, Sidebar,
SidebarContent, SidebarContent,
SidebarFooter, SidebarFooter,
SidebarTrigger, SidebarTrigger,
} from "../ui/sidebar"; } from "../ui/sidebar";
import { ProductionSideBar } from "./side-components/production"; import { AdminSideBar } from "./side-components/admin";
import { EomSideBar } from "./side-components/eom";
import { ForkliftSideBar } from "./side-components/forklift";
import { Header } from "./side-components/header"; import { Header } from "./side-components/header";
import { LogisticsSideBar } from "./side-components/logistics"; import { LogisticsSideBar } from "./side-components/logistics";
import { ProductionSideBar } from "./side-components/production";
import { QualitySideBar } from "./side-components/quality"; import { QualitySideBar } from "./side-components/quality";
import { ForkliftSideBar } from "./side-components/forklift";
import { EomSideBar } from "./side-components/eom";
import { AdminSideBar } from "./side-components/admin";
import { useSessionStore } from "../../lib/store/sessionStore";
import { hasAccess } from "../../utils/userAccess";
import { moduleActive } from "../../utils/moduleActive";
import { useModuleStore } from "../../lib/store/useModuleStore";
export function AppSidebar() { export function AppSidebar() {
const { user } = useSessionStore(); const { user } = useSessionStore();
const { modules } = useModuleStore(); const { modules } = useModuleStore();
return ( return (
<Sidebar collapsible="icon"> <Sidebar collapsible="icon">
<SidebarContent> <SidebarContent>
<Header /> <Header />
{moduleActive("production") && ( {moduleActive("production") && (
<ProductionSideBar <ProductionSideBar
user={user} user={user}
moduleID={ moduleID={
modules.filter((n) => n.name === "production")[0] modules.filter((n) => n.name === "production")[0]
.module_id as string .module_id as string
} }
/> />
)} )}
{moduleActive("logistics") && ( {moduleActive("logistics") && hasAccess(user, "logistics") && (
<LogisticsSideBar <LogisticsSideBar user={user} />
user={user} )}
moduleID={ {moduleActive("forklift") && hasAccess(user, "forklift") && (
modules.filter((n) => n.name === "logistics")[0] <ForkliftSideBar />
.module_id as string )}
} {moduleActive("eom") && hasAccess(user, "eom") && <EomSideBar />}
/> {moduleActive("quality") && hasAccess(user, "quality") && (
)} <QualitySideBar />
{moduleActive("forklift") && )}
hasAccess(user, "forklift", modules) && <ForkliftSideBar />} {moduleActive("admin") && hasAccess(user || [], "admin") && (
{moduleActive("eom") && hasAccess(user, "eom", modules) && ( <AdminSideBar />
<EomSideBar /> )}
)} </SidebarContent>
{moduleActive("quality") && <SidebarFooter>
hasAccess(user, "quality", modules) && <QualitySideBar />} <SidebarTrigger />
{moduleActive("admin") && hasAccess(user, "admin", modules) && ( </SidebarFooter>
<AdminSideBar /> </Sidebar>
)} );
</SidebarContent>
<SidebarFooter>
<SidebarTrigger />
</SidebarFooter>
</Sidebar>
);
} }

View File

@@ -1,64 +1,53 @@
import { Barcode, Cylinder, Package, Truck, Command } from "lucide-react"; import { Barcode, Command, Cylinder, Package, Truck } from "lucide-react";
import {
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from "../../ui/sidebar";
import { hasPageAccess } from "@/utils/userAccess";
import { User } from "@/types/users";
import { useSubModuleStore } from "@/lib/store/useSubModuleStore"; import { useSubModuleStore } from "@/lib/store/useSubModuleStore";
import { User } from "@/types/users";
import { hasPageAccess } from "@/utils/userAccess";
import {
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from "../../ui/sidebar";
const iconMap: any = { const iconMap: any = {
Package: Package, Package: Package,
Truck: Truck, Truck: Truck,
Cylinder: Cylinder, Cylinder: Cylinder,
Barcode: Barcode, Barcode: Barcode,
Command: Command, Command: Command,
}; };
export function LogisticsSideBar({ export function LogisticsSideBar({ user }: { user: User | null }) {
user, const { subModules } = useSubModuleStore();
moduleID,
}: {
user: User | null;
moduleID: string;
}) {
const { subModules } = useSubModuleStore();
const items = subModules.filter((m) => m.moduleName === "logistics"); const items = subModules.filter((m) => m.moduleName === "logistics");
return ( return (
<SidebarGroup> <SidebarGroup>
<SidebarGroupLabel>Logistics</SidebarGroupLabel> <SidebarGroupLabel>Logistics</SidebarGroupLabel>
<SidebarGroupContent> <SidebarGroupContent>
<SidebarMenu> <SidebarMenu>
{items.map((item) => { {items.map((item) => {
const Icon = iconMap[item.icon]; const Icon = iconMap[item.icon];
return ( return (
<SidebarMenuItem key={item.submodule_id}> <SidebarMenuItem key={item.submodule_id}>
<> <>
{hasPageAccess( {hasPageAccess(user, item.roles, item.name) && (
user, <SidebarMenuButton asChild>
item.roles, <a href={item.link}>
moduleID <Icon />
) && <span>{item.name}</span>
item.active && ( </a>
<SidebarMenuButton asChild> </SidebarMenuButton>
<a href={item.link}> )}
<Icon /> </>
<span>{item.name}</span> </SidebarMenuItem>
</a> );
</SidebarMenuButton> })}
)} </SidebarMenu>
</> </SidebarGroupContent>
</SidebarMenuItem> </SidebarGroup>
); );
})}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
);
} }

View File

@@ -1,15 +1,3 @@
import { LstCard } from "@/components/extendedUI/LstCard";
import { Button } from "@/components/ui/button";
import { CardHeader } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { getStockSilo } from "@/utils/querys/logistics/siloAdjustments/getStockSilo";
import { useForm } from "@tanstack/react-form"; import { useForm } from "@tanstack/react-form";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { Link } from "@tanstack/react-router"; import { Link } from "@tanstack/react-router";
@@ -18,246 +6,222 @@ import { format } from "date-fns";
import { CircleAlert } from "lucide-react"; import { CircleAlert } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import ChartData from "./ChartData"; import { LstCard } from "@/components/extendedUI/LstCard";
import { AttachSilo } from "./AttachSilo"; import { Button } from "@/components/ui/button";
import { DetachSilo } from "./DetachSilo"; import { CardHeader } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { useSessionStore } from "@/lib/store/sessionStore"; import { useSessionStore } from "@/lib/store/sessionStore";
import { useModuleStore } from "@/lib/store/useModuleStore";
import { useGetUserRoles } from "@/lib/store/useGetRoles"; import { useGetUserRoles } from "@/lib/store/useGetRoles";
import { useModuleStore } from "@/lib/store/useModuleStore";
import { getStockSilo } from "@/utils/querys/logistics/siloAdjustments/getStockSilo";
import { AttachSilo } from "./AttachSilo";
import ChartData from "./ChartData";
import { DetachSilo } from "./DetachSilo";
export default function SiloCard(data: any) { export default function SiloCard(data: any) {
const token = localStorage.getItem("auth_token"); const [submitting, setSubmitting] = useState(false);
const [submitting, setSubmitting] = useState(false); const { refetch } = useQuery(getStockSilo());
const { refetch } = useQuery(getStockSilo()); const { user } = useSessionStore();
const { user } = useSessionStore(); const { userRoles } = useGetUserRoles();
const { userRoles } = useGetUserRoles(); const { modules } = useModuleStore();
const { modules } = useModuleStore(); const silo = data.silo;
const silo = data.silo;
// roles that can do the silo adjustments // roles that can do the silo adjustments
const roles = ["systemAdmin", "technician", "admin", "manager"]; const roles = ["systemAdmin", "technician", "admin", "manager"];
const module = modules.filter((n) => n.name === "logistics"); const module = modules.filter((n) => n.name === "logistics");
const accessRoles = userRoles.filter( const accessRoles = userRoles.filter(
(n) => n.module_id === module[0]?.module_id (n: any) => n.module === module[0]?.name,
) as any; ) as any;
const form = useForm({ const form = useForm({
defaultValues: { defaultValues: {
newLevel: "", newLevel: "",
}, },
onSubmit: async ({ value }) => { onSubmit: async ({ value }) => {
setSubmitting(true); setSubmitting(true);
const dataToSubmit = { const dataToSubmit = {
quantity: parseFloat(value.newLevel), quantity: parseFloat(value.newLevel),
warehouseId: silo.WarehouseID, warehouseId: silo.WarehouseID,
laneId: silo.LocationID, laneId: silo.LocationID,
}; };
try { try {
const res = await axios.post( const res = await axios.post(
"/api/logistics/createsiloadjustment", "/api/logistics/createsiloadjustment",
dataToSubmit, dataToSubmit,
{ headers: { Authorization: `Bearer ${token}` } } { withCredentials: true },
); );
//console.log(res.data); //console.log(res.data);
if (res.data.success) { if (res.data.success) {
toast.success(res.data.message); toast.success(res.data.message);
refetch(); refetch();
form.reset(); form.reset();
} }
if (!res.data.success && res.data.data?.status === 400) { if (!res.data.success && res.data.data?.status === 400) {
if (res.data.data.status === 400) { if (res.data.data.status === 400) {
toast.error(res.data.data.data.errors[0].message); toast.error(res.data.data.data.errors[0].message);
} }
} else if (!res.data.success) { } else if (!res.data.success) {
toast.error(res.data.message); toast.error(res.data.message);
} }
setSubmitting(false); setSubmitting(false);
} catch (error: any) { } catch (error: any) {
//console.log(error); //console.log(error);
if (error.status === 401) { if (error.status === 401) {
toast.error(error.response.statusText); toast.error(error.response.statusText);
setSubmitting(false); setSubmitting(false);
} }
} }
}, },
}); });
console.log(accessRoles); return (
return ( <LstCard>
<LstCard> <div className="flex flex-row">
<div className="flex flex-row"> <LstCard className="grow m-1 max-w-[400px]">
<LstCard className="grow m-1 max-w-[400px]"> <CardHeader>{silo.Description}</CardHeader>
<CardHeader>{silo.Description}</CardHeader> <div className="m-1">
<div className="m-1"> <hr className="m-2" />
<hr className="m-2" /> <span>Current Stock: </span>
<span>Current Stock: </span> {silo.Stock_Total}
{silo.Stock_Total} <hr className="m-2" />
<hr className="m-2" /> <span>Last date adjusted </span>
<span>Last date adjusted </span> {format(silo.LastAdjustment, "M/dd/yyyy")}
{format(silo.LastAdjustment, "M/dd/yyyy")} <hr className="m-2" />
<hr className="m-2" /> </div>
</div> <div>
<div> {silo.Stock_Total === 0 ? (
{silo.Stock_Total === 0 ? ( <div className="flex justify-center flex-col">
<div className="flex justify-center flex-col"> <span>
<span> The silo is currently empty you will not be able to do an
The silo is currently empty you will not be adjustment until you have received material in.
able to do an adjustment until you have </span>
received material in. <hr />
</span> <ul>
<hr /> <li>
<ul> -Someone click "Take inventory on a empty location" in
<li> stock.
-Someone click "Take inventory on a </li>
empty location" in stock. <li>
</li> -Silo virtualy ran empty due to production over consumption.
<li> </li>
-Silo virtualy ran empty due to <li>
production over consumption. -Someone forgot to move a railcar compartment over to this
</li> location.
<li> </li>
-Someone forgot to move a railcar </ul>
compartment over to this location. </div>
</li> ) : (
</ul> <>
</div> {user && roles.includes(accessRoles[0]?.role) && (
) : ( <form
<> onSubmit={(e) => {
{user && e.preventDefault();
roles.includes(accessRoles[0]?.role) && ( e.stopPropagation();
<form }}
onSubmit={(e) => { >
e.preventDefault(); <form.Field
e.stopPropagation(); name="newLevel"
}} validators={{
> // We can choose between form-wide and field-specific validators
<form.Field onChange: ({ value }) =>
name="newLevel" value.length > 1
validators={{ ? undefined
// We can choose between form-wide and field-specific validators : "You must enter a value greate than 1",
onChange: ({ value }) => }}
value.length > 1 children={(field) => {
? undefined return (
: "You must enter a value greate than 1", <div className="m-2 min-w-48 max-w-96 p-2">
}} <div className="flex flex-row">
children={(field) => { <Label htmlFor="newLevel">New level</Label>
return ( <div>
<div className="m-2 min-w-48 max-w-96 p-2"> <Disclaimer />
<div className="flex flex-row"> </div>
<Label htmlFor="newLevel"> </div>
New level <div className="flex flex-row">
</Label> <Input
<div> name={field.name}
<Disclaimer /> value={field.state.value}
</div> onBlur={field.handleBlur}
</div> type="decimal"
<div className="flex flex-row"> onChange={(e) =>
<Input field.handleChange(e.target.value)
name={ }
field.name />
} <Button
value={ className="ml-1"
field variant="outline"
.state type="submit"
.value onClick={form.handleSubmit}
} disabled={submitting}
onBlur={ >
field.handleBlur {submitting ? (
} <span className="w-24">Submitting...</span>
type="decimal" ) : (
onChange={( <span className="w-24">Submit</span>
e )}
) => </Button>
field.handleChange( </div>
e
.target
.value
)
}
/>
<Button
className="ml-1"
variant="outline"
type="submit"
onClick={
form.handleSubmit
}
disabled={
submitting
}
>
{submitting ? (
<span className="w-24">
Submitting...
</span>
) : (
<span className="w-24">
Submit
</span>
)}
</Button>
</div>
{field.state.meta {field.state.meta.errors.length ? (
.errors <em>{field.state.meta.errors.join(",")}</em>
.length ? ( ) : null}
<em> </div>
{field.state.meta.errors.join( );
"," }}
)} />
</em> </form>
) : null} )}
</div> </>
); )}
}} </div>
/> </LstCard>
</form> <div className="grow max-w-[600px]">
)} <ChartData laneId={silo.LocationID} />
</>
)}
</div>
</LstCard>
<div className="grow max-w-[600px]">
<ChartData laneId={silo.LocationID} />
<div className="flex justify-end m-1 gap-3"> <div className="flex justify-end m-1 gap-3">
<AttachSilo silo={silo} /> <AttachSilo silo={silo} />
<DetachSilo silo={silo} /> <DetachSilo silo={silo} />
<Button variant="outline"> <Button variant="outline">
<Link <Link
to={"/siloAdjustments/$hist"} to={"/siloAdjustments/$hist"}
params={{ hist: silo.LocationID }} params={{ hist: silo.LocationID }}
> >
Historical Data Historical Data
</Link> </Link>
</Button> </Button>
</div> </div>
</div> </div>
</div> </div>
</LstCard> </LstCard>
); );
} }
const Disclaimer = () => { const Disclaimer = () => {
return ( return (
<TooltipProvider> <TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<CircleAlert className="ml-1 w-[14px]" /> <CircleAlert className="ml-1 w-[14px]" />
</TooltipTrigger> </TooltipTrigger>
<TooltipContent className="max-w-48"> <TooltipContent className="max-w-48">
<p className="text-pretty"> <p className="text-pretty">
If you have had this page open for a period of time If you have had this page open for a period of time before
before submitting your data, there is a chance that the submitting your data, there is a chance that the stock levels will
stock levels will be different from the ones you see be different from the ones you see above
above </p>
</p> </TooltipContent>
</TooltipContent> </Tooltip>
</Tooltip> </TooltipProvider>
</TooltipProvider> );
);
}; };

View File

@@ -1,273 +1,256 @@
import { useQuery } from "@tanstack/react-query";
import { LstCard } from "@/components/extendedUI/LstCard"; import { LstCard } from "@/components/extendedUI/LstCard";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Skeleton } from "@/components/ui/skeleton"; import { Skeleton } from "@/components/ui/skeleton";
import { import {
Table, Table,
TableBody, TableBody,
TableCell, TableCell,
TableHead, TableHead,
TableHeader, TableHeader,
TableRow, TableRow,
} from "@/components/ui/table"; } from "@/components/ui/table";
import { useSessionStore } from "@/lib/store/sessionStore"; import { useSessionStore } from "@/lib/store/sessionStore";
import { useGetUserRoles } from "@/lib/store/useGetRoles";
import { useModuleStore } from "@/lib/store/useModuleStore";
import { useSettingStore } from "@/lib/store/useSettings"; import { useSettingStore } from "@/lib/store/useSettings";
import { LotType } from "@/types/lots"; import { LotType } from "@/types/lots";
import { getlots } from "@/utils/querys/production/lots"; import { getlots } from "@/utils/querys/production/lots";
import { useQuery } from "@tanstack/react-query";
import ManualPrint from "./ManualPrinting/ManualPrint"; import ManualPrint from "./ManualPrinting/ManualPrint";
import ManualPrintForm from "./ManualPrinting/ManualPrintForm"; import ManualPrintForm from "./ManualPrinting/ManualPrintForm";
import { ScrollArea } from "@/components/ui/scroll-area";
import { useGetUserRoles } from "@/lib/store/useGetRoles";
import { useModuleStore } from "@/lib/store/useModuleStore";
let lotColumns = [ let lotColumns = [
{ {
key: "MachineDescription", key: "MachineDescription",
label: "Machine", label: "Machine",
}, },
{ {
key: "AV", key: "AV",
label: "AV", label: "AV",
}, },
{ {
key: "Alias", key: "Alias",
label: "AvDescription", label: "AvDescription",
}, },
{ {
key: "lot", key: "lot",
label: "LotNumber", label: "LotNumber",
}, },
{ {
key: "ProlinkLot", key: "ProlinkLot",
label: "ProlinkLot", label: "ProlinkLot",
}, },
{ {
key: "PlannedQTY", key: "PlannedQTY",
label: "PlannedQTY", label: "PlannedQTY",
}, },
{ {
key: "Produced", key: "Produced",
label: "Produced", label: "Produced",
}, },
{ {
key: "Remaining", key: "Remaining",
label: "Remaining", label: "Remaining",
}, },
{ {
key: "overPrinting", key: "overPrinting",
label: "Overprinting", label: "Overprinting",
}, },
// { // {
// key: "lastProlinkUpdate", // key: "lastProlinkUpdate",
// label: "Last ProlinkCheck", // label: "Last ProlinkCheck",
// }, // },
// { // {
// key: "printLabel", // key: "printLabel",
// label: "Print Label", // label: "Print Label",
// }, // },
]; ];
export default function Lots() { export default function Lots() {
const { data, isError, isLoading } = useQuery(getlots()); const { data, isError, isLoading } = useQuery(getlots());
const { user } = useSessionStore(); const { user } = useSessionStore();
const { settings } = useSettingStore(); const { settings } = useSettingStore();
const { userRoles } = useGetUserRoles(); const { userRoles } = useGetUserRoles();
const { modules } = useModuleStore(); const { modules } = useModuleStore();
const server = settings.filter((n) => n.name === "server")[0]?.value || ""; const server = settings.filter((n) => n.name === "server")[0]?.value || "";
const roles = ["systemAdmin", "technician", "admin", "manager", "operator"]; const roles = ["systemAdmin", "technician", "admin", "manager", "operator"];
const lotdata = data ? data : []; const lotdata = data ? data : [];
const module = modules.filter((n) => n.name === "logistics"); const module = modules.filter((n) => n.name === "logistics");
const accessRoles = userRoles.filter( const accessRoles = userRoles.filter(
(n) => n.module_id === module[0]?.module_id (n: any) => n.module === module[0]?.name,
) as any; ) as any;
if (user && roles.includes(accessRoles[0]?.role)) { if (user && roles.includes(accessRoles[0]?.role)) {
//width = 1280; //width = 1280;
const checkCol = lotColumns.some((l) => l.key === "printLabel"); const checkCol = lotColumns.some((l) => l.key === "printLabel");
if (!checkCol) { if (!checkCol) {
lotColumns = [ lotColumns = [
...lotColumns, ...lotColumns,
{ {
key: "printLabel", key: "printLabel",
label: "Print Label", label: "Print Label",
}, },
]; ];
} }
} }
if (isError) { if (isError) {
return ( return (
<div className="m-2 p-2 min-h-2/5"> <div className="m-2 p-2 min-h-2/5">
<ScrollArea className="max-h-1/2 rounded-md border p-4"> <ScrollArea className="max-h-1/2 rounded-md border p-4">
<LstCard> <LstCard>
<p className="text-center">Current Assigned lots</p> <p className="text-center">Current Assigned lots</p>
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow> <TableRow>
{lotColumns.map((l) => ( {lotColumns.map((l) => (
<TableHead key={l.key}> <TableHead key={l.key}>{l.label}</TableHead>
{l.label} ))}
</TableHead> </TableRow>
))} </TableHeader>
</TableRow>
</TableHeader>
<TableBody> <TableBody>
{Array(10) {Array(10)
.fill(0) .fill(0)
.map((_, i) => ( .map((_, i) => (
<TableRow key={i}> <TableRow key={i}>
<TableCell className="font-medium"> <TableCell className="font-medium">
<Skeleton className="h-4" /> <Skeleton className="h-4" />
</TableCell> </TableCell>
<TableCell> <TableCell>
<Skeleton className="h-4" /> <Skeleton className="h-4" />
</TableCell> </TableCell>
<TableCell> <TableCell>
<Skeleton className="h-4" /> <Skeleton className="h-4" />
</TableCell> </TableCell>
<TableCell> <TableCell>
<Skeleton className="h-4" /> <Skeleton className="h-4" />
</TableCell> </TableCell>
<TableCell> <TableCell>
<Skeleton className="h-4" /> <Skeleton className="h-4" />
</TableCell> </TableCell>
<TableCell> <TableCell>
<Skeleton className="h-4" /> <Skeleton className="h-4" />
</TableCell> </TableCell>
<TableCell> <TableCell>
<Skeleton className="h-4" /> <Skeleton className="h-4" />
</TableCell> </TableCell>
<TableCell> <TableCell>
<Skeleton className="h-4" /> <Skeleton className="h-4" />
</TableCell> </TableCell>
<TableCell> <TableCell>
<Skeleton className="h-4" /> <Skeleton className="h-4" />
</TableCell> </TableCell>
</TableRow> </TableRow>
))} ))}
</TableBody> </TableBody>
</Table> </Table>
</LstCard> </LstCard>
</ScrollArea> </ScrollArea>
</div> </div>
); );
} }
return ( return (
<LstCard className="m-2 p-2 min-h-2/5"> <LstCard className="m-2 p-2 min-h-2/5">
<ScrollArea className="h-[400px]"> <ScrollArea className="h-[400px]">
<p className="text-center">Current Assigned lots</p> <p className="text-center">Current Assigned lots</p>
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow> <TableRow>
{lotColumns.map((l) => ( {lotColumns.map((l) => (
<TableHead key={l.key}>{l.label}</TableHead> <TableHead key={l.key}>{l.label}</TableHead>
))} ))}
</TableRow> </TableRow>
</TableHeader> </TableHeader>
{isLoading ? ( {isLoading ? (
<> <>
<TableBody> <TableBody>
{Array(10) {Array(10)
.fill(0) .fill(0)
.map((_, i) => ( .map((_, i) => (
<TableRow key={i}> <TableRow key={i}>
<TableCell className="font-medium"> <TableCell className="font-medium">
<Skeleton className="h-4" /> <Skeleton className="h-4" />
</TableCell> </TableCell>
<TableCell> <TableCell>
<Skeleton className="h-4" /> <Skeleton className="h-4" />
</TableCell> </TableCell>
<TableCell> <TableCell>
<Skeleton className="h-4" /> <Skeleton className="h-4" />
</TableCell> </TableCell>
<TableCell> <TableCell>
<Skeleton className="h-4" /> <Skeleton className="h-4" />
</TableCell> </TableCell>
<TableCell> <TableCell>
<Skeleton className="h-4" /> <Skeleton className="h-4" />
</TableCell> </TableCell>
<TableCell> <TableCell>
<Skeleton className="h-4" /> <Skeleton className="h-4" />
</TableCell> </TableCell>
<TableCell> <TableCell>
<Skeleton className="h-4" /> <Skeleton className="h-4" />
</TableCell> </TableCell>
<TableCell> <TableCell>
<Skeleton className="h-4" /> <Skeleton className="h-4" />
</TableCell> </TableCell>
<TableCell> <TableCell>
<Skeleton className="h-4" /> <Skeleton className="h-4" />
</TableCell> </TableCell>
</TableRow> </TableRow>
))} ))}
</TableBody> </TableBody>
</> </>
) : ( ) : (
<TableBody> <TableBody>
{lotdata.map((lot: LotType) => ( {lotdata.map((lot: LotType) => (
<TableRow key={lot.LabelOnlineID}> <TableRow key={lot.LabelOnlineID}>
<TableCell className="font-medium"> <TableCell className="font-medium">
{lot.MachineLocation} {lot.MachineLocation}
</TableCell> </TableCell>
<TableCell className="font-medium"> <TableCell className="font-medium">{lot.AV}</TableCell>
{lot.AV} <TableCell className="font-medium">{lot.Alias}</TableCell>
</TableCell> <TableCell
<TableCell className="font-medium"> className={`font-medium ${lot.ProlinkLot != lot.lot ? "text-red-500" : ""}`}
{lot.Alias} >
</TableCell> {lot.lot}
<TableCell </TableCell>
className={`font-medium ${lot.ProlinkLot != lot.lot ? "text-red-500" : ""}`} <TableCell
> className={`font-medium ${lot.ProlinkLot != lot.lot ? "text-red-500" : ""}`}
{lot.lot} >
</TableCell> {lot.ProlinkLot}
<TableCell </TableCell>
className={`font-medium ${lot.ProlinkLot != lot.lot ? "text-red-500" : ""}`} <TableCell className="font-medium">
> {lot.PlannedQTY}
{lot.ProlinkLot} </TableCell>
</TableCell> <TableCell className="font-medium">{lot.Produced}</TableCell>
<TableCell className="font-medium"> <TableCell className="font-medium">{lot.Remaining}</TableCell>
{lot.PlannedQTY} <TableCell className="font-medium">
</TableCell> {lot.overPrinting}
<TableCell className="font-medium"> </TableCell>
{lot.Produced} {user && roles.includes(accessRoles[0]?.role) && (
</TableCell> <>
<TableCell className="font-medium"> {server === "usday1vms006" || server === "localhost" ? (
{lot.Remaining} <>
</TableCell> <TableCell className="flex justify-center">
<TableCell className="font-medium"> <ManualPrintForm />
{lot.overPrinting} </TableCell>
</TableCell> </>
{user && ) : (
roles.includes( <TableCell className="flex justify-center">
accessRoles[0]?.role <ManualPrint lot={lot} />
) && ( </TableCell>
<> )}
{server === "usday1vms006" || </>
server === "localhost" ? ( )}
<> </TableRow>
<TableCell className="flex justify-center"> ))}
<ManualPrintForm /> </TableBody>
</TableCell> )}
</> </Table>
) : ( </ScrollArea>
<TableCell className="flex justify-center"> </LstCard>
<ManualPrint );
lot={lot}
/>
</TableCell>
)}
</>
)}
</TableRow>
))}
</TableBody>
)}
</Table>
</ScrollArea>
</LstCard>
);
} }

View File

@@ -1,32 +1,67 @@
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useModuleStore } from "../../lib/store/useModuleStore";
import { useEffect } from "react"; import { useEffect } from "react";
import { useSettingStore } from "@/lib/store/useSettings"; import { useSessionStore } from "@/lib/store/sessionStore";
import { useAuthStore } from "@/lib/store/useAuthStore";
import { useGetUserRoles } from "@/lib/store/useGetRoles"; import { useGetUserRoles } from "@/lib/store/useGetRoles";
import { useSettingStore } from "@/lib/store/useSettings";
import { useSubModuleStore } from "@/lib/store/useSubModuleStore"; import { useSubModuleStore } from "@/lib/store/useSubModuleStore";
import { useModuleStore } from "../../lib/store/useModuleStore";
const queryClient = new QueryClient(); const queryClient = new QueryClient();
export const SessionProvider = ({ const reAuth = async (username: string, password: string) => {
children, const { setSession } = useSessionStore();
}: { try {
children: React.ReactNode; const response = await fetch("/api/auth/login", {
}) => { method: "POST",
const { fetchModules } = useModuleStore(); headers: {
const { fetchSettings } = useSettingStore(); "Content-Type": "application/json",
const { fetchUserRoles } = useGetUserRoles(); },
const { fetchSubModules } = useSubModuleStore(); body: JSON.stringify({
username: username,
password: password,
}),
});
useEffect(() => { const data = await response.json();
fetchModules();
fetchSettings();
fetchUserRoles();
fetchSubModules();
}, []);
return ( // Store token in localStorage
<QueryClientProvider client={queryClient}> // localStorage.setItem("auth_token", data.data.token);
{children} if (data.success) {
</QueryClientProvider> const prod = btoa(`${username.toLowerCase()}:${password}`);
); const prodUser = { ...data.user, prod: prod };
setSession(prodUser, data.data.token);
}
//console.log(data);
} catch (err) {
console.log(err);
}
};
export const SessionProvider = ({
children,
}: {
children: React.ReactNode;
}) => {
const { fetchModules } = useModuleStore();
const { fetchSettings } = useSettingStore();
const { fetchUserRoles } = useGetUserRoles();
const { fetchSubModules } = useSubModuleStore();
const { username, password } = useAuthStore();
useEffect(() => {
if (username !== "") {
reAuth(username, password);
}
fetchModules();
fetchSettings();
fetchUserRoles();
fetchSubModules();
}, []);
return (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
);
}; };

View File

@@ -1,16 +1,19 @@
import {useSessionStore} from "@/lib/store/sessionStore"; import { useRouter } from "@tanstack/react-router";
import {useRouter} from "@tanstack/react-router"; import { useSessionStore } from "@/lib/store/sessionStore";
import { useAuthStore } from "@/lib/store/useAuthStore";
export const useLogout = () => { export const useLogout = () => {
const {clearSession} = useSessionStore(); const { clearSession } = useSessionStore();
const router = useRouter(); const { clearUser } = useAuthStore();
const logout = async () => { const router = useRouter();
router.invalidate(); const logout = async () => {
router.clearCache(); router.invalidate();
clearSession(); router.clearCache();
clearSession();
clearUser();
window.location.reload(); window.location.reload();
}; };
return logout; return logout;
}; };

View File

@@ -1,60 +1,87 @@
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { useSessionStore } from "../lib/store/sessionStore"; import axios from "axios";
import { useEffect } from "react"; import { useEffect } from "react";
import { useSessionStore } from "../lib/store/sessionStore";
const fetchSession = async () => { const fetchSession = async () => {
const token = localStorage.getItem("auth_token"); const token = localStorage.getItem("auth_token");
if (!token) { if (!token) {
throw new Error("No token found"); throw new Error("No token found");
} }
try {
const res = await axios.get("/api/auth/session", {
headers: {
"Content-Type": "application/json",
},
withCredentials: true,
});
const res = await fetch("/api/auth/session", { if (res.status === 401) {
headers: { localStorage.removeItem("auth_token");
"Content-Type": "application/json", // remove these for a while if no session just until fully to 2.0 and clearly no one has ran lstv1 in a long time
Authorization: `Bearer ${token}`, localStorage.removeItem("ally-supports-cache");
}, localStorage.removeItem("auth-storage");
}); localStorage.removeItem("nextauth.message");
// console.log(res); localStorage.removeItem("prod");
if (!res.ok) {
localStorage.removeItem("auth_token");
// remove these for a while if no session just until fully to 2.0 and clearly no one has ran lstv1 in a long time
localStorage.removeItem("ally-supports-cache");
localStorage.removeItem("auth-storage");
localStorage.removeItem("nextauth.message");
localStorage.removeItem("prod");
throw new Error("Session not found"); throw new Error("Session not found");
} }
return res.json(); const userRoles = await axios.get("/api/auth/getuseraccess", {
withCredentials: true,
});
const userData = {
...res.data,
data: {
...res.data.data,
user: {
...res.data.data.user,
roles: userRoles.data.data,
},
token: "Just a token as this will be removed in the future",
},
};
return userData;
} catch (error) {
localStorage.removeItem("auth_token");
// remove these for a while if no session just until fully to 2.0 and clearly no one has ran lstv1 in a long time
localStorage.removeItem("ally-supports-cache");
localStorage.removeItem("auth-storage");
localStorage.removeItem("nextauth.message");
localStorage.removeItem("prod");
throw new Error("Session not found");
}
}; };
export const useSession = () => { export const useSession = () => {
const { setSession, clearSession, token } = useSessionStore(); const { setSession, clearSession, token } = useSessionStore();
// Fetch session only if token is available // Fetch session only if token is available
const { data, status, error } = useQuery({ const { data, status, error } = useQuery({
queryKey: ["session"], queryKey: ["session"],
queryFn: fetchSession, queryFn: fetchSession,
enabled: !!token, // Prevents query if token is null enabled: !!token, // Prevents query if token is null
staleTime: 60 * 1000, staleTime: 60 * 1000,
gcTime: 10 * 60 * 1000, // 10 mins //gcTime: 10 * 60 * 1000, // 10 mins
refetchOnWindowFocus: true, refetchOnWindowFocus: true,
//refetchInterval: 1000 * 60 * 2, // Auto-refetch every 2 minutes refetchInterval: 1000 * 60 * 2, // Auto-refetch every 2 minutes
}); });
useEffect(() => { useEffect(() => {
if (data) { if (data) {
setSession(data.data.user, data.data.token); setSession(data.data.user, data.data.token);
} }
if (error) { if (error) {
clearSession(); clearSession();
} }
}, [data, error]); }, [data, error]);
return { return {
session: data && token ? { user: data.user, token: data.token } : null, session: data && token ? { user: data.user, token: data.token } : null,
status, status,
error, error,
}; };
}; };

View File

@@ -1,43 +1,52 @@
import {User} from "@/types/users";
import axios from "axios"; import axios from "axios";
import {create} from "zustand"; import { create } from "zustand";
import { User } from "@/types/users";
export type SessionState = { export type SessionState = {
user: User | null; user: User | null;
token: string | null; token: string | null;
setSession: (user: User | null, token: string | null) => void; setSession: (user: User | null, token: string | null) => void;
clearSession: () => void; clearSession: () => void;
}; };
export const useSessionStore = create<SessionState>((set) => { export const useSessionStore = create<SessionState>((set) => {
// Initialize token from localStorage, but user remains in memory only // Initialize token from localStorage, but user remains in memory only
const storedToken = localStorage.getItem("auth_token"); const storedToken = localStorage.getItem("auth_token");
return { return {
user: null, // User is NOT stored in localStorage user: null, // User is NOT stored in localStorage
token: storedToken || null, token: storedToken || null,
setSession: async (user: any, token) => { setSession: async (user: any, token) => {
if (token) { if (token) {
localStorage.setItem("auth_token", token); localStorage.setItem("auth_token", token);
const response = await axios.get("/api/auth/getuseraccess", { const response = await axios.get("/api/auth/session", {
headers: {Authorization: `Bearer ${token}`}, withCredentials: true,
}); });
const data = response.data; //await response.json(); const userRoles = await axios.get("/api/auth/getuseraccess", {
user = {...user, roles: data.data}; withCredentials: true,
} else { });
localStorage.removeItem("auth_token"); const data = response.data; //await response.json();
}
//console.log("Setting session:", {user, token}); const rawUser = data.data.user;
set({user, token}); // user.map((u: any) => ({
}, // ...u,
// roles: userRoles.data.data,
// }));
user = { ...rawUser, roles: userRoles.data.data };
} else {
localStorage.removeItem("auth_token");
}
clearSession: () => { //console.log("Setting session:", {user, token});
localStorage.removeItem("auth_token"); set({ user, token });
set({user: null, token: null}); },
},
}; clearSession: () => {
localStorage.removeItem("auth_token");
set({ user: null, token: null });
},
};
}); });
export type SessionType = ReturnType<typeof useSessionStore>; export type SessionType = ReturnType<typeof useSessionStore>;

View File

@@ -0,0 +1,15 @@
import { create } from "zustand";
interface SettingState {
username: string;
password: string;
clearUser: () => void;
setUserInfo: (username: string, password: string) => void;
}
export const useAuthStore = create<SettingState>()((set) => ({
username: "",
password: "",
setUserInfo: (username, password) => set({ username, password }),
clearUser: () => set({ username: "", password: "" }),
}));

View File

@@ -1,38 +1,41 @@
import axios from "axios";
import { create } from "zustand"; import { create } from "zustand";
import { Modules } from "@/types/modules"; import { Modules } from "@/types/modules";
import axios from "axios";
interface SettingState { interface SettingState {
userRoles: Modules[]; userRoles: Modules[];
fetchUserRoles: () => Promise<void>; fetchUserRoles: () => Promise<void>;
setUserRoles: (userRoles: Modules[]) => void; setUserRoles: (userRoles: Modules[]) => void;
} }
interface FetchModulesResponse { interface FetchModulesResponse {
data: Modules[]; data: Modules[];
} }
export const useGetUserRoles = create<SettingState>()((set) => ({ export const useGetUserRoles = create<SettingState>()((set) => ({
userRoles: [], userRoles: [],
setUserRoles: (userRoles) => set({ userRoles }), setUserRoles: (userRoles) => set({ userRoles }),
fetchUserRoles: async () => { fetchUserRoles: async () => {
try { try {
//const response = await axios.get<{data: Setting[]}>(`${process.env.NEXT_PUBLIC_URL}/api/settings/client`); //const response = await axios.get<{data: Setting[]}>(`${process.env.NEXT_PUBLIC_URL}/api/settings/client`);
const token = localStorage.getItem("auth_token"); const token = localStorage.getItem("auth_token");
if (token) { if (token) {
const response = await axios.get("/api/auth/getuseraccess", { const response = await axios.get("/api/auth/getuseraccess", {
headers: { Authorization: `Bearer ${token}` }, withCredentials: true,
}); });
const data: FetchModulesResponse = response.data; //await response.json(); const data: FetchModulesResponse = response?.data; //await response.json();
//console.log(data); if (response.status === 401) {
set({ userRoles: data.data }); set({ userRoles: [] });
} else { }
//console.log(data); set({ userRoles: data?.data });
set({ userRoles: [] }); } else {
} //console.log(data);
} catch (error) { set({ userRoles: [] });
console.error("Failed to fetch settings:", error); }
} } catch (error) {
}, set({ userRoles: [] });
console.error("Failed to fetch settings:", error);
}
},
})); }));

View File

@@ -1,44 +1,52 @@
import { Modules } from "@/types/modules";
import { User } from "@/types/users"; import { User } from "@/types/users";
// user will need access to the module. // user will need access to the module.
// users role will determine there visual access // users role will determine there visual access
export function hasAccess( export function hasAccess(user: any, moduleName: string | null): boolean {
user: User | null, //console.log("has access user", user, moduleName);
moduleName: string | null, // get the modules for the id
modules: Modules[]
): boolean {
// get the modules for the id
const filteredModule = modules?.filter((f) => f.name === moduleName);
//console.log(filteredModule[0]);
// userroles and filter out by the module id,
const roleCheck: any = user?.roles.find( const filteredModule = user?.roles?.filter(
(role) => role.module_id === filteredModule[0].module_id (f: any) => f.module === moduleName,
); );
//console.log(filteredModule[0]);
// userroles and filter out by the module id,
//console.log("Has Module access", filteredModule);
// const roleCheck: any = user?.roles.find(
// (role) => role.module_id === filteredModule[0].module_id,
// );
if (filteredModule[0].roles.includes(roleCheck?.role)) { if (filteredModule && filteredModule.length > 0) {
return true; return true;
} }
//if(filteredModule[0].roles.includes(roleCheck.)) //if(filteredModule[0].roles.includes(roleCheck.))
return false; return false;
} }
export function hasPageAccess( export function hasPageAccess(
user: User | null, user: User | null,
role: any, role: any,
module_id: string moduleName: string,
): boolean { ): boolean {
if (role.includes("viewer")) return true; if (role.includes("viewer")) return true;
if (!user) return false; if (!user) return false;
// get only the module in the user profile const userRole = user?.roles.filter(
//console.log(user); (role: any) => role.module === moduleName,
const userRole = user?.roles.filter((role) => role.module_id === module_id); );
//console.log(userRole[0]?.role);
// if (role.includes(userRole[0]?.role)) {
// return true}; //console.log(user);
if (userRole.length !== 0) return true;
return false; // if (role.includes(userRole[0]?.role)) {
// return true};
//if (userRole.length > 0) return true;
if (userRole.length >= 1) {
//console.log(userRole);
return true;
} else {
return false;
}
//return false;
} }

View File

@@ -1,35 +1,34 @@
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
import { serve } from "@hono/node-server"; import { serve } from "@hono/node-server";
import { OpenAPIHono } from "@hono/zod-openapi";
import { serveStatic } from "@hono/node-server/serve-static"; import { serveStatic } from "@hono/node-server/serve-static";
import { logger } from "hono/logger"; import { OpenAPIHono } from "@hono/zod-openapi";
import { cors } from "hono/cors"; import { cors } from "hono/cors";
import { createLog } from "./services/logger/logger.js"; import { logger } from "hono/logger";
import os from "os";
import auth from "./services/auth/authService.js";
import dataMart from "./services/dataMart/dataMartService.js";
import eom from "./services/eom/eomService.js";
// custom routes // custom routes
import scalar from "./services/general/route/scalar.js"; import scalar from "./services/general/route/scalar.js";
import system from "./services/server/systemServer.js"; import { createLog } from "./services/logger/logger.js";
import auth from "./services/auth/authService.js";
import tcpServer from "./services/tcpServer/tcpServer.js";
import ocme from "./services/ocme/ocmeService.js";
import sqlService from "./services/sqlServer/sqlService.js";
import logistics from "./services/logistics/logisticsService.js";
import rfid from "./services/rfid/rfidService.js";
import printers from "./services/printers/printerService.js";
import loggerService from "./services/logger/loggerService.js"; import loggerService from "./services/logger/loggerService.js";
import ocpService from "./services/ocp/ocpService.js"; import logistics from "./services/logistics/logisticsService.js";
import os from "os";
import { sendEmail } from "./services/notifications/controller/sendMail.js"; import { sendEmail } from "./services/notifications/controller/sendMail.js";
import notify from "./services/notifications/notifyService.js"; import notify from "./services/notifications/notifyService.js";
import eom from "./services/eom/eomService.js"; import ocme from "./services/ocme/ocmeService.js";
import dataMart from "./services/dataMart/dataMartService.js"; import ocpService from "./services/ocp/ocpService.js";
import qualityRequest from "./services/quality/qualityService.js"; import printers from "./services/printers/printerService.js";
import produser from "./services/prodUser/prodUser.js"; import produser from "./services/prodUser/prodUser.js";
import qualityRequest from "./services/quality/qualityService.js";
import rfid from "./services/rfid/rfidService.js";
import { import {
getSettings, getSettings,
serverSettings, serverSettings,
} from "./services/server/controller/settings/getSettings.js"; } from "./services/server/controller/settings/getSettings.js";
import system from "./services/server/systemServer.js";
import sqlService from "./services/sqlServer/sqlService.js";
import tcpServer from "./services/tcpServer/tcpServer.js";
// create the main prodlogin here // create the main prodlogin here
const username = "lst_user"; const username = "lst_user";
@@ -48,28 +47,33 @@ export const lstAuth = btoa(`${username}:${password}`);
const serverIntialized: any = await getSettings(); const serverIntialized: any = await getSettings();
export const installed = export const installed =
serverIntialized.length === 0 && process.env.NODE_ENV !== "development" serverIntialized.length === 0 && process.env.NODE_ENV !== "development"
? false ? false
: true; : true;
createLog("info", "LST", "server", `Server is installed: ${installed}`); createLog("info", "LST", "server", `Server is installed: ${installed}`);
const app = new OpenAPIHono({ strict: false }); const app = new OpenAPIHono({ strict: false });
// middle ware // middle ware
if (process.env.NODE_ENV === "development") { if (process.env.NODE_ENV === "development") {
app.use("*", logger()); app.use("*", logger());
} }
app.use( app.use(
"*", "*",
cors({ cors({
origin: "*", // Allow all origins origin: [
allowHeaders: ["Content-Type", "Authorization", "X-Requested-With"], "http://localhost:3000",
allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"], "http://localhost:5173",
//exposeHeaders: ["Content-Length", "X-Kuma-Revision"], "http://localhost:4000",
credentials: true, // Allow credentials if needed "http://localhost:4200",
maxAge: 600, ], // Allow all origins
}) allowHeaders: ["Content-Type", "Authorization", "X-Requested-With"],
allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
//exposeHeaders: ["Content-Length", "X-Kuma-Revision"],
credentials: true, // Allow credentials if needed
maxAge: 600,
}),
); );
// Middleware to normalize route case // Middleware to normalize route case
@@ -86,34 +90,34 @@ app.use(
// }); // });
app.doc("/api/ref", { app.doc("/api/ref", {
openapi: "3.0.0", openapi: "3.0.0",
info: { info: {
version: "2.0.0", version: "2.0.0",
title: "LST API", title: "LST API",
}, },
}); });
const routes = [ const routes = [
scalar, scalar,
auth, auth,
// apiHits, // apiHits,
system, system,
tcpServer, tcpServer,
sqlService, sqlService,
logistics, logistics,
rfid, rfid,
printers, printers,
loggerService, loggerService,
ocpService, ocpService,
notify, notify,
eom, eom,
dataMart, dataMart,
qualityRequest, qualityRequest,
produser, produser,
] as const; ] as const;
const appRoutes = routes.forEach((route) => { const appRoutes = routes.forEach((route) => {
app.route("/api/", route); app.route("/api/", route);
}); });
app.route("/ocme/", ocme); app.route("/ocme/", ocme);
@@ -154,60 +158,60 @@ app.use("*", serveStatic({ path: "./frontend/dist/index.html" }));
// Handle app exit signals // Handle app exit signals
process.on("SIGINT", async () => { process.on("SIGINT", async () => {
console.log("\nGracefully shutting down..."); console.log("\nGracefully shutting down...");
//await closePool(); //await closePool();
process.exit(0); process.exit(0);
}); });
process.on("SIGTERM", async () => { process.on("SIGTERM", async () => {
console.log("Received termination signal, closing database..."); console.log("Received termination signal, closing database...");
//await closePool(); //await closePool();
process.exit(0); process.exit(0);
}); });
process.on("uncaughtException", async (err) => { process.on("uncaughtException", async (err) => {
console.log("Uncaught Exception:", err); console.log("Uncaught Exception:", err);
//await closePool(); //await closePool();
const emailData = { const emailData = {
email: "blake.matthes@alpla.com", // should be moved to the db so it can be reused. email: "blake.matthes@alpla.com", // should be moved to the db so it can be reused.
subject: `${os.hostname()} has just encountered a crash.`, subject: `${os.hostname()} has just encountered a crash.`,
template: "serverCrash", template: "serverCrash",
context: { context: {
error: err, error: err,
plant: `${os.hostname()}`, plant: `${os.hostname()}`,
}, },
}; };
await sendEmail(emailData); await sendEmail(emailData);
//process.exit(1); //process.exit(1);
}); });
process.on("beforeExit", async () => { process.on("beforeExit", async () => {
console.log("Process is about to exit..."); console.log("Process is about to exit...");
//await closePool(); //await closePool();
process.exit(0); process.exit(0);
}); });
const port = const port =
process.env.NODE_ENV === "development" process.env.NODE_ENV === "development"
? process.env.VITE_SERVER_PORT ? process.env.VITE_SERVER_PORT
: process.env.PROD_PORT; : process.env.PROD_PORT;
const ocmeport = process.env.OCME_PORT; const ocmeport = process.env.OCME_PORT;
serve( serve(
{ {
fetch: app.fetch, fetch: app.fetch,
port: Number(port), port: Number(port),
hostname: "0.0.0.0", hostname: "0.0.0.0",
}, },
(info) => { (info) => {
createLog( createLog(
"info", "info",
"LST", "LST",
"server", "server",
`Server is running on http://${info.address}:${info.port}` `Server is running on http://${info.address}:${info.port}`,
); );
} },
); );
/** /**
@@ -217,21 +221,21 @@ serve(
const setting = serverSettings; const setting = serverSettings;
const isActive = setting.filter((n) => n.name === "ocmeService"); const isActive = setting.filter((n) => n.name === "ocmeService");
if (ocmeport && isActive[0]?.value === "1") { if (ocmeport && isActive[0]?.value === "1") {
serve( serve(
{ {
fetch: app.fetch, fetch: app.fetch,
port: Number(ocmeport), port: Number(ocmeport),
hostname: "0.0.0.0", hostname: "0.0.0.0",
}, },
(info) => { (info) => {
createLog( createLog(
"info", "info",
"LST", "LST",
"server", "server",
`Ocme section is listening on http://${info.address}:${info.port}` `Ocme section is listening on http://${info.address}:${info.port}`,
); );
} },
); );
} }
export type AppRoutes = typeof appRoutes; export type AppRoutes = typeof appRoutes;

View File

@@ -1,45 +1,62 @@
import {type MiddlewareHandler} from "hono"; import axios from "axios";
import { type MiddlewareHandler } from "hono";
import jwt from "jsonwebtoken"; import jwt from "jsonwebtoken";
const {sign, verify} = jwt; const { sign, verify } = jwt;
export const authMiddleware: MiddlewareHandler = async (c, next) => { export const authMiddleware: MiddlewareHandler = async (c, next) => {
const authHeader = c.req.header("Authorization"); console.log("middleware checked");
const cookieHeader = c.req.header("Cookie");
if (!cookieHeader) return c.json({ error: "Unauthorized" }, 401);
if (!authHeader || !authHeader.startsWith("Bearer ")) { const res = await axios.get(`${process.env.LST_BASE_URL}/api/user/me`, {
return c.json({error: "Unauthorized"}, 401); headers: { Cookie: cookieHeader },
} });
const token = authHeader.split(" ")[1]; if (res.status === 401) return c.json({ error: "Unauthorized" }, 401);
try { //const user = await resp.json();
const decoded = verify(token, process.env.JWT_SECRET!, {ignoreExpiration: false}) as { c.set("user", res.data.user);
userId: number; return next();
exp: number;
};
const currentTime = Math.floor(Date.now() / 1000); // Get current timestamp
const timeLeft = decoded.exp - currentTime;
// If the token has less than REFRESH_THRESHOLD seconds left, refresh it
let newToken = null;
if (timeLeft < parseInt(process.env.REFRESH_THRESHOLD!)) {
newToken = sign({userId: decoded.userId}, process.env.JWT_SECRET!, {
expiresIn: parseInt(process.env.EXPIRATION_TIME!),
});
c.res.headers.set("Authorization", `Bearer ${newToken}`);
}
c.set("user", {id: decoded.userId});
await next();
// If a new token was generated, send it in response headers
if (newToken) {
console.log("token was refreshed");
c.res.headers.set("X-Refreshed-Token", newToken);
}
} catch (err) {
return c.json({error: "Invalid token"}, 401);
}
}; };
// export const authMiddleware: MiddlewareHandler = async (c, next) => {
// const authHeader = c.req.header("Authorization");
// if (!authHeader || !authHeader.startsWith("Bearer ")) {
// return c.json({error: "Unauthorized"}, 401);
// }
// const token = authHeader.split(" ")[1];
// try {
// const decoded = verify(token, process.env.JWT_SECRET!, {ignoreExpiration: false}) as {
// userId: number;
// exp: number;
// };
// const currentTime = Math.floor(Date.now() / 1000); // Get current timestamp
// const timeLeft = decoded.exp - currentTime;
// // If the token has less than REFRESH_THRESHOLD seconds left, refresh it
// let newToken = null;
// if (timeLeft < parseInt(process.env.REFRESH_THRESHOLD!)) {
// newToken = sign({userId: decoded.userId}, process.env.JWT_SECRET!, {
// expiresIn: parseInt(process.env.EXPIRATION_TIME!),
// });
// c.res.headers.set("Authorization", `Bearer ${newToken}`);
// }
// c.set("user", {id: decoded.userId});
// await next();
// // If a new token was generated, send it in response headers
// if (newToken) {
// console.log("token was refreshed");
// c.res.headers.set("X-Refreshed-Token", newToken);
// }
// } catch (err) {
// return c.json({error: "Invalid token"}, 401);
// }
// };

View File

@@ -1,85 +1,111 @@
import axios from "axios";
import { createMiddleware } from "hono/factory"; import { createMiddleware } from "hono/factory";
import type { CustomJwtPayload } from "../../../types/jwtToken.js"; // const hasCorrectRole = (requiredRole: string[], module: string) =>
import { verify } from "hono/jwt"; // createMiddleware(async (c, next) => {
import { db } from "../../../../database/dbclient.js"; // /**
import { modules } from "../../../../database/schema/modules.js"; // * We want to check to make sure you have the correct role to be here
import { and, eq } from "drizzle-orm"; // */
import { userRoles } from "../../../../database/schema/userRoles.js"; // const authHeader = c.req.header("Authorization");
import { tryCatch } from "../../../globalUtils/tryCatch.js";
// if (!authHeader || !authHeader.startsWith("Bearer ")) {
// return c.json({ error: "Unauthorized" }, 401);
// }
// const token = authHeader.split(" ")[1];
// // deal with token data
// const { data: tokenData, error: tokenError } = await tryCatch(
// verify(token, process.env.JWT_SECRET!),
// );
// if (tokenError) {
// return c.json({ error: "Invalid token" }, 401);
// }
// const customToken = tokenData as CustomJwtPayload;
// // Get the module
// const { data: mod, error: modError } = await tryCatch(
// db.select().from(modules).where(eq(modules.name, module)),
// );
// if (modError) {
// console.log(modError);
// return;
// }
// if (mod.length === 0) {
// return c.json({ error: "You have entered an invalid module name" }, 403);
// }
// // check if the user has the role needed to get into this module
// const { data: userRole, error: userRoleError } = await tryCatch(
// db
// .select()
// .from(userRoles)
// .where(
// and(
// eq(userRoles.module_id, mod[0].module_id),
// eq(userRoles.user_id, customToken.user?.user_id!),
// ),
// ),
// );
// if (userRoleError) {
// return;
// }
// if (!userRole) {
// return c.json(
// {
// error:
// "The module you are trying to access is not active or is invalid.",
// },
// 403,
// );
// }
// if (!requiredRole.includes(userRole[0]?.role)) {
// return c.json(
// { error: "You do not have access to this part of the app." },
// 403,
// );
// }
// await next();
// });
interface UserRole {
userRoleId: string;
userId: string;
module: string;
role: string;
}
const hasCorrectRole = (requiredRole: string[], module: string) => const hasCorrectRole = (requiredRole: string[], module: string) =>
createMiddleware(async (c, next) => { createMiddleware(async (c, next) => {
/** const cookieHeader = c.req.header("Cookie");
* We want to check to make sure you have the correct role to be here if (!cookieHeader) return c.json({ error: "Unauthorized" }, 401);
*/
const authHeader = c.req.header("Authorization");
if (!authHeader || !authHeader.startsWith("Bearer ")) { const res = await axios.get(`${process.env.LST_BASE_URL}/api/user/roles`, {
return c.json({ error: "Unauthorized" }, 401); headers: { Cookie: cookieHeader },
} });
const token = authHeader.split(" ")[1]; const currentRoles: UserRole[] = res.data.data;
const canAccess = currentRoles.some(
(r) => r.module === module && requiredRole.includes(r.role),
);
if (!canAccess) {
return c.json(
{
error: "Unauthorized",
message: `You do not have access to ${module}`,
},
400,
);
}
// deal with token data return next();
const { data: tokenData, error: tokenError } = await tryCatch( });
verify(token, process.env.JWT_SECRET!)
);
if (tokenError) {
return c.json({ error: "Invalid token" }, 401);
}
const customToken = tokenData as CustomJwtPayload;
// Get the module
const { data: mod, error: modError } = await tryCatch(
db.select().from(modules).where(eq(modules.name, module))
);
if (modError) {
console.log(modError);
return;
}
if (mod.length === 0) {
return c.json({ error: "You have entered an invalid module name" }, 403);
}
// check if the user has the role needed to get into this module
const { data: userRole, error: userRoleError } = await tryCatch(
db
.select()
.from(userRoles)
.where(
and(
eq(userRoles.module_id, mod[0].module_id),
eq(userRoles.user_id, customToken.user?.user_id!)
)
)
);
if (userRoleError) {
return;
}
if (!userRole) {
return c.json(
{
error:
"The module you are trying to access is not active or is invalid.",
},
403
);
}
if (!requiredRole.includes(userRole[0]?.role)) {
return c.json(
{ error: "You do not have access to this part of the app." },
403
);
}
await next();
});
export default hasCorrectRole; export default hasCorrectRole;

View File

@@ -1,97 +1,117 @@
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi"; import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import axios from "axios";
import { login } from "../controllers/login.js"; import { login } from "../controllers/login.js";
const app = new OpenAPIHono(); const app = new OpenAPIHono();
const UserSchema = z const UserSchema = z
.object({ .object({
username: z.string().optional().openapi({ example: "smith002" }), username: z.string().optional().openapi({ example: "smith002" }),
//email: z.string().optional().openapi({example: "s.smith@example.com"}), //email: z.string().optional().openapi({example: "s.smith@example.com"}),
password: z.string().openapi({ example: "password123" }), password: z.string().openapi({ example: "password123" }),
}) })
.openapi("User"); .openapi("User");
const route = createRoute({ const route = createRoute({
tags: ["Auth"], tags: ["Auth"],
summary: "Login as user", summary: "Login as user",
description: "Login as a user to get a JWT token", description: "Login as a user to get a JWT token",
method: "post", method: "post",
path: "/login", path: "/login",
request: { request: {
body: { body: {
content: { content: {
"application/json": { schema: UserSchema }, "application/json": { schema: UserSchema },
}, },
}, },
}, },
responses: { responses: {
200: { 200: {
content: { content: {
"application/json": { "application/json": {
schema: z.object({ schema: z.object({
success: z.boolean().openapi({ example: true }), success: z.boolean().openapi({ example: true }),
message: z.string().openapi({ example: "Logged in" }), message: z.string().openapi({ example: "Logged in" }),
}), }),
}, },
}, },
description: "Response message", description: "Response message",
}, },
400: { 400: {
content: { content: {
"application/json": { "application/json": {
schema: z.object({ schema: z.object({
success: z.boolean().openapi({ example: false }), success: z.boolean().openapi({ example: false }),
message: z message: z
.string() .string()
.openapi({ example: "Username and password required" }), .openapi({ example: "Username and password required" }),
}), }),
}, },
}, },
description: "Bad request", description: "Bad request",
}, },
401: { 401: {
content: { content: {
"application/json": { "application/json": {
schema: z.object({ schema: z.object({
success: z.boolean().openapi({ example: false }), success: z.boolean().openapi({ example: false }),
message: z message: z
.string() .string()
.openapi({ example: "Username and password required" }), .openapi({ example: "Username and password required" }),
}), }),
}, },
}, },
description: "Bad request", description: "Bad request",
}, },
}, },
}); });
app.openapi(route, async (c) => { app.openapi(route, async (c: any) => {
const { username, password, email } = await c.req.json(); const { username, password, email } = await c.req.json();
if (!username || !password) { if (!username || !password) {
return c.json( return c.json(
{ {
success: false, success: false,
message: "Username and password are required", message: "Username and password are required",
}, },
400 400,
); );
} }
try { try {
const { token, user } = await login(username.toLowerCase(), password); const loginResp = await axios.post(
`${process.env.LST_BASE_URL}/api/user/login`,
{ username: username.toLowerCase(), password },
{ withCredentials: true },
);
// Set the JWT as an HTTP-only cookie // Set the JWT as an HTTP-only cookie
//c.header("Set-Cookie", `auth_token=${token}; HttpOnly; Secure; Path=/; SameSite=None; Max-Age=3600`); //c.header("Set-Cookie", `auth_token=${token}; HttpOnly; Secure; Path=/; SameSite=None; Max-Age=3600`);
return c.json( const setCookie = loginResp.headers["set-cookie"] as any;
{ success: true, message: "Login successful", user, token },
200 if (setCookie) {
); c.header("Set-Cookie", setCookie);
} catch (err) { }
return c.json({ success: false, message: "Incorrect Credentials" }, 401); return c.json(
} { success: true, message: "Login successful", data: loginResp.data },
200,
);
} catch (err) {
// @ts-ignore
if (!err.response.data.success) {
// @ts-ignore
return c.json(
// @ts-ignore
{ success: false, message: err.response.data.message },
401,
);
} else {
return c.json({ success: false, message: "Incorrect Credentials" }, 401);
}
}
}); });
export default app; export default app;

View File

@@ -1,110 +1,149 @@
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi"; import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import axios from "axios";
import { verify } from "hono/jwt"; import { verify } from "hono/jwt";
import { authMiddleware } from "../middleware/authMiddleware.js";
import jwt from "jsonwebtoken"; import jwt from "jsonwebtoken";
import { authMiddleware } from "../middleware/authMiddleware.js";
const session = new OpenAPIHono(); const session = new OpenAPIHono();
const expiresIn = Number(process.env.JWT_EXPIRES!) || 60; const expiresIn = Number(process.env.JWT_EXPIRES!) || 60;
const secret: string = process.env.JWT_SECRET!; const secret: string = process.env.JWT_SECRET!;
const { sign } = jwt; const { sign } = jwt;
const UserSchema = z.object({ const UserSchema = z.object({
username: z username: z
.string() .string()
.regex(/^[a-zA-Z0-9_]{3,30}$/) .regex(/^[a-zA-Z0-9_]{3,30}$/)
.openapi({ example: "smith034" }), .openapi({ example: "smith034" }),
email: z.string().email().openapi({ example: "smith@example.com" }), email: z.string().email().openapi({ example: "smith@example.com" }),
password: z password: z
.string() .string()
.min(6, { message: "Passwords must be longer than 3 characters" }) .min(6, { message: "Passwords must be longer than 3 characters" })
.regex(/[A-Z]/, { .regex(/[A-Z]/, {
message: "Password must contain at least one uppercase letter", message: "Password must contain at least one uppercase letter",
}) })
.regex(/[\W_]/, { .regex(/[\W_]/, {
message: "Password must contain at least one special character", message: "Password must contain at least one special character",
}) })
.openapi({ example: "Password1!" }), .openapi({ example: "Password1!" }),
}); });
const activeSessions: Record<string, { lastSeen: number; expiresAt: number }> =
{};
const SESSION_TIMEOUT_MS = 60 * 60 * 1000; // 1 hour
session.openapi( session.openapi(
createRoute({ createRoute({
tags: ["Auth"], tags: ["Auth"],
summary: "Checks a user session based on there token", summary: "Checks a user session based on there token",
description: "Can post there via Authentiaction header or cookies", description: "Can post there via Authentiaction header or cookies",
method: "get", method: "get",
path: "/session", path: "/session",
middleware: authMiddleware, middleware: authMiddleware,
// request: { // request: {
// body: { // body: {
// content: { // content: {
// "application/json": {schema: UserSchema}, // "application/json": {schema: UserSchema},
// }, // },
// }, // },
// }, // },
responses: { responses: {
200: { 200: {
content: { content: {
"application/json": { "application/json": {
schema: z.object({ schema: z.object({
data: z.object({ data: z.object({
token: z token: z.string().openapi({
.string() example: "sdkjhgsldkvhdakl;jvhs;adkjfhvds.kvnsad;ovhads",
.openapi({ }),
example: "sdkjhgsldkvhdakl;jvhs;adkjfhvds.kvnsad;ovhads", // user: z.object({
}), // user_id: z.string().openapi({example: "04316c86-f086-4cc6-b3d4-cca164a26f3f"}),
// user: z.object({ // username: z.string().openapi({example: "smith"}),
// user_id: z.string().openapi({example: "04316c86-f086-4cc6-b3d4-cca164a26f3f"}), // email: z.string().openapi({example: "smith@example.com"}).optional(),
// username: z.string().openapi({example: "smith"}), // }),
// email: z.string().openapi({example: "smith@example.com"}).optional(), }),
// }), }),
}), },
}), },
}, description: "Login successful",
}, },
description: "Login successful", 401: {
}, content: {
401: { "application/json": {
content: { schema: z.object({
"application/json": { message: z.string().openapi({ example: "Unathenticated" }),
schema: z.object({ }),
message: z.string().openapi({ example: "Unathenticated" }), },
}), },
}, description: "Error of why you were not logged in.",
}, },
description: "Error of why you were not logged in.", },
}, }),
}, async (c: any) => {
}), const cookieHeader = c.req.header("Cookie");
async (c) => { if (!cookieHeader) return c.json({ error: "Unauthorized" }, 401);
const authHeader = c.req.header("Authorization");
if (authHeader?.includes("Basic")) { const res = await axios.get(`${process.env.LST_BASE_URL}/api/user/me`, {
return c.json( headers: { Cookie: cookieHeader },
{ message: "You are a Basic user! Please login to get a token" }, withCredentials: true,
401 });
);
}
if (!authHeader) { if (res.status === 401) return c.json({ error: "Unauthorized" }, 401);
return c.json({ message: "Unauthorized" }, 401);
}
const token = authHeader?.split("Bearer ")[1] || ""; const user = res.data.user;
try { // ── record session heartbeat ───────────────────────────────────────────
const payload = await verify(token, process.env.JWT_SECRET!); activeSessions[user.id] = {
lastSeen: Date.now(),
expiresAt: Date.now() + SESSION_TIMEOUT_MS,
};
// If it's valid, return a new token // clean up stale sessions in the background
const newToken = sign({ user: payload.user }, secret, { for (const [key, sess] of Object.entries(activeSessions)) {
expiresIn: expiresIn * 60, if (Date.now() > sess.expiresAt) delete activeSessions[key];
}); }
return c.json({ data: { token: newToken, user: payload.user } }, 200); const setCookie =
} catch (error) { res.headers &&
return c.json({ message: "Unauthorized" }, 401); ((res.headers["set-cookie"] || res.headers["Set-Cookie"]) as
} | string[]
} | undefined);
if (setCookie) c.header("Set-Cookie", setCookie);
return c.json(
{ data: { token: res.data.token, user: res.data.user } },
200,
);
// const authHeader = c.req.header("Authorization");
// if (authHeader?.includes("Basic")) {
// return c.json(
// { message: "You are a Basic user! Please login to get a token" },
// 401
// );
// }
// if (!authHeader) {
// return c.json({ message: "Unauthorized" }, 401);
// }
// const token = authHeader?.split("Bearer ")[1] || "";
// try {
// const payload = await verify(token, process.env.JWT_SECRET!);
// // If it's valid, return a new token
// const newToken = sign({ user: payload.user }, secret, {
// expiresIn: expiresIn * 60,
// });
// return c.json({ data: { token: newToken, user: payload.user } }, 200);
// } catch (error) {
// return c.json({ message: "Unauthorized" }, 401);
// }
},
); );
// const token = authHeader?.split("Bearer ")[1] || ""; // const token = authHeader?.split("Bearer ")[1] || "";

View File

@@ -1,59 +1,72 @@
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi"; import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import axios from "axios";
import jwt from "jsonwebtoken"; import jwt from "jsonwebtoken";
import type { CustomJwtPayload } from "../../../../types/jwtToken.js"; import type { CustomJwtPayload } from "../../../../types/jwtToken.js";
import { authMiddleware } from "../../middleware/authMiddleware.js";
import { roleCheck } from "../../controllers/userRoles/getUserAccess.js"; import { roleCheck } from "../../controllers/userRoles/getUserAccess.js";
import { authMiddleware } from "../../middleware/authMiddleware.js";
const { verify } = jwt; const { verify } = jwt;
const app = new OpenAPIHono(); const app = new OpenAPIHono();
const responseSchema = z.object({ const responseSchema = z.object({
message: z.string().optional().openapi({ example: "User Created" }), message: z.string().optional().openapi({ example: "User Created" }),
}); });
app.openapi( app.openapi(
createRoute({ createRoute({
tags: ["auth:user"], tags: ["auth:user"],
summary: "returns the users access", summary: "returns the users access",
method: "get", method: "get",
path: "/getuseraccess", path: "/getuseraccess",
middleware: [authMiddleware], middleware: [authMiddleware],
responses: { responses: {
200: { 200: {
content: { "application/json": { schema: responseSchema } }, content: { "application/json": { schema: responseSchema } },
description: "Retrieve the user", description: "Retrieve the user",
}, },
}, },
}), }),
async (c) => { async (c: any) => {
// apit hit // apit hit
//apiHit(c, { endpoint: "api/auth/getUserRoles" }); //apiHit(c, { endpoint: "api/auth/getUserRoles" });
const authHeader = c.req.header("Authorization"); const authHeader = c.req.header("Authorization");
const token = authHeader?.split("Bearer ")[1] || "";
try {
const secret = process.env.JWT_SECRET!;
if (!secret) {
throw new Error("JWT_SECRET is not defined in environment variables");
}
const payload = verify(token, secret) as CustomJwtPayload; const user = c.get("user");
const canAccess = await roleCheck(payload.user?.user_id); if (!user) {
return c.json(
{
success: true,
message: `Unauthorized`,
},
401,
);
}
try {
const cookieHeader = c.req.header("Cookie");
if (!cookieHeader) return c.json({ error: "Unauthorized" }, 401);
return c.json( const res = await axios.get(
{ `${process.env.LST_BASE_URL}/api/user/roles`,
sucess: true, {
message: `User ${payload.user?.username} can access`, headers: { Cookie: cookieHeader },
data: canAccess, },
}, );
200
);
} catch (error) {
console.log(error);
}
return c.json({ message: "UserRoles coming over" }); return c.json(
} {
success: true,
message: `User ${user.username} can access`,
data: res.data.data,
},
200,
);
} catch (error) {
console.log(error);
}
return c.json({ message: "UserRoles coming over" });
},
); );
export default app; export default app;

View File

@@ -1,154 +1,152 @@
import { eq } from "drizzle-orm";
import { db } from "../../../../../database/dbclient.js"; import { db } from "../../../../../database/dbclient.js";
import { settings } from "../../../../../database/schema/settings.js";
import { siloAdjustments } from "../../../../../database/schema/siloAdjustments.js";
import { greetingStuff } from "../../../../globalUtils/greetingEmail.js";
import { generateOneTimeKey } from "../../../../globalUtils/singleUseKey.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js"; import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { sendEmail } from "../../../notifications/controller/sendMail.js";
import {
getSettings,
serverSettings,
} from "../../../server/controller/settings/getSettings.js";
import { query } from "../../../sqlServer/prodSqlServer.js"; import { query } from "../../../sqlServer/prodSqlServer.js";
import { siloQuery } from "../../../sqlServer/querys/silo/siloQuery.js"; import { siloQuery } from "../../../sqlServer/querys/silo/siloQuery.js";
import { postAdjustment } from "./postAdjustment.js"; import { postAdjustment } from "./postAdjustment.js";
import { siloAdjustments } from "../../../../../database/schema/siloAdjustments.js";
import { greetingStuff } from "../../../../globalUtils/greetingEmail.js";
import { sendEmail } from "../../../notifications/controller/sendMail.js";
import { settings } from "../../../../../database/schema/settings.js";
import { generateOneTimeKey } from "../../../../globalUtils/singleUseKey.js";
import { eq } from "drizzle-orm";
import {
getSettings,
serverSettings,
} from "../../../server/controller/settings/getSettings.js";
export const createSiloAdjustment = async ( export const createSiloAdjustment = async (
data: any | null, data: any | null,
user: any | null user: any | null,
) => { ) => {
/** /**
* Creates a silo adjustment based off warehouse, location, and qty. * Creates a silo adjustment based off warehouse, location, and qty.
* qty will come from the hmi, prolink, or silo patrol * qty will come from the hmi, prolink, or silo patrol
*/ */
// const { data: set, error: setError } = await tryCatch( // const { data: set, error: setError } = await tryCatch(
// db.select().from(settings) // db.select().from(settings)
// ); // );
// const { data: set, error: setError } = await tryCatch(getSettings()); // const { data: set, error: setError } = await tryCatch(getSettings());
// if (setError) { // if (setError) {
// return { // return {
// success: false, // success: false,
// message: `There was an error getting setting data to post to the server.`, // message: `There was an error getting setting data to post to the server.`,
// data: setError, // data: setError,
// }; // };
// } // }
const set = serverSettings.length === 0 ? [] : serverSettings; const set = serverSettings.length === 0 ? [] : serverSettings;
// getting stock data first so we have it prior to the adjustment // getting stock data first so we have it prior to the adjustment
const { data: s, error: stockError } = await tryCatch( const { data: s, error: stockError } = await tryCatch(
query(siloQuery, "Silo data Query") query(siloQuery, "Silo data Query"),
); );
if (stockError) { if (stockError) {
return { return {
success: false, success: false,
message: `There was an error getting stock data to post to the server.`, message: `There was an error getting stock data to post to the server.`,
data: stockError, data: stockError,
}; };
} }
const stock: any = s?.data as any; const stock: any = s?.data as any;
const { data: a, error: errorAdj } = await tryCatch( const { data: a, error: errorAdj } = await tryCatch(postAdjustment(data));
postAdjustment(data, user.prod)
);
if (errorAdj) { if (errorAdj) {
return { return {
success: false, success: false,
message: `There was an error doing the silo adjustment.`, message: `There was an error doing the silo adjustment.`,
data: errorAdj, data: errorAdj,
}; };
} }
/** /**
* Checking to see the difference, and send email if +/- 5% will change later if needed * Checking to see the difference, and send email if +/- 5% will change later if needed
*/ */
const sa: any = a; const sa: any = a;
if (!sa.success) { if (!sa.success) {
console.log(`insde error`); console.log(`inside error`);
return { return {
success: sa.success, success: sa.success,
message: sa.message, message: sa.message,
data: sa.data, data: sa.data,
}; };
} }
const stockNummy = stock.filter((s: any) => s.LocationID === data.laneId); const stockNummy = stock.filter((s: any) => s.LocationID === data.laneId);
const theDiff = const theDiff =
((data.quantity - stockNummy[0].Stock_Total) / ((data.quantity - stockNummy[0].Stock_Total) /
((data.quantity + stockNummy[0].Stock_Total) / 2)) * ((data.quantity + stockNummy[0].Stock_Total) / 2)) *
100; 100;
/** /**
* Post the data to our db. * Post the data to our db.
*/ */
//console.log(stockNummy); //console.log(stockNummy);
const { data: postAdj, error: postAdjError } = await tryCatch( const { data: postAdj, error: postAdjError } = await tryCatch(
db db
.insert(siloAdjustments) .insert(siloAdjustments)
.values({ .values({
warehouseID: data.warehouseId, warehouseID: data.warehouseId,
locationID: data.laneId, locationID: data.laneId,
currentStockLevel: stockNummy[0].Stock_Total, currentStockLevel: stockNummy[0].Stock_Total,
newLevel: data.quantity, newLevel: data.quantity,
lastDateAdjusted: new Date(stockNummy[0].LastAdjustment), lastDateAdjusted: new Date(stockNummy[0].LastAdjustment),
add_user: user.username, add_user: user.username,
}) })
.returning({ id: siloAdjustments.siloAdjust_id }) .returning({ id: siloAdjustments.siloAdjust_id }),
); );
if (postAdjError) { if (postAdjError) {
//console.log(postAdjError); //console.log(postAdjError);
return { return {
success: false, success: false,
message: `There was an error posting the new adjustment.`, message: `There was an error posting the new adjustment.`,
data: postAdjError, data: postAdjError,
}; };
} }
let adj: any = a; let adj: any = a;
if (Math.abs(theDiff) > 5) { if (Math.abs(theDiff) > 5) {
// console.log(`Send for comment due to being: ${theDiff.toFixed(2)}%`); // console.log(`Send for comment due to being: ${theDiff.toFixed(2)}%`);
const server = set.filter((n: any) => n.name === "server"); const server = set.filter((n: any) => n.name === "server");
const port = set.filter((n: any) => n.name === "serverPort"); const port = set.filter((n: any) => n.name === "serverPort");
const key = await generateOneTimeKey(); const key = await generateOneTimeKey();
const updateKey = await db const updateKey = await db
.update(siloAdjustments) .update(siloAdjustments)
.set({ commentKey: key }) .set({ commentKey: key })
.where(eq(siloAdjustments.siloAdjust_id, postAdj[0].id)); .where(eq(siloAdjustments.siloAdjust_id, postAdj[0].id));
const emailSetup = { const emailSetup = {
email: user.email, email: user.email,
subject: `Alert - Siloadjustment was done with a descrepancy of 5% or greater`, subject: `Alert - Siloadjustment was done with a descrepancy of 5% or greater`,
template: "siloAdjustmentComment", template: "siloAdjustmentComment",
context: { context: {
greeting: await greetingStuff(), greeting: await greetingStuff(),
siloName: stockNummy[0].Description, siloName: stockNummy[0].Description,
variance: `${theDiff.toFixed(2)}%`, variance: `${theDiff.toFixed(2)}%`,
currentLevel: stockNummy[0].Stock_Total, currentLevel: stockNummy[0].Stock_Total,
newLevel: data.quantity, newLevel: data.quantity,
variancePer: 5, variancePer: 5,
adjustID: `${postAdj[0].id}&${key}`, adjustID: `${postAdj[0].id}&${key}`,
server: server[0].value, server: server[0].value,
port: port[0].value, port: port[0].value,
}, },
}; };
//console.log(emailSetup); //console.log(emailSetup);
await sendEmail(emailSetup); await sendEmail(emailSetup);
return { return {
success: adj.success, success: adj.success,
message: `Silo adjustmnet was completed you will also receive and email due to the adjustment having a variation of ${Math.abs( message: `Silo adjustmnet was completed you will also receive and email due to the adjustment having a variation of ${Math.abs(
theDiff theDiff,
).toFixed(2)}%`, ).toFixed(2)}%`,
data: adj.data, data: adj.data,
}; };
} else { } else {
return { success: adj.success, message: adj.message, data: adj.data }; return { success: adj.success, message: adj.message, data: adj.data };
} }
}; };

View File

@@ -2,99 +2,99 @@ import axios from "axios";
import { prodEndpointCreation } from "../../../../globalUtils/createUrl.js"; import { prodEndpointCreation } from "../../../../globalUtils/createUrl.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js"; import { tryCatch } from "../../../../globalUtils/tryCatch.js";
export const postAdjustment = async (data: any, prod: any) => { export const postAdjustment = async (data: any) => {
if (data.warehouseId === undefined) { if (data.warehouseId === undefined) {
return { return {
sucess: false, sucess: false,
message: `Missing mandatory field: warehouseID`, message: `Missing mandatory field: warehouseID`,
data: { error: `Missing mandatory field: warehouseID` }, data: { error: `Missing mandatory field: warehouseID` },
}; };
} }
if (data.laneId === undefined) { if (data.laneId === undefined) {
return { return {
sucess: false, sucess: false,
message: `Missing mandatory field: locationID`, message: `Missing mandatory field: locationID`,
data: { error: `Missing mandatory field: locationID` }, data: { error: `Missing mandatory field: locationID` },
}; };
} }
if (data.quantity == "0") { if (data.quantity == "0") {
return { return {
sucess: false, sucess: false,
message: `You entered 0 for the quantity to post, quantity needs to be at leave 1`, message: `You entered 0 for the quantity to post, quantity needs to be at leave 1`,
data: { data: {
error: `You entered 0 for the quantity to post, quantity needs to be at leave 1`, error: `You entered 0 for the quantity to post, quantity needs to be at leave 1`,
}, },
}; };
} }
const siloAdjustment = { const siloAdjustment = {
warehouseId: data.warehouseId, warehouseId: data.warehouseId,
laneId: data.laneId, laneId: data.laneId,
quantity: data.quantity, quantity: data.quantity,
}; };
let url = await prodEndpointCreation( let url = await prodEndpointCreation(
"/public/v1.0/Warehousing/AdjustSiloStockLevel" "/public/v1.0/Warehousing/AdjustSiloStockLevel",
); );
const { data: silo, error } = await tryCatch( const { data: silo, error } = await tryCatch(
axios.post(url, siloAdjustment, { axios.post(url, siloAdjustment, {
headers: { headers: {
"X-API-Key": process.env.TEC_API_KEY || "", "X-API-Key": process.env.TEC_API_KEY || "",
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}) }),
); );
let e = error as any; let e = error as any;
if (e) { if (e) {
console.log(e.response); console.log(e.response);
if (e.status === 401) { if (e.status === 401) {
const data = { const data = {
success: false, success: false,
message: `There was error posting the data: ${JSON.stringify( message: `There was error posting the data: ${JSON.stringify(
e.response?.data e.response?.data,
)}`, )}`,
data: { data: {
status: e.response?.status, status: e.response?.status,
statusText: e.response?.statusText, statusText: e.response?.statusText,
data: e.response?.data, data: e.response?.data,
}, },
}; };
return data; return data;
} else { } else {
return { return {
success: false, success: false,
message: "Error in posting the silo adjustment.", message: "Error in posting the silo adjustment.",
data: { data: {
status: e.response?.status, status: e.response?.status,
statusText: e.response?.statusText, statusText: e.response?.statusText,
data: e.response?.data, data: e.response?.data,
}, },
}; };
} }
} }
if (silo?.status !== 200) { if (silo?.status !== 200) {
return { return {
success: false, success: false,
message: "Error in posting the silo adjustment", message: "Error in posting the silo adjustment",
data: { data: {
status: silo?.status, status: silo?.status,
statusText: silo?.statusText, statusText: silo?.statusText,
data: silo?.data, data: silo?.data,
}, },
}; };
} else { } else {
return { return {
success: true, success: true,
message: "Adjustment was completed", message: "Adjustment was completed",
data: { data: {
status: silo.status, status: silo.status,
statusText: silo.statusText, statusText: silo.statusText,
data: silo.data, data: silo.data,
}, },
}; };
} }
}; };

View File

@@ -1,9 +1,10 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi"; import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
import { ordersIn } from "../../controller/dm/ordersIn/ordersIn.js";
import { verify } from "hono/jwt"; import { verify } from "hono/jwt";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { apiHit } from "../../../../globalUtils/apiHits.js"; import { apiHit } from "../../../../globalUtils/apiHits.js";
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { authMiddleware } from "../../../auth/middleware/authMiddleware.js";
import { ordersIn } from "../../controller/dm/ordersIn/ordersIn.js";
const app = new OpenAPIHono(); const app = new OpenAPIHono();
@@ -15,61 +16,53 @@ const app = new OpenAPIHono();
// }) // })
// .openapi("User"); // .openapi("User");
app.openapi( app.openapi(
createRoute({ createRoute({
tags: ["logistics"], tags: ["logistics"],
summary: "Post orders to DM", summary: "Post orders to DM",
method: "post", method: "post",
path: "/postbulkorders", path: "/postbulkorders",
// request: { middleware: authMiddleware,
// body: { // request: {
// content: { // body: {
// "application/json": { schema: Body }, // content: {
// }, // "application/json": { schema: Body },
// }, // },
// }, // },
// description: // },
// "Provided a running number and lot number you can consume material.", // description:
responses: responses(), // "Provided a running number and lot number you can consume material.",
}), responses: responses(),
async (c) => { }),
apiHit(c, { endpoint: "/postbulkorders" }); async (c) => {
const body = await c.req.parseBody(); apiHit(c, { endpoint: "/postbulkorders" });
const authHeader = c.req.header("Authorization"); const body = await c.req.parseBody();
const token = authHeader?.split("Bearer ")[1] || ""; //console.log(body); // File | string
//console.log(body); // File | string
// if (body["fileType"] === "standard") { // if (body["fileType"] === "standard") {
// console.log(`doing standard orders in.`); // console.log(`doing standard orders in.`);
// } // }
const { data: payload, error: pe } = await tryCatch(
verify(token, process.env.JWT_SECRET!)
);
if (pe) { const { data: orders, error } = await tryCatch(
return c.json({ success: false, message: "Unauthorized" }, 401); ordersIn(body, c.get("user")),
} );
const { data: orders, error } = await tryCatch( if (error) {
ordersIn(body, payload.user) console.log(error);
); return c.json(
{
success: false,
message: "Error posting Orders",
data: error,
},
400,
);
}
if (error) { return c.json({
console.log(error); success: orders?.success ?? false,
return c.json( message: orders?.message ?? "Error posting forecast",
{ data: orders?.data ?? [],
success: false, });
message: "Error posting Orders", },
data: error,
},
400
);
}
return c.json({
success: orders?.success ?? false,
message: orders?.message ?? "Error posting forecast",
data: orders?.data ?? [],
});
}
); );
export default app; export default app;

View File

@@ -1,10 +1,11 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi"; import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
import { ordersIn } from "../../controller/dm/ordersIn/ordersIn.js";
import { verify } from "hono/jwt"; import { verify } from "hono/jwt";
import { forecastIn } from "../../controller/dm/forecast/forecastIn.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { apiHit } from "../../../../globalUtils/apiHits.js"; import { apiHit } from "../../../../globalUtils/apiHits.js";
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { authMiddleware } from "../../../auth/middleware/authMiddleware.js";
import { forecastIn } from "../../controller/dm/forecast/forecastIn.js";
import { ordersIn } from "../../controller/dm/ordersIn/ordersIn.js";
const app = new OpenAPIHono(); const app = new OpenAPIHono();
@@ -16,61 +17,54 @@ const app = new OpenAPIHono();
// }) // })
// .openapi("User"); // .openapi("User");
app.openapi( app.openapi(
createRoute({ createRoute({
tags: ["logistics"], tags: ["logistics"],
summary: "Post forecast to DM", summary: "Post forecast to DM",
method: "post", method: "post",
path: "/postforecastin", path: "/postforecastin",
// request: { middleware: authMiddleware,
// body: { // request: {
// content: { // body: {
// "application/json": { schema: Body }, // content: {
// }, // "application/json": { schema: Body },
// }, // },
// }, // },
// description: // },
// "Provided a running number and lot number you can consume material.", // description:
responses: responses(), // "Provided a running number and lot number you can consume material.",
}), responses: responses(),
async (c) => { }),
apiHit(c, { endpoint: "/postforecastin" }); async (c) => {
const body = await c.req.parseBody(); apiHit(c, { endpoint: "/postforecastin" });
const authHeader = c.req.header("Authorization"); const body = await c.req.parseBody();
const token = authHeader?.split("Bearer ")[1] || "";
//console.log(body); // File | string
// if (body["fileType"] === "standard") { //console.log(body); // File | string
// console.log(`doing standard orders in.`);
// }
const { data: payload, error: pe } = await tryCatch(
verify(token, process.env.JWT_SECRET!)
);
if (pe) { // if (body["fileType"] === "standard") {
return c.json({ success: false, message: "Unauthorized" }, 401); // console.log(`doing standard orders in.`);
} // }
const { data: orders, error } = await tryCatch( const { data: orders, error } = await tryCatch(
forecastIn(body, payload.user) forecastIn(body, c.get("user")),
); );
if (error) { if (error) {
console.log(error); console.log(error);
return c.json( return c.json(
{ {
success: false, success: false,
message: "Error posting forecast", message: "Error posting forecast",
data: error, data: error,
}, },
400 400,
); );
} }
return c.json({ return c.json({
success: orders?.success ?? false, success: orders?.success ?? false,
message: orders?.message ?? "Error posting forecast", message: orders?.message ?? "Error posting forecast",
data: orders?.data ?? [], data: orders?.data ?? [],
}); });
} },
); );
export default app; export default app;

View File

@@ -1,10 +1,10 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi"; import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { format } from "date-fns"; import { format } from "date-fns";
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
import { standardTemplate } from "../../controller/dm/ordersIn/createTemplate.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { standardForCastTemplate } from "../../controller/dm/forecast/createTemplate.js";
import { apiHit } from "../../../../globalUtils/apiHits.js"; import { apiHit } from "../../../../globalUtils/apiHits.js";
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { standardForCastTemplate } from "../../controller/dm/forecast/createTemplate.js";
const app = new OpenAPIHono(); const app = new OpenAPIHono();
@@ -16,52 +16,52 @@ const app = new OpenAPIHono();
// }) // })
// .openapi("User"); // .openapi("User");
app.openapi( app.openapi(
createRoute({ createRoute({
tags: ["logistics"], tags: ["logistics"],
summary: "Gets the standard Forecast Template", summary: "Gets the standard Forecast Template",
method: "get", method: "get",
path: "/bulkforcasttemplate", path: "/bulkforcasttemplate",
// request: { // request: {
// body: { // body: {
// content: { // content: {
// "application/json": { schema: Body }, // "application/json": { schema: Body },
// }, // },
// }, // },
// }, // },
// description: // description:
// "Provided a running number and lot number you can consume material.", // "Provided a running number and lot number you can consume material.",
responses: responses(), responses: responses(),
}), }),
async (c: any) => { async (c: any) => {
apiHit(c, { endpoint: "/bulkforcasttemplate" }); apiHit(c, { endpoint: "/bulkforcasttemplate" });
const defaultFilename = `bulkForcastTemplate-${format( const defaultFilename = `bulkForcastTemplate-${format(
new Date(Date.now()), new Date(Date.now()),
"M-d-yyyy" "M-d-yyyy",
)}.xlsx`; )}.xlsx`;
const filename = c.req.query("filename") || defaultFilename; const filename = c.req.query("filename") || defaultFilename;
const { data, error } = await tryCatch(standardForCastTemplate()); const { data, error } = await tryCatch(standardForCastTemplate());
if (error) { if (error) {
return c.json({ return c.json({
success: false, success: false,
message: "Error creating template", message: "Error creating template",
data: error, data: error,
}); });
} }
return new Response(data, { return new Response(data, {
headers: { headers: {
"Content-Type": "Content-Type":
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"Content-Disposition": `attachment; filename="${filename}"`, "Content-Disposition": `attachment; filename="${filename}"`,
}, },
}); });
// return c.json({ // return c.json({
// success: data.success, // success: data.success,
// message: data.message, // message: data.message,
// data: data.data, // data: data.data,
// }); // });
} },
); );
export default app; export default app;

View File

@@ -1,78 +1,74 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi"; import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { verify } from "hono/jwt"; import { verify } from "hono/jwt";
import { authMiddleware } from "../../../auth/middleware/authMiddleware.js";
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
import { createSiloAdjustment } from "../../controller/siloAdjustments/createSiloAdjustment.js";
import { apiHit } from "../../../../globalUtils/apiHits.js"; import { apiHit } from "../../../../globalUtils/apiHits.js";
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js"; import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { authMiddleware } from "../../../auth/middleware/authMiddleware.js";
import { createSiloAdjustment } from "../../controller/siloAdjustments/createSiloAdjustment.js";
const app = new OpenAPIHono(); const app = new OpenAPIHono();
const responseSchema = z.object({ const responseSchema = z.object({
success: z.boolean().optional().openapi({ example: true }), success: z.boolean().optional().openapi({ example: true }),
message: z.string().optional().openapi({ example: "user access" }), message: z.string().optional().openapi({ example: "user access" }),
}); });
app.openapi( app.openapi(
createRoute({ createRoute({
tags: ["logistics"], tags: ["logistics"],
summary: "Creates silo adjustmennt", summary: "Creates silo adjustmennt",
method: "post", method: "post",
path: "/createsiloadjustment", path: "/createsiloadjustment",
middleware: authMiddleware, middleware: authMiddleware,
description: description:
"Creates a silo adjustment for the silo if and stores the stock numbers.", "Creates a silo adjustment for the silo if and stores the stock numbers.",
responses: responses(), responses: responses(),
}), }),
async (c) => { async (c) => {
const { data, error } = await tryCatch(c.req.json()); const { data, error } = await tryCatch(c.req.json());
if (error) { if (error) {
return c.json( return c.json(
{ {
success: false, success: false,
message: "Missing data please try again", message: "Missing data please try again",
error, error,
}, },
400 400,
); );
} }
apiHit(c, { endpoint: "/createsiloadjustment", lastBody: data }); apiHit(c, { endpoint: "/createsiloadjustment", lastBody: data });
const authHeader = c.req.header("Authorization");
const token = authHeader?.split("Bearer ")[1] || "";
try { const user = c.get("user");
const payload = await verify(token, process.env.JWT_SECRET!);
try {
//return apiReturn(c, true, access?.message, access?.data, 200);
const createSiloAdj = await createSiloAdjustment(
data,
payload.user
);
return c.json( try {
{ try {
success: createSiloAdj.success, //return apiReturn(c, true, access?.message, access?.data, 200);
message: createSiloAdj.message, const createSiloAdj = await createSiloAdjustment(data, c.get("user"));
data: createSiloAdj.data,
}, return c.json(
200 {
); success: createSiloAdj.success,
} catch (error) { message: createSiloAdj.message,
//console.log(error); data: createSiloAdj.data,
//return apiReturn(c, false, "Error in setting the user access", error, 400); },
return c.json( 200,
{ );
success: false, } catch (error) {
message: "Missing data please try again", //console.log(error);
error, //return apiReturn(c, false, "Error in setting the user access", error, 400);
}, return c.json(
400 {
); success: false,
} message: "Missing data please try again",
} catch (error) { error,
return c.json({ success: false, message: "Unauthorized" }, 401); },
} 400,
} );
}
} catch (error) {
return c.json({ success: false, message: "Unauthorized" }, 401);
}
},
); );
export default app; export default app;

View File

@@ -1,88 +1,85 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi"; import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { verify } from "hono/jwt"; import { verify } from "hono/jwt";
import { authMiddleware } from "../../../auth/middleware/authMiddleware.js"; import { apiHit } from "../../../../globalUtils/apiHits.js";
import { responses } from "../../../../globalUtils/routeDefs/responses.js"; import { responses } from "../../../../globalUtils/routeDefs/responses.js";
import { authMiddleware } from "../../../auth/middleware/authMiddleware.js";
import { createSiloAdjustment } from "../../controller/siloAdjustments/createSiloAdjustment.js"; import { createSiloAdjustment } from "../../controller/siloAdjustments/createSiloAdjustment.js";
import { postSiloComment } from "../../controller/siloAdjustments/postComment.js"; import { postSiloComment } from "../../controller/siloAdjustments/postComment.js";
import { apiHit } from "../../../../globalUtils/apiHits.js";
const app = new OpenAPIHono(); const app = new OpenAPIHono();
const ParamsSchema = z.object({ const ParamsSchema = z.object({
adjId: z adjId: z
.string() .string()
.min(3) .min(3)
.openapi({ .openapi({
param: { param: {
name: "adjId", name: "adjId",
in: "path", in: "path",
}, },
example: "3b555052-a960-4301-8d38-a6f1acb98dbe", example: "3b555052-a960-4301-8d38-a6f1acb98dbe",
}), }),
}); });
const Body = z.object({ const Body = z.object({
comment: z comment: z
.string() .string()
.openapi({ example: "Reason to why i had a badd adjustment." }), .openapi({ example: "Reason to why i had a badd adjustment." }),
}); });
app.openapi( app.openapi(
createRoute({ createRoute({
tags: ["logistics"], tags: ["logistics"],
summary: "Post a comment to why you had a discrepancy", summary: "Post a comment to why you had a discrepancy",
method: "post", method: "post",
path: "/postcomment/:adjId", path: "/postcomment/:adjId",
middleware: authMiddleware, middleware: authMiddleware,
request: { request: {
params: ParamsSchema, params: ParamsSchema,
body: { content: { "application/json": { schema: Body } } }, body: { content: { "application/json": { schema: Body } } },
}, },
// description: // description:
// "Creates a silo adjustment for the silo if and stores the stock numbers.", // "Creates a silo adjustment for the silo if and stores the stock numbers.",
responses: responses(), responses: responses(),
}), }),
async (c: any) => { async (c: any) => {
apiHit(c, { endpoint: "/postcomment" }); apiHit(c, { endpoint: "/postcomment" });
const authHeader = c.req.header("Authorization"); const { adjId } = c.req.valid("param");
const token = authHeader?.split("Bearer ")[1] || "";
const { adjId } = c.req.valid("param");
try { try {
const payload = await verify(token, process.env.JWT_SECRET!); try {
try { //return apiReturn(c, true, access?.message, access?.data, 200);
//return apiReturn(c, true, access?.message, access?.data, 200); const data = await c.req.json();
const data = await c.req.json();
const addComment = await postSiloComment( const addComment = await postSiloComment(
adjId, adjId,
data.comment, data.comment,
data.key, data.key,
payload.user c.get("user"),
); );
console.log(addComment); console.log(addComment);
return c.json( return c.json(
{ {
success: addComment.success, success: addComment.success,
message: addComment.message, message: addComment.message,
data: addComment.data, data: addComment.data,
}, },
200 200,
); );
} catch (error) { } catch (error) {
return c.json( return c.json(
{ {
success: false, success: false,
message: "Missing data please try again", message: "Missing data please try again",
error, error,
}, },
400 400,
); );
} }
} catch (error) { } catch (error) {
return c.json({ success: false, message: "Unauthorized" }, 401); return c.json({ success: false, message: "Unauthorized" }, 401);
} }
} },
); );
export default app; export default app;

View File

@@ -1,31 +1,33 @@
// an external way to creating logs // an external way to creating logs
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi"; import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { getAllJobs } from "../utils/processNotifications.js";
import { apiHit } from "../../../globalUtils/apiHits.js"; import { apiHit } from "../../../globalUtils/apiHits.js";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { authMiddleware } from "../../auth/middleware/authMiddleware.js";
import hasCorrectRole from "../../auth/middleware/roleCheck.js";
import { getAllJobs } from "../utils/processNotifications.js";
const app = new OpenAPIHono({ strict: false }); const app = new OpenAPIHono({ strict: false });
app.openapi( app.openapi(
createRoute({ createRoute({
tags: ["server"], tags: ["server"],
summary: "Returns current active notifications.", summary: "Returns current active notifications.",
method: "get", method: "get",
path: "/activenotifications", path: "/activenotifications",
//middleware: authMiddleware, middleware: [authMiddleware, hasCorrectRole(["systemAdmin"], "admin")],
responses: responses(), responses: responses(),
}), }),
async (c) => { async (c) => {
apiHit(c, { endpoint: "/activenotifications" }); apiHit(c, { endpoint: "/activenotifications" });
const jobs = getAllJobs(); const jobs = getAllJobs();
return c.json({ return c.json({
success: true, success: true,
message: message:
jobs.length === 0 jobs.length === 0
? "There are no active Notifications Currently." ? "There are no active Notifications Currently."
: "Current Active notifications", : "Current Active notifications",
data: jobs, data: jobs,
}); });
} },
); );
export default app; export default app;

View File

@@ -6,253 +6,254 @@
import { db } from "../../../../database/dbclient.js"; import { db } from "../../../../database/dbclient.js";
import { subModules } from "../../../../database/schema/subModules.js"; import { subModules } from "../../../../database/schema/subModules.js";
import { createLog } from "../../logger/logger.js"; import { createLog } from "../../logger/logger.js";
// "view", "technician", "supervisor","manager", "admin", "systemAdmin" // "view", "technician", "supervisor","manager", "admin", "systemAdmin"
const newSubModules = [ const newSubModules = [
{ {
name: "RFID", name: "RFID",
moduleName: "prodcution", moduleName: "prodcution",
description: "RFID stuff", description: "RFID stuff",
link: "/rfid", link: "/rfid",
icon: "Tags", icon: "Tags",
active: true, active: true,
roles: [ roles: [
"viewer", "viewer",
"technician", "technician",
"supervisor", "supervisor",
"manager", "manager",
"admin", "admin",
"systemAdmin", "systemAdmin",
], ],
subSubModule: [], subSubModule: [],
}, },
{ {
name: "Silo Adjustments", name: "siloAdjustments",
moduleName: "logistics", moduleName: "logistics",
description: "Do a silo adjustmnet", description: "Do a silo adjustments",
link: "/siloAdjustments", link: "/siloAdjustments",
icon: "Cylinder", icon: "Cylinder",
active: false, active: false,
roles: ["technician", "supervisor", "manager", "admin", "systemAdmin"], roles: ["technician", "supervisor", "manager", "admin", "systemAdmin"],
subSubModule: [], subSubModule: [],
}, },
{ {
name: "Demand Management", name: "demandManagement",
moduleName: "logistics", moduleName: "logistics",
description: "Bulk order and Forecast imports", description: "Bulk order and Forecast imports",
link: "/dm", link: "/dm",
icon: "Truck", icon: "Truck",
roles: ["technician", "supervisor", "manager", "admin", "systemAdmin"], roles: ["technician", "supervisor", "manager", "admin", "systemAdmin"],
active: false, active: false,
subSubModule: [], subSubModule: [],
}, },
{ {
name: "Forecast", name: "Forecast",
moduleName: "logistics", moduleName: "logistics",
description: "", description: "",
link: "#", link: "#",
icon: "Truck", icon: "Truck",
roles: ["systemAdmin"], roles: ["systemAdmin"],
active: false, active: false,
subSubModule: [], subSubModule: [],
}, },
{ {
name: "Material Helper", name: "Material Helper",
moduleName: "logistics", moduleName: "logistics",
description: "", description: "",
link: "/materialHelper/consumption", link: "/materialHelper/consumption",
icon: "Package", icon: "Package",
roles: [ roles: [
"viewer", "viewer",
"technician", "technician",
"supervisor", "supervisor",
"manager", "manager",
"admin", "admin",
"systemAdmin", "systemAdmin",
], ],
active: false, active: false,
subSubModule: [], subSubModule: [],
}, },
{ {
name: "Ocme Cyclecount", name: "Ocme Cyclecount",
moduleName: "logistics", moduleName: "logistics",
description: "", description: "",
link: "/cyclecount", link: "/cyclecount",
icon: "Package", icon: "Package",
roles: ["technician", "supervisor", "manager", "admin", "systemAdmin"], roles: ["technician", "supervisor", "manager", "admin", "systemAdmin"],
active: false, active: false,
subSubModule: [], subSubModule: [],
}, },
{ {
name: "Open Orders", name: "Open Orders",
moduleName: "logistics", moduleName: "logistics",
description: "Open orders", description: "Open orders",
link: "/openOrders", link: "/openOrders",
icon: "Truck", icon: "Truck",
roles: [ roles: [
"viewer", "viewer",
"technician", "technician",
"supervisor", "supervisor",
"manager", "manager",
"admin", "admin",
"systemAdmin", "systemAdmin",
], ],
active: false, active: false,
subSubModule: [], subSubModule: [],
}, },
{ {
name: "Barcodes", name: "Barcodes",
moduleName: "logistics", moduleName: "logistics",
description: "Barcodes, lanes and scanable", description: "Barcodes, lanes and scanable",
link: "/barcodegen", link: "/barcodegen",
icon: "Barcode", icon: "Barcode",
roles: [ roles: [
"viewer", "viewer",
"technician", "technician",
"supervisor", "supervisor",
"manager", "manager",
"admin", "admin",
"systemAdmin", "systemAdmin",
], ],
active: true, active: true,
subSubModule: [], subSubModule: [],
}, },
{ {
name: "Helper Commands", name: "Helper Commands",
moduleName: "logistics", moduleName: "logistics",
description: "Commands to assist when a scanner is not avalible", description: "Commands to assist when a scanner is not avalible",
link: "/helpercommands", link: "/helpercommands",
icon: "Command", icon: "Command",
roles: ["technician", "supervisor", "manager", "admin", "systemAdmin"], roles: ["technician", "supervisor", "manager", "admin", "systemAdmin"],
active: true, active: true,
subSubModule: [], subSubModule: [],
}, },
// admin module // admin module
{ {
name: "Servers", name: "Servers",
moduleName: "admin", moduleName: "admin",
description: "Do a silo adjustmnet", description: "Do a silo adjustmnet",
link: "/servers", link: "/servers",
icon: "Server", icon: "Server",
roles: ["tester", "systemAdmin"], roles: ["tester", "systemAdmin"],
isActive: true, isActive: true,
subSubModule: [], subSubModule: [],
}, },
{ {
name: "Admin", name: "Admin",
moduleName: "admin", moduleName: "admin",
description: "Do a silo adjustmnet", description: "Do a silo adjustmnet",
link: "#", // when link is # this will mean its a button link: "#", // when link is # this will mean its a button
icon: "ShieldCheck", icon: "ShieldCheck",
active: true, active: true,
roles: ["tester", "admin", "systemAdmin"], roles: ["tester", "admin", "systemAdmin"],
subSubModule: [ subSubModule: [
{ {
name: "Settings", name: "Settings",
link: "/settings", link: "/settings",
icon: "Settings", icon: "Settings",
newWindow: false, newWindow: false,
isActive: true, isActive: true,
}, },
{ {
name: "Modules", name: "Modules",
link: "/modules", link: "/modules",
icon: "Settings", icon: "Settings",
newWindow: false, newWindow: false,
isActive: true, isActive: true,
}, },
{ {
name: "Sub Modules", name: "Sub Modules",
link: "/subModules", link: "/subModules",
icon: "Settings", icon: "Settings",
newWindow: false, newWindow: false,
isActive: true, isActive: true,
}, },
{ {
name: "Notifcations", name: "Notifcations",
link: "notificationMGT", link: "notificationMGT",
icon: "Webhook", icon: "Webhook",
newWindow: false, newWindow: false,
isActive: true, isActive: true,
}, },
{ {
name: "Swagger", name: "Swagger",
link: "#", link: "#",
icon: "Webhook", icon: "Webhook",
newWindow: true, newWindow: true,
isActive: true, isActive: true,
}, },
{ {
name: "Logs", name: "Logs",
link: "#", link: "#",
icon: "Logs", icon: "Logs",
newWindow: false, newWindow: false,
isActive: false, isActive: false,
}, },
{ {
name: "Users", name: "Users",
link: "/users", link: "/users",
icon: "Users", icon: "Users",
newWindow: false, newWindow: false,
isActive: true, isActive: true,
}, },
{ {
name: "Prod Perms", name: "Prod Perms",
link: "/produsers", link: "/produsers",
icon: "Users", icon: "Users",
newWindow: false, newWindow: false,
isActive: true, isActive: true,
}, },
{ {
name: "UCD", name: "UCD",
link: "https://ucd.alpla.net:8443/", link: "https://ucd.alpla.net:8443/",
icon: "Atom", icon: "Atom",
newWindow: false, newWindow: false,
isActive: true, isActive: true,
}, },
{ {
name: "Lst Api", name: "Lst Api",
link: "/api/docs", link: "/api/docs",
icon: "Webhook", icon: "Webhook",
newWindow: false, newWindow: false,
isActive: true, isActive: true,
}, },
], ],
}, },
]; ];
export const areSubModulesIn = async () => { export const areSubModulesIn = async () => {
try { try {
for (let i = 0; i < newSubModules.length; i++) { for (let i = 0; i < newSubModules.length; i++) {
const subModuleUpdate = await db const subModuleUpdate = await db
.insert(subModules) .insert(subModules)
.values(newSubModules[i]) .values(newSubModules[i])
.onConflictDoUpdate({ .onConflictDoUpdate({
target: subModules.name, target: subModules.name,
set: { set: {
name: newSubModules[i].name, name: newSubModules[i].name,
moduleName: newSubModules[i].moduleName, moduleName: newSubModules[i].moduleName,
description: newSubModules[i].description, description: newSubModules[i].description,
roles: newSubModules[i].roles, roles: newSubModules[i].roles,
link: newSubModules[i].link, link: newSubModules[i].link,
subSubModule: newSubModules[i].subSubModule, subSubModule: newSubModules[i].subSubModule,
icon: newSubModules[i].icon, icon: newSubModules[i].icon,
}, },
}) // this will only update the ones that are new :D }) // this will only update the ones that are new :D
.returning({ name: subModules.name }); .returning({ name: subModules.name });
} }
createLog( createLog(
"info", "info",
"lst", "lst",
"server", "server",
"SubModules were just added due to missing them on server startup" "SubModules were just added due to missing them on server startup",
); );
} catch (error) { } catch (error) {
console.log(error); console.log(error);
createLog( createLog(
"error", "error",
"lst", "lst",
"server", "server",
"There was an error adding new subModules to the db" "There was an error adding new subModules to the db",
); );
} }
}; };