Compare commits
5 Commits
cc3e823a7d
...
404974dde0
| Author | SHA1 | Date | |
|---|---|---|---|
| 404974dde0 | |||
| 4e6d35bc67 | |||
| 04fe1f1bfe | |||
| 2c3a6065bd | |||
| b4dbdd6932 |
17
README.md
17
README.md
@@ -18,18 +18,21 @@ Quick summary of current rewrite/migration goal.
|
|||||||
| User Authentication | ~~Login~~, ~~Signup~~, API Key | 🟨 In Progress |
|
| User Authentication | ~~Login~~, ~~Signup~~, API Key | 🟨 In Progress |
|
||||||
| User Profile | Edit profile, upload avatar | ⏳ Not Started |
|
| User Profile | Edit profile, upload avatar | ⏳ Not Started |
|
||||||
| User Admin | Edit user, create user, remove user, alplaprod user integration | ⏳ Not Started |
|
| User Admin | Edit user, create user, remove user, alplaprod user integration | ⏳ Not Started |
|
||||||
| Notifications | Subscribe, Create, Update | ⏳ Not Started |
|
| Notifications | Subscribe, Create, Update, Remove, Manual Trigger | ⏳ Not Started |
|
||||||
| Datamart | Create, Update, Run | 🔧 In Progress |
|
| Datamart | Create, Update, Run, Deactivate | 🔧 In Progress |
|
||||||
| Frontend | Analytics and charts | ⏳ Not Started |
|
| Frontend | Analytics and charts | ⏳ Not Started |
|
||||||
| One Click Print | Get printers, monitor printers, label process, material process | ⏳ Not Started |
|
| Docs | Instructions and trouble shooting | ⏳ Not Started |
|
||||||
| Silo Adjustments | Adjustments | ⏳ Not Started |
|
| One Click Print | Get printers, monitor printers, label process, material process, Special processes | ⏳ Not Started |
|
||||||
| Demand Management | Orders, Forecast | ⏳ Not Started |
|
| Silo Adjustments | Create, History, Comments | ⏳ Not Started |
|
||||||
|
| Demand Management | Orders, Forecast, Special Mappings, Create trucks, Load Trucks (tablet scanning) | ⏳ Not Started |
|
||||||
| Open Docks | Integrations | ⏳ Not Started |
|
| Open Docks | Integrations | ⏳ Not Started |
|
||||||
| Transport Insight | Integrations | ⏳ Not Started |
|
| Transport Insight | Integrations | ⏳ Not Started |
|
||||||
| Quality | Request Tool | ⏳ Not Started |
|
| Quality Request Tool | Add Pallet, Monitor for moved, status changes, alerts | ⏳ Not Started |
|
||||||
|
| Logistics | Consume material, return and print, label info, relocate | ⏳ Not Started |
|
||||||
|
| EOM | Endpoints, Report Pull for finance | ⏳ Not Started |
|
||||||
| OCME | Custom integration | ⏳ Not Started |
|
| OCME | Custom integration | ⏳ Not Started |
|
||||||
| API Migration | Moving to new REST endpoints | 🔧 In Progress |
|
| API Migration | Moving to new REST endpoints | 🔧 In Progress |
|
||||||
| System | Tests, Updates, Remote Logging | ⏳ Not Started |
|
| System | Tests,Builds, Updates, Remote Logging, DB Backups, Alerting | ⏳ Not Started |
|
||||||
|
|
||||||
_Status legend:_
|
_Status legend:_
|
||||||
✅ Complete 🟨 In Progress ⏳ Not Started
|
✅ Complete 🟨 In Progress ⏳ Not Started
|
||||||
|
|||||||
@@ -8,6 +8,32 @@ import { user } from "../db/schema/auth.schema.js";
|
|||||||
import { auth } from "../utils/auth.utils.js";
|
import { auth } from "../utils/auth.utils.js";
|
||||||
import { apiReturn } from "../utils/returnHelper.utils.js";
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
|
||||||
|
// interface EmailLoginRequest {
|
||||||
|
// email: string;
|
||||||
|
// password: string;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// interface LoginResponse {
|
||||||
|
// redirect: boolean;
|
||||||
|
// token: string;
|
||||||
|
// user: {
|
||||||
|
// name: string;
|
||||||
|
// email: string;
|
||||||
|
// emailVerified: boolean;
|
||||||
|
// image: string | null;
|
||||||
|
// createdAt: string;
|
||||||
|
// updatedAt: string;
|
||||||
|
// role: string;
|
||||||
|
// banned: boolean;
|
||||||
|
// banReason: string | null;
|
||||||
|
// banExpires: string | null;
|
||||||
|
// username: string;
|
||||||
|
// displayUsername: string;
|
||||||
|
// lastLogin: string;
|
||||||
|
// id: string;
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
|
||||||
const base = {
|
const base = {
|
||||||
password: z.string().min(8, "Password must be at least 8 characters"),
|
password: z.string().min(8, "Password must be at least 8 characters"),
|
||||||
};
|
};
|
||||||
@@ -28,7 +54,7 @@ const signin = z.union([
|
|||||||
const r = Router();
|
const r = Router();
|
||||||
|
|
||||||
r.post("/", async (req, res) => {
|
r.post("/", async (req, res) => {
|
||||||
let login: unknown = [];
|
let login: unknown;
|
||||||
try {
|
try {
|
||||||
const validated = signin.parse(req.body);
|
const validated = signin.parse(req.body);
|
||||||
if ("email" in validated) {
|
if ("email" in validated) {
|
||||||
@@ -69,6 +95,19 @@ r.post("/", async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make sure we update the lastLogin
|
||||||
|
// if (login?.user?.id) {
|
||||||
|
// const updated = await db
|
||||||
|
// .update(user)
|
||||||
|
// .set({ lastLogin: sql`NOW()` })
|
||||||
|
// .where(eq(user.id, login.user.id))
|
||||||
|
// .returning({ lastLogin: user.lastLogin });
|
||||||
|
|
||||||
|
// const lastLoginTimestamp = updated[0]?.lastLogin;
|
||||||
|
// console.log("Updated lastLogin:", lastLoginTimestamp);
|
||||||
|
// } else
|
||||||
|
// console.warn("User ID unavailable — skipping lastLogin update");
|
||||||
|
|
||||||
return apiReturn(res, {
|
return apiReturn(res, {
|
||||||
success: true,
|
success: true,
|
||||||
level: "info", //connect.success ? "info" : "error",
|
level: "info", //connect.success ? "info" : "error",
|
||||||
@@ -108,6 +147,16 @@ r.post("/", async (req, res) => {
|
|||||||
status: 400, //connect.success ? 200 : 400,
|
status: 400, //connect.success ? 200 : 400,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "routes",
|
||||||
|
subModule: "auth",
|
||||||
|
message: "System Error",
|
||||||
|
data: [err],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,17 @@ export const openApiBase: OpenAPIV3_1.Document = {
|
|||||||
scheme: "bearer",
|
scheme: "bearer",
|
||||||
bearerFormat: "JWT",
|
bearerFormat: "JWT",
|
||||||
},
|
},
|
||||||
|
ApiKeyAuth: {
|
||||||
|
type: "apiKey",
|
||||||
|
description: "API key required for authentication",
|
||||||
|
name: "api_key",
|
||||||
|
in: "header",
|
||||||
|
},
|
||||||
|
basicAuth: {
|
||||||
|
type: "http",
|
||||||
|
scheme: "basic",
|
||||||
|
description: "Basic authentication using username and password",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
// schemas: {
|
// schemas: {
|
||||||
// Error: {
|
// Error: {
|
||||||
@@ -47,12 +58,24 @@ export const openApiBase: OpenAPIV3_1.Document = {
|
|||||||
// message: { type: "string" },
|
// message: { type: "string" },
|
||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
// },
|
// },.
|
||||||
},
|
},
|
||||||
|
|
||||||
tags: [
|
tags: [
|
||||||
// { name: "Health", description: "Health check endpoints" },
|
{
|
||||||
// { name: "Printing", description: "Label printing operations" },
|
name: "Auth",
|
||||||
// { name: "Silo", description: "Silo management" },
|
description:
|
||||||
|
"Authentication section where you get and create users and api keys",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "System",
|
||||||
|
description: "All system endpoints that will be available to run",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Datamart",
|
||||||
|
description:
|
||||||
|
"All Special queries to run based on there names.\n Refer to the docs to see all possible queries that can be ran here, you can also run the getQueries to see available.",
|
||||||
|
},
|
||||||
// { name: "TMS", description: "TMS integration" },
|
// { name: "TMS", description: "TMS integration" },
|
||||||
],
|
],
|
||||||
paths: {}, // Will be populated
|
paths: {}, // Will be populated
|
||||||
@@ -96,6 +119,7 @@ export const setupApiDocsRoutes = (baseUrl: string, app: Express) => {
|
|||||||
targetKey: "node",
|
targetKey: "node",
|
||||||
clientKey: "axios",
|
clientKey: "axios",
|
||||||
},
|
},
|
||||||
|
|
||||||
documentDownloadType: "json",
|
documentDownloadType: "json",
|
||||||
hideClientButton: true,
|
hideClientButton: true,
|
||||||
hiddenClients: {
|
hiddenClients: {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* each endpoint will be something like
|
* each endpoint will be something like
|
||||||
* /api/datamart/{name}?{options}
|
* /api/datamart/{name}?{criteria}
|
||||||
*
|
*
|
||||||
* when getting the current queries we will need to map through the available queries we currently have and send back.
|
* when getting the current queries we will need to map through the available queries we currently have and send back.
|
||||||
* example
|
* example
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
* "name": "getopenorders",
|
* "name": "getopenorders",
|
||||||
* "endpoint": "/api/datamart/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",
|
* "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"
|
* "options": "sDay,eDay"
|
||||||
* },
|
* },
|
||||||
*
|
*
|
||||||
* when a criteria is password over we will handle it by counting how many were passed up to 3 then deal with each one respectively
|
* when a criteria is password over we will handle it by counting how many were passed up to 3 then deal with each one respectively
|
||||||
|
|||||||
@@ -1,18 +1,56 @@
|
|||||||
import { Router } from "express";
|
import { Router } from "express";
|
||||||
|
import z from "zod";
|
||||||
|
import type { NewDatamart } from "../db/schema/datamart.schema.js";
|
||||||
import { apiReturn } from "../utils/returnHelper.utils.js";
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
|
||||||
const r = Router();
|
const r = Router();
|
||||||
|
|
||||||
r.post("/add", async (_, res) => {
|
const newQuery = z.object({
|
||||||
apiReturn(res, {
|
name: z.string().min(5),
|
||||||
success: true,
|
description: z.string().min(30),
|
||||||
level: "info",
|
query: z.string().min(10),
|
||||||
|
options: z
|
||||||
|
.string()
|
||||||
|
.describe("This should be a set of keys separated by a comma")
|
||||||
|
.optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
r.post("/add", async (req, res) => {
|
||||||
|
try {
|
||||||
|
const v = newQuery.parse(req.body);
|
||||||
|
|
||||||
|
const query: NewDatamart = { ...v };
|
||||||
|
|
||||||
|
console.log(query);
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof z.ZodError) {
|
||||||
|
const flattened = z.flattenError(err);
|
||||||
|
// return res.status(400).json({
|
||||||
|
// error: "Validation failed",
|
||||||
|
// details: flattened,
|
||||||
|
// });
|
||||||
|
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error", //connect.success ? "info" : "error",
|
||||||
module: "routes",
|
module: "routes",
|
||||||
subModule: "prodSql",
|
subModule: "auth",
|
||||||
|
message: "Validation failed",
|
||||||
|
data: [flattened.fieldErrors],
|
||||||
|
status: 400, //connect.success ? 200 : 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "routes",
|
||||||
|
subModule: "datamart",
|
||||||
message: "connect.message",
|
message: "connect.message",
|
||||||
data: [{ connect: "" }],
|
data: [{ connect: "" }],
|
||||||
status: 200,
|
status: 200,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default r;
|
export default r;
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ export const user = pgTable("user", {
|
|||||||
banExpires: timestamp("ban_expires"),
|
banExpires: timestamp("ban_expires"),
|
||||||
username: text("username").unique(),
|
username: text("username").unique(),
|
||||||
displayUsername: text("display_username"),
|
displayUsername: text("display_username"),
|
||||||
lastLogin: timestamp("last_login").defaultNow(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const session = pgTable(
|
export const session = pgTable(
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export const datamart = pgTable("datamart", {
|
|||||||
name: text("name"),
|
name: text("name"),
|
||||||
description: text("description").notNull(),
|
description: text("description").notNull(),
|
||||||
query: text("query"),
|
query: text("query"),
|
||||||
version: integer("version").notNull(),
|
version: integer("version").default(1).notNull(),
|
||||||
active: boolean("active").default(true),
|
active: boolean("active").default(true),
|
||||||
options: text("checked").default(""),
|
options: text("checked").default(""),
|
||||||
add_date: timestamp("add_date").defaultNow(),
|
add_date: timestamp("add_date").defaultNow(),
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { betterAuth, type User } from "better-auth";
|
import { betterAuth } from "better-auth";
|
||||||
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
||||||
import {
|
import {
|
||||||
admin,
|
admin,
|
||||||
apiKey,
|
apiKey,
|
||||||
|
createAuthMiddleware,
|
||||||
customSession,
|
customSession,
|
||||||
jwt,
|
jwt,
|
||||||
|
lastLoginMethod,
|
||||||
username,
|
username,
|
||||||
} from "better-auth/plugins";
|
} from "better-auth/plugins";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
@@ -38,7 +40,7 @@ export const auth = betterAuth({
|
|||||||
},
|
},
|
||||||
lastLogin: {
|
lastLogin: {
|
||||||
type: "date",
|
type: "date",
|
||||||
required: false,
|
required: true,
|
||||||
input: false,
|
input: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -47,6 +49,7 @@ export const auth = betterAuth({
|
|||||||
jwt({ jwt: { expirationTime: "1h" } }),
|
jwt({ jwt: { expirationTime: "1h" } }),
|
||||||
apiKey(),
|
apiKey(),
|
||||||
admin(),
|
admin(),
|
||||||
|
lastLoginMethod(),
|
||||||
username({
|
username({
|
||||||
minUsernameLength: 5,
|
minUsernameLength: 5,
|
||||||
usernameValidator: (username) => {
|
usernameValidator: (username) => {
|
||||||
@@ -119,12 +122,22 @@ export const auth = betterAuth({
|
|||||||
secure: false,
|
secure: false,
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
},
|
},
|
||||||
events: {
|
hooks: {
|
||||||
async onSignInSuccess({ user }: { user: User }) {
|
after: createAuthMiddleware(async (ctx) => {
|
||||||
await db
|
if (ctx.path.startsWith("/login")) {
|
||||||
.update(rawSchema.user)
|
const newSession = ctx.context.newSession;
|
||||||
.set({ lastLogin: new Date() })
|
if (newSession) {
|
||||||
.where(eq(schema.user.id, user.id));
|
// something here later
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
|
events: {
|
||||||
|
// async onSignInSuccess({ user }: { user: User }) {
|
||||||
|
// await db
|
||||||
|
// .update(rawSchema.user)
|
||||||
|
// .set({ lastLogin: new Date() })
|
||||||
|
// .where(eq(schema.user.id, user.id));
|
||||||
|
// },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ interface Data {
|
|||||||
| "prodSql"
|
| "prodSql"
|
||||||
| "query"
|
| "query"
|
||||||
| "sendmail"
|
| "sendmail"
|
||||||
| "auth";
|
| "auth"
|
||||||
|
| "datamart";
|
||||||
level: "info" | "error" | "debug" | "fatal";
|
level: "info" | "error" | "debug" | "fatal";
|
||||||
message: string;
|
message: string;
|
||||||
data?: unknown[];
|
data?: unknown[];
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
|
import os from "node:os";
|
||||||
|
import path from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { promisify } from "node:util";
|
||||||
import type { Transporter } from "nodemailer";
|
import type { Transporter } from "nodemailer";
|
||||||
import nodemailer from "nodemailer";
|
import nodemailer from "nodemailer";
|
||||||
import type Mail from "nodemailer/lib/mailer/index.js";
|
import type Mail from "nodemailer/lib/mailer/index.js";
|
||||||
import type { Address } from "nodemailer/lib/mailer/index.js";
|
import type { Address } from "nodemailer/lib/mailer/index.js";
|
||||||
import type SMTPTransport from "nodemailer/lib/smtp-transport/index.js";
|
import type SMTPTransport from "nodemailer/lib/smtp-transport/index.js";
|
||||||
import hbs from "nodemailer-express-handlebars";
|
import hbs from "nodemailer-express-handlebars";
|
||||||
import os from "os";
|
|
||||||
import path from "path";
|
|
||||||
import { fileURLToPath } from "url";
|
|
||||||
import { promisify } from "util";
|
|
||||||
import { returnFunc } from "./returnHelper.utils.js";
|
import { returnFunc } from "./returnHelper.utils.js";
|
||||||
import { tryCatch } from "./trycatch.utils.js";
|
import { tryCatch } from "./trycatch.utils.js";
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ describe("Prod SQL connection", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
if (pool && pool.close) await pool.close();
|
if (pool?.close) await pool.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should connect and return expected data", async () => {
|
it("should connect and return expected data", async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user