feat(lstv2 move): moved lstv2 into this app to keep them combined and easier to maintain
This commit is contained in:
74
lstV2/server/globalUtils/apiHits.ts
Normal file
74
lstV2/server/globalUtils/apiHits.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import type { Context } from "hono";
|
||||
import { z, ZodError } from "zod";
|
||||
import { getConnInfo } from "@hono/node-server/conninfo";
|
||||
import { tryCatch } from "./tryCatch.js";
|
||||
import { db } from "../../database/dbclient.js";
|
||||
import { apiHits } from "../../database/schema/apiHits.js";
|
||||
import { sql } from "drizzle-orm";
|
||||
|
||||
// Define the request body schema
|
||||
const requestSchema = z.object({
|
||||
ip: z.string().optional(),
|
||||
endpoint: z.string(),
|
||||
action: z.string().optional(),
|
||||
lastBody: z.array(z.object({})).or(z.object({})).optional(),
|
||||
stats: z.string().optional(),
|
||||
});
|
||||
|
||||
type ApiHitData = z.infer<typeof requestSchema>;
|
||||
|
||||
export const apiHit = async (
|
||||
c: Context,
|
||||
data: ApiHitData
|
||||
): Promise<{ success: boolean; data?: ApiHitData; errors?: any[] }> => {
|
||||
const info = getConnInfo(c);
|
||||
// console.log(`Your remote address is ${info.remote.address}`);
|
||||
try {
|
||||
// Extract IP from request headers or connection info
|
||||
const forwarded = c.req.header("host");
|
||||
|
||||
//console.log(forwarded);
|
||||
// Validate the data
|
||||
const checkData = {
|
||||
ip: info.remote.address!,
|
||||
endpoint: data?.endpoint,
|
||||
lastBody: data?.lastBody,
|
||||
action: data?.action,
|
||||
//stats: data?.stats,
|
||||
};
|
||||
const validatedData = requestSchema.parse(checkData);
|
||||
|
||||
const { data: apitHitData, error } = await tryCatch(
|
||||
db
|
||||
.insert(apiHits)
|
||||
.values(checkData)
|
||||
.onConflictDoUpdate({
|
||||
target: [apiHits.endpoint, apiHits.ip],
|
||||
set: {
|
||||
stats: sql`${apiHits.stats} + 1`,
|
||||
lastBody: data?.lastBody,
|
||||
action: data?.action,
|
||||
upd_date: sql`NOW()`,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
if (error) {
|
||||
console.log(error);
|
||||
}
|
||||
// Proceed with the validated data
|
||||
return { success: true, data: validatedData };
|
||||
} catch (error) {
|
||||
// Explicitly check if the error is an instance of ZodError
|
||||
if (error instanceof ZodError) {
|
||||
console.log({ success: false, errors: error.errors });
|
||||
return { success: false, errors: error.errors };
|
||||
}
|
||||
|
||||
// Catch other unexpected errors
|
||||
return {
|
||||
success: false,
|
||||
errors: [{ message: "An unknown error occurred" }],
|
||||
};
|
||||
}
|
||||
};
|
||||
15
lstV2/server/globalUtils/apiReturn.ts
Normal file
15
lstV2/server/globalUtils/apiReturn.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import type {Context} from "hono";
|
||||
import type {ContentfulStatusCode} from "hono/utils/http-status";
|
||||
|
||||
export const apiReturn = async (
|
||||
c: Context,
|
||||
success: boolean,
|
||||
message: string,
|
||||
data: any,
|
||||
code: ContentfulStatusCode
|
||||
): Promise<Response> => {
|
||||
/**
|
||||
* This is just a global return function to reduce constacnt typing the same thing lol
|
||||
*/
|
||||
return c.json({success, message, data}, code);
|
||||
};
|
||||
15
lstV2/server/globalUtils/appInfo.ts
Normal file
15
lstV2/server/globalUtils/appInfo.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import path from "path";
|
||||
import {createLog} from "../services/logger/logger.js";
|
||||
import fs from "fs";
|
||||
|
||||
export const getAppInfo = async (appLock: string) => {
|
||||
try {
|
||||
const packagePath = path.join(appLock, "package.json");
|
||||
const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf-8"));
|
||||
//const version = packageJson.version;
|
||||
return packageJson;
|
||||
} catch (error) {
|
||||
createLog("error", "lst", "zipUpBuild", `Error in getting the version: ${error}`);
|
||||
return error;
|
||||
}
|
||||
};
|
||||
67
lstV2/server/globalUtils/createSSCC.ts
Normal file
67
lstV2/server/globalUtils/createSSCC.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { query } from "../services/sqlServer/prodSqlServer.js";
|
||||
import { plantInfo } from "../services/sqlServer/querys/dataMart/plantInfo.js";
|
||||
import { createLog } from "../services/logger/logger.js";
|
||||
import { getSettings } from "../services/server/controller/settings/getSettings.js";
|
||||
|
||||
export const createSSCC = async (runningNumber: number) => {
|
||||
// get the token
|
||||
|
||||
let serverSettings = (await getSettings()) as any;
|
||||
const plantToken = serverSettings?.filter(
|
||||
(n: any) => n.name === "plantToken"
|
||||
);
|
||||
// const plantToken = await db
|
||||
// .select()
|
||||
// .from(settings)
|
||||
// .where(eq(settings.name, "plantToken"));
|
||||
let global: any = []; // get from plant address in basis enter the entire string here.
|
||||
|
||||
try {
|
||||
const res: any = await query(
|
||||
plantInfo.replaceAll("[token]", plantToken[0].value),
|
||||
"plantInfo"
|
||||
);
|
||||
global = res.data;
|
||||
} catch (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
"globalUtils",
|
||||
`There was an error getting the GLN: Error: ${error}`
|
||||
);
|
||||
}
|
||||
|
||||
// create the sscc without the check diget and make sure we have it all correct
|
||||
|
||||
let step1SSCC =
|
||||
global[0].gln.toString().slice(0, 7).padStart(10, "0") +
|
||||
runningNumber.toString().padStart(9, "0");
|
||||
|
||||
let sum = 0;
|
||||
for (let i = 0; i < step1SSCC.length; i++) {
|
||||
let digit = parseInt(step1SSCC[i], 10);
|
||||
if (i % 2 === 0) {
|
||||
// Even index in 0-based index system means odd position in 1-based index system
|
||||
sum += digit * 3;
|
||||
} else {
|
||||
sum += digit;
|
||||
}
|
||||
}
|
||||
let checkDigit = (10 - (sum % 10)) % 10;
|
||||
|
||||
// make sure the check digit is never 10
|
||||
|
||||
// let checkDigit = 10 - ((oddSum * 3 + evenSum) % 10) === 10 ? 0 : (oddSum * 3 + evenSum) % 10;
|
||||
|
||||
// return the true sscc
|
||||
let sscc = step1SSCC + checkDigit;
|
||||
// console.log(step1SSCC);
|
||||
// console.log(checkDigit);
|
||||
return sscc.padStart(20, "0");
|
||||
};
|
||||
|
||||
// let rn = 518475;
|
||||
|
||||
// console.log(`Creating sscc`);
|
||||
// let sscc = await createSSCC(rn);
|
||||
// console.log(sscc);
|
||||
44
lstV2/server/globalUtils/createUrl.ts
Normal file
44
lstV2/server/globalUtils/createUrl.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../../database/dbclient.js";
|
||||
|
||||
import { getSettings } from "../services/server/controller/settings/getSettings.js";
|
||||
|
||||
// create the test server stuff
|
||||
const testServers = [
|
||||
{ token: "test1", port: 8940 },
|
||||
{ token: "test2", port: 8941 },
|
||||
{ token: "test3", port: 8942 },
|
||||
];
|
||||
|
||||
export const prodEndpointCreation = async (endpoint: string) => {
|
||||
let url = "";
|
||||
//get the plant token
|
||||
let serverSettings = await getSettings();
|
||||
const plantToken = serverSettings?.filter((n) => n.name === "plantToken");
|
||||
// await db
|
||||
// .select()
|
||||
// .from(settings)
|
||||
// .where(eq(settings.name, "plantToken"));
|
||||
|
||||
// check if we are a test server
|
||||
const testServer = testServers.some(
|
||||
(server) => server.token === plantToken[0]?.value
|
||||
);
|
||||
const server = serverSettings?.filter((n) => n.name === "dbServer");
|
||||
// await db
|
||||
// .select()
|
||||
// .from(settings)
|
||||
// .where(eq(settings.name, "dbServer"));
|
||||
|
||||
if (testServer) {
|
||||
//filter out what testserver we are
|
||||
const test = testServers.filter((t) => t.token === plantToken[0].value);
|
||||
// "https://usmcd1vms036.alpla.net:8942/application/public/v1.0/DemandManagement/ORDERS"
|
||||
// "https://usmcd1vms036.alpla.net:8492/application/public/v1.0/DemandManagement/ORDERS"
|
||||
url = `https://${server[0]?.value}.alpla.net:${test[0]?.port}/application${endpoint}`;
|
||||
return url;
|
||||
} else {
|
||||
url = `https://${plantToken[0]?.value}prod.alpla.net/application${endpoint}`;
|
||||
return url;
|
||||
}
|
||||
};
|
||||
23
lstV2/server/globalUtils/dbCleanUp/labelCleanUp.ts
Normal file
23
lstV2/server/globalUtils/dbCleanUp/labelCleanUp.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { lt } from "drizzle-orm";
|
||||
import { db } from "../../../database/dbclient.js";
|
||||
import { prodlabels } from "../../../database/schema/prodLabels.js";
|
||||
import { addDays } from "date-fns";
|
||||
import { createLog } from "../../services/logger/logger.js";
|
||||
|
||||
export const deleteLabels = async () => {
|
||||
/**
|
||||
* Deletes labels older than 90 days from lst... all label data can be found in alpla prod.
|
||||
*/
|
||||
try {
|
||||
await db
|
||||
.delete(prodlabels)
|
||||
.where(lt(prodlabels.upd_date, addDays(new Date(Date.now()), -90)));
|
||||
} catch (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"labeling",
|
||||
"ocp",
|
||||
`Error deleting labels older than 90 days`
|
||||
);
|
||||
}
|
||||
};
|
||||
3
lstV2/server/globalUtils/delay.ts
Normal file
3
lstV2/server/globalUtils/delay.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const delay = (ms: number) => {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
};
|
||||
69
lstV2/server/globalUtils/freightClass.ts
Normal file
69
lstV2/server/globalUtils/freightClass.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
export const freightClass = (
|
||||
weight: number,
|
||||
length: number,
|
||||
width: number,
|
||||
height: number
|
||||
) => {
|
||||
// mm to in conversion
|
||||
const convertMM = 25.4;
|
||||
|
||||
const convertKG = 2.20462;
|
||||
// Inputs
|
||||
const weightPounds = weight * convertKG;
|
||||
const lengthInches = length / convertMM;
|
||||
const widthInches = width / convertMM;
|
||||
const heightInches = height / convertMM;
|
||||
|
||||
// Calculate volume in cubic inches
|
||||
const volumeCubicInches = lengthInches * widthInches * heightInches;
|
||||
|
||||
// Convert cubic inches to cubic feet
|
||||
const volumeCubicFeet = volumeCubicInches / 1728;
|
||||
|
||||
// Calculate density
|
||||
const density = weightPounds / volumeCubicFeet;
|
||||
|
||||
// Determine freight class
|
||||
let freightClass;
|
||||
|
||||
if (density >= 50) {
|
||||
freightClass = 50;
|
||||
} else if (density >= 35) {
|
||||
freightClass = 55;
|
||||
} else if (density >= 30) {
|
||||
freightClass = 60;
|
||||
} else if (density >= 22.5) {
|
||||
freightClass = 65;
|
||||
} else if (density >= 15) {
|
||||
freightClass = 70;
|
||||
} else if (density >= 13.5) {
|
||||
freightClass = 77.5;
|
||||
} else if (density >= 12) {
|
||||
freightClass = 85;
|
||||
} else if (density >= 10.5) {
|
||||
freightClass = 92.5;
|
||||
} else if (density >= 9) {
|
||||
freightClass = 100;
|
||||
} else if (density >= 8) {
|
||||
freightClass = 110;
|
||||
} else if (density >= 7) {
|
||||
freightClass = 125;
|
||||
} else if (density >= 6) {
|
||||
freightClass = 150;
|
||||
} else if (density >= 5) {
|
||||
freightClass = 175;
|
||||
} else if (density >= 4) {
|
||||
freightClass = 200;
|
||||
} else if (density >= 3) {
|
||||
freightClass = 250;
|
||||
} else if (density >= 2) {
|
||||
freightClass = 300;
|
||||
} else if (density >= 1) {
|
||||
freightClass = 400;
|
||||
} else {
|
||||
freightClass = 500;
|
||||
}
|
||||
|
||||
// Output the freight class
|
||||
return freightClass;
|
||||
};
|
||||
12
lstV2/server/globalUtils/greetingEmail.ts
Normal file
12
lstV2/server/globalUtils/greetingEmail.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { getHours } from "date-fns";
|
||||
export const greetingStuff = async (date = new Date()) => {
|
||||
const hour = getHours(date);
|
||||
|
||||
if (hour < 12) {
|
||||
return "Good morning";
|
||||
} else if (hour < 18) {
|
||||
return "Good afternoon";
|
||||
} else {
|
||||
return "Good evening";
|
||||
}
|
||||
};
|
||||
65
lstV2/server/globalUtils/pingServer.ts
Normal file
65
lstV2/server/globalUtils/pingServer.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import dns from "dns";
|
||||
import net from "net";
|
||||
|
||||
// Usage example
|
||||
//const hostnamePort = "example.com:80"; // Replace with your hostname:port
|
||||
//checkHostnamePort(hostnamePort);
|
||||
|
||||
// Function to resolve a hostname to an IP address
|
||||
export function resolveHostname(hostname: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
dns.lookup(hostname, (err, address) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(address);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Function to check if a port is open
|
||||
export function checkPort(ip: string, port: number): Promise<boolean> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const socket = new net.Socket();
|
||||
|
||||
socket.setTimeout(2000); // Set a timeout for the connection attempt
|
||||
|
||||
socket.on("connect", () => {
|
||||
socket.destroy(); // Close the connection
|
||||
resolve(true); // Port is open
|
||||
});
|
||||
|
||||
socket.on("timeout", () => {
|
||||
socket.destroy(); // Close the connection
|
||||
reject(new Error("Connection timed out")); // Port is not reachable
|
||||
});
|
||||
|
||||
socket.on("error", (err: any) => {
|
||||
reject(new Error(`Unknown error: ${err}`)); // Handle non-Error types
|
||||
});
|
||||
|
||||
socket.connect(port, ip);
|
||||
});
|
||||
}
|
||||
|
||||
// Main function to check hostname:port
|
||||
export async function checkHostnamePort(hostnamePort: string): Promise<boolean> {
|
||||
try {
|
||||
// Split the input into hostname and port
|
||||
const [hostname, port] = hostnamePort.split(":");
|
||||
if (!hostname || !port) {
|
||||
return false; // Invalid format
|
||||
}
|
||||
|
||||
// Resolve the hostname to an IP address
|
||||
const ip = (await resolveHostname(hostname)) as string;
|
||||
|
||||
// Check if the port is open
|
||||
const portCheck = await checkPort(ip, parseInt(port, 10));
|
||||
|
||||
return true; // Hostname:port is reachable
|
||||
} catch (err) {
|
||||
return false; // Any error means the hostname:port is not reachable
|
||||
}
|
||||
}
|
||||
77
lstV2/server/globalUtils/rateLimiter.ts
Normal file
77
lstV2/server/globalUtils/rateLimiter.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { Hono } from "hono";
|
||||
import { type Context, type Next } from "hono";
|
||||
|
||||
const app = new Hono();
|
||||
|
||||
// --- In-Memory Store for Rate Limits ---
|
||||
// This Map will store when each user/key last accessed a rate-limited endpoint.
|
||||
// Key: string (e.g., 'ip_address' or 'user_id_endpoint')
|
||||
// Value: number (timestamp of last access in milliseconds)
|
||||
const rateLimitStore = new Map<string, number>();
|
||||
|
||||
// --- Configuration ---
|
||||
const FIFTEEN_MINUTES_MS = 5 * 60 * 1000; // 15 minutes in milliseconds
|
||||
|
||||
// --- Rate Limiting Middleware ---
|
||||
export const simpleRateLimit = async (c: Context, next: Next) => {
|
||||
// 1. Define a unique key for the rate limit
|
||||
// For simplicity, we'll use a placeholder for user identification.
|
||||
// In a real app:
|
||||
// - If unauthenticated: Use c.req.header('x-forwarded-for') or c.req.ip (if configured/available)
|
||||
// - If authenticated: Get user ID from c.req.user or similar after authentication middleware
|
||||
const userIdentifier = c.req.header("x-forwarded-for") || "anonymous_user"; // Basic IP-like identifier
|
||||
|
||||
// You can also make the key specific to the route to have different limits per route
|
||||
const routeKey = `${userIdentifier}:${c.req.path}`;
|
||||
|
||||
const now = Date.now();
|
||||
const lastAccessTime = rateLimitStore.get(routeKey);
|
||||
|
||||
if (lastAccessTime) {
|
||||
const timeElapsed = now - lastAccessTime;
|
||||
|
||||
if (timeElapsed < FIFTEEN_MINUTES_MS) {
|
||||
// Limit exceeded
|
||||
const timeRemainingMs = FIFTEEN_MINUTES_MS - timeElapsed;
|
||||
const timeRemainingSeconds = Math.ceil(timeRemainingMs / 1000);
|
||||
|
||||
c.status(429); // HTTP 429: Too Many Requests
|
||||
return c.json({
|
||||
error: "Too Many Requests",
|
||||
message: `Please wait ${timeRemainingSeconds} seconds before trying again.`,
|
||||
retryAfter: timeRemainingSeconds, // Standard header for rate limiting clients
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// If no previous access, or the 15 minutes have passed, allow the request
|
||||
// and update the last access time.
|
||||
rateLimitStore.set(routeKey, now);
|
||||
|
||||
// Continue to the next middleware or route handler
|
||||
await next();
|
||||
};
|
||||
|
||||
// --- Apply the Middleware to Specific Routes ---
|
||||
|
||||
app.get("/", (c) => {
|
||||
return c.text("Welcome! This is a public endpoint.");
|
||||
});
|
||||
|
||||
// This endpoint will be rate-limited
|
||||
app.get("/privileged", simpleRateLimit, (c) => {
|
||||
return c.text("You successfully accessed the privileged endpoint!");
|
||||
});
|
||||
|
||||
// Another rate-limited endpoint
|
||||
app.post("/submit-data", simpleRateLimit, async (c) => {
|
||||
// In a real app, you'd process form data or JSON here
|
||||
return c.text("Data submitted successfully (rate-limited).");
|
||||
});
|
||||
|
||||
// Example of an endpoint that is NOT rate-limited
|
||||
app.get("/health", (c) => {
|
||||
return c.text("Server is healthy!");
|
||||
});
|
||||
|
||||
export default app;
|
||||
8
lstV2/server/globalUtils/routeDefs/options.ts
Normal file
8
lstV2/server/globalUtils/routeDefs/options.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export const apiOptions = () => {
|
||||
return {
|
||||
tags: ["rfid"],
|
||||
summary: "Add new reader",
|
||||
method: "post",
|
||||
path: "/addreader",
|
||||
};
|
||||
};
|
||||
45
lstV2/server/globalUtils/routeDefs/responses.ts
Normal file
45
lstV2/server/globalUtils/routeDefs/responses.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import {z} from "@hono/zod-openapi";
|
||||
|
||||
const responseSchema = z.object({
|
||||
success: z.boolean().openapi({example: true}),
|
||||
message: z.string().optional(),
|
||||
data: z
|
||||
.array(z.object({}).optional())
|
||||
.optional()
|
||||
.openapi({example: [{data: "hi"}]}),
|
||||
});
|
||||
|
||||
export const responses = () => {
|
||||
return {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {schema: responseSchema},
|
||||
},
|
||||
description: "Response message",
|
||||
},
|
||||
400: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({message: z.string().optional().openapi({example: "Internal Server error"})}),
|
||||
},
|
||||
},
|
||||
description: "Internal Server Error",
|
||||
},
|
||||
401: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({message: z.string().optional().openapi({example: "Unauthenticated"})}),
|
||||
},
|
||||
},
|
||||
description: "Unauthorized",
|
||||
},
|
||||
500: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({message: z.string().optional().openapi({example: "Internal Server error"})}),
|
||||
},
|
||||
},
|
||||
description: "Internal Server Error",
|
||||
},
|
||||
};
|
||||
};
|
||||
49
lstV2/server/globalUtils/routeDefs/returnRes.ts
Normal file
49
lstV2/server/globalUtils/routeDefs/returnRes.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { createLog } from "../../services/logger/logger.js";
|
||||
|
||||
export function returnRes<T>(
|
||||
success: true,
|
||||
message: string,
|
||||
service: string,
|
||||
user: string,
|
||||
level: "info" | "error",
|
||||
data: T
|
||||
): { success: true; message: string; data: T };
|
||||
|
||||
export function returnRes<T>(
|
||||
success: false,
|
||||
message: string,
|
||||
service: string,
|
||||
user: string,
|
||||
level: "info" | "error",
|
||||
data?: T
|
||||
): { success: false; message: string; error: T | string };
|
||||
|
||||
export function returnRes<T>(
|
||||
success: boolean,
|
||||
message: string,
|
||||
service: string,
|
||||
user: string,
|
||||
level: "info" | "error",
|
||||
data?: T
|
||||
) {
|
||||
createLog(level, user, service, message);
|
||||
|
||||
if (success) {
|
||||
return { success: true, message, data: data as T };
|
||||
} else {
|
||||
return {
|
||||
success: false,
|
||||
message,
|
||||
error: data ?? "An unknown error occurred",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// export const returnApi = (c:Context,success: boolean, message: string, data?: any, code: number)=>{
|
||||
// /**
|
||||
// * just a simple return to reduce the typing and make sure we are always consitant with our returns.
|
||||
// *
|
||||
// * data can be an error as well.
|
||||
// */
|
||||
// return c.json({success, message, data}, code);
|
||||
// }
|
||||
92
lstV2/server/globalUtils/runProdApi.ts
Normal file
92
lstV2/server/globalUtils/runProdApi.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import axios from "axios";
|
||||
import { prodEndpointCreation } from "./createUrl.js";
|
||||
import { tryCatch } from "./tryCatch.js";
|
||||
import { createLog } from "../services/logger/logger.js";
|
||||
|
||||
type bodyData = any;
|
||||
|
||||
type Data = {
|
||||
endpoint: string;
|
||||
data: bodyData[];
|
||||
};
|
||||
export const runProdApi = async (data: Data) => {
|
||||
/**
|
||||
* Detachs a silo
|
||||
*/
|
||||
|
||||
let url = await prodEndpointCreation(data.endpoint);
|
||||
|
||||
const { data: d, error } = await tryCatch(
|
||||
axios.post(url, data.data[0], {
|
||||
headers: {
|
||||
"X-API-Key": process.env.TEC_API_KEY || "",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
let e = error as any;
|
||||
if (e) {
|
||||
//console.log(e.response);
|
||||
if (e.status === 401) {
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
"logistics",
|
||||
`Not autorized: ${JSON.stringify(e.response?.data)}`
|
||||
);
|
||||
const data = {
|
||||
success: false,
|
||||
message: `Not autorized: ${JSON.stringify(e.response?.data)}`,
|
||||
data: {
|
||||
status: e.response?.status,
|
||||
statusText: e.response?.statusText,
|
||||
data: e.response?.data,
|
||||
},
|
||||
};
|
||||
return data;
|
||||
} else {
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
"logistics",
|
||||
`There was an error processing the endpoint: ${JSON.stringify(
|
||||
e.response?.data
|
||||
)}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: `There was an error processing the endpoint: ${JSON.stringify(
|
||||
e.response?.data
|
||||
)}`,
|
||||
data: {
|
||||
status: e.response?.status,
|
||||
statusText: e.response?.statusText,
|
||||
data: e.response?.data,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (d?.status !== 200) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Error processing endpoint",
|
||||
data: {
|
||||
status: d?.status,
|
||||
statusText: d?.statusText,
|
||||
data: d?.data,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
success: true,
|
||||
message: "Endpoint was processed",
|
||||
data: {
|
||||
status: d.status,
|
||||
statusText: d.statusText,
|
||||
data: d.data,
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
11
lstV2/server/globalUtils/singleUseKey.ts
Normal file
11
lstV2/server/globalUtils/singleUseKey.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import crypto from "crypto";
|
||||
|
||||
export const generateOneTimeKey = async (length = 32) => {
|
||||
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
let key = "";
|
||||
const bytes = crypto.randomBytes(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
key += chars[bytes[i] % chars.length];
|
||||
}
|
||||
return key.match(/.{1,4}/g)!.join("-"); // group by 4 chars
|
||||
};
|
||||
13
lstV2/server/globalUtils/timeZoneFix.ts
Normal file
13
lstV2/server/globalUtils/timeZoneFix.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { addHours } from "date-fns";
|
||||
|
||||
export const timeZoneFix = () => {
|
||||
/**
|
||||
* Returns iso date based on current timezone.
|
||||
*/
|
||||
const rawDate = new Date(Date.now()).toISOString();
|
||||
const offsetMinutes = new Date().getTimezoneOffset(); // in minutes
|
||||
const offsetHours =
|
||||
-offsetMinutes / 60 >= 0 ? offsetMinutes / 60 : -offsetMinutes / 60;
|
||||
|
||||
return addHours(rawDate, offsetHours).toISOString();
|
||||
};
|
||||
24
lstV2/server/globalUtils/tryCatch.ts
Normal file
24
lstV2/server/globalUtils/tryCatch.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
// Types for the result object with discriminated union
|
||||
type Success<T> = {
|
||||
data: T;
|
||||
error: null;
|
||||
};
|
||||
|
||||
type Failure<E> = {
|
||||
data: null;
|
||||
error: E;
|
||||
};
|
||||
|
||||
type Result<T, E = Error> = Success<T> | Failure<E>;
|
||||
|
||||
// Main wrapper function
|
||||
export async function tryCatch<T, E = Error>(
|
||||
promise: Promise<T>
|
||||
): Promise<Result<T, E>> {
|
||||
try {
|
||||
const data = await promise;
|
||||
return { data, error: null };
|
||||
} catch (error) {
|
||||
return { data: null, error: error as E };
|
||||
}
|
||||
}
|
||||
241
lstV2/server/index.ts
Normal file
241
lstV2/server/index.ts
Normal file
@@ -0,0 +1,241 @@
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
||||
import { serve } from "@hono/node-server";
|
||||
import { OpenAPIHono } from "@hono/zod-openapi";
|
||||
|
||||
import { serveStatic } from "@hono/node-server/serve-static";
|
||||
import { logger } from "hono/logger";
|
||||
import { cors } from "hono/cors";
|
||||
import { createLog } from "./services/logger/logger.js";
|
||||
|
||||
// custom routes
|
||||
import scalar from "./services/general/route/scalar.js";
|
||||
import system from "./services/server/systemServer.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 ocpService from "./services/ocp/ocpService.js";
|
||||
import { db } from "../database/dbclient.js";
|
||||
import { settings } from "../database/schema/settings.js";
|
||||
import os from "os";
|
||||
import { tryCatch } from "./globalUtils/tryCatch.js";
|
||||
import { sendEmail } from "./services/notifications/controller/sendMail.js";
|
||||
import notify from "./services/notifications/notifyService.js";
|
||||
import eom from "./services/eom/eomService.js";
|
||||
import dataMart from "./services/dataMart/dataMartService.js";
|
||||
import qualityRequest from "./services/quality/qualityService.js";
|
||||
import produser from "./services/prodUser/prodUser.js";
|
||||
import {
|
||||
getSettings,
|
||||
serverSettings,
|
||||
} from "./services/server/controller/settings/getSettings.js";
|
||||
import type { Settings } from "./types/settings.js";
|
||||
|
||||
// create the main prodlogin here
|
||||
const username = "lst_user";
|
||||
const password = "Alpla$$Prod";
|
||||
export const lstAuth = btoa(`${username}:${password}`);
|
||||
|
||||
// checking to make sure we have the settings intialized
|
||||
// const { data: settingsData, error: settingError } = await tryCatch(
|
||||
// db.select().from(settings)
|
||||
// );
|
||||
|
||||
// if (settingError) {
|
||||
// throw Error("Error getting settings from the db. critical error.");
|
||||
// }
|
||||
|
||||
const serverIntialized: any = await getSettings();
|
||||
|
||||
export const installed =
|
||||
serverIntialized.length === 0 && process.env.NODE_ENV !== "development"
|
||||
? false
|
||||
: true;
|
||||
createLog("info", "LST", "server", `Server is installed: ${installed}`);
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
|
||||
// middle ware
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
app.use("*", logger());
|
||||
}
|
||||
|
||||
app.use(
|
||||
"*",
|
||||
cors({
|
||||
origin: "*", // 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
|
||||
// app.use("*", async (c, next) => {
|
||||
// // const lowercasedUrl = c.req.url.toLowerCase();
|
||||
// console.log("Incoming Request:", c.req.url, c.req.method);
|
||||
// // // If the URL is already lowercase, continue as usual
|
||||
// // if (c.req.url === lowercasedUrl) {
|
||||
// await next();
|
||||
// // }
|
||||
|
||||
// // // Otherwise, re-route internally
|
||||
// // return c.redirect(lowercasedUrl, 308); // 308 preserves the HTTP method
|
||||
// });
|
||||
|
||||
app.doc("/api/ref", {
|
||||
openapi: "3.0.0",
|
||||
info: {
|
||||
version: "2.0.0",
|
||||
title: "LST API",
|
||||
},
|
||||
});
|
||||
|
||||
const routes = [
|
||||
scalar,
|
||||
auth,
|
||||
// apiHits,
|
||||
system,
|
||||
tcpServer,
|
||||
sqlService,
|
||||
logistics,
|
||||
rfid,
|
||||
printers,
|
||||
loggerService,
|
||||
ocpService,
|
||||
notify,
|
||||
eom,
|
||||
dataMart,
|
||||
qualityRequest,
|
||||
produser,
|
||||
] as const;
|
||||
|
||||
const appRoutes = routes.forEach((route) => {
|
||||
app.route("/api/", route);
|
||||
});
|
||||
|
||||
app.route("/ocme/", ocme);
|
||||
|
||||
//--------------- lst v1 proxy ----------------------\\
|
||||
// app.all("/api/v1/*", (c) => {
|
||||
// const path = c.req.path.replace("/api/v1/", ""); // Extract the subpath
|
||||
// const query = c.req.query() ? "?" + new URLSearchParams(c.req.query()).toString() : ""; // Get query params
|
||||
// return proxy(`http://localhost:4900/${path}${query}`, {
|
||||
// headers: {
|
||||
// ...c.req.header(),
|
||||
// "X-Forwarded-For": "127.0.0.1",
|
||||
// "X-Forwarded-Host": c.req.header("host"),
|
||||
// },
|
||||
// });
|
||||
// });
|
||||
|
||||
// app.all("/system/*", (c) => {
|
||||
// const path = c.req.path.replace("/system/", ""); // Extract the subpath
|
||||
// const query = c.req.query() ? "?" + new URLSearchParams(c.req.query()).toString() : ""; // Get query params
|
||||
// return proxy(`http://localhost:4200/${path}${query}`, {
|
||||
// headers: {
|
||||
// ...c.req.header(),
|
||||
// "X-Forwarded-For": "127.0.0.1",
|
||||
// "X-Forwarded-Host": c.req.header("host"),
|
||||
// },
|
||||
// });
|
||||
// });
|
||||
|
||||
//---------------------------------------------------\\
|
||||
|
||||
// the catch all api route
|
||||
app.all("/api/*", (c) => c.json({ error: "API route not found" }, 404));
|
||||
|
||||
// front end static files
|
||||
app.use("/*", serveStatic({ root: "./frontend/dist" }));
|
||||
app.use("*", serveStatic({ path: "./frontend/dist/index.html" }));
|
||||
|
||||
// Handle app exit signals
|
||||
process.on("SIGINT", async () => {
|
||||
console.log("\nGracefully shutting down...");
|
||||
//await closePool();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
process.on("SIGTERM", async () => {
|
||||
console.log("Received termination signal, closing database...");
|
||||
//await closePool();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
process.on("uncaughtException", async (err) => {
|
||||
console.log("Uncaught Exception:", err);
|
||||
//await closePool();
|
||||
const emailData = {
|
||||
email: "blake.matthes@alpla.com", // should be moved to the db so it can be reused.
|
||||
subject: `${os.hostname()} has just encountered a crash.`,
|
||||
template: "serverCrash",
|
||||
context: {
|
||||
error: err,
|
||||
plant: `${os.hostname()}`,
|
||||
},
|
||||
};
|
||||
|
||||
await sendEmail(emailData);
|
||||
//process.exit(1);
|
||||
});
|
||||
|
||||
process.on("beforeExit", async () => {
|
||||
console.log("Process is about to exit...");
|
||||
//await closePool();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
const port =
|
||||
process.env.NODE_ENV === "development"
|
||||
? process.env.VITE_SERVER_PORT
|
||||
: process.env.PROD_PORT;
|
||||
|
||||
const ocmeport = process.env.OCME_PORT;
|
||||
serve(
|
||||
{
|
||||
fetch: app.fetch,
|
||||
port: Number(port),
|
||||
hostname: "0.0.0.0",
|
||||
},
|
||||
(info) => {
|
||||
createLog(
|
||||
"info",
|
||||
"LST",
|
||||
"server",
|
||||
`Server is running on http://${info.address}:${info.port}`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Only for ocme until we get them switched over to the single port setup.
|
||||
*/
|
||||
// const setting = await db.select().from(settings);
|
||||
const setting = serverSettings;
|
||||
const isActive = setting.filter((n) => n.name === "ocmeService");
|
||||
if (ocmeport && isActive[0]?.value === "1") {
|
||||
serve(
|
||||
{
|
||||
fetch: app.fetch,
|
||||
port: Number(ocmeport),
|
||||
hostname: "0.0.0.0",
|
||||
},
|
||||
(info) => {
|
||||
createLog(
|
||||
"info",
|
||||
"LST",
|
||||
"server",
|
||||
`Ocme section is listening on http://${info.address}:${info.port}`
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export type AppRoutes = typeof appRoutes;
|
||||
35
lstV2/server/scripts/build.ps1
Normal file
35
lstV2/server/scripts/build.ps1
Normal file
@@ -0,0 +1,35 @@
|
||||
param (
|
||||
[string]$dir,
|
||||
[string]$app
|
||||
)
|
||||
# dir is the location of the root folder.
|
||||
|
||||
# Store the original directory
|
||||
$originalDir = Get-Location
|
||||
|
||||
Write-Host $originalDir
|
||||
|
||||
# Check if the directory is provided
|
||||
if (-not $dir) {
|
||||
Write-Host "Error: Directory parameter is required."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check if the directory exists
|
||||
if (-not (Test-Path $dir)) {
|
||||
Write-Host "Error: Directory '$dir' does not exist."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Navigate to the directory
|
||||
Set-Location -Path $dir
|
||||
|
||||
# Run npm run build
|
||||
Write-Host "Running 'npm run build' in directory: $dir"
|
||||
npm run build
|
||||
|
||||
Write-Host "Build completed successfully."
|
||||
|
||||
# Restore the original directory
|
||||
Set-Location -Path $originalDir
|
||||
exit 0
|
||||
65
lstV2/server/scripts/copyScripts.ts
Normal file
65
lstV2/server/scripts/copyScripts.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import fs from "fs-extra";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
// Get the current directory of the module
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
const sourceDir = path.join(__dirname, "/");
|
||||
const destDir = path.join("./", "dist", "server", "scripts");
|
||||
|
||||
// Copy only .ps1 files
|
||||
fs.readdir(sourceDir)
|
||||
.then((files) => {
|
||||
files.forEach((file) => {
|
||||
if (path.extname(file) === ".ps1") {
|
||||
const sourceFile = path.join(sourceDir, file);
|
||||
const destFile = path.join(destDir, file);
|
||||
|
||||
// Copy each .ps1 file
|
||||
fs.copy(sourceFile, destFile)
|
||||
.then(() => {
|
||||
console.log(`Copied: ${file}`);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(`Error copying file: ${file}`, err);
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Error reading source directory:", err);
|
||||
});
|
||||
|
||||
// Paths for source and destination of serverData.json
|
||||
const sourceFile = path.join(
|
||||
"./",
|
||||
"server",
|
||||
"services",
|
||||
"server",
|
||||
"utils",
|
||||
"serverData.json"
|
||||
);
|
||||
const serverDataDest = path.join(
|
||||
"./",
|
||||
"dist",
|
||||
"server",
|
||||
"services",
|
||||
"server",
|
||||
"utils"
|
||||
);
|
||||
const destFile = path.join(serverDataDest, "serverData.json");
|
||||
|
||||
// Ensure the destination directory exists
|
||||
fs.ensureDir(destDir)
|
||||
.then(() => {
|
||||
// Copy the serverData.json file
|
||||
return fs.copy(sourceFile, destFile);
|
||||
})
|
||||
.then(() => {
|
||||
console.log("serverData.json copied successfully!");
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Error copying serverData.json:", err);
|
||||
});
|
||||
61
lstV2/server/scripts/copyToLst.ps1
Normal file
61
lstV2/server/scripts/copyToLst.ps1
Normal file
@@ -0,0 +1,61 @@
|
||||
param(
|
||||
[string]$IncludesFile = ".includes",
|
||||
[string]$Destination = "C:\Users\matthes01\Documents\lst\lstV2",
|
||||
[string]$BaseDir = "C:\Users\matthes01\Documents\lst"
|
||||
)
|
||||
|
||||
# .\copy-includes.ps1 will run with defaults
|
||||
# .\copy-includes.ps1 -IncludesFile ".\mylist.txt" -Destination "D:\build\lstV2" will override defaults
|
||||
|
||||
if (-Not (Test-Path $IncludesFile)) {
|
||||
Write-Error "Includes file not found: $IncludesFile"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Ensure destination exists
|
||||
if (!(Test-Path -Path $Destination)) {
|
||||
New-Item -ItemType Directory -Path $Destination | Out-Null
|
||||
Write-Host "Folder created: $Destination"
|
||||
}
|
||||
|
||||
# Empty the destination folder
|
||||
Get-ChildItem -Path $Destination -Recurse -Force | Remove-Item -Recurse -Force
|
||||
|
||||
# If BaseDir wasn’t explicitly passed in, use IncludesFile directory
|
||||
if (-not $PSBoundParameters.ContainsKey('BaseDir')) {
|
||||
$BaseDir = Split-Path -Parent (Resolve-Path $IncludesFile)
|
||||
}
|
||||
|
||||
# Read includes list (ignore blank lines & comments)
|
||||
$items = Get-Content $IncludesFile |
|
||||
ForEach-Object { $_.Trim() } |
|
||||
Where-Object { $_ -and -not $_.StartsWith("#") }
|
||||
|
||||
foreach ($item in $items) {
|
||||
if ([System.IO.Path]::IsPathRooted($item)) {
|
||||
# Absolute path (rare case)
|
||||
$sourcePath = $item
|
||||
$relative = Split-Path $item -Leaf # just take folder/file name
|
||||
} else {
|
||||
# Relative to BaseDir
|
||||
$sourcePath = Join-Path $BaseDir $item
|
||||
$relative = $item # keep full relative path e.g. "frontend\dist"
|
||||
}
|
||||
|
||||
if (-Not (Test-Path $sourcePath)) {
|
||||
Write-Warning "Skipping missing path: $sourcePath"
|
||||
continue
|
||||
}
|
||||
|
||||
# Destination path should preserve the relative structure
|
||||
$targetPath = Join-Path $Destination $relative
|
||||
|
||||
# Ensure the parent folder exists
|
||||
$targetDir = Split-Path $targetPath -Parent
|
||||
if (-not (Test-Path $targetDir)) {
|
||||
New-Item -ItemType Directory -Path $targetDir -Force | Out-Null
|
||||
}
|
||||
|
||||
Write-Host "Copying $sourcePath -> $targetPath" -ForegroundColor Cyan
|
||||
Copy-Item -Path $sourcePath -Destination $targetPath -Recurse -Force
|
||||
}
|
||||
205
lstV2/server/scripts/install.md
Normal file
205
lstV2/server/scripts/install.md
Normal file
@@ -0,0 +1,205 @@
|
||||
# Install
|
||||
|
||||
## Files needed to be downloaded before install.
|
||||
|
||||
### To run the server
|
||||
|
||||
- [PostgresSQL](https://www.postgresql.org/download/windows/) - current version using is 17
|
||||
- [NodeJS](https://nodejs.org)
|
||||
- [NSSM](https://nssm.cc/)
|
||||
|
||||
### To manage the server
|
||||
|
||||
- [VSCODE](https://code.visualstudio.com/)
|
||||
- [Postman](https://www.postman.com/downloads/)
|
||||
|
||||
## Creating directories needed
|
||||
|
||||
- Create a new folder where we will host the server files.
|
||||
- Copy the nssm.exe into this folder
|
||||
- Copy the build files to the server (only needed for intial install).
|
||||
- This will house all the compiles and minified files needed to start the server up, this includes the frontend.
|
||||
- Save the nssm.exe into this folder as well, this will be used to control the service.
|
||||
|
||||
## Do the intial install
|
||||
|
||||
### DB instal setup
|
||||
|
||||
1. Install postgres
|
||||
2. Open pgAdmin
|
||||
3. create a new Database named lst_db
|
||||
|
||||
### Intial server setup
|
||||
|
||||
1. Open VSCode and navigate to the folder where you extracted the files.
|
||||
2. Click trusted when it pops up.
|
||||
3. Open a terminal window inside vscode.
|
||||
4. Run the install script this will install all dependaceys needed as well as do all the database migrations
|
||||
|
||||
```bash
|
||||
npm run prodinstall
|
||||
```
|
||||
|
||||
Next we want to do an intial build for the db
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Create the .env file
|
||||
|
||||
In the root of the folder create a new .env file
|
||||
add in the below and change each setting area that says change me to something that suits your needs
|
||||
|
||||
```env
|
||||
# PORTS
|
||||
PROD_PORT=4000
|
||||
# To keep it all simple we will pass VITE to the ports that are used on both sides.
|
||||
VITE_SERVER_PORT=4000
|
||||
|
||||
# logLevel
|
||||
LOG_LEVEL=info
|
||||
# Auth stuff
|
||||
SALTING=12
|
||||
SECRET=CHANGEME
|
||||
JWT_SECRET=CHANGEME
|
||||
JWT_REFRESH_SECRET=CHANGEME
|
||||
|
||||
# Expire info plus refresh change as needed
|
||||
JWT_EXPIRES=60
|
||||
JWT_REFRESH_THRESHOLD=30
|
||||
JWT_ACCESS_EXPIRATION="1h"
|
||||
JWT_REFRESH_EXPIRATION="7d"
|
||||
|
||||
# this code will need to be used when a user needs to have access to everything.
|
||||
SECRETOVERRIDECODE="supersecretKey"
|
||||
|
||||
# Database url - please change the password if this is all you changed
|
||||
DATABASE_URL="postgresql://postgres:PASSWORD@localhost:5432/lst_db"
|
||||
|
||||
# This is for usday1 restrictions with the lgvs and customer constraints.
|
||||
FIFO=100
|
||||
MAXLOTS=3
|
||||
```
|
||||
|
||||
### Run the start command to get all the basic settings and modules installed
|
||||
|
||||
1. Run the below
|
||||
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
This command will start up the server and seed the database.
|
||||
|
||||
- Settings will be set here.
|
||||
- All modules will be added.
|
||||
|
||||
2. Press CTRL + C to stop the server.
|
||||
3. Reopen postgres and review the settings make the changes to match the server your going to be running in.
|
||||
- Change the server
|
||||
- change the dbServer
|
||||
- change plantToken
|
||||
- then the remaining settings confirm if you need on or want to leave as default.
|
||||
|
||||
### Creating first user.
|
||||
|
||||
1. Start the server back up.
|
||||
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
2. Open http://[SERVER]:[PORT]/api/docs or postman and create a user.
|
||||
- Please do not try to manually enter a new user this is due to how the password is hashed, as well as setting systemAdmin for the first user.
|
||||
- Change the server and port to what you changed in the DB.
|
||||
3. Stop the server again with CTRL + C.
|
||||
|
||||
### Running as a serivice.
|
||||
|
||||
You want to CD into the scripts folder.
|
||||
|
||||
```bash
|
||||
cd .\dist\server\scripts\
|
||||
```
|
||||
|
||||
Next use the example command below to get the service up and running.
|
||||
|
||||
- Options legend
|
||||
- serviceName = not recommended to change to reduce issues with the update process
|
||||
- option = use install for the install, but you can use this script later to stop, start, restart the service.
|
||||
- appPath = where did you extract the server files
|
||||
- description = no need to change this unless you want it to be something else
|
||||
- command = do not change this unless you know what your doing and really need to change this.
|
||||
|
||||
```powershell
|
||||
.\services.ps1 -serviceName "LSTV2" -option "install" -appPath "E:\LST\lstV2" -description "Logistics Support Tool V2" -command "run start"
|
||||
```
|
||||
|
||||
### Adding servers to the mix to update on from the front end
|
||||
|
||||
you will need to add your servers into the serverData.json.
|
||||
when the server starts up it will look at this file and make changes as needed.
|
||||
below is an example of the server
|
||||
|
||||
```JSON
|
||||
{
|
||||
"sName": "Kansas City",
|
||||
"serverDNS": "usksc1vms006",
|
||||
"plantToken": "usksc1",
|
||||
"idAddress": "10.42.9.26",
|
||||
"greatPlainsPlantCode": "85",
|
||||
"streetAddress": "1800 E 94th St Suite 300",
|
||||
"cityState": "Kansas City, MO",
|
||||
"zipcode": "64131",
|
||||
"contactEmail": "example@example.com",
|
||||
"contactPhone": "555-555-5555",
|
||||
"customerTiAcc": "ALPL01KCINT",
|
||||
"lstServerPort": "4000",
|
||||
"active": false,
|
||||
"serverLoc": "E:\\LST\\lstv2",
|
||||
"oldVersion": "E:\\LST\\lst_backend",
|
||||
"shippingHours": "[{\"early\": \"06:30\", \"late\": \"23:00\"}]",
|
||||
"tiPostTime": "[{\"from\": \"24\", \"to\": \"24\"}]",
|
||||
"otherSettings": [{ "specialInstructions": "" }]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
# Migrating From V1 to V2
|
||||
|
||||
## User migration
|
||||
|
||||
1. Open the sqlite db and export to sql the users table
|
||||
2. OPen the sql in notepad++ or your editor of choice and change the query to be similar to below.
|
||||
- we only need to have save the username, role, email, password
|
||||
|
||||
An example new query will look like
|
||||
|
||||
- Below is how it looks when exported from sqlite
|
||||
|
||||
```sql
|
||||
INSERT INTO "User" ("id", "username", "email", "role", "password", "passwordToken", "tokenExpire", "active", "pinCode", "lastLogin", "add_user", "add_date", "upd_user", "upd_date") VALUES
|
||||
(1, 'matthes01', 'blake.matthes@alpla.com', 'admin', 'JDJiJDEMUJEdGtL', NULL, NULL, '1', NULL, '1721075647687', 'LST_System', '1721075647687', 'LST_System', '1721075647687');
|
||||
```
|
||||
|
||||
The way we want to put recreate the query to work with the new db
|
||||
|
||||
- Below example
|
||||
|
||||
```sql
|
||||
INSERT INTO "users" ("username", "email", "role", "password") VALUES
|
||||
('matthes01','blake.matthes@alpla.com','admin','JDJiJDE1FuNFpkYlk4NGdHUXpEMzlHR1BD'),
|
||||
('leland001','jordan.leland@alpla.com','manager','vekJhN1dIVVVZa3pxR1l0T2hX'),
|
||||
('brandon001','brandon.harry@alpla.com','manager','wdm1RSXJlZnJDYTZP');
|
||||
;
|
||||
```
|
||||
|
||||
- You could have many users and just add like above with the identical info from the db
|
||||
|
||||
## Running v1 along Side V2 for the interm
|
||||
|
||||
- change v2 prod port to 4000 in the env and db
|
||||
- change v1 env to 4400 in the env. and in the db you will need to change the auth server to 4000 and the serverPort to 4400
|
||||
|
||||
This will change so that v2 is the main server now, this is needed for ocme mainly.
|
||||
191
lstV2/server/scripts/services.ps1
Normal file
191
lstV2/server/scripts/services.ps1
Normal file
@@ -0,0 +1,191 @@
|
||||
param (
|
||||
[string]$serviceName,
|
||||
[string]$option,
|
||||
[string]$appPath,
|
||||
[string]$command, # just the command like run start or what ever you have in npm.
|
||||
[string]$description,
|
||||
[string]$remote,
|
||||
[string]$server,
|
||||
[string]$username,
|
||||
[string]$admpass
|
||||
)
|
||||
|
||||
# Example string to run with the parameters in it.
|
||||
# .\services.ps1 -serviceName "LSTV2" -option "install" -appPath "E:\LST\lstV2" -description "Logistics Support Tool V2" -command "run start"
|
||||
|
||||
|
||||
|
||||
$nssmPath = $AppPath + "\nssm.exe"
|
||||
$npmPath = "C:\Program Files\nodejs\npm.cmd" # Path to npm.cmd
|
||||
|
||||
# Convert the plain-text password to a SecureString
|
||||
$securePass = ConvertTo-SecureString $admpass -AsPlainText -Force
|
||||
$credentials = New-Object System.Management.Automation.PSCredential($username, $securePass)
|
||||
|
||||
if($remote -eq "true"){
|
||||
|
||||
# if(-not $username -or -not $admpass){
|
||||
# Write-host "Missing adm account info please try again."
|
||||
# exit 1
|
||||
# }
|
||||
|
||||
$plantFunness = {
|
||||
param ($service, $processType, $location)
|
||||
# Call your PowerShell script inside plantFunness
|
||||
# & "$($location)\dist\server\scripts\services.ps1" -serviceName $service -option $processType -appPath $location
|
||||
|
||||
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
|
||||
Write-Host "Error: This script must be run as Administrator."
|
||||
exit 1
|
||||
}
|
||||
|
||||
if(-not $service -or -not $processType){
|
||||
Write-host "The service name or option is missing please enter one of them and try again."
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ($processType -eq "start"){
|
||||
write-host "Starting $($service)."
|
||||
Start-Service $service
|
||||
}
|
||||
|
||||
if ($processType -eq "stop"){
|
||||
write-host "Stoping $($service)."
|
||||
Stop-Service $service
|
||||
}
|
||||
|
||||
if ($processType -eq "restart"){
|
||||
write-host "Stoping $($service) to be restarted"
|
||||
Stop-Service $service
|
||||
Start-Sleep 3 # so we give it enough time to fully stop
|
||||
write-host "Starting $($service)"
|
||||
Start-Service $service
|
||||
}
|
||||
|
||||
if ($processType -eq "prodStop"){
|
||||
if(-not $location){
|
||||
Write-host "The path to the app is missing please add it in and try again."
|
||||
exit 1
|
||||
}
|
||||
& $nssmPath stop $service
|
||||
write-host "Removing $($service)"
|
||||
#& $nssmPath remove $serviceName confirm
|
||||
sc.exe config $service start= disabled
|
||||
|
||||
}
|
||||
|
||||
if ($processType -eq "prodStart"){
|
||||
if(-not $location){
|
||||
Write-host "The path to the app is missing please add it in and try again."
|
||||
exit 1
|
||||
}
|
||||
& $nssmPath start $service
|
||||
write-host "Removing $($service)"
|
||||
#& $nssmPath remove $serviceName confirm
|
||||
sc.exe config $service start= auto
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Invoke-Command -ComputerName $server -ScriptBlock $plantFunness -ArgumentList $serviceName, $option, $appPath -Credential $credentials
|
||||
} else {
|
||||
|
||||
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
|
||||
Write-Host "Error: This script must be run as Administrator."
|
||||
exit 1
|
||||
}
|
||||
|
||||
if(-not $serviceName -or -not $option){
|
||||
Write-host "The service name or option is missing please enter one of them and try again."
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ($option -eq "start"){
|
||||
write-host "Starting $($serviceName)."
|
||||
Start-Service $serviceName
|
||||
}
|
||||
|
||||
if ($option -eq "stop"){
|
||||
write-host "Stoping $($serviceName)."
|
||||
Stop-Service $serviceName
|
||||
}
|
||||
|
||||
if ($option -eq "restart"){
|
||||
write-host "Stoping $($serviceName) to be restarted"
|
||||
Stop-Service $serviceName
|
||||
Start-Sleep 3 # so we give it enough time to fully stop
|
||||
write-host "Starting $($serviceName)"
|
||||
Start-Service $serviceName
|
||||
}
|
||||
|
||||
if ($option -eq "delete"){
|
||||
if(-not $appPath){
|
||||
Write-host "The path to the app is missing please add it in and try again."
|
||||
exit 1
|
||||
}
|
||||
& $nssmPath stop $serviceName
|
||||
write-host "Removing $($serviceName)"
|
||||
& $nssmPath remove $serviceName confirm
|
||||
|
||||
}
|
||||
|
||||
if ($option -eq "prodStop"){
|
||||
if(-not $appPath){
|
||||
Write-host "The path to the app is missing please add it in and try again."
|
||||
exit 1
|
||||
}
|
||||
& $nssmPath stop $serviceName
|
||||
write-host "Removing $($serviceName)"
|
||||
#& $nssmPath remove $serviceName confirm
|
||||
sc.exe config $serviceName start= disabled
|
||||
|
||||
}
|
||||
|
||||
if ($option -eq "prodStart"){
|
||||
if(-not $appPath){
|
||||
Write-host "The path to the app is missing please add it in and try again."
|
||||
exit 1
|
||||
}
|
||||
& $nssmPath start $serviceName
|
||||
write-host "Removing $($serviceName)"
|
||||
#& $nssmPath remove $serviceName confirm
|
||||
sc.exe config $serviceName start= auto
|
||||
|
||||
}
|
||||
|
||||
if($option -eq "install"){
|
||||
if(-not $appPath -or -not $description -or -not $command){
|
||||
Write-host "Please check all parameters are passed to install the app.."
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
$service = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
|
||||
|
||||
if(-not $service){
|
||||
write-host $serviceName "is not installed we will install it now"
|
||||
|
||||
Write-Host "Installing $serviceName..."
|
||||
& $nssmPath install $serviceName $npmPath $command
|
||||
& $nssmPath set $serviceName AppDirectory $appPath
|
||||
& $nssmPath set $serviceName Description $description
|
||||
# Set recovery options
|
||||
sc.exe failure $serviceName reset= 0 actions= restart/5000/restart/5000/restart/5000
|
||||
& $nssmPath start $serviceName
|
||||
}else{
|
||||
write-host $serviceName "is already installed will push the updated info"
|
||||
Write-Host "Updating $serviceName..."
|
||||
& $nssmPath stop $serviceName
|
||||
& $nssmPath set $serviceName AppDirectory $appPath
|
||||
& $nssmPath set $serviceName Description $description
|
||||
# Set recovery options
|
||||
sc.exe failure $serviceName reset= 0 actions= restart/5000/restart/5000/restart/5000
|
||||
Start-Sleep 4
|
||||
& $nssmPath start $serviceName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
453
lstV2/server/scripts/update.ps1
Normal file
453
lstV2/server/scripts/update.ps1
Normal file
@@ -0,0 +1,453 @@
|
||||
param (
|
||||
[string]$server,
|
||||
[string]$token,
|
||||
[string]$location,
|
||||
[string]$devFolder,
|
||||
[string]$serverIP,
|
||||
[string]$build,
|
||||
[string]$type,
|
||||
[string]$username,
|
||||
[string]$admpass,
|
||||
[string]$obslst,
|
||||
[string]$obsBuild
|
||||
)
|
||||
|
||||
# Convert the plain-text password to a SecureString
|
||||
$securePass = ConvertTo-SecureString $admpass -AsPlainText -Force
|
||||
$credentials = New-Object System.Management.Automation.PSCredential($username, $securePass)
|
||||
|
||||
# lets get the version of the app we are updating
|
||||
$pkgFile = "$devFolder\package.json"
|
||||
$package = Get-Content -Path $pkgFile -Raw | ConvertFrom-Json
|
||||
|
||||
$version = "$($package.version)-$($package.admConfig.build -1)"
|
||||
|
||||
# Checking to make sure the server is up and online
|
||||
Write-Output "Checking if $($token) is online to update."
|
||||
$pingResult = Test-Connection -ComputerName $serverIP -Count 2 -Quiet
|
||||
|
||||
if (-not $pingResult) {
|
||||
Write-Output "Server $($server) $($serverIP) is NOT reachable. Exiting script."
|
||||
exit 1 # Terminate the script with a non-zero exit code
|
||||
}
|
||||
|
||||
Write-Output "Server $($server) ($serverIP) is online."
|
||||
|
||||
# get the file name we want to grab
|
||||
|
||||
$buildZip = "lstv2-$version.zip"
|
||||
|
||||
if (-Not (Test-Path -Path "$($build)\$($buildZip)")) {
|
||||
Write-Host "Build is missing from the build folder."
|
||||
Write-host $buildZip
|
||||
exit
|
||||
}
|
||||
|
||||
Write-Host "---------------Starting the update Process----------------------------------"
|
||||
Write-Host "File to be copied over is $buildZip"
|
||||
Write-Host "Coping files to $($server)"
|
||||
$destination = "\\$($server)\$($location)" -replace ":", "$"
|
||||
Write-Host $destination
|
||||
Write-Host "Forcing the removal of the mapped drive."
|
||||
Get-PSDrive -Name "z" -ErrorAction SilentlyContinue | Remove-PSDrive -Force
|
||||
|
||||
# Create a mapped drive with credentials using New-PSDrive for the current session
|
||||
|
||||
try {
|
||||
|
||||
New-PSDrive -Name "z" -PSProvider FileSystem -Root $destination -Credential $credentials
|
||||
|
||||
# Create the update folder if it doesn't exist
|
||||
if (-not (Test-Path -Path $destination)) {
|
||||
New-Item -ItemType Directory -Path $destination -Force
|
||||
}
|
||||
|
||||
# Copying files to the server
|
||||
Write-Host "Copying files to $($server)"
|
||||
Copy-Item -Path "$($build)\$($buildZip)" -Destination "z:\" -Recurse -Force
|
||||
Write-Host "Files copied to $($server)"
|
||||
} catch {
|
||||
Write-Host "Error: $_"
|
||||
} finally {
|
||||
# Remove the mapped drive after copying
|
||||
if (Get-PSDrive -Name "z" -ErrorAction SilentlyContinue) {
|
||||
Write-Host "Removing mapped drive..."
|
||||
Remove-PSDrive -Name "z"
|
||||
}
|
||||
}
|
||||
|
||||
write-Host $extractedFolderPath = "$server\$location$(if ($token -eq "usiow2") { "_2" })"
|
||||
|
||||
# The script that runs inside the plant.
|
||||
$plantFunness = {
|
||||
param ($server, $token, $location, $buildFile, $buildLoc, $obslst, $obsBuild)
|
||||
|
||||
$localPath = $location -replace '\$', ':'
|
||||
$serverFile = "$($localPath)\$buildFile"
|
||||
$serverPath = "$($localPath)"
|
||||
$appPath = $extractedFolderPath
|
||||
$nssmPath = $serverPath + "\nssm.exe"
|
||||
$npmPath = "C:\Program Files\nodejs\npm.cmd" # Path to npm.cmd
|
||||
|
||||
|
||||
Write-Host "In the plant we go!!!!!"
|
||||
|
||||
######################################################################################
|
||||
# Removing the fist and frontend folder to make sure we keep them the same and clean.
|
||||
######################################################################################
|
||||
|
||||
# Delete the directories after extraction
|
||||
Write-Host "Deleting Dist and Frontend..."
|
||||
|
||||
Set-Location $serverPath
|
||||
npm run removeOld # --omit=dev
|
||||
|
||||
Write-Host "Unzipping the folder..."
|
||||
|
||||
$extractedFolderPath = $serverPath
|
||||
|
||||
# Extract the files to the build path
|
||||
try {
|
||||
# Expand the archive
|
||||
Expand-Archive -Path $serverFile -DestinationPath $extractedFolderPath -Force
|
||||
|
||||
# Delete the zip file after extraction
|
||||
Write-Host "Deleting the zip file..."
|
||||
Remove-Item -Path $serverFile -Force
|
||||
} catch {
|
||||
Write-Host "Error: $_"
|
||||
exit 1 # Exit with a non-zero code if there's an error
|
||||
}
|
||||
|
||||
# Write-Host "-----------------------Dealing with LSTv1 Stuff ------------------------------------"
|
||||
# try {
|
||||
# # Expand the archive
|
||||
# Expand-Archive -Path "$($localPath)\$($obsBuild)" -DestinationPath $obslst -Force
|
||||
|
||||
# # Delete the zip file after extraction
|
||||
# Write-Host "Deleting the zip file..."
|
||||
# Remove-Item -Path "$($localPath)\$($obsBuild)" -Force
|
||||
# } catch {
|
||||
# Write-Host "Error: $_"
|
||||
# exit 1 # Exit with a non-zero code if there's an error
|
||||
# }
|
||||
|
||||
# # for iowa 2 need to change the port config on the start up of nextjs server
|
||||
# if($token -eq "usiow2"){
|
||||
# $jsonPkgloc = "$($obslst)\apps\frontend\package.json"
|
||||
# #read the file
|
||||
# $jsonContent = Get-Content -Path $jsonPkgloc | ConvertFrom-Json
|
||||
# #change the second we want to update
|
||||
# $jsonContent.scripts.start = "next start -p 3001"
|
||||
# # convert back to json
|
||||
# $jsonContent | ConvertTo-Json | Set-Content -Path $jsonPkgloc
|
||||
# }
|
||||
|
||||
############################################################################
|
||||
Write-Host "Stopping the services to do the updates, pkgs and db changes."
|
||||
|
||||
#Write-Host "Stopping services to update"
|
||||
$serviceGateway = "LST-Gateway$(if ($token -eq "usiow2") { "_2" })"
|
||||
$serviceAuth = "LST-Auth$(if ($token -eq "usiow2") { "_2" })"
|
||||
$serviceSystem = "LST-System$(if ($token -eq "usiow2") { "_2" })"
|
||||
$serviceApp = "LST-App$(if ($token -eq "usiow2") { "_2" })"
|
||||
$serviceFrontEnd = "LST-frontend$(if ($token -eq "usiow2") { "_2" })"
|
||||
$serviceOcme = "LST-Ocme$(if ($token -eq "usiow2") { "_2" })"
|
||||
$serviceLstV2 = "LSTV2$(if ($token -eq "usiow2") { "_2" })"
|
||||
|
||||
# if($token -eq "usday1"){
|
||||
# Write-Host "Stopping $($serviceOcme)"
|
||||
# Stop-Service -DisplayName $serviceOcme -Force
|
||||
# }
|
||||
|
||||
# Write-Host "Stopping $($serviceGateway)"
|
||||
# Stop-Service -DisplayName $serviceGateway -Force
|
||||
# Start-Sleep -Seconds 1
|
||||
|
||||
# Write-Host "Stopping $($serviceAuth)"
|
||||
# Stop-Service -DisplayName $serviceAuth -Force
|
||||
# Start-Sleep -Seconds 1
|
||||
|
||||
# Write-Host "Stopping $($serviceSystem)"
|
||||
# Stop-Service -DisplayName $serviceSystem -Force
|
||||
# Start-Sleep -Seconds 1
|
||||
|
||||
# Write-Host "Stopping $($serviceApp)"
|
||||
# Stop-Service -DisplayName $serviceApp -Force
|
||||
# Start-Sleep -Seconds 1
|
||||
|
||||
# Write-Host "Stopping $($serviceFrontEnd)"
|
||||
# Stop-Service -DisplayName $serviceFrontEnd -Force
|
||||
# Start-Sleep -Seconds 1
|
||||
|
||||
Write-Host "Stopping $($serviceLstV2)"
|
||||
Stop-Service -DisplayName $serviceLstV2 -Force
|
||||
Start-Sleep -Seconds 1
|
||||
|
||||
#################################################################
|
||||
# Service removoal and making sure we have the new version added
|
||||
#################################################################
|
||||
|
||||
#################################################################
|
||||
# Removing all the old services
|
||||
#################################################################
|
||||
Write-Host "Removing services that are no longer used."
|
||||
& $nssmPath remove "LogisticsSupportTool" confirm
|
||||
& $nssmPath remove $serviceAuth confirm
|
||||
& $nssmPath remove $serviceGateway confirm
|
||||
& $nssmPath remove $serviceSystem confirm
|
||||
& $nssmPath remove $serviceApp confirm
|
||||
& $nssmPath remove $serviceFrontEnd confirm
|
||||
& $nssmPath remove $serviceOcme confirm
|
||||
# & $nssmPath remove $serviceGateway confirm
|
||||
# if($token -eq "usday1"){
|
||||
# & $nssmPath remove $serviceOcme confirm
|
||||
# }
|
||||
Start-Sleep -Seconds 2
|
||||
|
||||
$service = Get-Service -Name $serviceLstV2 -ErrorAction SilentlyContinue
|
||||
|
||||
if(-not $service){
|
||||
## adding in lstAdm
|
||||
Write-Host "Adding $($serviceLstV2)... incase its missing."
|
||||
$commandToRun = "run start"
|
||||
$description = "logistics Support Tool"
|
||||
& $nssmPath install $serviceLstV2 $npmPath $commandToRun
|
||||
Write-Host "Setting the app directory"
|
||||
& $nssmPath set $serviceLstV2 AppDirectory $appPath
|
||||
Write-Host "Setting the description"
|
||||
& $nssmPath set $serviceLstV2 Description $description
|
||||
Write-Host "Setting recovery options"
|
||||
# Set recovery options
|
||||
sc.exe failure $serviceLstV2 reset= 0 actions= restart/5000/restart/5000/restart/5000
|
||||
}
|
||||
# Doing an install
|
||||
Write-Host "Running the install to make sure everything is updated."
|
||||
Set-Location $serverPath
|
||||
npm run prodinstall # --omit=dev
|
||||
Write-Host "Finished doing updates"
|
||||
# Start-Sleep -Seconds 1
|
||||
# Write-HOst "Running db migrations"
|
||||
# npm run db:migrate
|
||||
|
||||
###########################################################
|
||||
# Old system still active until we have everything off it
|
||||
###########################################################
|
||||
|
||||
###########################################################
|
||||
# Frontend env
|
||||
###########################################################
|
||||
|
||||
# Write-Host "Creating the env file in the front end"
|
||||
# $envContentTemplatef = @"
|
||||
# NEXTAUTH_SECRET= "12348fssad5sdg2f2354afvfw34"
|
||||
# NEXTAUTH_URL_INTERNAL= "http://localhost:3000"
|
||||
# NEXTAUTH_URL="{url}"
|
||||
# API_KEY= "E3ECD3619A943B98C6F33E3322362"
|
||||
# "@
|
||||
|
||||
# try {
|
||||
# $url = "http://$($token)vms006:3000"
|
||||
|
||||
# if ($token -eq "usiow2") {
|
||||
# $url = "http://usiow1vms006:3001"
|
||||
# }
|
||||
|
||||
# if ($token -in @("test1", "test2", "test3")) {
|
||||
# $url = "http://usmcd1vms036:3000"
|
||||
# }
|
||||
|
||||
# # Replace {url} with the actual $url
|
||||
# $envContentf = $envContentTemplatef -replace "{url}", $url
|
||||
|
||||
# # Define the path where the .env file should be created
|
||||
# $envFilePathf = $obslst + "\apps\frontend\.env"
|
||||
# Write-Host "Final URL: $url"
|
||||
# # Write the content to the .env file
|
||||
# $envContentf | Out-File -FilePath $envFilePathf -Encoding UTF8 -Force
|
||||
|
||||
# # Optional: Verify the file was created
|
||||
# if (Test-Path $envFilePathf) {
|
||||
# Write-Host "`.env` file created successfully on $env:COMPUTERNAME at $envFilePathf"
|
||||
# } else {
|
||||
# Write-Host "Failed to create `.env` file on $env:COMPUTERNAME"
|
||||
# }
|
||||
|
||||
# } catch {
|
||||
# Write-Host "Error: Failed to create `.env` file on $server - $_"
|
||||
# }
|
||||
|
||||
###########################################################
|
||||
# DB env
|
||||
###########################################################
|
||||
|
||||
# Write-Host "Creating the env file in the front end"
|
||||
# $envContentTemplateb = @"
|
||||
# DATABASE_URL="file:E:\LST\db\{dbLink}.db"
|
||||
# "@
|
||||
|
||||
# try {
|
||||
|
||||
# $dbLink = "lstBackendDB"
|
||||
|
||||
# if ($token -eq "usiow2") {
|
||||
# $dbLink = "lstBackendDB_2"
|
||||
# }
|
||||
|
||||
# if ($token -in @("test1", "test2", "test3")) {
|
||||
# $dbLink = "lstBackendDB"
|
||||
# }
|
||||
|
||||
# # Replace {url} with the actual $url
|
||||
# $envContentb = $envContentTemplateb -replace "{dbLink}", $dbLink
|
||||
|
||||
# # Define the path where the .env file should be created
|
||||
# $envFilePathb = $obslst + "\packages\database\.env"
|
||||
|
||||
# # Write the content to the .env file
|
||||
# $envContentb | Out-File -FilePath $envFilePathb -Encoding UTF8 -Force
|
||||
|
||||
# # Optional: Verify the file was created
|
||||
# if (Test-Path $envFilePathb) {
|
||||
# Write-Host "`.env` file created successfully on $env:COMPUTERNAME at $envFilePathb"
|
||||
# } else {
|
||||
# Write-Host "Failed to create `.env` file on $env:COMPUTERNAME"
|
||||
# }
|
||||
|
||||
# } catch {
|
||||
# Write-Host "Error: Failed to create `.env` file on $server - $_"
|
||||
# }
|
||||
|
||||
###########################################################
|
||||
# backend env
|
||||
###########################################################
|
||||
|
||||
# Write-Host "Creating the env file in the front end"
|
||||
# $envContentTemplated = @"
|
||||
# # Server env
|
||||
# NODE_ENV = production
|
||||
# # server apiKey
|
||||
# API_KEY = E3ECD3619A943B98C6F33E3322362
|
||||
# # Prisma DB link
|
||||
# DATABASE_URL="file:E:\LST\db\{dbLink}.db"
|
||||
# # if you still want the db in the same folder as the server install you need to do like the example below else use the relevent link
|
||||
# DATEBASE_LOC="E:\LST\db\{dbLink}.db"
|
||||
# DATABASE_BACKUP_LOC="E:\LST\backups"
|
||||
# # Server port
|
||||
# GATEWAY_PORT={gatewayport}
|
||||
# AUTH_PORT=4100
|
||||
# SYSTEM_APP_PORT={systemport}
|
||||
# OCME_PORT={ocme}
|
||||
|
||||
# # This should me removed once we have the entire app broke out to its own apps
|
||||
# OLD_APP_PORT={appPort}
|
||||
|
||||
# # Logging
|
||||
# LOG_LEVEL = info
|
||||
# LOG_LOC ="E:\\LST\\logs"
|
||||
|
||||
# # authentication
|
||||
# SALTING = 12
|
||||
# SECRET = E3ECD3619A943B98C6F33E3322362
|
||||
# JWT_SECRET = 12348fssad5sdg2f2354afvfw34
|
||||
# JWT_EXPIRES_TIME = 1h
|
||||
|
||||
# # cookie time is in min please take this into consideration when creating all the times
|
||||
# COOKIE_EXPIRES_TIME = 60
|
||||
|
||||
# # password token reset in mintues
|
||||
# RESET_TOKEN = 330
|
||||
# "@
|
||||
|
||||
# try {
|
||||
|
||||
# $dbLink = "lstBackendDB"
|
||||
# $gatewayport = "4400"
|
||||
# $systemport = "4200"
|
||||
# $ocmeport = "4300"
|
||||
# $appport = "4400"
|
||||
|
||||
# if ($token -eq "usiow2") {
|
||||
# $dbLink = "lstBackendDB_2"
|
||||
# $gatewayport = "4401"
|
||||
# $systemport = "4201"
|
||||
# $ocmeport = "4301"
|
||||
# $appport = "4401"
|
||||
# }
|
||||
|
||||
# if ($token -in @("test1", "test2", "test3")) {
|
||||
# $dbLink = "lstBackendDB"
|
||||
# }
|
||||
|
||||
# #
|
||||
# $port1 = $envContentTemplated -replace "{gatewayport}", $gatewayport
|
||||
# $port2 = $port1 -replace "{systemport}", $systemport
|
||||
# $port3 = $port2 -replace "{ocme}", $ocmeport
|
||||
# $port4 = $port3 -replace "{appPort}", $appport
|
||||
# $envContentd = $port4 -replace "{dbLink}", $dbLink
|
||||
|
||||
|
||||
# # Define the path where the .env file should be created
|
||||
# $envFilePathd = $obslst + "\.env"
|
||||
|
||||
# # Write the content to the .env file
|
||||
# $envContentd | Out-File -FilePath $envFilePathd -Encoding UTF8 -Force
|
||||
|
||||
# # Optional: Verify the file was created
|
||||
# if (Test-Path $envFilePathd) {
|
||||
# Write-Host "`.env` file created successfully on $env:COMPUTERNAME at $envFilePathd"
|
||||
# } else {
|
||||
# Write-Host "Failed to create `.env` file on $env:COMPUTERNAME"
|
||||
# }
|
||||
|
||||
# } catch {
|
||||
# Write-Host "Error: Failed to create `.env` file on $server - $_"
|
||||
# }
|
||||
|
||||
|
||||
# Write-Host "Running install on obs server."
|
||||
# Set-Location $obslst
|
||||
# npm run newinstall # --omit=dev
|
||||
# Write-Host "Update the frontend"
|
||||
# npm run install:front
|
||||
# npm run install:ui
|
||||
# npm run install:db
|
||||
|
||||
# Write-Host "Running db updates"
|
||||
# npm run db:migrate
|
||||
# Start-Sleep -Seconds 1
|
||||
# npm run db:gen
|
||||
# Start-Sleep -Seconds 1
|
||||
# Write-Host "incase a new default setting was added we want to add it in."
|
||||
# npm run db:init
|
||||
|
||||
###########################################################
|
||||
# Starting the services back up.
|
||||
###########################################################
|
||||
# Write-Host "Starting the services"
|
||||
# Write-Host "Starting $($serviceSystem)"
|
||||
# Start-Service -DisplayName $serviceSystem
|
||||
# Start-Sleep -Seconds 1
|
||||
# Write-Host "Starting $($serviceGateway)"
|
||||
# Start-Service -DisplayName $serviceGateway
|
||||
# Start-Sleep -Seconds 1
|
||||
# Write-Host "Starting $($serviceAuth)"
|
||||
# Start-Service -DisplayName $serviceAuth
|
||||
# Start-Sleep -Seconds 1
|
||||
# Write-Host "Starting $($serviceApp)"
|
||||
# Start-Service -DisplayName $serviceApp
|
||||
# Start-Sleep -Seconds 1
|
||||
# Write-Host "Starting $($serviceFrontEnd)"
|
||||
# Start-Service -DisplayName $serviceFrontEnd
|
||||
# Start-Sleep -Seconds 1
|
||||
Write-Host "Starting $( $serviceLstV2)"
|
||||
Start-Service -DisplayName $serviceLstV2
|
||||
Start-Sleep -Seconds 1
|
||||
Write-Host "$($server) finished updating"
|
||||
# if($token -eq "usday1"){
|
||||
# Write-Host "Starting $($serviceOcme)"
|
||||
# Start-Service -DisplayName $serviceOcme
|
||||
# }
|
||||
|
||||
}
|
||||
Invoke-Command -ComputerName $server -ScriptBlock $plantFunness -ArgumentList $server, $token, $location, $buildZip, $buildLoc, $obslst, $obsBuild -Credential $credentials
|
||||
22
lstV2/server/scripts/updatePermissions.ps1
Normal file
22
lstV2/server/scripts/updatePermissions.ps1
Normal file
@@ -0,0 +1,22 @@
|
||||
# Define the array of folders
|
||||
$folders = @(
|
||||
"AlplaBasis",
|
||||
"AlplaBudget",
|
||||
"AlplaINVOICE",
|
||||
"AlplaLabel",
|
||||
"AlplaOrder",
|
||||
"AlplaPlanning",
|
||||
"AlplaPurchase",
|
||||
"AlplaStock",
|
||||
"PDF24",
|
||||
"Module shortcuts"
|
||||
)
|
||||
|
||||
# Set permissions using icacls
|
||||
$permissions = "Everyone:(OI)(CI)F"
|
||||
|
||||
# Loop through each folder and set permissions
|
||||
foreach ($folder in $folders) {
|
||||
$folderPath = "C:\Sources\AlplaPROD\$folder"
|
||||
icacls $folderPath /grant $permissions /t /c /q
|
||||
}
|
||||
219
lstV2/server/scripts/updateServers.ts
Normal file
219
lstV2/server/scripts/updateServers.ts
Normal file
@@ -0,0 +1,219 @@
|
||||
import { spawn } from "child_process";
|
||||
import { getAppInfo } from "../globalUtils/appInfo.js";
|
||||
import { db } from "../../database/dbclient.js";
|
||||
import { serverData } from "../../database/schema/serverData.js";
|
||||
import { eq, sql } from "drizzle-orm";
|
||||
import { createLog } from "../services/logger/logger.js";
|
||||
import { serverSettings } from "../services/server/controller/settings/getSettings.js";
|
||||
|
||||
type UpdateServerResponse = {
|
||||
success: boolean;
|
||||
message: string;
|
||||
};
|
||||
|
||||
export const updateServer = async (
|
||||
devApp: string,
|
||||
server: string | null,
|
||||
all?: boolean | null
|
||||
): Promise<UpdateServerResponse> => {
|
||||
const app = await getAppInfo(devApp);
|
||||
const serverInfo = await db
|
||||
.select()
|
||||
.from(serverData)
|
||||
.where(eq(serverData.plantToken, server?.toLowerCase() ?? ""));
|
||||
|
||||
if (serverInfo.length === 0) {
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
"serverUpdater",
|
||||
`Looks like you are missing the plant token or have entered an incorrect one please try again.`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message:
|
||||
"Looks like you are missing the plant token or have entered an incorrect one please try again.",
|
||||
};
|
||||
}
|
||||
|
||||
if (serverInfo[0].isUpgrading && !all) {
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
"serverUpdater",
|
||||
`Looks like ${serverInfo[0].plantToken} is upgrading already you cant do this again.`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: `Looks like ${serverInfo[0].plantToken} is upgrading already you cant do this again.`,
|
||||
};
|
||||
}
|
||||
|
||||
console.log(serverInfo);
|
||||
const scriptPath = `${process.env.DEVFOLDER}\\server\\scripts\\update.ps1 `;
|
||||
const args = [
|
||||
"-NoProfile",
|
||||
"-ExecutionPolicy",
|
||||
"Bypass",
|
||||
"-File",
|
||||
scriptPath,
|
||||
"-username",
|
||||
process.env.ADMUSER, // needs moved to somewhere else.
|
||||
"-admpass",
|
||||
process.env.ADMPASSWORD, // needs moved to somewhere else.
|
||||
"-devFolder",
|
||||
process.env.DEVFOLDER,
|
||||
"-server",
|
||||
serverInfo[0].serverDNS,
|
||||
"-serverIP",
|
||||
serverInfo[0].idAddress,
|
||||
"-token",
|
||||
serverInfo[0].plantToken,
|
||||
"-build",
|
||||
`${process.env.DEVFOLDER}\\builds`,
|
||||
"-location",
|
||||
serverInfo[0].serverLoc,
|
||||
"-obslst",
|
||||
serverInfo[0].oldVersion,
|
||||
"-obsBuild",
|
||||
app.admConfig.oldBuild,
|
||||
,
|
||||
];
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const process = spawn("powershell", args);
|
||||
// change the server to upgradeing
|
||||
await db
|
||||
.update(serverData)
|
||||
.set({ isUpgrading: true })
|
||||
.where(eq(serverData.plantToken, server?.toLowerCase() ?? ""));
|
||||
//let stdout = "";
|
||||
//let stderr = "";
|
||||
|
||||
// Collect stdout data
|
||||
process.stdout.on("data", (data) => {
|
||||
const output = data.toString().trim();
|
||||
createLog("info", "lst", "serverUpdater", `${output}`);
|
||||
//onData(output);
|
||||
});
|
||||
|
||||
// Collect stderr data
|
||||
process.stderr.on("data", (data) => {
|
||||
const output = data.toString().trim();
|
||||
createLog("info", "lst", "serverUpdater", `${output}`);
|
||||
//onData(output);
|
||||
});
|
||||
|
||||
// Handle process close
|
||||
process.on("close", async (code) => {
|
||||
if (code === 0) {
|
||||
// if (count >= servers) {
|
||||
// //onClose(`Server completed with code: ${code}`);
|
||||
// }
|
||||
createLog("info", "lst", "serverUpdater", `${server}`);
|
||||
|
||||
//update the last build.
|
||||
try {
|
||||
await db
|
||||
.update(serverData)
|
||||
.set({ lastUpdated: sql`NOW()`, isUpgrading: false })
|
||||
.where(
|
||||
eq(
|
||||
serverData.plantToken,
|
||||
server?.toLowerCase() ?? ""
|
||||
)
|
||||
);
|
||||
createLog(
|
||||
"info",
|
||||
"lst",
|
||||
"serverUpdater",
|
||||
`${server?.toLowerCase()}, has been updated and can now be used again.`
|
||||
);
|
||||
} catch (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
"serverUpdater",
|
||||
`There was an error updating the last time the server was updated: ${error}`
|
||||
);
|
||||
}
|
||||
|
||||
resolve({
|
||||
success: true,
|
||||
message: `${server?.toLowerCase()}, has been updated and can now be used again.`,
|
||||
});
|
||||
} else {
|
||||
const errorMessage = `Process exited with code ${code}`;
|
||||
|
||||
// if (count >= servers) {
|
||||
// //onClose(code);
|
||||
// }
|
||||
|
||||
reject({
|
||||
success: false,
|
||||
message: `${server?.toLowerCase()}, Has encounted an error while updating.`,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Handle errors with the process itself
|
||||
process.on("error", (error) => {
|
||||
//onError(err.message);
|
||||
createLog("error", "lst", "serverUpdater", `${error}`);
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export async function processAllServers(devApp: string) {
|
||||
const servers = await db.select().from(serverData);
|
||||
|
||||
//change all servers to be upgrading
|
||||
await db.update(serverData).set({ isUpgrading: true });
|
||||
|
||||
createLog(
|
||||
"info",
|
||||
"lst",
|
||||
"serverUpdater",
|
||||
`Running the update on all servers`
|
||||
);
|
||||
let count = 1;
|
||||
for (const server of servers) {
|
||||
try {
|
||||
const updateToServer = await updateServer(
|
||||
devApp,
|
||||
server.plantToken,
|
||||
true
|
||||
);
|
||||
createLog(
|
||||
"info",
|
||||
"lst",
|
||||
"serverUpdater",
|
||||
`${server.sName} was updated.`
|
||||
);
|
||||
count = count + 1;
|
||||
|
||||
// return {
|
||||
// success: true,
|
||||
// message: `${server.sName} was updated.`,
|
||||
// data: updateToServer,
|
||||
// };
|
||||
} catch (error: any) {
|
||||
createLog(
|
||||
"info",
|
||||
"lst",
|
||||
"serverUpdater",
|
||||
`Error updating ${server.sName}: ${error.message}`
|
||||
);
|
||||
// return {
|
||||
// success: false,
|
||||
// message: `Error updating ${server.sName}: ${error.message}`,
|
||||
// };
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `All Servers are being updated this will take some time.`,
|
||||
};
|
||||
}
|
||||
223
lstV2/server/scripts/zipUpBuild.ts
Normal file
223
lstV2/server/scripts/zipUpBuild.ts
Normal file
@@ -0,0 +1,223 @@
|
||||
import AdmZip from "adm-zip";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import { execSync } from "child_process";
|
||||
import { createLog } from "../services/logger/logger.js";
|
||||
import { getAppInfo } from "../globalUtils/appInfo.js";
|
||||
|
||||
// create the ignore list
|
||||
const ignoreList = [
|
||||
".git",
|
||||
"builds",
|
||||
"server",
|
||||
"node_modules",
|
||||
"apiDocsLSTV2",
|
||||
"testFiles",
|
||||
".env",
|
||||
".gitignore",
|
||||
".versionrc.json",
|
||||
"drizzle-dev.config.ts",
|
||||
"nssm.exe",
|
||||
"postgresql-17.2-3-windows-x64.exe",
|
||||
// front end ignore
|
||||
"frontend/node_modules",
|
||||
"fonrtend/.env",
|
||||
"frontend/public",
|
||||
"frontend/src",
|
||||
"frontend/.gitignore",
|
||||
"frontend/eslint.config.js",
|
||||
"frontend/index.html",
|
||||
"frontend/package.json",
|
||||
"frontend/package-lock.json",
|
||||
"frontend/README.md",
|
||||
"frontend/tsconfig.json",
|
||||
"frontend/tsconfig.app.json",
|
||||
"frontend/tsconfig.node.json",
|
||||
"frontend/vite.config.ts",
|
||||
"frontend/components.json",
|
||||
//misc files
|
||||
"jsTesting",
|
||||
"dotnetwrapper",
|
||||
"prodBuild",
|
||||
];
|
||||
|
||||
const shouldIgnore = (itemPath: any) => {
|
||||
const normalizedItemPath = itemPath.replace(/\\/g, "/");
|
||||
|
||||
return ignoreList.some((ignorePattern) => {
|
||||
const normalizedIgnorePatther = ignorePattern.replace(/\\/g, "/");
|
||||
return (
|
||||
normalizedItemPath === normalizedIgnorePatther ||
|
||||
normalizedItemPath.startsWith(`${normalizedIgnorePatther}/`)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const addToZip = (zip: any, currentPath: string, rootPath: string) => {
|
||||
const items = fs.readdirSync(currentPath);
|
||||
|
||||
items.forEach((item) => {
|
||||
const itemPath = path.join(currentPath, item);
|
||||
const relativePath = path.relative(rootPath, itemPath);
|
||||
|
||||
// Skip if the item is in the ignore list
|
||||
if (shouldIgnore(relativePath)) {
|
||||
createLog("info", "lst", "zipUpBuild", `Ignoring: ${relativePath}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const stat = fs.statSync(itemPath);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
// If it's a directory, recursively add its contents
|
||||
addToZip(zip, itemPath, rootPath);
|
||||
} else {
|
||||
// If it's a file, add it to the zip with the preserved folder structure
|
||||
zip.addLocalFile(itemPath, path.dirname(relativePath));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const updateBuildNumber = (appLock: string) => {
|
||||
const packagePath = path.join(appLock, "package.json"); // Adjust path if necessary
|
||||
|
||||
try {
|
||||
// Read package.json
|
||||
const pkgData = fs.readFileSync(packagePath, "utf8");
|
||||
const pkgJson = JSON.parse(pkgData);
|
||||
|
||||
// Ensure admConfig exists
|
||||
if (pkgJson.admConfig && typeof pkgJson.admConfig.build === "number") {
|
||||
// Increment the build number
|
||||
pkgJson.admConfig.build += 1;
|
||||
|
||||
// Write the updated data back
|
||||
fs.writeFileSync(
|
||||
packagePath,
|
||||
JSON.stringify(pkgJson, null, 2),
|
||||
"utf8"
|
||||
);
|
||||
|
||||
createLog(
|
||||
"info",
|
||||
"lst",
|
||||
"zipUpBuild",
|
||||
`Build number updated to: ${pkgJson.admConfig.build}`
|
||||
);
|
||||
// Auto-commit changes
|
||||
execSync("git add package.json");
|
||||
execSync(
|
||||
`git commit -m "ci(release): bump build number to ${pkgJson.admConfig.build}"`
|
||||
);
|
||||
} else {
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
"zipUpBuild",
|
||||
"admConfig.build is missing or not a number"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
"zipUpBuild",
|
||||
`Error updating build number: ${error}`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const createZip = async (appLock: string) => {
|
||||
const app = await getAppInfo(appLock);
|
||||
const zip = new AdmZip();
|
||||
|
||||
//dest path for this app... hard coded for meow will be in db later
|
||||
const destPath = `${process.env.DEVFOLDER}\\builds`;
|
||||
const srcPath = `${process.env.DEVFOLDER}`;
|
||||
|
||||
addToZip(zip, srcPath, srcPath);
|
||||
|
||||
// Write the zip file to disk
|
||||
const outputZipPath = path.join(
|
||||
destPath,
|
||||
`${app.name}-${app.version}-${app.admConfig.build}.zip`
|
||||
);
|
||||
zip.writeZip(outputZipPath);
|
||||
|
||||
createLog(
|
||||
"info",
|
||||
"lst",
|
||||
"zipUpBuild",
|
||||
`Zip file created at ${outputZipPath}`
|
||||
);
|
||||
updateBuildNumber(appLock);
|
||||
|
||||
// only keep the last 5 builds for the type we have.
|
||||
try {
|
||||
const appFiles = fs
|
||||
.readdirSync(destPath)
|
||||
.filter((file) => file.startsWith(app.name)) // Ensure only backend files are matched
|
||||
.map((file) => ({
|
||||
name: file,
|
||||
time: fs.statSync(path.join(destPath, file)).mtime.getTime(),
|
||||
}))
|
||||
.sort((a, b) => a.time - b.time); // Sort by modification time (oldest first)
|
||||
|
||||
createLog(
|
||||
"info",
|
||||
"lst",
|
||||
"zipUpBuild",
|
||||
`app Files (sorted by time):", ${JSON.stringify(appFiles)}`
|
||||
);
|
||||
|
||||
if (appFiles.length > 20) {
|
||||
appFiles.slice(0, -20).forEach((file) => {
|
||||
const filePath = path.join(destPath, file.name);
|
||||
try {
|
||||
fs.unlinkSync(filePath);
|
||||
createLog(
|
||||
"info",
|
||||
"lst",
|
||||
"zipUpBuild",
|
||||
`Deleted: ${file.name}`
|
||||
);
|
||||
} catch (error: any) {
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
"zipUpBuild",
|
||||
`Failed to delete ${file.name}: ${error.message}`
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
createLog("info", "lst", "zipUpBuild", "No files to delete.");
|
||||
}
|
||||
} catch (error: any) {
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
"zipUpBuild",
|
||||
`Error reading directory or deleting files:", ${error.message}`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
//createZip("C:\\Users\\matthes01\\Documents\\lstv2");
|
||||
|
||||
// Only call `createZip` if the script is executed directly
|
||||
|
||||
if (process.argv.length > 2) {
|
||||
const location = process.argv[2];
|
||||
|
||||
if (!location) {
|
||||
createLog("error", "lst", "zipUpBuild", "Error: No location provided.");
|
||||
process.exit(1);
|
||||
} else {
|
||||
createLog("info", "lst", "zipUpBuild", "Startiing the zip process.");
|
||||
}
|
||||
|
||||
createZip(location);
|
||||
} else {
|
||||
createLog("error", "lst", "zipUpBuild", "Error: No location provided.");
|
||||
}
|
||||
44
lstV2/server/services/auth/authService.ts
Normal file
44
lstV2/server/services/auth/authService.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { OpenAPIHono } from "@hono/zod-openapi";
|
||||
import login from "./routes/login.js";
|
||||
import register from "./routes/register.js";
|
||||
import session from "./routes/session.js";
|
||||
import getAccess from "./routes/user/getUserRoles.js";
|
||||
import setAccess from "./routes/userAdmin/setUserRoles.js";
|
||||
import profile from "./routes/user/profileUpdate.js";
|
||||
import { areRolesIn } from "./utils/roleCheck.js";
|
||||
import createUser from "./routes/userAdmin/createUser.js";
|
||||
import allUsers from "./routes/userAdmin/getUsers.js";
|
||||
import updateUser from "./routes/userAdmin/updateUser.js";
|
||||
import allUserRoles from "./routes/userAdmin/getAllUserRoles.js";
|
||||
import { massAccountCreation } from "./utils/DefaultAccountCreation.js";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
// run the role check
|
||||
setTimeout(() => {
|
||||
areRolesIn();
|
||||
}, 5000);
|
||||
|
||||
const routes = [
|
||||
login,
|
||||
register,
|
||||
session,
|
||||
profile,
|
||||
getAccess,
|
||||
setAccess,
|
||||
createUser,
|
||||
allUsers,
|
||||
allUserRoles,
|
||||
updateUser,
|
||||
] as const;
|
||||
|
||||
// app.route("/server", modules);
|
||||
const appRoutes = routes.forEach((route) => {
|
||||
app.route("/auth", route);
|
||||
});
|
||||
|
||||
// setTimeout(() => {
|
||||
// massAccountCreation();
|
||||
// }, 1000 * 60);
|
||||
|
||||
export default app;
|
||||
70
lstV2/server/services/auth/controllers/login.ts
Normal file
70
lstV2/server/services/auth/controllers/login.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import jwt from "jsonwebtoken";
|
||||
import {db} from "../../../../database/dbclient.js";
|
||||
import {users} from "../../../../database/schema/users.js";
|
||||
import {eq, sql} from "drizzle-orm";
|
||||
import {checkPassword} from "../utils/checkPassword.js";
|
||||
import {roleCheck} from "./userRoles/getUserAccess.js";
|
||||
import {createLog} from "../../logger/logger.js";
|
||||
import {differenceInDays} from "date-fns";
|
||||
|
||||
/**
|
||||
* Authenticate a user and return a JWT.
|
||||
*/
|
||||
const {sign} = jwt;
|
||||
|
||||
export async function login(
|
||||
username: string,
|
||||
password: string
|
||||
): Promise<{token: string; user: {user_id: string; username: string}}> {
|
||||
const user = await db.select().from(users).where(eq(users.username, username));
|
||||
|
||||
//console.log(user);
|
||||
if (user.length === 0) {
|
||||
throw new Error("Invalid or Missing user");
|
||||
}
|
||||
// check the password
|
||||
const checkedPass = await checkPassword(password, user[0]?.password);
|
||||
//console.log(checkedPass);
|
||||
if (!checkedPass) {
|
||||
throw new Error("Invalid Password");
|
||||
}
|
||||
|
||||
// Create a JWT
|
||||
const secret: string = process.env.JWT_SECRET!;
|
||||
const expiresIn = Number(process.env.JWT_EXPIRES!) || 60;
|
||||
|
||||
// get the user roles
|
||||
const roles = await roleCheck(user[0].user_id);
|
||||
const userData = {
|
||||
user_id: user[0].user_id,
|
||||
username: user[0].username,
|
||||
email: user[0].email,
|
||||
//roles: roles || null,
|
||||
role: user[0].role || null, // this should be removed onces full migration to v2 is completed
|
||||
prod: btoa(`${username.toLowerCase()}:${password}`),
|
||||
};
|
||||
|
||||
// update the user last login
|
||||
try {
|
||||
const lastLog = await db
|
||||
.update(users)
|
||||
.set({lastLogin: sql`NOW()`})
|
||||
.where(eq(users.user_id, user[0].user_id))
|
||||
.returning({lastLogin: users.lastLogin});
|
||||
createLog(
|
||||
"info",
|
||||
"lst",
|
||||
"auth",
|
||||
`Its been ${differenceInDays(lastLog[0]?.lastLogin ?? "", new Date(Date.now()))} days since ${
|
||||
user[0].username
|
||||
} has logged in`
|
||||
);
|
||||
//]);
|
||||
} catch (error) {
|
||||
createLog("error", "lst", "auth", "There was an error updating the user last login");
|
||||
}
|
||||
|
||||
const token = sign({user: userData}, secret, {expiresIn: expiresIn * 60});
|
||||
|
||||
return {token, user: userData};
|
||||
}
|
||||
8
lstV2/server/services/auth/controllers/logout.ts
Normal file
8
lstV2/server/services/auth/controllers/logout.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Logout (clear the token).
|
||||
* This is a placeholder function since JWTs are stateless.
|
||||
* In a real app, you might want to implement token blacklisting.
|
||||
*/
|
||||
export function logout(): {message: string} {
|
||||
return {message: "Logout successful"};
|
||||
}
|
||||
62
lstV2/server/services/auth/controllers/register.ts
Normal file
62
lstV2/server/services/auth/controllers/register.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../../../../database/dbclient.js";
|
||||
import { users } from "../../../../database/schema/users.js";
|
||||
import { createPassword } from "../utils/createPassword.js";
|
||||
import { setSysAdmin } from "./userRoles/setSysAdmin.js";
|
||||
import { createLog } from "../../logger/logger.js";
|
||||
|
||||
export const registerUser = async (
|
||||
username: string,
|
||||
password: string,
|
||||
email: string
|
||||
) => {
|
||||
const usercount = await db.select().from(users);
|
||||
|
||||
// make sure the user dose not already exist in the system
|
||||
const userCheck = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.username, username));
|
||||
|
||||
if (userCheck.length === 1) {
|
||||
return {
|
||||
success: false,
|
||||
message: `${username} already exists please login or reset password, if you feel this is an error please contact your admin.`,
|
||||
};
|
||||
}
|
||||
|
||||
// make sure we only send over a username that is all lowercase
|
||||
username = username.toLowerCase();
|
||||
|
||||
// get the good kinda password
|
||||
password = await createPassword(password);
|
||||
|
||||
try {
|
||||
const user = await db
|
||||
.insert(users)
|
||||
.values({ username, email, password })
|
||||
.returning({ user: users.username, email: users.email });
|
||||
|
||||
if (usercount.length === 0) {
|
||||
createLog(
|
||||
"info",
|
||||
"auth",
|
||||
"auth",
|
||||
`${username} is the first user and will be set to system admin.`
|
||||
);
|
||||
const updateUser = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.username, username));
|
||||
setSysAdmin(updateUser, "systemAdmin");
|
||||
}
|
||||
|
||||
return { success: true, message: "User Registered", user };
|
||||
} catch (error) {
|
||||
createLog("error", "auth", "auth", `${error}`);
|
||||
return {
|
||||
success: false,
|
||||
message: `${username} already exists please login or reset password, if you feel this is an error please contact your admin.`,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { userRoles } from "../../../../../database/schema/userRoles.js";
|
||||
import { returnRes } from "../../../../globalUtils/routeDefs/returnRes.js";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../../logger/logger.js";
|
||||
|
||||
export const getAllUsersRoles = async () => {
|
||||
/**
|
||||
* returns all users that are in lst
|
||||
*/
|
||||
createLog("info", "apiAuthedRoute", "auth", "Get all users");
|
||||
const { data, error } = await tryCatch(db.select().from(userRoles));
|
||||
|
||||
if (error) {
|
||||
return returnRes(
|
||||
false,
|
||||
"auth",
|
||||
"auth",
|
||||
"There was an error getting users",
|
||||
"error",
|
||||
new Error("No user exists.")
|
||||
);
|
||||
}
|
||||
|
||||
return returnRes(true, "auth", "auth", "All users.", "info", data);
|
||||
};
|
||||
45
lstV2/server/services/auth/controllers/userAdmin/getUsers.ts
Normal file
45
lstV2/server/services/auth/controllers/userAdmin/getUsers.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { userRoles } from "../../../../../database/schema/userRoles.js";
|
||||
import { users } from "../../../../../database/schema/users.js";
|
||||
import { returnRes } from "../../../../globalUtils/routeDefs/returnRes.js";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../../logger/logger.js";
|
||||
|
||||
export const getAllUsers = async () => {
|
||||
/**
|
||||
* returns all users that are in lst
|
||||
*/
|
||||
createLog("info", "apiAuthedRoute", "auth", "Get all users");
|
||||
|
||||
// get all users
|
||||
const { data, error } = await tryCatch(db.select().from(users));
|
||||
|
||||
// add there modules they are in
|
||||
const { data: m, error: em } = await tryCatch(db.select().from(userRoles));
|
||||
|
||||
const user: any = data;
|
||||
const userData = user.map((i: any) => {
|
||||
// module in
|
||||
const module = m?.filter((x: any) => x.user_id === i.user_id);
|
||||
|
||||
if (module) {
|
||||
return { ...i, moduleRoles: module };
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
if (error) {
|
||||
returnRes(
|
||||
false,
|
||||
"auth",
|
||||
"auth",
|
||||
"There was an error getting users",
|
||||
"error",
|
||||
new Error("No user exists.")
|
||||
);
|
||||
}
|
||||
|
||||
//returnRes(true, "All users.", data);
|
||||
return { success: true, message: "All users", data: userData };
|
||||
};
|
||||
@@ -0,0 +1,102 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { users } from "../../../../../database/schema/users.js";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
import type { User } from "../../../../types/users.js";
|
||||
import { createPassword } from "../../utils/createPassword.js";
|
||||
import { createLog } from "../../../logger/logger.js";
|
||||
import { sendEmail } from "../../../notifications/controller/sendMail.js";
|
||||
import { settings } from "../../../../../database/schema/settings.js";
|
||||
import { getSettings } from "../../../server/controller/settings/getSettings.js";
|
||||
|
||||
export const updateUserADM = async (userData: User) => {
|
||||
/**
|
||||
* The user model will need to be passed over so we can update per the request on the user.
|
||||
* password, username, email.
|
||||
*/
|
||||
|
||||
createLog(
|
||||
"info",
|
||||
"apiAuthedRoute",
|
||||
"auth",
|
||||
`${userData.user_id} is being updated.`
|
||||
);
|
||||
// get the orignal user info
|
||||
const { data: user, error: userError } = await tryCatch(
|
||||
db.select().from(users).where(eq(users.user_id, userData.user_id!))
|
||||
);
|
||||
|
||||
if (userError) {
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error getting the user",
|
||||
userError,
|
||||
};
|
||||
}
|
||||
if (user?.length === 0) {
|
||||
return {
|
||||
success: false,
|
||||
message:
|
||||
"The user you are looking for has either been deleted or dose not exist.",
|
||||
};
|
||||
}
|
||||
|
||||
//const { data: s, error: se } = await tryCatch(db.select().from(settings));
|
||||
const { data: s, error: se } = await tryCatch(getSettings());
|
||||
if (se) {
|
||||
return {
|
||||
success: false,
|
||||
message: `There was an error getting setting data to post to the server.`,
|
||||
data: se,
|
||||
};
|
||||
}
|
||||
|
||||
const set: any = s;
|
||||
const server = set.filter((n: any) => n.name === "server");
|
||||
const port = set.filter((n: any) => n.name === "serverPort");
|
||||
|
||||
const upd_user = user as User;
|
||||
const password: string = userData.password
|
||||
? await createPassword(userData.password!)
|
||||
: upd_user.password!;
|
||||
const data = {
|
||||
username: userData.username ? userData.username : upd_user?.username,
|
||||
password: password,
|
||||
email: userData.email ? userData.email : upd_user.email,
|
||||
role: userData.role ? userData.role : upd_user.role,
|
||||
};
|
||||
|
||||
// term ? ilike(posts.title, term) : undefined
|
||||
const { data: updData, error: updError } = await tryCatch(
|
||||
db.update(users).set(data).where(eq(users.user_id, userData.user_id!))
|
||||
);
|
||||
|
||||
if (updError) {
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error getting the user",
|
||||
updError,
|
||||
};
|
||||
}
|
||||
|
||||
if (userData?.password!.length > 0) {
|
||||
// send this user an email so they have the randomized password.
|
||||
await sendEmail({
|
||||
email: user[0]?.email,
|
||||
subject: "LST - Password reset.",
|
||||
template: "passwordReset",
|
||||
context: {
|
||||
password: userData.password!,
|
||||
username: user[0].username!,
|
||||
server: server[0].value,
|
||||
port: port[0].value,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `${userData.username} has been updated.`,
|
||||
updData,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
pass over a users uuid and return all modules they have permission too.
|
||||
in the login route we attach it to user under roles.
|
||||
*/
|
||||
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { userRoles } from "../../../../../database/schema/userRoles.js";
|
||||
|
||||
export const roleCheck = async (user_id: string | undefined) => {
|
||||
if (!user_id) {
|
||||
throw Error("Missing user_id");
|
||||
}
|
||||
let returnRoles: any = [];
|
||||
// get the user roles by the user_id
|
||||
returnRoles = await db
|
||||
.select({
|
||||
user_id: userRoles.user_id,
|
||||
role_id: userRoles.role_id,
|
||||
module_id: userRoles.module_id,
|
||||
role: userRoles.role,
|
||||
})
|
||||
.from(userRoles)
|
||||
.where(eq(userRoles.user_id, user_id));
|
||||
|
||||
if (returnRoles[0]?.role.includes("systemAdmin")) {
|
||||
const roles = await db
|
||||
.select({
|
||||
user_id: userRoles.user_id,
|
||||
role_id: userRoles.role_id,
|
||||
module_id: userRoles.module_id,
|
||||
role: userRoles.role,
|
||||
})
|
||||
.from(userRoles)
|
||||
.where(eq(userRoles.user_id, user_id));
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
return returnRoles;
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
import {users} from "../../../../../database/schema/users.js";
|
||||
import {eq} from "drizzle-orm";
|
||||
import {db} from "../../../../../database/dbclient.js";
|
||||
import {userRoles} from "../../../../../database/schema/userRoles.js";
|
||||
import {modules} from "../../../../../database/schema/modules.js";
|
||||
import {roles} from "../../../../../database/schema/roles.js";
|
||||
import {createLog} from "../../../logger/logger.js";
|
||||
|
||||
export const setSysAdmin = async (user: any, roleName: any): Promise<void> => {
|
||||
// remove all userRoles to prevent errors
|
||||
try {
|
||||
const remove = await db.delete(userRoles).where(eq(userRoles.user_id, user[0].user_id));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
// now we want to add the user to the system admin.
|
||||
const module = await db.select().from(modules);
|
||||
const role = await db.select().from(roles).where(eq(roles.name, roleName));
|
||||
|
||||
for (let i = 0; i < module.length; i++) {
|
||||
try {
|
||||
const userRole = await db.insert(userRoles).values({
|
||||
user_id: user[0].user_id,
|
||||
role_id: role[0].role_id,
|
||||
module_id: module[i].module_id,
|
||||
role: roleName,
|
||||
});
|
||||
createLog(
|
||||
"info",
|
||||
user[0].username,
|
||||
"auth",
|
||||
`${user[0].username} has been granted access to ${module[i].name} with the role ${roleName}`
|
||||
);
|
||||
} catch (error) {
|
||||
createLog("info", "lst", "auth", `Error settings user access: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
};
|
||||
111
lstV2/server/services/auth/controllers/userRoles/setUserRoles.ts
Normal file
111
lstV2/server/services/auth/controllers/userRoles/setUserRoles.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
pass over a users uuid and return all modules they have permission too.
|
||||
in the login route we attach it to user under roles.
|
||||
*/
|
||||
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { userRoles } from "../../../../../database/schema/userRoles.js";
|
||||
import { users } from "../../../../../database/schema/users.js";
|
||||
import { modules } from "../../../../../database/schema/modules.js";
|
||||
import { roles } from "../../../../../database/schema/roles.js";
|
||||
import { setSysAdmin } from "./setSysAdmin.js";
|
||||
|
||||
export const setUserAccess = async (
|
||||
username: string,
|
||||
moduleName: string,
|
||||
roleName: string,
|
||||
override?: string
|
||||
) => {
|
||||
// get the user roles by the user_id
|
||||
const user = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.username, username));
|
||||
const module = await db
|
||||
.select()
|
||||
.from(modules)
|
||||
.where(eq(modules.name, moduleName));
|
||||
|
||||
if (
|
||||
process.env.SECRETOVERRIDECODE != override &&
|
||||
roleName === "systemAdmin"
|
||||
) {
|
||||
return {
|
||||
success: false,
|
||||
message: "The override code provided is invalid.",
|
||||
};
|
||||
}
|
||||
|
||||
const role = await db.select().from(roles).where(eq(roles.name, roleName));
|
||||
|
||||
/**
|
||||
* For system admin we want to do a little more
|
||||
*/
|
||||
|
||||
if (roleName === "systemAdmin") {
|
||||
await setSysAdmin(user, roleName);
|
||||
return {
|
||||
success: true,
|
||||
message: `${username} has been granted access to ${moduleName} with the role ${roleName}`,
|
||||
};
|
||||
}
|
||||
//console.log(user, module, role);
|
||||
|
||||
// set the user
|
||||
try {
|
||||
const userRole = await db
|
||||
.insert(userRoles)
|
||||
.values({
|
||||
user_id: user[0].user_id,
|
||||
role_id: role[0].role_id,
|
||||
module_id: module[0].module_id,
|
||||
role: roleName,
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: userRoles.user_id,
|
||||
set: { role_id: role[0].role_id, role: roleName },
|
||||
});
|
||||
//.returning({user: users.username, email: users.email});
|
||||
|
||||
// return c.json({message: "User Registered", user}, 200);
|
||||
return {
|
||||
success: true,
|
||||
message: `${username} has been granted access to ${moduleName} with the role ${roleName}`,
|
||||
};
|
||||
} catch (error) {
|
||||
await changeRole(
|
||||
roleName,
|
||||
user[0].user_id,
|
||||
module[0].module_id,
|
||||
role[0].role_id
|
||||
);
|
||||
return {
|
||||
success: true,
|
||||
message: `${username} access on ${moduleName} has been changed to ${roleName}`,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const changeRole = async (
|
||||
role: any,
|
||||
userID: any,
|
||||
moduleID: any,
|
||||
roleID: any
|
||||
) => {
|
||||
await db
|
||||
.delete(userRoles)
|
||||
.where(
|
||||
and(
|
||||
eq(userRoles.user_id, userID),
|
||||
eq(userRoles.module_id, moduleID)
|
||||
)
|
||||
);
|
||||
|
||||
const userRole = await db.insert(userRoles).values({
|
||||
user_id: userID,
|
||||
role_id: roleID,
|
||||
module_id: moduleID,
|
||||
role: role,
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
import {eq, sql} from "drizzle-orm";
|
||||
import {db} from "../../../../../database/dbclient.js";
|
||||
import {users} from "../../../../../database/schema/users.js";
|
||||
import {createLog} from "../../../logger/logger.js";
|
||||
import {createPassword} from "../../utils/createPassword.js";
|
||||
|
||||
const blacklistedTokens = new Set();
|
||||
|
||||
function blacklistToken(token: string) {
|
||||
blacklistedTokens.add(token);
|
||||
setTimeout(() => blacklistedTokens.delete(token), 3600 * 1000); // Remove after 1 hour
|
||||
}
|
||||
|
||||
function isTokenBlacklisted(token: string) {
|
||||
return blacklistedTokens.has(token);
|
||||
}
|
||||
|
||||
export const updateProfile = async (user: any, data: any, token: string) => {
|
||||
if (isTokenBlacklisted(token)) {
|
||||
createLog("warn", user.username, "auth", `${user.username} is trying to use a black listed token`);
|
||||
throw Error("This token was already used");
|
||||
}
|
||||
|
||||
//re salt and encrypt the password
|
||||
try {
|
||||
const saltPass = await createPassword(data.password);
|
||||
// update the password
|
||||
const profileUpdate = await db
|
||||
.update(users)
|
||||
.set({password: saltPass, upd_user: user.username, upd_date: sql`NOW()`})
|
||||
.where(eq(users.user_id, user.user_id));
|
||||
|
||||
blacklistToken(token);
|
||||
} catch (error) {
|
||||
createLog(
|
||||
"error",
|
||||
user.username,
|
||||
"auth",
|
||||
`Error: ${JSON.stringify(error)}, "There was an error updating the users profile"`
|
||||
);
|
||||
}
|
||||
};
|
||||
17
lstV2/server/services/auth/controllers/verifyToken.ts
Normal file
17
lstV2/server/services/auth/controllers/verifyToken.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import {sign, verify} from "jsonwebtoken";
|
||||
|
||||
/**
|
||||
* Verify a JWT and return the decoded payload.
|
||||
*/
|
||||
|
||||
const secret: string = process.env.JWT_SECRET! || "bnghsjhsd";
|
||||
const expiresIn: string = process.env.JWT_EXPIRES! || "1h";
|
||||
|
||||
export function verifyToken(token: string): {userId: number} {
|
||||
try {
|
||||
const payload = verify(token, secret) as {userId: number};
|
||||
return payload;
|
||||
} catch (err) {
|
||||
throw new Error("Invalid token");
|
||||
}
|
||||
}
|
||||
45
lstV2/server/services/auth/middleware/authMiddleware.ts
Normal file
45
lstV2/server/services/auth/middleware/authMiddleware.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import {type MiddlewareHandler} from "hono";
|
||||
import jwt from "jsonwebtoken";
|
||||
|
||||
const {sign, verify} = jwt;
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
85
lstV2/server/services/auth/middleware/roleCheck.ts
Normal file
85
lstV2/server/services/auth/middleware/roleCheck.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { createMiddleware } from "hono/factory";
|
||||
|
||||
import type { CustomJwtPayload } from "../../../types/jwtToken.js";
|
||||
import { verify } from "hono/jwt";
|
||||
import { db } from "../../../../database/dbclient.js";
|
||||
import { modules } from "../../../../database/schema/modules.js";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { userRoles } from "../../../../database/schema/userRoles.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
|
||||
const hasCorrectRole = (requiredRole: string[], module: string) =>
|
||||
createMiddleware(async (c, next) => {
|
||||
/**
|
||||
* We want to check to make sure you have the correct role to be here
|
||||
*/
|
||||
const authHeader = c.req.header("Authorization");
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
export default hasCorrectRole;
|
||||
97
lstV2/server/services/auth/routes/login.ts
Normal file
97
lstV2/server/services/auth/routes/login.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi";
|
||||
import { login } from "../controllers/login.js";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
const UserSchema = z
|
||||
.object({
|
||||
username: z.string().optional().openapi({ example: "smith002" }),
|
||||
//email: z.string().optional().openapi({example: "s.smith@example.com"}),
|
||||
password: z.string().openapi({ example: "password123" }),
|
||||
})
|
||||
.openapi("User");
|
||||
|
||||
const route = createRoute({
|
||||
tags: ["Auth"],
|
||||
summary: "Login as user",
|
||||
description: "Login as a user to get a JWT token",
|
||||
method: "post",
|
||||
path: "/login",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": { schema: UserSchema },
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
success: z.boolean().openapi({ example: true }),
|
||||
message: z.string().openapi({ example: "Logged in" }),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Response message",
|
||||
},
|
||||
|
||||
400: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
success: z.boolean().openapi({ example: false }),
|
||||
message: z
|
||||
.string()
|
||||
.openapi({ example: "Username and password required" }),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Bad request",
|
||||
},
|
||||
401: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
success: z.boolean().openapi({ example: false }),
|
||||
message: z
|
||||
.string()
|
||||
.openapi({ example: "Username and password required" }),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Bad request",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
app.openapi(route, async (c) => {
|
||||
const { username, password, email } = await c.req.json();
|
||||
|
||||
if (!username || !password) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "Username and password are required",
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const { token, user } = await login(username.toLowerCase(), password);
|
||||
|
||||
// Set the JWT as an HTTP-only cookie
|
||||
//c.header("Set-Cookie", `auth_token=${token}; HttpOnly; Secure; Path=/; SameSite=None; Max-Age=3600`);
|
||||
|
||||
return c.json(
|
||||
{ success: true, message: "Login successful", user, token },
|
||||
200
|
||||
);
|
||||
} catch (err) {
|
||||
return c.json({ success: false, message: "Incorrect Credentials" }, 401);
|
||||
}
|
||||
});
|
||||
|
||||
export default app;
|
||||
110
lstV2/server/services/auth/routes/register.ts
Normal file
110
lstV2/server/services/auth/routes/register.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
import { registerUser } from "../controllers/register.js";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
const UserSchema = z.object({
|
||||
username: z
|
||||
.string()
|
||||
.regex(/^[a-zA-Z0-9_]{3,30}$/)
|
||||
.openapi({ example: "smith034" }),
|
||||
email: z.string().email().openapi({ example: "smith@example.com" }),
|
||||
password: z
|
||||
.string()
|
||||
.min(6, { message: "Passwords must be longer than 3 characters" })
|
||||
.regex(/[A-Z]/, {
|
||||
message: "Password must contain at least one uppercase letter",
|
||||
})
|
||||
.regex(/[\W_]/, {
|
||||
message: "Password must contain at least one special character",
|
||||
})
|
||||
.openapi({ example: "Password1!" }),
|
||||
});
|
||||
|
||||
type User = z.infer<typeof UserSchema>;
|
||||
|
||||
const responseSchema = z.object({
|
||||
success: z.boolean().optional().openapi({ example: true }),
|
||||
message: z.string().optional().openapi({ example: "User Created" }),
|
||||
});
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["Auth"],
|
||||
summary: "Register a new user",
|
||||
method: "post",
|
||||
path: "/register",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": { schema: UserSchema },
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: { "application/json": { schema: responseSchema } },
|
||||
description: "Retrieve the user",
|
||||
},
|
||||
400: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
success: z.boolean().openapi({ example: false }),
|
||||
message: z
|
||||
.string()
|
||||
.openapi({ example: "Invalid credentials passed" }),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Retrieve the user",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
// apit hit
|
||||
apiHit(c, { endpoint: "api/auth/register" });
|
||||
let { username, email, password } = await c.req.json();
|
||||
|
||||
if (!username || !email || !password) {
|
||||
return c.json({ success: false, message: "Credentials missing" }, 400);
|
||||
}
|
||||
|
||||
// some usernames that should be ignored
|
||||
const badActors = ["admin", "root"];
|
||||
if (badActors.includes(username)) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: `${username} is not a valid name to be registerd please try again`,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const register = await registerUser(username, password, email);
|
||||
|
||||
return c.json(
|
||||
{
|
||||
success: register.success,
|
||||
message: register.message,
|
||||
user: register?.user,
|
||||
},
|
||||
200
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: `${username} already exists please login or reset password, if you feel this is an error please contact your admin.`,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export default app;
|
||||
122
lstV2/server/services/auth/routes/session.ts
Normal file
122
lstV2/server/services/auth/routes/session.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi";
|
||||
import { verify } from "hono/jwt";
|
||||
|
||||
import { authMiddleware } from "../middleware/authMiddleware.js";
|
||||
import jwt from "jsonwebtoken";
|
||||
|
||||
const session = new OpenAPIHono();
|
||||
const expiresIn = Number(process.env.JWT_EXPIRES!) || 60;
|
||||
const secret: string = process.env.JWT_SECRET!;
|
||||
|
||||
const { sign } = jwt;
|
||||
const UserSchema = z.object({
|
||||
username: z
|
||||
.string()
|
||||
.regex(/^[a-zA-Z0-9_]{3,30}$/)
|
||||
.openapi({ example: "smith034" }),
|
||||
email: z.string().email().openapi({ example: "smith@example.com" }),
|
||||
password: z
|
||||
.string()
|
||||
.min(6, { message: "Passwords must be longer than 3 characters" })
|
||||
.regex(/[A-Z]/, {
|
||||
message: "Password must contain at least one uppercase letter",
|
||||
})
|
||||
.regex(/[\W_]/, {
|
||||
message: "Password must contain at least one special character",
|
||||
})
|
||||
.openapi({ example: "Password1!" }),
|
||||
});
|
||||
|
||||
session.openapi(
|
||||
createRoute({
|
||||
tags: ["Auth"],
|
||||
summary: "Checks a user session based on there token",
|
||||
description: "Can post there via Authentiaction header or cookies",
|
||||
method: "get",
|
||||
path: "/session",
|
||||
middleware: authMiddleware,
|
||||
// request: {
|
||||
// body: {
|
||||
// content: {
|
||||
// "application/json": {schema: UserSchema},
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
data: z.object({
|
||||
token: z
|
||||
.string()
|
||||
.openapi({
|
||||
example: "sdkjhgsldkvhdakl;jvhs;adkjfhvds.kvnsad;ovhads",
|
||||
}),
|
||||
// user: z.object({
|
||||
// user_id: z.string().openapi({example: "04316c86-f086-4cc6-b3d4-cca164a26f3f"}),
|
||||
// username: z.string().openapi({example: "smith"}),
|
||||
// email: z.string().openapi({example: "smith@example.com"}).optional(),
|
||||
// }),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Login successful",
|
||||
},
|
||||
401: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
message: z.string().openapi({ example: "Unathenticated" }),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Error of why you were not logged in.",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
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] || "";
|
||||
|
||||
// try {
|
||||
// const payload = await verify(token, process.env.JWT_SECRET!);
|
||||
// //console.log(payload);
|
||||
// //return c.json({data: {token, user: payload.user}}, 200);
|
||||
// return c.json({message: "something"});
|
||||
// } catch (err) {
|
||||
// return c.json({error: "Invalid or expired token"}, 401);
|
||||
// }
|
||||
// });
|
||||
|
||||
export default session;
|
||||
59
lstV2/server/services/auth/routes/user/getUserRoles.ts
Normal file
59
lstV2/server/services/auth/routes/user/getUserRoles.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi";
|
||||
import jwt from "jsonwebtoken";
|
||||
import type { CustomJwtPayload } from "../../../../types/jwtToken.js";
|
||||
import { authMiddleware } from "../../middleware/authMiddleware.js";
|
||||
import { roleCheck } from "../../controllers/userRoles/getUserAccess.js";
|
||||
|
||||
const { verify } = jwt;
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
const responseSchema = z.object({
|
||||
message: z.string().optional().openapi({ example: "User Created" }),
|
||||
});
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["auth:user"],
|
||||
summary: "returns the users access",
|
||||
method: "get",
|
||||
path: "/getuseraccess",
|
||||
middleware: [authMiddleware],
|
||||
responses: {
|
||||
200: {
|
||||
content: { "application/json": { schema: responseSchema } },
|
||||
description: "Retrieve the user",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
// apit hit
|
||||
//apiHit(c, { endpoint: "api/auth/getUserRoles" });
|
||||
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 canAccess = await roleCheck(payload.user?.user_id);
|
||||
|
||||
return c.json(
|
||||
{
|
||||
sucess: true,
|
||||
message: `User ${payload.user?.username} can access`,
|
||||
data: canAccess,
|
||||
},
|
||||
200
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
return c.json({ message: "UserRoles coming over" });
|
||||
}
|
||||
);
|
||||
|
||||
export default app;
|
||||
130
lstV2/server/services/auth/routes/user/profileUpdate.ts
Normal file
130
lstV2/server/services/auth/routes/user/profileUpdate.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { authMiddleware } from "../../middleware/authMiddleware.js";
|
||||
import { updateProfile } from "../../controllers/users/updateProfile.js";
|
||||
import { verify } from "hono/jwt";
|
||||
import { createLog } from "../../../logger/logger.js";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
const UserSchema = z.object({
|
||||
password: z
|
||||
.string()
|
||||
.min(6, { message: "Passwords must be longer than 3 characters" })
|
||||
.regex(/[A-Z]/, {
|
||||
message: "Password must contain at least one uppercase letter",
|
||||
})
|
||||
.regex(/[\W_]/, {
|
||||
message: "Password must contain at least one special character",
|
||||
})
|
||||
.openapi({ example: "Password1!" }),
|
||||
});
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["auth:user"],
|
||||
summary: "Updates a users Profile",
|
||||
description: "Currently you can only update your password over the API",
|
||||
method: "patch",
|
||||
path: "/profile",
|
||||
middleware: authMiddleware,
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": { schema: UserSchema },
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
message: z.string().optional().openapi({
|
||||
example: "User Profile has been updated",
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Sucess return",
|
||||
},
|
||||
401: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
message: z
|
||||
.string()
|
||||
.optional()
|
||||
.openapi({ example: "Unauthenticated" }),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Unauthorized",
|
||||
},
|
||||
500: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
message: z
|
||||
.string()
|
||||
.optional()
|
||||
.openapi({ example: "Internal Server error" }),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Internal Server Error",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
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({ success: false, message: "Unauthorized" }, 401);
|
||||
}
|
||||
|
||||
const token = authHeader?.split("Bearer ")[1] || "";
|
||||
let user;
|
||||
|
||||
try {
|
||||
const payload = await verify(token, process.env.JWT_SECRET!);
|
||||
user = payload.user;
|
||||
} catch (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
"auth",
|
||||
"Failed session check, user must be logged out"
|
||||
);
|
||||
return c.json({ success: false, message: "Unauthorized" }, 401);
|
||||
}
|
||||
|
||||
// now pass all the data over to update the user info
|
||||
try {
|
||||
const data = await c?.req.json();
|
||||
await updateProfile(user, data, token);
|
||||
return c.json({
|
||||
success: true,
|
||||
message: "Your profile has been updated",
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return c.json({
|
||||
success: false,
|
||||
message: "There was an error",
|
||||
error,
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export default app;
|
||||
115
lstV2/server/services/auth/routes/userAdmin/createUser.ts
Normal file
115
lstV2/server/services/auth/routes/userAdmin/createUser.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi";
|
||||
import { registerUser } from "../../controllers/register.js";
|
||||
import { authMiddleware } from "../../middleware/authMiddleware.js";
|
||||
import hasCorrectRole from "../../middleware/roleCheck.js";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
const UserSchema = z.object({
|
||||
username: z
|
||||
.string()
|
||||
.regex(/^[a-zA-Z0-9_]{3,30}$/)
|
||||
.openapi({ example: "smith034" }),
|
||||
email: z.string().email().openapi({ example: "smith@example.com" }),
|
||||
password: z
|
||||
.string()
|
||||
.min(6, { message: "Passwords must be longer than 3 characters" })
|
||||
.regex(/[A-Z]/, {
|
||||
message: "Password must contain at least one uppercase letter",
|
||||
})
|
||||
.regex(/[\W_]/, {
|
||||
message: "Password must contain at least one special character",
|
||||
})
|
||||
.openapi({ example: "Password1!" }),
|
||||
});
|
||||
|
||||
type User = z.infer<typeof UserSchema>;
|
||||
|
||||
const responseSchema = z.object({
|
||||
success: z.boolean().optional().openapi({ example: true }),
|
||||
message: z.string().optional().openapi({ example: "User Created" }),
|
||||
});
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["Auth:admin"],
|
||||
summary: "Creates user",
|
||||
method: "post",
|
||||
path: "/",
|
||||
middleware: [
|
||||
authMiddleware,
|
||||
hasCorrectRole(["admin", "systemAdmin"], "admin"),
|
||||
],
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": { schema: UserSchema },
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: { "application/json": { schema: responseSchema } },
|
||||
description: "Retrieve the user",
|
||||
},
|
||||
400: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
success: z.boolean().openapi({ example: false }),
|
||||
message: z
|
||||
.string()
|
||||
.openapi({ example: "Invalid credentials passed" }),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Retrieve the user",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
// apit hit
|
||||
//apiHit(c, {endpoint: "api/auth/register"});
|
||||
let { username, email, password } = await c.req.json();
|
||||
|
||||
if (!username || !email || !password) {
|
||||
return c.json({ success: false, message: "Credentials missing" }, 400);
|
||||
}
|
||||
|
||||
// some usernames that should be ignored
|
||||
const badActors = ["admin", "root"];
|
||||
if (badActors.includes(username)) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: `${username} is not a valid name to be registerd please try again`,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const register = await registerUser(username, password, email);
|
||||
|
||||
return c.json(
|
||||
{
|
||||
success: register.success,
|
||||
message: register.message,
|
||||
user: register?.user,
|
||||
},
|
||||
200
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: `${username} already exists please login or reset password, if you feel this is an error please contact your admin.`,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export default app;
|
||||
@@ -0,0 +1,34 @@
|
||||
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi";
|
||||
|
||||
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
|
||||
import { authMiddleware } from "../../middleware/authMiddleware.js";
|
||||
import hasCorrectRole from "../../middleware/roleCheck.js";
|
||||
import { getAllUsersRoles } from "../../controllers/userAdmin/getAllUserRoles.js";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["Auth:admin"],
|
||||
summary: "Gets Users Roles",
|
||||
method: "get",
|
||||
path: "/allusersroles",
|
||||
middleware: [
|
||||
authMiddleware,
|
||||
hasCorrectRole(["admin", "systemAdmin"], "admin"),
|
||||
],
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
// apit hit
|
||||
//apiHit(c, {endpoint: "api/auth/register"});
|
||||
const allUsers: any = await getAllUsersRoles();
|
||||
return c.json({
|
||||
success: allUsers?.success,
|
||||
message: allUsers?.message,
|
||||
data: allUsers?.data,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
export default app;
|
||||
35
lstV2/server/services/auth/routes/userAdmin/getUsers.ts
Normal file
35
lstV2/server/services/auth/routes/userAdmin/getUsers.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi";
|
||||
|
||||
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
|
||||
|
||||
import { getAllUsers } from "../../controllers/userAdmin/getUsers.js";
|
||||
import { authMiddleware } from "../../middleware/authMiddleware.js";
|
||||
import hasCorrectRole from "../../middleware/roleCheck.js";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["Auth:admin"],
|
||||
summary: "Gets Users",
|
||||
method: "get",
|
||||
path: "/allusers",
|
||||
middleware: [
|
||||
authMiddleware,
|
||||
hasCorrectRole(["admin", "systemAdmin"], "admin"),
|
||||
],
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
// apit hit
|
||||
//apiHit(c, {endpoint: "api/auth/register"});
|
||||
const allUsers: any = await getAllUsers();
|
||||
return c.json({
|
||||
success: allUsers?.success,
|
||||
message: allUsers?.message,
|
||||
data: allUsers?.data,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
export default app;
|
||||
80
lstV2/server/services/auth/routes/userAdmin/setUserRoles.ts
Normal file
80
lstV2/server/services/auth/routes/userAdmin/setUserRoles.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { setUserAccess } from "../../controllers/userRoles/setUserRoles.js";
|
||||
import { apiHit } from "../../../../globalUtils/apiHits.js";
|
||||
import { apiReturn } from "../../../../globalUtils/apiReturn.js";
|
||||
import { authMiddleware } from "../../middleware/authMiddleware.js";
|
||||
import hasCorrectRole from "../../middleware/roleCheck.js";
|
||||
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
const responseSchema = z.object({
|
||||
success: z.boolean().openapi({ example: true }),
|
||||
message: z.string().optional().openapi({ example: "user access" }),
|
||||
data: z.array(z.object({})).optional().openapi({ example: [] }),
|
||||
});
|
||||
|
||||
const UserAccess = z.object({
|
||||
username: z
|
||||
.string()
|
||||
.regex(/^[a-zA-Z0-9_]{3,30}$/)
|
||||
.openapi({ example: "smith034" }),
|
||||
module: z.string().openapi({ example: "production" }),
|
||||
role: z.string().openapi({ example: "viewer" }),
|
||||
override: z.string().optional().openapi({ example: "secretString" }),
|
||||
});
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["Auth:admin"],
|
||||
summary: "Sets Users access",
|
||||
method: "post",
|
||||
path: "/setuseraccess",
|
||||
middleware: [
|
||||
authMiddleware,
|
||||
hasCorrectRole(["admin", "systemAdmin"], "admin"),
|
||||
],
|
||||
description: "When logged in you will be able to grant new permissions",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": { schema: UserAccess },
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
//apiHit(c, { endpoint: "api/auth/setUserRoles" });
|
||||
const { username, module, role, override } = await c.req.json();
|
||||
try {
|
||||
const access = await setUserAccess(
|
||||
username,
|
||||
module,
|
||||
role,
|
||||
override
|
||||
);
|
||||
//return apiReturn(c, true, access?.message, access?.data, 200);
|
||||
return c.json(
|
||||
{
|
||||
success: access.success,
|
||||
message: access.message,
|
||||
data: [], //access?.data,
|
||||
},
|
||||
200
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
//return apiReturn(c, false, "Error in setting the user access", error, 400);
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "Error in setting the user access",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
85
lstV2/server/services/auth/routes/userAdmin/updateUser.ts
Normal file
85
lstV2/server/services/auth/routes/userAdmin/updateUser.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { setUserAccess } from "../../controllers/userRoles/setUserRoles.js";
|
||||
import { apiHit } from "../../../../globalUtils/apiHits.js";
|
||||
import { apiReturn } from "../../../../globalUtils/apiReturn.js";
|
||||
import { authMiddleware } from "../../middleware/authMiddleware.js";
|
||||
import hasCorrectRole from "../../middleware/roleCheck.js";
|
||||
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
|
||||
import { updateUserADM } from "../../controllers/userAdmin/updateUserAdm.js";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
const responseSchema = z.object({
|
||||
success: z.boolean().openapi({ example: true }),
|
||||
message: z.string().optional().openapi({ example: "user access" }),
|
||||
data: z.array(z.object({})).optional().openapi({ example: [] }),
|
||||
});
|
||||
|
||||
const UserAccess = z.object({
|
||||
user_id: z.string().openapi({ example: "users UUID" }),
|
||||
username: z
|
||||
.string()
|
||||
.regex(/^[a-zA-Z0-9_]{3,30}$/)
|
||||
.optional()
|
||||
.openapi({ example: "smith034" }),
|
||||
email: z
|
||||
.string()
|
||||
.email()
|
||||
.optional()
|
||||
.openapi({ example: "smith@example.com" }),
|
||||
password: z
|
||||
.string()
|
||||
|
||||
.optional()
|
||||
.openapi({ example: "Password1!" }),
|
||||
});
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["Auth:admin"],
|
||||
summary: "updates a specific user",
|
||||
method: "patch",
|
||||
path: "/updateuser",
|
||||
middleware: [
|
||||
authMiddleware,
|
||||
hasCorrectRole(["admin", "systemAdmin"], "admin"),
|
||||
],
|
||||
//description: "When logged in you will be able to grant new permissions",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": { schema: UserAccess },
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
//apiHit(c, { endpoint: "api/auth/setUserRoles" });
|
||||
const userData = await c.req.json();
|
||||
try {
|
||||
const userUPD: any = await updateUserADM(userData);
|
||||
//return apiReturn(c, true, access?.message, access?.data, 200);
|
||||
return c.json(
|
||||
{
|
||||
success: userUPD.success,
|
||||
message: userUPD.message,
|
||||
data: userUPD.data,
|
||||
},
|
||||
200
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
//return apiReturn(c, false, "Error in setting the user access", error, 400);
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "Error in setting the user access",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
56
lstV2/server/services/auth/utils/DefaultAccountCreation.ts
Normal file
56
lstV2/server/services/auth/utils/DefaultAccountCreation.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { db } from "../../../../database/dbclient.js";
|
||||
import { users } from "../../../../database/schema/users.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../logger/logger.js";
|
||||
import { setSysAdmin } from "../controllers/userRoles/setSysAdmin.js";
|
||||
import { createPassword } from "./createPassword.js";
|
||||
|
||||
export const massAccountCreation = async () => {
|
||||
/**
|
||||
* This will create a new account for all users before if they are already in there it will update just there password.
|
||||
*
|
||||
*/
|
||||
|
||||
const user: any = [
|
||||
// {
|
||||
// username: "landa002",
|
||||
// email: "Oscar.Landa@alpla.com",
|
||||
// password: "Frostlike-Petri5-Ungreased!",
|
||||
// },
|
||||
];
|
||||
|
||||
for (let i = 0; i < user.length; i++) {
|
||||
const updatedUser = {
|
||||
username: user[i].username,
|
||||
email: user[i].email,
|
||||
password: await createPassword(user[i].password),
|
||||
};
|
||||
const { data, error } = await tryCatch(
|
||||
db
|
||||
.insert(users)
|
||||
.values(updatedUser)
|
||||
.onConflictDoUpdate({
|
||||
target: users.username,
|
||||
set: {
|
||||
password: updatedUser.password,
|
||||
email: updatedUser.email,
|
||||
},
|
||||
})
|
||||
.returning({
|
||||
user_id: users.user_id,
|
||||
username: users.username,
|
||||
})
|
||||
);
|
||||
|
||||
await setSysAdmin(data, "systemAdmin");
|
||||
|
||||
if (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
"auth",
|
||||
`There was an error creating ${user[i].username}`
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
18
lstV2/server/services/auth/utils/checkPassword.ts
Normal file
18
lstV2/server/services/auth/utils/checkPassword.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import bcrypt from "bcryptjs";
|
||||
export const checkPassword = async (
|
||||
currentPassword: string,
|
||||
dbPassword: string
|
||||
) => {
|
||||
let decyptPass = "";
|
||||
try {
|
||||
decyptPass = atob(dbPassword);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
// encypt password
|
||||
const pass: string | undefined = process.env.SECRET;
|
||||
|
||||
const checked = bcrypt.compareSync(pass + currentPassword, decyptPass);
|
||||
|
||||
return checked;
|
||||
};
|
||||
17
lstV2/server/services/auth/utils/createPassword.ts
Normal file
17
lstV2/server/services/auth/utils/createPassword.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import bcrypt from "bcryptjs";
|
||||
|
||||
export const createPassword = async (password: string) => {
|
||||
// encypt password
|
||||
let pass: string | undefined = process.env.SECRET;
|
||||
let salt: string | undefined = process.env.SALTING;
|
||||
|
||||
if (!pass || !salt) {
|
||||
pass = "error";
|
||||
} else {
|
||||
pass = bcrypt.hashSync(pass + password, parseInt(salt));
|
||||
|
||||
pass = btoa(pass);
|
||||
}
|
||||
|
||||
return pass;
|
||||
};
|
||||
44
lstV2/server/services/auth/utils/roleCheck.ts
Normal file
44
lstV2/server/services/auth/utils/roleCheck.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* check if the roles are in and if not add them.
|
||||
* this will only run on a server start up
|
||||
*/
|
||||
|
||||
import {db} from "../../../../database/dbclient.js";
|
||||
import {roles} from "../../../../database/schema/roles.js";
|
||||
import {createLog} from "../../logger/logger.js";
|
||||
// "view", "technician", "supervisor","manager", "admin", "systemAdmin"
|
||||
const newRoles = [
|
||||
{name: "viewer"},
|
||||
{name: "technician"},
|
||||
{name: "supervisor"},
|
||||
{name: "manager"},
|
||||
{name: "admin"},
|
||||
{name: "tester"},
|
||||
{name: "systemAdmin"},
|
||||
];
|
||||
export const areRolesIn = async () => {
|
||||
// get the roles
|
||||
try {
|
||||
const roleCheck = await db.select().from(roles);
|
||||
|
||||
if (roleCheck.length !== newRoles.length) {
|
||||
try {
|
||||
const newRole = await db
|
||||
.insert(roles)
|
||||
.values(newRoles)
|
||||
.onConflictDoNothing() // this will only update the ones that are new :D
|
||||
.returning({name: roles.name});
|
||||
createLog(
|
||||
"info",
|
||||
"lst",
|
||||
"auth",
|
||||
`${JSON.stringify(newRole)}, "Roles were just added due to missing them on server startup"`
|
||||
);
|
||||
} catch (error) {
|
||||
createLog("error", "lst", "auth", `There was an error adding new roles to the db, ${error}`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
createLog("error", "lst", "auth", `There was an error adding new roles to the db, ${error}`);
|
||||
}
|
||||
};
|
||||
0
lstV2/server/services/auth/utils/verifyToken.ts
Normal file
0
lstV2/server/services/auth/utils/verifyToken.ts
Normal file
47
lstV2/server/services/dataMart/controller/fakeEDIUpdate.ts
Normal file
47
lstV2/server/services/dataMart/controller/fakeEDIUpdate.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { format } from "date-fns-tz";
|
||||
import { createLog } from "../../logger/logger.js";
|
||||
import { query } from "../../sqlServer/prodSqlServer.js";
|
||||
import { fakeEDIUpdate } from "../../sqlServer/querys/dataMart/fakeEDIUpdate.js";
|
||||
|
||||
export const getFakeEDI = async (address: string) => {
|
||||
let fakeEDI: any = [];
|
||||
|
||||
let updatedQuery = fakeEDIUpdate;
|
||||
|
||||
if (address) {
|
||||
createLog(
|
||||
"info",
|
||||
"datamart",
|
||||
"datamart",
|
||||
"The user requested a specific address."
|
||||
);
|
||||
updatedQuery = fakeEDIUpdate.replace(
|
||||
"--and IdAdresse = 14",
|
||||
`and IdAdresse = ${address}`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
fakeEDI = await query(updatedQuery, "Gets fakeEDI orders to be fixed");
|
||||
|
||||
const correctedData = fakeEDI.data.map((n: any) => {
|
||||
return {
|
||||
...n,
|
||||
DeliveryDate: format(n.DeliveryDate, "M/d/yyyy HH:mm"),
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Current open orders",
|
||||
data: correctedData,
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error open orders",
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
import { query } from "../../sqlServer/prodSqlServer.js";
|
||||
import { activeArticle } from "../../sqlServer/querys/dataMart/article.js";
|
||||
|
||||
export const getActiveAv = async () => {
|
||||
let articles: any = [];
|
||||
try {
|
||||
const res = await query(activeArticle, "Get active articles");
|
||||
articles = res?.data;
|
||||
} catch (error) {
|
||||
articles = error;
|
||||
}
|
||||
|
||||
return articles;
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
import { query } from "../../sqlServer/prodSqlServer.js";
|
||||
import { customerInvNoHold } from "../../sqlServer/querys/dataMart/customerInventoryQuerys.js";
|
||||
|
||||
export const getCurrentCustomerInv = async (data: any | null) => {
|
||||
//console.log(data.customer[0]);
|
||||
|
||||
let updatedQuery = customerInvNoHold;
|
||||
|
||||
if (data.customer) {
|
||||
//console.log(data.customer);
|
||||
updatedQuery = customerInvNoHold.replaceAll(
|
||||
"--and IdAdressen",
|
||||
`and IdAdressen = ${data.customer[0]}`
|
||||
);
|
||||
}
|
||||
|
||||
if (data.whseToInclude) {
|
||||
updatedQuery = updatedQuery.replaceAll(
|
||||
"--and x.IdWarenlager in (14,15)",
|
||||
`and x.IdWarenlager in (${data.whseToInclude[0]})`
|
||||
);
|
||||
}
|
||||
try {
|
||||
const inventory: any = await query(
|
||||
updatedQuery,
|
||||
"Get active inventory"
|
||||
);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "All customer inventory minus holds",
|
||||
data: inventory.data,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error getting the inventory",
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,84 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../../../../database/dbclient.js";
|
||||
import { settings } from "../../../../database/schema/settings.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { query } from "../../sqlServer/prodSqlServer.js";
|
||||
import { deliveryByDateRange } from "../../sqlServer/querys/dataMart/deleveryByDateRange.js";
|
||||
import { addDays, format } from "date-fns";
|
||||
|
||||
export const getDeliveryByDateRange = async (data: any | null) => {
|
||||
// const { data: plantToken, error: plantError } = await tryCatch(
|
||||
// db.select().from(settings).where(eq(settings.name, "plantToken"))
|
||||
// );
|
||||
// if (plantError) {
|
||||
// return {
|
||||
// success: false,
|
||||
// message: "Error getting Settings",
|
||||
// data: plantError,
|
||||
// };
|
||||
// }
|
||||
let deliverys: any = [];
|
||||
|
||||
let updatedQuery = deliveryByDateRange;
|
||||
|
||||
// start days can be sent over
|
||||
if (data?.start) {
|
||||
updatedQuery = updatedQuery.replaceAll("[startDate]", data.start[0]);
|
||||
} else {
|
||||
updatedQuery = updatedQuery.replaceAll("[startDate]", "1990-1-1");
|
||||
}
|
||||
|
||||
// end days can be sent over
|
||||
if (data?.end) {
|
||||
updatedQuery = updatedQuery.replaceAll("[endDate]", data.end[0]);
|
||||
} else {
|
||||
const defaultEndDate = format(
|
||||
addDays(new Date(Date.now()), 5),
|
||||
"yyyy-M-d"
|
||||
);
|
||||
updatedQuery = updatedQuery.replaceAll("[endDate]", defaultEndDate);
|
||||
}
|
||||
|
||||
try {
|
||||
const res: any = await query(
|
||||
updatedQuery,
|
||||
"Get Delivery by date range"
|
||||
);
|
||||
deliverys = res.data;
|
||||
//console.log(res.data);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return {
|
||||
success: false,
|
||||
message: "All Deliveries within the range.",
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
deliverys = deliverys.splice(1000, 0);
|
||||
}
|
||||
// add plant token in
|
||||
// const pOrders = deliverys.map((item: any) => {
|
||||
// // const dateCon = new Date(item.loadingDate).toLocaleString("en-US", {
|
||||
// // month: "numeric",
|
||||
// // day: "numeric",
|
||||
// // year: "numeric",
|
||||
// // hour: "2-digit",
|
||||
// // minute: "2-digit",
|
||||
// // hour12: false,
|
||||
// // });
|
||||
|
||||
// //const dateCon = new Date(item.loadingDate).toISOString().replace("T", " ").split(".")[0];
|
||||
// const dateCon = new Date(item.loadingDate).toISOString().split("T")[0];
|
||||
// //const delDate = new Date(item.deliveryDate).toISOString().replace("T", " ").split(".")[0];
|
||||
// const delDate = new Date(item.deliveryDate).toISOString().split("T")[0];
|
||||
// return {
|
||||
// plantToken: plantToken[0].value,
|
||||
// ...item,
|
||||
// loadingDate: dateCon,
|
||||
// deliveryDate: delDate,
|
||||
// };
|
||||
// });
|
||||
return { success: true, message: "Current open orders", data: deliverys };
|
||||
};
|
||||
18
lstV2/server/services/dataMart/controller/getFifoIndex.ts
Normal file
18
lstV2/server/services/dataMart/controller/getFifoIndex.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { desc } from "drizzle-orm";
|
||||
import { db } from "../../../../database/dbclient.js";
|
||||
import { fifoIndex } from "../../../../database/schema/fifoIndex.js";
|
||||
|
||||
export const getFifoIndex = async () => {
|
||||
let articles: any = [];
|
||||
try {
|
||||
const res = await db
|
||||
.select()
|
||||
.from(fifoIndex)
|
||||
.orderBy(desc(fifoIndex.add_Date));
|
||||
articles = res;
|
||||
} catch (error) {
|
||||
articles = error;
|
||||
}
|
||||
|
||||
return articles;
|
||||
};
|
||||
24
lstV2/server/services/dataMart/controller/getFinanceAudit.ts
Normal file
24
lstV2/server/services/dataMart/controller/getFinanceAudit.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { format } from "date-fns-tz";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { query } from "../../sqlServer/prodSqlServer.js";
|
||||
import { financeAudit } from "../../sqlServer/querys/dataMart/financeAudit.js";
|
||||
|
||||
export const getfinanceAudit = async (date: any) => {
|
||||
let inventoryAudit: any = [];
|
||||
|
||||
const { data, error } = (await tryCatch(
|
||||
query(financeAudit.replace("[date]", date), "inventory audit")
|
||||
)) as any;
|
||||
|
||||
//console.log(data);
|
||||
|
||||
if (error) {
|
||||
return [];
|
||||
}
|
||||
|
||||
inventoryAudit = data.data.map((i: any) => {
|
||||
return { ...i, bookinDate: format(i.bookinDate, "MM/dd/yyyy") };
|
||||
});
|
||||
|
||||
return inventoryAudit;
|
||||
};
|
||||
73
lstV2/server/services/dataMart/controller/getOpenOrders.ts
Normal file
73
lstV2/server/services/dataMart/controller/getOpenOrders.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../../../../database/dbclient.js";
|
||||
import { settings } from "../../../../database/schema/settings.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { query } from "../../sqlServer/prodSqlServer.js";
|
||||
import { openOrders } from "../../sqlServer/querys/dataMart/openOrders.js";
|
||||
import { serverSettings } from "../../server/controller/settings/getSettings.js";
|
||||
|
||||
export const getOpenOrders = async (data: any | null) => {
|
||||
// const { data: plantToken, error: plantError } = await tryCatch(
|
||||
// db.select().from(settings).where(eq(settings.name, "plantToken"))
|
||||
// );
|
||||
// if (plantError) {
|
||||
// return {
|
||||
// success: false,
|
||||
// message: "Error getting Settings",
|
||||
// data: plantError,
|
||||
// };
|
||||
// }
|
||||
|
||||
const plantToken = serverSettings.filter((n) => n.name === "plantToken");
|
||||
let orders: any = [];
|
||||
|
||||
let updatedQuery = openOrders;
|
||||
|
||||
// start days can be sent over
|
||||
if (data?.sDay) {
|
||||
updatedQuery = updatedQuery.replaceAll("[sDay]", data.sDay[0]);
|
||||
} else {
|
||||
updatedQuery = updatedQuery.replaceAll("[sDay]", "15");
|
||||
}
|
||||
|
||||
// end days can be sent over
|
||||
if (data?.eDay) {
|
||||
updatedQuery = updatedQuery.replaceAll("[eDay]", data.eDay[0]);
|
||||
} else {
|
||||
updatedQuery = updatedQuery.replaceAll("[eDay]", "5");
|
||||
}
|
||||
|
||||
try {
|
||||
orders = await query(updatedQuery, "Get active openorders");
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Errot getting current open orders",
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
|
||||
// add plant token in
|
||||
const pOrders = orders.data.map((item: any) => {
|
||||
// const dateCon = new Date(item.loadingDate).toLocaleString("en-US", {
|
||||
// month: "numeric",
|
||||
// day: "numeric",
|
||||
// year: "numeric",
|
||||
// hour: "2-digit",
|
||||
// minute: "2-digit",
|
||||
// hour12: false,
|
||||
// });
|
||||
|
||||
//const dateCon = new Date(item.loadingDate).toISOString().replace("T", " ").split(".")[0];
|
||||
const dateCon = new Date(item.loadingDate).toISOString().split("T")[0];
|
||||
//const delDate = new Date(item.deliveryDate).toISOString().replace("T", " ").split(".")[0];
|
||||
const delDate = new Date(item.deliveryDate).toISOString().split("T")[0];
|
||||
return {
|
||||
plantToken: plantToken[0].value,
|
||||
...item,
|
||||
loadingDate: dateCon,
|
||||
deliveryDate: delDate,
|
||||
};
|
||||
});
|
||||
return { success: true, message: "Current open orders", data: pOrders };
|
||||
};
|
||||
34
lstV2/server/services/dataMart/controller/getinventory.ts
Normal file
34
lstV2/server/services/dataMart/controller/getinventory.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { createLog } from "../../logger/logger.js";
|
||||
import { query } from "../../sqlServer/prodSqlServer.js";
|
||||
import {
|
||||
totalInvNoRn,
|
||||
totalInvRn,
|
||||
} from "../../sqlServer/querys/dataMart/totalINV.js";
|
||||
|
||||
export const getINV = async (rn: boolean) => {
|
||||
let inventory: any = [];
|
||||
|
||||
let updatedQuery = totalInvNoRn;
|
||||
|
||||
if (rn) {
|
||||
createLog(
|
||||
"info",
|
||||
"datamart",
|
||||
"datamart",
|
||||
"The user requested the running numbers this could take a while."
|
||||
);
|
||||
updatedQuery = totalInvRn;
|
||||
}
|
||||
|
||||
try {
|
||||
inventory = await query(updatedQuery, "Gets Curruent inv");
|
||||
return { success: true, message: "Current inv", data: inventory.data };
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error getting the inventory",
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../logger/logger.js";
|
||||
import { query } from "../../sqlServer/prodSqlServer.js";
|
||||
import { articleInfo } from "../../sqlServer/querys/psiReport/articleData.js";
|
||||
|
||||
// type ArticleData = {
|
||||
// id: string
|
||||
// }
|
||||
export const getGetPSIArticleData = async (avs: string) => {
|
||||
let articles: any = [];
|
||||
|
||||
if (!avs) {
|
||||
return {
|
||||
success: false,
|
||||
message: `Missing av's please send at least one over`,
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
|
||||
const { data, error } = (await tryCatch(
|
||||
query(articleInfo.replace("[articles]", avs), "PSI article info")
|
||||
)) as any;
|
||||
|
||||
if (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"datamart",
|
||||
"datamart",
|
||||
`There was an error getting the article info: ${JSON.stringify(
|
||||
error
|
||||
)}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
messsage: `There was an error getting the article info`,
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
|
||||
articles = data.data;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "PSI Article Data",
|
||||
data: articles,
|
||||
};
|
||||
};
|
||||
63
lstV2/server/services/dataMart/controller/psiGetInventory.ts
Normal file
63
lstV2/server/services/dataMart/controller/psiGetInventory.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { and, between, inArray, sql } from "drizzle-orm";
|
||||
import { db } from "../../../../database/dbclient.js";
|
||||
import { invHistoricalData } from "../../../../database/schema/historicalINV.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../logger/logger.js";
|
||||
|
||||
// type ArticleData = {
|
||||
// id: string
|
||||
// }
|
||||
export const psiGetInventory = async (
|
||||
avs: string,
|
||||
startDate: string,
|
||||
endDate: string
|
||||
) => {
|
||||
let articles: any = [];
|
||||
|
||||
if (!avs) {
|
||||
return {
|
||||
success: false,
|
||||
message: `Missing av's please send at least one over`,
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
|
||||
const ids = avs.split(",").map((id) => id.trim());
|
||||
|
||||
const { data, error } = (await tryCatch(
|
||||
db
|
||||
.select()
|
||||
.from(invHistoricalData)
|
||||
.where(
|
||||
and(
|
||||
inArray(invHistoricalData.article, ids),
|
||||
between(invHistoricalData.histDate, startDate, endDate)
|
||||
)
|
||||
)
|
||||
//.limit(100)
|
||||
)) as any;
|
||||
|
||||
if (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"datamart",
|
||||
"datamart",
|
||||
`There was an error getting the planning info: ${JSON.stringify(
|
||||
error
|
||||
)}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
messsage: `There was an error getting the planning info`,
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
|
||||
articles = data;
|
||||
console.log(articles.length);
|
||||
return {
|
||||
success: true,
|
||||
message: "PSI planning Data",
|
||||
data: articles,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../logger/logger.js";
|
||||
import { query } from "../../sqlServer/prodSqlServer.js";
|
||||
import { planningNumbersByAVDate } from "../../sqlServer/querys/psiReport/planningNumbersByAv.js";
|
||||
|
||||
// type ArticleData = {
|
||||
// id: string
|
||||
// }
|
||||
export const psiGetPlanningData = async (
|
||||
avs: string,
|
||||
startDate: string,
|
||||
endDate: string
|
||||
) => {
|
||||
let articles: any = [];
|
||||
|
||||
if (!avs) {
|
||||
return {
|
||||
success: false,
|
||||
message: `Missing av's please send at least one over`,
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
|
||||
const { data, error } = (await tryCatch(
|
||||
query(
|
||||
planningNumbersByAVDate
|
||||
.replace("[articles]", avs)
|
||||
.replace("[startDate]", startDate)
|
||||
.replace("[endDate]", endDate),
|
||||
"PSI planning info"
|
||||
)
|
||||
)) as any;
|
||||
|
||||
if (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"datamart",
|
||||
"datamart",
|
||||
`There was an error getting the planning info: ${JSON.stringify(
|
||||
error
|
||||
)}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
messsage: `There was an error getting the planning info`,
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
|
||||
articles = data.data;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "PSI planning Data",
|
||||
data: articles.map((n: any) => {
|
||||
if (n.PalDay) {
|
||||
return { ...n, PalDay: n.PalDay.toFixed(2) };
|
||||
}
|
||||
|
||||
return n;
|
||||
}),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../logger/logger.js";
|
||||
import { query } from "../../sqlServer/prodSqlServer.js";
|
||||
import { productionNumbers } from "../../sqlServer/querys/psiReport/prodcuctionNumbers.js";
|
||||
|
||||
// type ArticleData = {
|
||||
// id: string
|
||||
// }
|
||||
export const psiGetProductionData = async (
|
||||
avs: string,
|
||||
startDate: string,
|
||||
endDate: string
|
||||
) => {
|
||||
let articles: any = [];
|
||||
|
||||
if (!avs) {
|
||||
return {
|
||||
success: false,
|
||||
message: `Missing av's please send at least one over`,
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
|
||||
const { data, error } = (await tryCatch(
|
||||
query(
|
||||
productionNumbers
|
||||
.replace("[articles]", avs)
|
||||
.replace("[startDate]", startDate)
|
||||
.replace("[endDate]", endDate),
|
||||
"PSI production info"
|
||||
)
|
||||
)) as any;
|
||||
|
||||
if (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"datamart",
|
||||
"datamart",
|
||||
`There was an error getting the planning info: ${JSON.stringify(
|
||||
error
|
||||
)}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
messsage: `There was an error getting the planning info`,
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
|
||||
articles = data.data;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "PSI planning Data",
|
||||
data: articles.map((n: any) => {
|
||||
if (n.PalDay) {
|
||||
return { ...n, PalDay: n.PalDay.toFixed(2) };
|
||||
}
|
||||
|
||||
return n;
|
||||
}),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
import { createLog } from "../../logger/logger.js";
|
||||
import { query } from "../../sqlServer/prodSqlServer.js";
|
||||
import { validateCityState } from "../../sqlServer/querys/dataMart/validatecityState.js";
|
||||
|
||||
export const validateCS = async () => {
|
||||
let cs: any = [];
|
||||
|
||||
let updatedQuery = validateCityState;
|
||||
|
||||
try {
|
||||
cs = await query(updatedQuery, "Get address data");
|
||||
return { success: true, message: "City State Data", data: cs.data };
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error getting city state data.",
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
};
|
||||
40
lstV2/server/services/dataMart/dataMartService.ts
Normal file
40
lstV2/server/services/dataMart/dataMartService.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { OpenAPIHono } from "@hono/zod-openapi";
|
||||
import activequerys from "./route/getCurrentQuerys.js";
|
||||
import getArticles from "./route/getActiveArticles.js";
|
||||
import currentInv from "./route/getInventory.js";
|
||||
import getCustomerInv from "./route/getCustomerInv.js";
|
||||
import getOpenOrders from "./route/getOpenOrders.js";
|
||||
import getDeliveryByDate from "./route/getDeliveryDateByRange.js";
|
||||
import fakeEDI from "./route/fakeEDI.js";
|
||||
import addressCorrections from "./route/getCityStateData.js";
|
||||
import fifoIndex from "./route/getFifoIndex.js";
|
||||
import financeAudit from "./route/getFinanceAudit.js";
|
||||
import psiArticleData from "./route/getPsiArticleData.js";
|
||||
import psiPlanningData from "./route/getPsiPlanningData.js";
|
||||
import psiProductionData from "./route/getPsiProductionData.js";
|
||||
import psiInventory from "./route/getPsiinventory.js";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
const routes = [
|
||||
activequerys,
|
||||
getArticles,
|
||||
currentInv,
|
||||
getCustomerInv,
|
||||
getOpenOrders,
|
||||
getDeliveryByDate,
|
||||
fakeEDI,
|
||||
addressCorrections,
|
||||
fifoIndex,
|
||||
financeAudit,
|
||||
psiArticleData,
|
||||
psiPlanningData,
|
||||
psiProductionData,
|
||||
psiInventory,
|
||||
] as const;
|
||||
|
||||
const appRoutes = routes.forEach((route) => {
|
||||
app.route("/datamart", route);
|
||||
});
|
||||
|
||||
export default app;
|
||||
54
lstV2/server/services/dataMart/route/fakeEDI.ts
Normal file
54
lstV2/server/services/dataMart/route/fakeEDI.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { getINV } from "../controller/getinventory.js";
|
||||
import { getFakeEDI } from "../controller/fakeEDIUpdate.js";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
const Body = z.object({
|
||||
address: z.string().openapi({ example: "x" }),
|
||||
});
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["dataMart"],
|
||||
summary: "Returns all open orders that need to be updated in fake edi.",
|
||||
method: "get",
|
||||
path: "/fakeediupdate",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": { schema: Body },
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
const address: string = c.req.query("address") ?? "";
|
||||
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
apiHit(c, { endpoint: "/fakeediupdate" });
|
||||
const { data, error } = await tryCatch(
|
||||
getFakeEDI(address.toString() ?? "")
|
||||
);
|
||||
|
||||
if (error) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "There was an error getting the inv.",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
return c.json({
|
||||
success: data.success,
|
||||
message: data.message,
|
||||
data: data.data,
|
||||
});
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
47
lstV2/server/services/dataMart/route/getActiveArticles.ts
Normal file
47
lstV2/server/services/dataMart/route/getActiveArticles.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
import { getActiveAv } from "../controller/getActiveArticles.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
const EomStat = z.object({
|
||||
plant: z.string().openapi({ example: "Salt Lake City" }),
|
||||
userRan: z.string().openapi({ example: "smith034" }),
|
||||
eomSheetVersion: z.string().openapi({ example: "0.0.223" }),
|
||||
});
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["dataMart"],
|
||||
summary: "Returns all the Active articles.",
|
||||
method: "get",
|
||||
path: "/getarticles",
|
||||
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
//const body = await c.req.json();
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
apiHit(c, { endpoint: "/getarticles" });
|
||||
try {
|
||||
return c.json(
|
||||
{
|
||||
success: true,
|
||||
message: "Current active Articles",
|
||||
data: await getActiveAv(),
|
||||
},
|
||||
200
|
||||
);
|
||||
} catch (error) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "There was an error posting the eom stat.",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
50
lstV2/server/services/dataMart/route/getCityStateData.ts
Normal file
50
lstV2/server/services/dataMart/route/getCityStateData.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { getINV } from "../controller/getinventory.js";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
import { validateCS } from "../controller/validateCityState.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
const Body = z.object({
|
||||
includeRunnningNumbers: z.string().openapi({ example: "x" }),
|
||||
});
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["dataMart"],
|
||||
summary: "Returns Address with incorrect city state.",
|
||||
method: "get",
|
||||
path: "/getaddressdata",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": { schema: Body },
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
apiHit(c, { endpoint: "/getaddressdata" });
|
||||
const { data, error } = await tryCatch(validateCS());
|
||||
|
||||
if (error) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "There was an error getting address data.",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
return c.json({
|
||||
success: data.success,
|
||||
message: data.message,
|
||||
data: data.data,
|
||||
});
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
146
lstV2/server/services/dataMart/route/getCurrentQuerys.ts
Normal file
146
lstV2/server/services/dataMart/route/getCurrentQuerys.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
const current: any = [
|
||||
{
|
||||
name: "getActiveAv",
|
||||
endpoint: "/api/datamart/getarticles",
|
||||
description: "Gets all current active AV, with specific critiera.",
|
||||
},
|
||||
// {
|
||||
// name: "getStockLaneDims",
|
||||
// endpoint: "/api/v1/masterData/getStockDims",
|
||||
// description: "Returns the lane dims along with a column to send actaul dims to be updated.",
|
||||
// },
|
||||
// {
|
||||
// name: "getAddressInfo",
|
||||
// endpoint: "/api/v1/masterData/getAddressInfo",
|
||||
// description: "Returns current active addresses with street and zip",
|
||||
// },
|
||||
// {
|
||||
// name: "getMissingPkgData",
|
||||
// endpoint: "/api/v1/masterData/getMissingPKGData",
|
||||
// description: "Returns all packaging data that is missing either printer, layout, or carton layout",
|
||||
// },
|
||||
{
|
||||
name: "getCustomerInventory",
|
||||
endpoint: "/api/datamart/getcustomerinventory",
|
||||
description:
|
||||
"Returns specific customer inventory based on there address ID, with optional to include warehouses, IE 36,41,5. leaving warehouse blank will just pull everything",
|
||||
criteria: "customer,whseToInclude",
|
||||
},
|
||||
// {
|
||||
// name: "getPalletLabels",
|
||||
// endpoint: "/api/v1/masterData/getPalletLabels",
|
||||
// description: "Returns specific amount of pallets RN, Needs label number and printer, Specfic to Dayton.",
|
||||
// criteria: "runningNumber,printerName,count",
|
||||
// },
|
||||
{
|
||||
name: "getopenorders",
|
||||
endpoint: "/api/datamart/getopenorders",
|
||||
description:
|
||||
"Returns open orders based on day count sent over, sDay 15 days in the past eDay 5 days in the future, can be left empty for this default days",
|
||||
criteria: "sDay,eDay",
|
||||
},
|
||||
// {
|
||||
// name: "getOpenIncoming",
|
||||
// endpoint: "/api/v1/masterData/getOpenIncoming",
|
||||
// description:
|
||||
// "Returns open orders based on day count sent over, sDay 15 days in the past eDay 5 days in the future, can be left empty for this default days",
|
||||
// criteria: "sDay,eDay",
|
||||
// },
|
||||
// {
|
||||
// name: "planningCheckPkg",
|
||||
// endpoint: "/api/v1/masterData/planningPkgCheck",
|
||||
// description: "Returns all lots starting later than today and has a pkg that is missing layouts.",
|
||||
// },
|
||||
{
|
||||
name: "getinventory",
|
||||
endpoint: "/api/datamart/getinventory",
|
||||
// description: "Returns all inventory, by default excludes running numebrs, also excludes inv locations.",
|
||||
description:
|
||||
"Returns all inventory, excludes inv locations. no running numbers",
|
||||
criteria: "includeRunnningNumbers", // uncomment this out once the improt process can be faster
|
||||
},
|
||||
// {
|
||||
// name: "getOpenOrderUpdates",
|
||||
// endpoint: "/api/v1/masterData/getOpenOrderUpdates",
|
||||
// // description: "Returns all inventory, by default excludes running numebrs, also excludes inv locations.",
|
||||
// description: "Returns all orders based on customer id, leaving empty will pull everythinng in.",
|
||||
// criteria: "customer", // uncomment this out once the improt process can be faster
|
||||
// },
|
||||
{
|
||||
name: "getSiloAdjustment",
|
||||
endpoint: "/api/logistics/getsilosdjustment",
|
||||
// description: "Returns all inventory, by default excludes running numebrs, also excludes inv locations.",
|
||||
description:
|
||||
"Returns all siloadjustments in selected date range IE: 1/1/2025 to 1/31/2025",
|
||||
criteria: "startDate,endDate", // uncomment this out once the improt process can be faster
|
||||
},
|
||||
{
|
||||
name: "Delivery by date trange",
|
||||
endpoint: "/api/datamart/deliverybydaterange",
|
||||
// description: "Returns all inventory, by default excludes running numebrs, also excludes inv locations.",
|
||||
description:
|
||||
"Returns all Deliverys in selected date range IE: 1/1/2025 to 1/31/2025",
|
||||
criteria: "start,end", // uncomment this out once the improt process can be faster
|
||||
},
|
||||
{
|
||||
name: "Fake Edi Update",
|
||||
endpoint: "/api/datamart/fakeediupdate",
|
||||
// description: "Returns all inventory, by default excludes running numebrs, also excludes inv locations.",
|
||||
description:
|
||||
"Returns all open orders to correct and resubmit, leaving blank will get everything putting an address only returns the specified address",
|
||||
criteria: "address", // uncomment this out once the improt process can be faster
|
||||
},
|
||||
{
|
||||
name: "Address Corrections",
|
||||
endpoint: "/api/datamart/getaddressdata",
|
||||
// description: "Returns all inventory, by default excludes running numebrs, also excludes inv locations.",
|
||||
description:
|
||||
"Returns all addresses that will not process correctly in tms due to incorrect city state setup.",
|
||||
//criteria: "address", // uncomment this out once the improt process can be faster
|
||||
},
|
||||
{
|
||||
name: "Fifo index",
|
||||
endpoint: "/api/datamart/getfifoindex",
|
||||
// description: "Returns all inventory, by default excludes running numebrs, also excludes inv locations.",
|
||||
description:
|
||||
"Returns fifo index for all pallets shipped within the last 90 days.",
|
||||
//criteria: "address", // uncomment this out once the improt process can be faster
|
||||
},
|
||||
{
|
||||
name: "Finance Audit inv",
|
||||
endpoint: "/api/datamart/getfinanceaudit",
|
||||
// description: "Returns all inventory, by default excludes running numebrs, also excludes inv locations.",
|
||||
description:
|
||||
"Returns all inventory past the date provided, ie: 5/31/2025",
|
||||
criteria: "date", // uncomment this out once the improt process can be faster
|
||||
},
|
||||
];
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["dataMart"],
|
||||
summary: "Returns all avalible querys.",
|
||||
method: "get",
|
||||
path: "/getavalibleaquerys",
|
||||
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
//const body = await c.req.json();
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
apiHit(c, { endpoint: "/getavalibleaquerys" });
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
message: "All Current Active Querys.",
|
||||
sheetVersion: 2.8,
|
||||
data: current,
|
||||
});
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
54
lstV2/server/services/dataMart/route/getCustomerInv.ts
Normal file
54
lstV2/server/services/dataMart/route/getCustomerInv.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { getINV } from "../controller/getinventory.js";
|
||||
import { getCurrentCustomerInv } from "../controller/getCustomerInventory.js";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
const Body = z.object({
|
||||
includeRunnningNumbers: z.string().openapi({ example: "x" }),
|
||||
});
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["dataMart"],
|
||||
summary: "Returns All customer minus holds.",
|
||||
method: "get",
|
||||
path: "/getcustomerinventory",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": { schema: Body },
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
const customerData: any = c.req.queries();
|
||||
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
apiHit(c, { endpoint: "/getcustomerinventory" });
|
||||
const { data, error } = await tryCatch(
|
||||
getCurrentCustomerInv(customerData ? customerData : null)
|
||||
);
|
||||
|
||||
if (error) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "There was an error getting the inv.",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
return c.json({
|
||||
success: data.success,
|
||||
message: data.message,
|
||||
data: data.data,
|
||||
});
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
@@ -0,0 +1,54 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { getDeliveryByDateRange } from "../controller/getDeliveryByDateRange.js";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
const Body = z.object({
|
||||
includeRunnningNumbers: z.string().openapi({ example: "x" }),
|
||||
});
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["dataMart"],
|
||||
summary: "Returns deliverys by daterange.",
|
||||
method: "get",
|
||||
path: "/deliverybydaterange",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": { schema: Body },
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
const delivery: any = c.req.queries();
|
||||
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
apiHit(c, { endpoint: "/deliverybydaterange" });
|
||||
const { data, error } = await tryCatch(
|
||||
getDeliveryByDateRange(delivery ? delivery : null)
|
||||
);
|
||||
|
||||
if (error) {
|
||||
console.log(error);
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "There was an error getting the deliveries.",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
return c.json({
|
||||
success: data.success,
|
||||
message: data.message,
|
||||
data: data.data,
|
||||
});
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
48
lstV2/server/services/dataMart/route/getFifoIndex.ts
Normal file
48
lstV2/server/services/dataMart/route/getFifoIndex.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
import { getActiveAv } from "../controller/getActiveArticles.js";
|
||||
import { getFifoIndex } from "../controller/getFifoIndex.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
const EomStat = z.object({
|
||||
plant: z.string().openapi({ example: "Salt Lake City" }),
|
||||
userRan: z.string().openapi({ example: "smith034" }),
|
||||
eomSheetVersion: z.string().openapi({ example: "0.0.223" }),
|
||||
});
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["dataMart"],
|
||||
summary: "Returns all the fifo data.",
|
||||
method: "get",
|
||||
path: "/getfifoindex",
|
||||
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
//const body = await c.req.json();
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
apiHit(c, { endpoint: "/getfifoindex" });
|
||||
try {
|
||||
return c.json(
|
||||
{
|
||||
success: true,
|
||||
message: "Fifo index data for the last 90days",
|
||||
data: await getFifoIndex(),
|
||||
},
|
||||
200
|
||||
);
|
||||
} catch (error) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "There was an error getting fifo index data.",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
69
lstV2/server/services/dataMart/route/getFinanceAudit.ts
Normal file
69
lstV2/server/services/dataMart/route/getFinanceAudit.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
import { getfinanceAudit } from "../controller/getFinanceAudit.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
const EomStat = z.object({
|
||||
plant: z.string().openapi({ example: "Salt Lake City" }),
|
||||
userRan: z.string().openapi({ example: "smith034" }),
|
||||
eomSheetVersion: z.string().openapi({ example: "0.0.223" }),
|
||||
});
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["dataMart"],
|
||||
summary:
|
||||
"Returns inventory to date and current sales prices and booking date of the inventory.",
|
||||
method: "get",
|
||||
path: "/getfinanceaudit",
|
||||
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c: any) => {
|
||||
//const body = await c.req.json();
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
const { data, error } = (await tryCatch(c.req.query())) as any;
|
||||
|
||||
//console.log(data.date);
|
||||
|
||||
if (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Missing Mandatory Data",
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
|
||||
if (!data.date) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Missing Date",
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
apiHit(c, { endpoint: "/getfinanceaudit", lastBody: data });
|
||||
|
||||
try {
|
||||
return c.json(
|
||||
{
|
||||
success: true,
|
||||
message: `Inventory older than ${data.date}`,
|
||||
data: await getfinanceAudit(data.date),
|
||||
},
|
||||
200
|
||||
);
|
||||
} catch (error) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "There was an error getting inventory data",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
54
lstV2/server/services/dataMart/route/getInventory.ts
Normal file
54
lstV2/server/services/dataMart/route/getInventory.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { getINV } from "../controller/getinventory.js";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
const Body = z.object({
|
||||
includeRunnningNumbers: z.string().openapi({ example: "x" }),
|
||||
});
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["dataMart"],
|
||||
summary: "Returns All current inventory.",
|
||||
method: "get",
|
||||
path: "/getinventory",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": { schema: Body },
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
const includeRunnningNumbers: string =
|
||||
c.req.query("includeRunnningNumbers") ?? "";
|
||||
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
apiHit(c, { endpoint: "/getinventory" });
|
||||
const { data, error } = await tryCatch(
|
||||
getINV(includeRunnningNumbers?.length > 0 ? true : false)
|
||||
);
|
||||
|
||||
if (error) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "There was an error getting the inv.",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
return c.json({
|
||||
success: data.success,
|
||||
message: data.message,
|
||||
data: data.data,
|
||||
});
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
54
lstV2/server/services/dataMart/route/getOpenOrders.ts
Normal file
54
lstV2/server/services/dataMart/route/getOpenOrders.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { getOpenOrders } from "../controller/getOpenOrders.js";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
// const Body = z.object({
|
||||
// includeRunnningNumbers: z.string().openapi({ example: "x" }),
|
||||
// });
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["dataMart"],
|
||||
summary: "Returns All open orders.",
|
||||
method: "get",
|
||||
path: "/getopenorders",
|
||||
// request: {
|
||||
// body: {
|
||||
// content: {
|
||||
// "application/json": { schema: Body },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
const customer: any = c.req.queries();
|
||||
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
apiHit(c, { endpoint: "/getopenorders" });
|
||||
const { data, error } = await tryCatch(
|
||||
getOpenOrders(customer ? customer : null)
|
||||
);
|
||||
|
||||
if (error) {
|
||||
console.log(error);
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "There was an error getting the inv.",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
return c.json({
|
||||
success: data.success,
|
||||
message: data.message,
|
||||
data: data.data,
|
||||
});
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
61
lstV2/server/services/dataMart/route/getPsiArticleData.ts
Normal file
61
lstV2/server/services/dataMart/route/getPsiArticleData.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { getDeliveryByDateRange } from "../controller/getDeliveryByDateRange.js";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
import { getGetPSIArticleData } from "../controller/psiGetArticleData.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
const Body = z.object({
|
||||
includeRunnningNumbers: z.string().openapi({ example: "x" }),
|
||||
});
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["dataMart"],
|
||||
summary: "Returns the psiarticleData.",
|
||||
method: "get",
|
||||
path: "/psiarticledata",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": { schema: Body },
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
const articles: any = c.req.queries();
|
||||
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
apiHit(c, { endpoint: "/psiarticledata" });
|
||||
//console.log(articles["avs"][0]);
|
||||
const { data, error } = await tryCatch(
|
||||
getGetPSIArticleData(articles ? articles["avs"][0] : null)
|
||||
);
|
||||
|
||||
if (error) {
|
||||
console.log(error);
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "There was an error getting the articles.",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
//console.log(data);
|
||||
|
||||
return c.json(
|
||||
{
|
||||
success: data.success,
|
||||
message: data.message,
|
||||
data: data.data,
|
||||
},
|
||||
data.success ? 200 : 400
|
||||
);
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
64
lstV2/server/services/dataMart/route/getPsiPlanningData.ts
Normal file
64
lstV2/server/services/dataMart/route/getPsiPlanningData.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
import { psiGetPlanningData } from "../controller/psiGetPlanningData.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
const Body = z.object({
|
||||
includeRunnningNumbers: z.string().openapi({ example: "x" }),
|
||||
});
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["dataMart"],
|
||||
summary: "Returns the psiarticleData.",
|
||||
method: "get",
|
||||
path: "/psiplanningdata",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": { schema: Body },
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
const q: any = c.req.queries();
|
||||
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
apiHit(c, { endpoint: "/psiplanningdata" });
|
||||
//console.log(articles["avs"][0]);
|
||||
const { data, error } = await tryCatch(
|
||||
psiGetPlanningData(
|
||||
q["avs"] ? q["avs"][0] : null,
|
||||
q["startDate"] ? q["startDate"][0] : null,
|
||||
q["endDate"] ? q["endDate"][0] : null
|
||||
)
|
||||
);
|
||||
|
||||
if (error) {
|
||||
console.log(error);
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "There was an error getting the planning.",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
//console.log(data);
|
||||
|
||||
return c.json(
|
||||
{
|
||||
success: data.success,
|
||||
message: data.message,
|
||||
data: data.data,
|
||||
},
|
||||
data.success ? 200 : 400
|
||||
);
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
64
lstV2/server/services/dataMart/route/getPsiProductionData.ts
Normal file
64
lstV2/server/services/dataMart/route/getPsiProductionData.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
import { psiGetProductionData } from "../controller/psiGetProductionData.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
const Body = z.object({
|
||||
includeRunnningNumbers: z.string().openapi({ example: "x" }),
|
||||
});
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["dataMart"],
|
||||
summary: "Returns the psiproductiondata.",
|
||||
method: "get",
|
||||
path: "/psiproductiondata",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": { schema: Body },
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
const q: any = c.req.queries();
|
||||
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
apiHit(c, { endpoint: "/psiproductiondata" });
|
||||
//console.log(articles["avs"][0]);
|
||||
const { data, error } = await tryCatch(
|
||||
psiGetProductionData(
|
||||
q["avs"] ? q["avs"][0] : null,
|
||||
q["startDate"] ? q["startDate"][0] : null,
|
||||
q["endDate"] ? q["endDate"][0] : null
|
||||
)
|
||||
);
|
||||
|
||||
if (error) {
|
||||
console.log(error);
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "There was an error getting the production.",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
//console.log(data);
|
||||
|
||||
return c.json(
|
||||
{
|
||||
success: data.success,
|
||||
message: data.message,
|
||||
data: data.data,
|
||||
},
|
||||
data.success ? 200 : 400
|
||||
);
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
64
lstV2/server/services/dataMart/route/getPsiinventory.ts
Normal file
64
lstV2/server/services/dataMart/route/getPsiinventory.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
import { psiGetInventory } from "../controller/psiGetInventory.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
const Body = z.object({
|
||||
includeRunnningNumbers: z.string().openapi({ example: "x" }),
|
||||
});
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["dataMart"],
|
||||
summary: "Returns the getPsiinventory.",
|
||||
method: "get",
|
||||
path: "/getpsiinventory",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": { schema: Body },
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
const q: any = c.req.queries();
|
||||
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
apiHit(c, { endpoint: "/getpsiinventory" });
|
||||
//console.log(articles["avs"][0]);
|
||||
const { data, error } = await tryCatch(
|
||||
psiGetInventory(
|
||||
q["avs"] ? q["avs"][0] : null,
|
||||
q["startDate"] ? q["startDate"][0] : null,
|
||||
q["endDate"] ? q["endDate"][0] : null
|
||||
)
|
||||
);
|
||||
|
||||
if (error) {
|
||||
console.log(error);
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "There was an error getting the production.",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
//console.log(data);
|
||||
|
||||
return c.json(
|
||||
{
|
||||
success: data.success,
|
||||
message: data.message,
|
||||
data: data.data,
|
||||
},
|
||||
data.success ? 200 : 400
|
||||
);
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
139
lstV2/server/services/eom/controller/addHistorical.ts
Normal file
139
lstV2/server/services/eom/controller/addHistorical.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
// import cron from "node-cron";
|
||||
// import {runQuery, prisma, totalInvNoRn, activeArticle, getShiftTime, historicalInv} from "database";
|
||||
// import {createLog} from "logging";
|
||||
// import {deleteHistory} from "./deleteHistory.js";
|
||||
|
||||
// export const historyInv = async (date) => {
|
||||
// //console.log(date);
|
||||
// if (!date) {
|
||||
// return `Missing Data`;
|
||||
// }
|
||||
// // date should be sent over as a string IE: 2024-01-01
|
||||
// let inv = [];
|
||||
// try {
|
||||
// inv = await prisma.historyInventory.findMany({where: {histDate: date}});
|
||||
// console.log(inv.length);
|
||||
// // if the date returns nothing we need to pull the historical data
|
||||
// if (inv.length === 0) {
|
||||
// const result = await prisma.settings.findFirst({where: {name: "plantToken"}});
|
||||
// try {
|
||||
// const plantUpdate = historicalInv.replaceAll("test1", result.value);
|
||||
// const queryDate = plantUpdate.replaceAll("[date]", date);
|
||||
// inv = await runQuery(queryDate, "Get histical inv");
|
||||
|
||||
// return inv;
|
||||
// } catch (error) {
|
||||
// createLog("general/eom", "error", "There was an error getting the historical inv.");
|
||||
// return error;
|
||||
// }
|
||||
// } else {
|
||||
// return inv;
|
||||
// }
|
||||
// //return inv;
|
||||
// } catch (error) {
|
||||
// console.log(error);
|
||||
// return error;
|
||||
// }
|
||||
// };
|
||||
|
||||
// // start the cron job for getting the hostrical inv based on the plants shift time
|
||||
// export const startCronHist = () => {
|
||||
// let shiftTime = ["06", "00", "00"];
|
||||
// const startProcess = async () => {
|
||||
// let inv = [];
|
||||
// let articles = [];
|
||||
// let plantToken = "test1";
|
||||
// const date = new Date();
|
||||
// const dateString = date.toISOString().split("T")[0];
|
||||
// date.setDate(date.getDate() - 30);
|
||||
// const oldDate = date.toISOString().split("T")[0];
|
||||
|
||||
// // checking if even need to run this
|
||||
// // before adding more make sure we dont already have data
|
||||
// const checkInv = await prisma.historyInventory.findFirst({where: {histDate: dateString}});
|
||||
// if (checkInv) {
|
||||
// createLog(
|
||||
// "general/eom",
|
||||
// "warn",
|
||||
// `There seems to already be inventory added for ${dateString}, no new data will be added`
|
||||
// );
|
||||
// return;
|
||||
// }
|
||||
// // get plant token
|
||||
// try {
|
||||
// const result = await prisma.settings.findFirst({where: {name: "plantToken"}});
|
||||
// plantToken = result.value;
|
||||
// } catch (error) {
|
||||
// createLog("general/eom", "error", "failed to get planttoken");
|
||||
// }
|
||||
// //get shift time
|
||||
// try {
|
||||
// const result = await runQuery(getShiftTime.replaceAll("test1", plantToken), "GettingShift time");
|
||||
// shiftTime = result[0].shiftStartTime.split(":");
|
||||
// } catch (error) {
|
||||
// createLog("general/eom", "error", `Error running getShift Query: ${error}`);
|
||||
// }
|
||||
|
||||
// // get inventory
|
||||
// try {
|
||||
// const result = await runQuery(totalInvNoRn.replaceAll("test1", plantToken), "getting inventory");
|
||||
// inv = result;
|
||||
// } catch (error) {
|
||||
// createLog("general/eom", "error", `Error running get inventory Query: ${error}`);
|
||||
// }
|
||||
|
||||
// // get active articles
|
||||
// try {
|
||||
// const result = await runQuery(activeArticle.replaceAll("test1", plantToken), "Get active articles");
|
||||
// articles = result;
|
||||
// } catch (error) {
|
||||
// createLog("general/eom", "error", `Error running get article: ${error}`);
|
||||
// }
|
||||
|
||||
// //add the inventory to the historical table
|
||||
// try {
|
||||
// let hist = Object.entries(inv).map(([key, value]) => {
|
||||
// // remove the values we dont want in the historical view
|
||||
// const {total_Pallets, avalible_Pallets, coa_Pallets, held_Pallets, ...histData} = value;
|
||||
|
||||
// // get av tyep
|
||||
// const avType = articles.filter((a) => (a.IdArtikelvarianten = inv[key].av))[0].TypeOfMaterial;
|
||||
// // add in the new fields
|
||||
// const hist = {
|
||||
// ...histData,
|
||||
// histDate: dateString, //new Date(Date.now()).toISOString().split("T")[0],
|
||||
// avType,
|
||||
// };
|
||||
// return hist;
|
||||
// });
|
||||
|
||||
// try {
|
||||
// const addHistData = await prisma.historyInventory.createMany({data: hist});
|
||||
// createLog(
|
||||
// "general/eom",
|
||||
// "info",
|
||||
// `${addHistData.count} were just added to the historical inventory for date ${dateString}`
|
||||
// );
|
||||
// } catch (error) {
|
||||
// createLog("general/eom", "error", `Adding new historical inventory error: ${error}`);
|
||||
// }
|
||||
|
||||
// // delete the older inventory
|
||||
// deleteHistory(oldDate);
|
||||
// } catch (error) {
|
||||
// createLog("general/eom", "error", `Adding new historical inventory error: ${error}`);
|
||||
// }
|
||||
// };
|
||||
|
||||
// // actaully run the process once after restaart just to make sure we have inventory
|
||||
// startProcess();
|
||||
|
||||
// // setup the cron stuff
|
||||
// const startHour = shiftTime[0];
|
||||
// const startMin = shiftTime[1];
|
||||
// createLog("general/eom", "info", `Historical Data will run at ${shiftTime[0]}:${shiftTime[1]} daily`);
|
||||
// cron.schedule(`${startMin} ${startHour} * * *`, () => {
|
||||
// createLog("general/eom", "info", "Running historical invnetory.");
|
||||
// startProcess();
|
||||
// });
|
||||
// };
|
||||
@@ -0,0 +1,32 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../../../../database/dbclient.js";
|
||||
import { invHistoricalData } from "../../../../database/schema/historicalINV.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { format } from "date-fns";
|
||||
|
||||
export const historicalInvByDate = async (date: string) => {
|
||||
const histDate = new Date(date);
|
||||
|
||||
const { data, error } = (await tryCatch(
|
||||
db
|
||||
.select()
|
||||
.from(invHistoricalData)
|
||||
.where(
|
||||
eq(invHistoricalData.histDate, format(histDate, "yyyy-MM-dd"))
|
||||
)
|
||||
)) as any;
|
||||
|
||||
if (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error with getting the inventory",
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Historical inventory for ${date}`,
|
||||
data: data,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { format } from "date-fns";
|
||||
import { query } from "../../sqlServer/prodSqlServer.js";
|
||||
import { lastPurchasePrice } from "../../sqlServer/querys/eom/lstPurchasePrice.js";
|
||||
|
||||
export const lastPurchase = async () => {
|
||||
const { data, error } = (await tryCatch(
|
||||
query(lastPurchasePrice, "Last purchase price")
|
||||
)) as any;
|
||||
|
||||
if (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Error getting the last purchase price",
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Last purchase price for all av in the last 5 years`,
|
||||
data: data.data,
|
||||
};
|
||||
};
|
||||
23
lstV2/server/services/eom/controller/getLastestSalesPrice.ts
Normal file
23
lstV2/server/services/eom/controller/getLastestSalesPrice.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { query } from "../../sqlServer/prodSqlServer.js";
|
||||
import { lastSalesPriceCheck } from "../../sqlServer/querys/eom/lastSalesprice.js";
|
||||
|
||||
export const lastSales = async (date: string) => {
|
||||
const { data, error } = (await tryCatch(
|
||||
query(lastSalesPriceCheck.replace("[date]", date), "Last sales price")
|
||||
)) as any;
|
||||
|
||||
if (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Error getting the last sales price",
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Last sales price for all av in the last 5 years`,
|
||||
data: data.data,
|
||||
};
|
||||
};
|
||||
51
lstV2/server/services/eom/eomService.ts
Normal file
51
lstV2/server/services/eom/eomService.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { OpenAPIHono } from "@hono/zod-openapi";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
import stats from "./route/stats.js";
|
||||
import history from "./route/invHistory.js";
|
||||
import { createJob } from "../notifications/utils/processNotifications.js";
|
||||
import { historicalInvIMmport } from "./utils/historicalInv.js";
|
||||
import { tryCatch } from "../../globalUtils/tryCatch.js";
|
||||
import { query } from "../sqlServer/prodSqlServer.js";
|
||||
import { shiftChange } from "../sqlServer/querys/misc/shiftChange.js";
|
||||
import { createLog } from "../logger/logger.js";
|
||||
import lastPurch from "./route/getLastPurchPrice.js";
|
||||
import lastSales from "./route/getLastSalesPrice.js";
|
||||
|
||||
const routes = [stats, history, lastPurch, lastSales] as const;
|
||||
|
||||
const appRoutes = routes.forEach((route) => {
|
||||
app.route("/eom", route);
|
||||
});
|
||||
|
||||
setTimeout(async () => {
|
||||
const { data: shift, error: shiftError } = (await tryCatch(
|
||||
query(shiftChange, "shift change from material.")
|
||||
)) as any;
|
||||
|
||||
if (shiftError) {
|
||||
createLog(
|
||||
"error",
|
||||
"eom",
|
||||
"eom",
|
||||
"There was an error getting the shift times will use fallback times"
|
||||
);
|
||||
}
|
||||
|
||||
// shift split
|
||||
const shiftTimeSplit = shift?.data[0]?.shiftChange.split(":");
|
||||
|
||||
const cronSetup = `${
|
||||
shiftTimeSplit?.length > 0 ? `${parseInt(shiftTimeSplit[1])}` : "0"
|
||||
} ${
|
||||
shiftTimeSplit?.length > 0 ? `${parseInt(shiftTimeSplit[0])}` : "7"
|
||||
} * * *`;
|
||||
|
||||
//console.log(cronSetup);
|
||||
createJob("eom_historical_inv", cronSetup, historicalInvIMmport);
|
||||
}, 5 * 1000);
|
||||
// the time we want to run the hostircal data should be the same time the historical data run on the server
|
||||
// getting this from the shift time
|
||||
|
||||
export default app;
|
||||
41
lstV2/server/services/eom/route/getLastPurchPrice.ts
Normal file
41
lstV2/server/services/eom/route/getLastPurchPrice.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
|
||||
import { lastPurchase } from "../controller/getLastPurchasesPrice.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["eom"],
|
||||
summary: "Returns last sales price.",
|
||||
method: "get",
|
||||
path: "/lastpurchprice",
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
//const body = await c.req.json();
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
|
||||
apiHit(c, { endpoint: "/lastpurchprice" });
|
||||
try {
|
||||
const res = await lastPurchase();
|
||||
|
||||
return c.json(
|
||||
{ success: res.success, message: res.message, data: res.data },
|
||||
200
|
||||
);
|
||||
} catch (error) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "There was an error posting the eom stat.",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
43
lstV2/server/services/eom/route/getLastSalesPrice.ts
Normal file
43
lstV2/server/services/eom/route/getLastSalesPrice.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
|
||||
import { lastPurchase } from "../controller/getLastPurchasesPrice.js";
|
||||
import { lastSales } from "../controller/getLastestSalesPrice.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["eom"],
|
||||
summary: "Returns last sales price.",
|
||||
method: "get",
|
||||
path: "/lastsalesprice",
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
//const body = await c.req.json();
|
||||
const month: string = c.req.query("month") ?? "";
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
|
||||
apiHit(c, { endpoint: "/lastsalesprice" });
|
||||
try {
|
||||
const res = await lastSales(month);
|
||||
|
||||
return c.json(
|
||||
{ success: res.success, message: res.message, data: res.data },
|
||||
200
|
||||
);
|
||||
} catch (error) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "There was an error posting the eom stat.",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
46
lstV2/server/services/eom/route/invHistory.ts
Normal file
46
lstV2/server/services/eom/route/invHistory.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
import { historicalInvByDate } from "../controller/getHistoricalInvByDate.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
const EomStat = z.object({
|
||||
plant: z.string().openapi({ example: "Salt Lake City" }),
|
||||
userRan: z.string().openapi({ example: "smith034" }),
|
||||
eomSheetVersion: z.string().openapi({ example: "0.0.223" }),
|
||||
});
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["eom"],
|
||||
summary: "Gets History Data by date.",
|
||||
method: "get",
|
||||
path: "/histinv",
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
//const body = await c.req.json();
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
const month: string = c.req.query("month") ?? "";
|
||||
|
||||
apiHit(c, { endpoint: "/histinv" });
|
||||
try {
|
||||
const res = await historicalInvByDate(month);
|
||||
|
||||
return c.json(
|
||||
{ success: res.success, message: res.message, data: res.data },
|
||||
200
|
||||
);
|
||||
} catch (error) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "There was an error posting the eom stat.",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
41
lstV2/server/services/eom/route/stats.ts
Normal file
41
lstV2/server/services/eom/route/stats.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
const EomStat = z.object({
|
||||
plant: z.string().openapi({ example: "Salt Lake City" }),
|
||||
userRan: z.string().openapi({ example: "smith034" }),
|
||||
eomSheetVersion: z.string().openapi({ example: "0.0.223" }),
|
||||
});
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["eom"],
|
||||
summary: "Adds in the stats for the eom.",
|
||||
method: "post",
|
||||
path: "/stats",
|
||||
request: {
|
||||
params: EomStat,
|
||||
},
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
//const body = await c.req.json();
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
apiHit(c, { endpoint: "/stats" });
|
||||
try {
|
||||
return c.json({ success: true, message: "", data: [] }, 200);
|
||||
} catch (error) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "There was an error posting the eom stat.",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
114
lstV2/server/services/eom/utils/historicalInv.ts
Normal file
114
lstV2/server/services/eom/utils/historicalInv.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { sql } from "drizzle-orm";
|
||||
import { db } from "../../../../database/dbclient.js";
|
||||
import { invHistoricalData } from "../../../../database/schema/historicalINV.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../logger/logger.js";
|
||||
import { query } from "../../sqlServer/prodSqlServer.js";
|
||||
import { totalInvNoRn } from "../../sqlServer/querys/dataMart/totalINV.js";
|
||||
import { format } from "date-fns-tz";
|
||||
import { serverSettings } from "../../server/controller/settings/getSettings.js";
|
||||
import { deleteHistory } from "./removeHistorical.js";
|
||||
import { activeArticle } from "../../sqlServer/querys/dataMart/article.js";
|
||||
|
||||
export const historicalInvIMmport = async () => {
|
||||
const plantToken = serverSettings.filter((n) => n.name === "plantToken");
|
||||
const { data, error } = (await tryCatch(
|
||||
db.select().from(invHistoricalData)
|
||||
)) as any;
|
||||
|
||||
if (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"eom",
|
||||
"eom",
|
||||
`There was an error getting the historical data`
|
||||
);
|
||||
}
|
||||
// check if we have data already for today this way we dont duplicate anything.
|
||||
const today = new Date();
|
||||
today.setDate(today.getDate() - 1);
|
||||
|
||||
const dateCheck = data?.filter(
|
||||
(i: any) => i.histDate === format(today, "yyyy-MM-dd")
|
||||
);
|
||||
|
||||
if (dateCheck.length === 0) {
|
||||
// get the historical data from the sql
|
||||
const { data: inv, error: invError } = (await tryCatch(
|
||||
query(totalInvNoRn, "eom historical data")
|
||||
)) as any;
|
||||
|
||||
if (invError) {
|
||||
createLog(
|
||||
"error",
|
||||
"eom",
|
||||
"eom",
|
||||
`There was an error getting the sql data`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (inv.data.length === 0) {
|
||||
createLog("error", "eom", "eom", inv.message);
|
||||
return;
|
||||
}
|
||||
|
||||
const { data: articles, error: avError } = (await tryCatch(
|
||||
query(activeArticle, "Get active articles")
|
||||
)) as any;
|
||||
|
||||
const av = articles.data.length > 0 ? articles.data : ([] as any);
|
||||
|
||||
const importInv = inv.data ? inv.data : [];
|
||||
const eomImportData = importInv.map((i: any) => {
|
||||
return {
|
||||
histDate: sql`(NOW() - INTERVAL '1 day')::date`,
|
||||
plantToken: plantToken[0].value,
|
||||
article: i.av,
|
||||
articleDescription: i.Alias,
|
||||
materialType:
|
||||
av.filter((a: any) => a.IdArtikelvarianten === i.av)
|
||||
.length > 0
|
||||
? av.filter(
|
||||
(a: any) => a.IdArtikelvarianten === i.av
|
||||
)[0]?.TypeOfMaterial
|
||||
: "Item not defined",
|
||||
total_QTY: i.Total_PalletQTY,
|
||||
avaliable_QTY: i.Avaliable_PalletQTY,
|
||||
coa_QTY: i.COA_QTY,
|
||||
held_QTY: i.Held_QTY,
|
||||
consignment: i.Consigment,
|
||||
lot_Number: i.lot,
|
||||
};
|
||||
});
|
||||
|
||||
const { data: dataImport, error: errorImport } = await tryCatch(
|
||||
db.insert(invHistoricalData).values(eomImportData)
|
||||
);
|
||||
|
||||
if (errorImport) {
|
||||
createLog(
|
||||
"error",
|
||||
"eom",
|
||||
"eom",
|
||||
`There was an error importing all the inventory data.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dataImport) {
|
||||
createLog(
|
||||
"info",
|
||||
"eom",
|
||||
"eom",
|
||||
`All data was imported succefully.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
createLog("info", "eom", "eom", `Yesterdays Data already in..`);
|
||||
}
|
||||
|
||||
// do the check to delete old data
|
||||
deleteHistory();
|
||||
};
|
||||
51
lstV2/server/services/eom/utils/removeHistorical.ts
Normal file
51
lstV2/server/services/eom/utils/removeHistorical.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
// import {prisma} from "database";
|
||||
// import {createLog} from "logging";
|
||||
|
||||
import { lte, sql } from "drizzle-orm";
|
||||
import { db } from "../../../../database/dbclient.js";
|
||||
import { invHistoricalData } from "../../../../database/schema/historicalINV.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../logger/logger.js";
|
||||
|
||||
// export const deleteHistory = async (date: string) => {
|
||||
// // delete the inventory if it equals this date
|
||||
// try {
|
||||
// const remove = await prisma.$executeRaw`
|
||||
// DELETE FROM historyInventory
|
||||
// WHERE histDate < ${date}
|
||||
// `;
|
||||
// createLog("general/eom", "info", `${remove} were just remove from the historical inventory for date: ${date}`);
|
||||
// } catch (error) {
|
||||
// createLog("general/eom", "error", `Removing historical inventory error: ${error}`);
|
||||
// }
|
||||
// };
|
||||
|
||||
export const deleteHistory = async () => {
|
||||
const { data, error } = await tryCatch(
|
||||
db
|
||||
.delete(invHistoricalData)
|
||||
.where(
|
||||
lte(
|
||||
invHistoricalData.histDate,
|
||||
sql`(NOW() - INTERVAL '365 day')::date`
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"eom",
|
||||
"eom",
|
||||
"There was an error deleting the historical data."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
createLog(
|
||||
"info",
|
||||
"eom",
|
||||
"eom",
|
||||
"Data older than 45 days has been deleted."
|
||||
);
|
||||
};
|
||||
52
lstV2/server/services/general/route/apiHits.ts
Normal file
52
lstV2/server/services/general/route/apiHits.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
// Define the request body schema
|
||||
const requestSchema = z.object({
|
||||
ip: z.string().optional(),
|
||||
endpoint: z.string().optional(),
|
||||
action: z.string().optional(),
|
||||
stats: z.string().optional(),
|
||||
});
|
||||
|
||||
// Define the response schema
|
||||
const responseSchema = z.object({
|
||||
message: z.string(),
|
||||
});
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["api"],
|
||||
summary: "Tracks the API posts and how often",
|
||||
method: "post",
|
||||
path: "/hits",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": {schema: requestSchema},
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {schema: responseSchema},
|
||||
},
|
||||
description: "Response message",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
const data = await c.req.json();
|
||||
|
||||
//apiHit(data);
|
||||
|
||||
// Return response with the received data
|
||||
return c.json({
|
||||
message: `Received name: ${data.name}, arrayData: ${data.arrayData}`,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
export default app;
|
||||
90
lstV2/server/services/general/route/scalar.ts
Normal file
90
lstV2/server/services/general/route/scalar.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { OpenAPIHono } from "@hono/zod-openapi";
|
||||
import { apiReference } from "@scalar/hono-api-reference";
|
||||
import { settings } from "../../../../database/schema/settings.js";
|
||||
import { db } from "../../../../database/dbclient.js";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { getSettings } from "../../server/controller/settings/getSettings.js";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
const serverSettings = await getSettings();
|
||||
const plantToken = serverSettings.filter((n) => n.name === "plantToken") as any; //await db.select().from(settings).where(eq(settings.name, "plantToken"));
|
||||
|
||||
let pToken = plantToken[0]?.value;
|
||||
const testServers = ["test1", "test2", "test3"];
|
||||
if (testServers.includes(plantToken[0]?.value)) {
|
||||
pToken = "usbow1";
|
||||
}
|
||||
app.get(
|
||||
"/docs",
|
||||
apiReference({
|
||||
theme: "kepler",
|
||||
layout: "classic",
|
||||
defaultHttpClient: { targetKey: "node", clientKey: "axios" },
|
||||
pageTitle: "Lst API Reference",
|
||||
hiddenClients: [
|
||||
"libcurl",
|
||||
"clj_http",
|
||||
"httpclient",
|
||||
"restsharp",
|
||||
"native",
|
||||
"http1.1",
|
||||
"asynchttp",
|
||||
"nethttp",
|
||||
"okhttp",
|
||||
"unirest",
|
||||
"xhr",
|
||||
"fetch",
|
||||
"jquery",
|
||||
"okhttp",
|
||||
"native",
|
||||
"request",
|
||||
"unirest",
|
||||
"nsurlsession",
|
||||
"cohttp",
|
||||
"curl",
|
||||
"guzzle",
|
||||
"http1",
|
||||
"http2",
|
||||
"webrequest",
|
||||
"restmethod",
|
||||
"python3",
|
||||
"requests",
|
||||
"httr",
|
||||
"native",
|
||||
"curl",
|
||||
"httpie",
|
||||
"wget",
|
||||
"nsurlsession",
|
||||
"undici",
|
||||
],
|
||||
|
||||
url: "/api/ref",
|
||||
|
||||
baseServerURL: "https://scalar.com",
|
||||
servers: [
|
||||
{
|
||||
url: `http://${pToken}vms006:${process.env.VITE_SERVER_PORT}`,
|
||||
description: "Production",
|
||||
},
|
||||
{
|
||||
url: `http://localhost:${process.env.VITE_SERVER_PORT}`,
|
||||
description: "dev server",
|
||||
},
|
||||
],
|
||||
// authentication: {
|
||||
// preferredSecurityScheme: {'bearerAuth'},
|
||||
// },
|
||||
|
||||
// metaData: {
|
||||
// title: "Page title",
|
||||
// description: "My page page",
|
||||
// ogDescription: "Still about my my page",
|
||||
// ogTitle: "Page title",
|
||||
// ogImage: "https://example.com/image.png",
|
||||
// twitterCard: "summary_large_image",
|
||||
// // Add more...
|
||||
// },
|
||||
})
|
||||
);
|
||||
|
||||
export default app;
|
||||
30
lstV2/server/services/logger/controller/clearLog.ts
Normal file
30
lstV2/server/services/logger/controller/clearLog.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { eq, sql } from "drizzle-orm";
|
||||
import { db } from "../../../../database/dbclient.js";
|
||||
import { logs } from "../../../../database/schema/logs.js";
|
||||
import { createLog } from "../logger.js";
|
||||
|
||||
export const clearLog = async (id: string) => {
|
||||
/**
|
||||
* mark the log as cleared
|
||||
*/
|
||||
|
||||
try {
|
||||
const clear = await db
|
||||
.update(logs)
|
||||
.set({ checked: true, created_at: sql`NOW()` })
|
||||
.where(eq(logs.log_id, id));
|
||||
createLog("info", "lst", "logger", "Log just cleared.");
|
||||
return { success: true, message: "Log was just cleared." };
|
||||
} catch (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
"logger",
|
||||
"There was an error clearing the log."
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error clearing the log.",
|
||||
};
|
||||
}
|
||||
};
|
||||
42
lstV2/server/services/logger/controller/getLogs.ts
Normal file
42
lstV2/server/services/logger/controller/getLogs.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { and, desc, eq, gte, inArray, lte, sql } from "drizzle-orm";
|
||||
import { db } from "../../../../database/dbclient.js";
|
||||
import { logs } from "../../../../database/schema/logs.js";
|
||||
import { createLog } from "../logger.js";
|
||||
|
||||
export const getLogs = async (data: any) => {
|
||||
try {
|
||||
// clear all remaining logs ne to info.
|
||||
const checked =
|
||||
data.checked && data.checked[0] === "true" ? true : false || false;
|
||||
const logData = await db
|
||||
.select()
|
||||
.from(logs)
|
||||
.where(
|
||||
and(
|
||||
gte(
|
||||
logs.created_at,
|
||||
sql.raw(`NOW() - INTERVAL '${data.hours ?? "4"} hours'`)
|
||||
),
|
||||
inArray(logs.service, data.service),
|
||||
inArray(logs.level, data.level),
|
||||
eq(logs.checked, checked)
|
||||
)
|
||||
)
|
||||
.orderBy(desc(logs.created_at));
|
||||
|
||||
return { success: true, message: "logs returned", data: logData };
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
"logger",
|
||||
`There was an error deleteing server logs. ${error}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: "An error occured while trying to get the logs",
|
||||
error,
|
||||
};
|
||||
}
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user