feat(lstv2 move): moved lstv2 into this app to keep them combined and easier to maintain

This commit is contained in:
2025-09-19 22:22:05 -05:00
parent caf2315191
commit e4477402ad
847 changed files with 165801 additions and 0 deletions

View 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;

View 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};
}

View 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"};
}

View 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.`,
};
}
};

View File

@@ -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);
};

View 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 };
};

View File

@@ -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,
};
};

View File

@@ -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;
};

View File

@@ -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;
};

View 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,
});
};

View File

@@ -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"`
);
}
};

View 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");
}
}

View 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);
}
};

View 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;

View 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;

View 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;

View 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;

View 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;

View 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;

View 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;

View File

@@ -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;

View 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;

View 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;

View 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;

View 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}`
);
}
}
};

View 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;
};

View 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;
};

View 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}`);
}
};