feat(user stuff): added in all the user stuff

This commit is contained in:
2025-10-28 21:06:37 -05:00
parent 147d189a81
commit 0ddf67d815
21 changed files with 412 additions and 159 deletions

View File

@@ -4,7 +4,7 @@ meta {
seq: 1
}
post {
get {
url: {{url}}/lst/old/api/ocp/updateprinters
body: none
auth: inherit

View File

@@ -0,0 +1,26 @@
meta {
name: Change user password
type: http
seq: 5
}
patch {
url: {{url}}/lst/api/admin/users/changePassword/:userId
body: json
auth: inherit
}
params:path {
userId: 0hlO48C7Jw1J804FxrCnonKjQ2zh48R6
}
body:json {
{
"password":"nova0511"
}
}
settings {
encodeUrl: true
timeout: 0
}

View File

@@ -0,0 +1,25 @@
meta {
name: Create user
type: http
seq: 4
}
post {
url: {{url}}/lst/api/admin/users
body: none
auth: inherit
}
body:json {
{
"username":"matthes01",
"name":"blake",
"email":"blake.matthes@alpla.com",
"password":"nova0511"
}
}
settings {
encodeUrl: true
timeout: 0
}

View File

@@ -0,0 +1,26 @@
meta {
name: Delete User
type: http
seq: 6
}
delete {
url: {{url}}/lst/api/admin/users/delete/:userId
body: json
auth: inherit
}
params:path {
userId: 0hlO48C7Jw1J804FxrCnonKjQ2zh48R6
}
body:json {
{
"password":"nova0511"
}
}
settings {
encodeUrl: true
timeout: 0
}

View File

@@ -21,4 +21,5 @@ body:json {
settings {
encodeUrl: true
timeout: 0
}

View File

@@ -5,7 +5,7 @@ meta {
}
patch {
url: {{url}}/lst/api/admin/:userID/grant
url: {{url}}/lst/api/admin/users/:userID/grant
body: json
auth: inherit
}

View File

@@ -5,7 +5,7 @@ meta {
}
patch {
url: {{url}}/lst/api/admin/:userID/revoke
url: {{url}}/lst/api/admin/users/:userID/revoke
body: json
auth: inherit
}

View File

@@ -1,5 +1,5 @@
vars {
url: https://usiow2prod.alpla.net
url: http://localhost:4200
session_cookie:
urlv2: http://localhost:3000
jwtV2:

View File

@@ -0,0 +1,42 @@
import type { User } from "better-auth";
import { DrizzleQueryError } from "drizzle-orm";
import { auth } from "../../../../pkg/auth/auth.js";
import { tryCatch } from "../../../../pkg/utils/tryCatch.js";
export type NewUser = {
email: string;
password: string;
username: string;
statusCode: number;
message: string;
};
export const createNewUser = async (userData: NewUser) => {
const { data, error } = await tryCatch(
auth.api.createUser({
body: {
email: userData.email, // required
password: userData.password, // required
name: userData.username, // required
role: "user",
data: { username: userData.username },
},
}),
);
if (error) {
if (error instanceof DrizzleQueryError) {
// @ts-ignore
if (error?.cause.message.includes("unique constraint")) {
return {
statusCode: 400,
message: `${userData.username} already exists`,
};
}
}
return error;
}
return data;
};

View File

@@ -1,11 +1,9 @@
import type { Express, Request, Response } from "express";
import { requireAuth } from "../../pkg/middleware/authMiddleware.js";
import { mainServerSync } from "./controller/servers/matchServers.js";
//admin routes
import users from "./routes/getUserRoles.js";
import grantRoles from "./routes/grantRole.js";
import revokeRoles from "./routes/revokeRole.js";
import servers from "./routes/servers/serverRoutes.js";
//admin routes
import users from "./routes/users/userRoutes.js";
export const setupAdminRoutes = (app: Express, basePath: string) => {
app.use(
@@ -15,22 +13,10 @@ export const setupAdminRoutes = (app: Express, basePath: string) => {
app.use(
basePath + "/api/admin/users",
requireAuth("user", ["systemAdmin"]), // will pass bc system admin but this is just telling us we need this
requireAuth("user", ["systemAdmin", "admin"]), // will pass bc system admin but this is just telling us we need this
users,
);
app.use(
basePath + "/api/admin",
requireAuth("user", ["systemAdmin", "admin"]), // will pass bc system admin but this is just telling us we need this
grantRoles,
);
app.use(
basePath + "/api/admin",
requireAuth("user", ["systemAdmin", "admin"]), // will pass bc system admin but this is just telling us we need this
revokeRoles,
);
// run the sync only on startup
setTimeout(() => {
mainServerSync();

View File

@@ -1,52 +0,0 @@
import { Router } from "express";
import type { Request, Response } from "express";
import { tryCatch } from "../../../pkg/utils/tryCatch.js";
import { db } from "../../../pkg/db/db.js";
import { user } from "../../../pkg/db/schema/auth-schema.js";
import { userRoles } from "../../../pkg/db/schema/user_roles.js";
const router = Router();
router.post("/", async (req: Request, res: Response) => {
// should get all users
const { data: users, error: userError } = await tryCatch(
db.select().from(user)
);
if (userError) {
return res.status(500).json({
success: false,
message: "Failed to get users",
error: userError,
});
}
// should get all roles
const { data: userRole, error: userRoleError } = await tryCatch(
db.select().from(userRoles)
);
if (userRoleError) {
return res.status(500).json({
success: false,
message: "Failed to get userRoless",
error: userRoleError,
});
}
// add the roles and return
const usersWithRoles = users.map((user) => {
const roles = userRole
.filter((ur) => ur.userId === user.id)
.map((ur) => ({ module: ur.module, role: ur.role }));
return { ...user, roles };
});
return res
.status(200)
.json({ success: true, message: "User data", data: usersWithRoles });
});
export default router;

View File

@@ -0,0 +1,25 @@
import { type Request, type Response, Router } from "express";
import { auth } from "../../../../pkg/auth/auth.js";
const router = Router();
router.patch("/:userId", async (req: Request, res: Response) => {
const userId = req.params.userId;
const cookieHeader = req.headers.cookie ?? "";
const authorization = req.headers.authorization ?? "";
const data = await auth.api.setUserPassword({
body: {
newPassword: req.body.password, // required
userId: userId, // required
},
// This endpoint requires session cookies.
headers: {
cookie: cookieHeader,
authorization,
},
});
return res.status(200).json({ message: "Password was just changed." });
});
export default router;

View File

@@ -0,0 +1,20 @@
import { type Request, type Response, Router } from "express";
import { createNewUser, type NewUser } from "../../controller/users/newUser.js";
const router = Router();
router.post("/", async (req: Request, res: Response) => {
const body = req.body;
const user = (await createNewUser(body)) as NewUser;
if (user?.statusCode === 400) {
return res.status(user?.statusCode).json({
message: user?.message,
});
}
return res
.status(200)
.json({ message: `${body.username}, was just created` });
});
export default router;

View File

@@ -0,0 +1,24 @@
import { type Request, type Response, Router } from "express";
import { auth } from "../../../../pkg/auth/auth.js";
const router = Router();
router.delete("/:userId", async (req: Request, res: Response) => {
const userId = req.params.userId;
const cookieHeader = req.headers.cookie ?? "";
const authorization = req.headers.authorization ?? "";
const data = await auth.api.removeUser({
body: {
userId: userId, // required
},
// This endpoint requires session cookies.
headers: {
cookie: cookieHeader,
authorization,
},
});
return res.status(200).json({ message: "User was just deleted." });
});
export default router;

View File

@@ -0,0 +1,25 @@
import { type Request, type Response, Router } from "express";
import { auth } from "../../../../pkg/auth/auth.js";
const router = Router();
router.patch("/:userId", async (req: Request, res: Response) => {
const userId = req.params.userId;
const cookieHeader = req.headers.cookie ?? "";
const authorization = req.headers.authorization ?? "";
const data = await auth.api.setUserPassword({
body: {
newPassword: req.body.password, // required
userId: userId, // required
},
// This endpoint requires session cookies.
headers: {
cookie: cookieHeader,
authorization,
},
});
return res.status(200).json({ message: "Password was just changed." });
});
export default router;

View File

@@ -0,0 +1,52 @@
import type { Request, Response } from "express";
import { Router } from "express";
import { db } from "../../../../pkg/db/db.js";
import { user } from "../../../../pkg/db/schema/auth-schema.js";
import { userRoles } from "../../../../pkg/db/schema/user_roles.js";
import { tryCatch } from "../../../../pkg/utils/tryCatch.js";
const router = Router();
router.post("/", async (req: Request, res: Response) => {
// should get all users
const { data: users, error: userError } = await tryCatch(
db.select().from(user),
);
if (userError) {
return res.status(500).json({
success: false,
message: "Failed to get users",
error: userError,
});
}
// should get all roles
const { data: userRole, error: userRoleError } = await tryCatch(
db.select().from(userRoles),
);
if (userRoleError) {
return res.status(500).json({
success: false,
message: "Failed to get userRoless",
error: userRoleError,
});
}
// add the roles and return
const usersWithRoles = users.map((user) => {
const roles = userRole
.filter((ur) => ur.userId === user.id)
.map((ur) => ({ module: ur.module, role: ur.role }));
return { ...user, roles };
});
return res
.status(200)
.json({ success: true, message: "User data", data: usersWithRoles });
});
export default router;

View File

@@ -1,10 +1,9 @@
import type { Request, Response } from "express";
import { Router } from "express";
import z from "zod";
import { db } from "../../../pkg/db/db.js";
import { userRoles } from "../../../pkg/db/schema/user_roles.js";
import { createLogger } from "../../../pkg/logger/logger.js";
import { tryCatch } from "../../../pkg/utils/tryCatch.js";
import { db } from "../../../../pkg/db/db.js";
import { userRoles } from "../../../../pkg/db/schema/user_roles.js";
import { createLogger } from "../../../../pkg/logger/logger.js";
const roleSchema = z.object({
module: z.enum([

View File

@@ -2,10 +2,9 @@ import { and, eq } from "drizzle-orm";
import type { Request, Response } from "express";
import { Router } from "express";
import z from "zod";
import { db } from "../../../pkg/db/db.js";
import { userRoles } from "../../../pkg/db/schema/user_roles.js";
import { createLogger } from "../../../pkg/logger/logger.js";
import { tryCatch } from "../../../pkg/utils/tryCatch.js";
import { db } from "../../../../pkg/db/db.js";
import { userRoles } from "../../../../pkg/db/schema/user_roles.js";
import { createLogger } from "../../../../pkg/logger/logger.js";
const roleSchema = z.object({
module: z.enum([

View File

@@ -0,0 +1,51 @@
import { fromNodeHeaders } from "better-auth/node";
import type { Request, Response } from "express";
import { Router } from "express";
import { auth } from "../../../../pkg/auth/auth.js";
import { requireAuth } from "../../../../pkg/middleware/authMiddleware.js";
import changePassword from "./changeUserPassword.js";
import createUser from "./createUser.js";
import deleteUser from "./deleteUser.js";
import users from "./getUserRoles.js";
import grantRoles from "./grantRole.js";
import revokeRoles from "./revokeRole.js";
const router = Router();
router.use(
"/",
requireAuth("user", ["systemAdmin", "admin"]), // will pass bc system admin but this is just telling us we need this
grantRoles,
);
router.use(
"/new",
requireAuth("user", ["systemAdmin", "admin"]), // will pass bc system admin but this is just telling us we need this
createUser,
);
router.use(
"/",
requireAuth("user", ["systemAdmin", "admin"]), // will pass bc system admin but this is just telling us we need this
revokeRoles,
);
router.use(
"/",
requireAuth("user", ["systemAdmin", "admin"]), // will pass bc system admin but this is just telling us we need this
users,
);
router.use(
"/changePassword",
requireAuth("user", ["systemAdmin", "admin"]), // will pass bc system admin but this is just telling us we need this
changePassword,
);
router.use(
"/delete",
requireAuth("user", ["systemAdmin", "admin"]), // will pass bc system admin but this is just telling us we need this
deleteUser,
);
export default router;

View File

@@ -1,10 +1,10 @@
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "../db/db.js";
import { username, admin, apiKey, jwt } from "better-auth/plugins";
import { betterAuth } from "better-auth";
import * as rawSchema from "../db/schema/auth-schema.js";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { admin, apiKey, jwt, username } from "better-auth/plugins";
import type { User } from "better-auth/types";
import { eq } from "drizzle-orm";
import { db } from "../db/db.js";
import * as rawSchema from "../db/schema/auth-schema.js";
import { sendEmail } from "../utils/mail/sendMail.js";
export const schema = {
@@ -39,9 +39,7 @@ export const auth = betterAuth({
const expiryMinutes = Math.floor(RESET_EXPIRY_SECONDS / 60);
const expiryText =
expiryMinutes >= 60
? `${expiryMinutes / 60} hour${
expiryMinutes === 60 ? "" : "s"
}`
? `${expiryMinutes / 60} hour${expiryMinutes === 60 ? "" : "s"}`
: `${expiryMinutes} minutes`;
const emailData = {
email: user.email,
@@ -73,9 +71,15 @@ export const auth = betterAuth({
freshAge: 60 * 2,
cookieCache: {
enabled: true,
maxAge: 5 * 60, // Cache duration in seconds
maxAge: 5 * 60,
},
},
cookie: {
path: "/lst/app",
sameSite: "lax",
secure: false,
httpOnly: true,
},
events: {
async onSignInSuccess({ user }: { user: User }) {
await db

View File

@@ -59,7 +59,7 @@ export default function ExpandedRow({ row }: { row: any }) {
// user,
// });
try {
const result = await api.patch(`/api/admin/${user.id}/grant`, {
const result = await api.patch(`/api/admin/users/${user.id}/grant`, {
module: module,
role: role,
});
@@ -83,7 +83,7 @@ export default function ExpandedRow({ row }: { row: any }) {
const onDeleteRole = async (module: string) => {
try {
const result = await api.patch(`/api/admin/${user.id}/revoke`, {
const result = await api.patch(`/api/admin/users/${user.id}/revoke`, {
module: module,
});