intial setting and auth intergrated

This commit is contained in:
2026-02-24 15:53:58 -06:00
parent 326c2e125c
commit c3379919b9
17 changed files with 304 additions and 52 deletions

View File

@@ -60,7 +60,8 @@
"opendock",
"opendocks",
"ppoo",
"prodlabels"
"prodlabels",
"trycatch"
],
"gitea.token": "8456def90e1c651a761a8711763d6ef225d6b2db",
"gitea.instanceURL": "https://git.tuffraid.net",

View File

@@ -54,7 +54,8 @@ const signin = z.union([
const r = Router();
r.post("/", async (req, res) => {
let login: unknown;
let login: unknown | any;
try {
const validated = signin.parse(req.body);
if ("email" in validated) {
@@ -92,6 +93,15 @@ r.post("/", async (req, res) => {
password: validated.password,
},
headers: fromNodeHeaders(req.headers),
asResponse: true,
});
login.headers.forEach((value: string, key: string) => {
if (key.toLowerCase() === "set-cookie") {
res.append("set-cookie", value);
} else {
res.setHeader(key, value);
}
});
}

View File

@@ -2,27 +2,55 @@ import { fromNodeHeaders } from "better-auth/node";
import type { NextFunction, Request, Response } from "express";
import { auth } from "../utils/auth.utils.js";
declare global {
namespace Express {
interface Request {
user?: {
id: string;
email?: string;
roles?: string | null | undefined; //Record<string, string[]>;
username?: string | null | undefined;
};
}
}
}
// function toWebHeaders(nodeHeaders: Request["headers"]): Headers {
// const h = new Headers();
// for (const [key, value] of Object.entries(nodeHeaders)) {
// if (Array.isArray(value)) {
// value.forEach((v) => h.append(key, v));
// } else if (value !== undefined) {
// h.set(key, value);
// }
// }
// return h;
// }
export const requireAuth = async (
req: Request,
res: Response,
next: NextFunction,
) => {
// TODO: add the real auth stuff in later.
try {
const session = await auth.api.getSession({
headers: fromNodeHeaders(req.headers),
//query: { disableCookieCache: true },
});
if (!session) {
//return res.status(401).json({ error: "Unauthorized" });
console.info("not auth of course");
return res.status(401).json({ error: "Unauthorized" });
}
// attach session to request for later use
(req as any).session = session;
console.info(
"Just passing the middleware and reminder that we need to add the real stuff in.",
);
//console.log(session);
req.user = {
id: session.user.id,
email: session.user.email,
roles: session.user.role,
username: session.user.username,
};
next();
} catch {
return res.status(401).json({ error: "Unauthorized" });

View File

@@ -58,7 +58,7 @@ export const prodQuery = async (queryToRun: string, name: string) => {
return {
success: true,
message: `Query results for: ${name}`,
data: result.recordset,
data: result.recordset ?? [],
};
} catch (error: unknown) {
const err = error as SqlError;

View File

@@ -0,0 +1,44 @@
import { type Response, Router } from "express";
import { db } from "../db/db.controller.js";
import { settings } from "../db/schema/settings.schema.js";
import { apiReturn } from "../utils/returnHelper.utils.js";
import { tryCatch } from "../utils/trycatch.utils.js";
// export const updateSetting = async (setting: Setting) => {
// // TODO: when the setting is a feature setting we will need to have it run each kill switch on the crons well just stop them and during a reset it just wont start them
// // TODO: when the setting is a system we will need to force an app restart
// // TODO: when the setting is standard we don't do anything.
// };
const r = Router();
r.get("/", async (_, res: Response) => {
const { data: sName, error: sError } = await tryCatch(
db.select().from(settings),
);
if (sError) {
return apiReturn(res, {
success: false,
level: "error",
module: "system",
subModule: "settings",
message: `There was an error getting the settings `,
data: [sError],
status: 400,
});
}
return apiReturn(res, {
success: true,
level: "info",
module: "system",
subModule: "settings",
message: `All current settings`,
data: sName ?? [],
status: 200,
});
});
export default r;

View File

@@ -1,7 +0,0 @@
import type { Setting } from "../db/schema/settings.schema.js";
export const updateSetting = async (setting: Setting) => {
// TODO: when the setting is a feature setting we will need to have it run each kill switch on the crons well just stop them and during a reset it just wont start them
// TODO: when the setting is a system we will need to force an app restart
// TODO: when the setting is standard we don't do anything.
};

View File

@@ -0,0 +1,93 @@
import { eq, sql } from "drizzle-orm";
import { type Request, type Response, Router } from "express";
import { db } from "../db/db.controller.js";
import { settings } from "../db/schema/settings.schema.js";
import { apiReturn } from "../utils/returnHelper.utils.js";
import { tryCatch } from "../utils/trycatch.utils.js";
// export const updateSetting = async (setting: Setting) => {
// // TODO: when the setting is a feature setting we will need to have it run each kill switch on the crons well just stop them and during a reset it just wont start them
// // TODO: when the setting is a system we will need to force an app restart
// // TODO: when the setting is standard we don't do anything.
// };
const r = Router();
r.patch("/:name", async (req: Request, res: Response) => {
const { name } = req.params;
const updates: Record<string, unknown | null> = {};
// lets see if we even have a setting name
const { data: sName, error: sError } = await tryCatch(
db
.select()
.from(settings)
.where(eq(settings.name, name ?? "")),
);
if (sError) {
return apiReturn(res, {
success: false,
level: "error",
module: "system",
subModule: "settings",
message: `There was an error checking the name of the setting`,
data: [sError],
status: 400,
});
}
// if (sName?.length === 0) {
// return apiReturn(res, {
// success: false,
// level: "error",
// module: "system",
// subModule: "settings",
// message: `The setting "${name}" dose not appear to be a valid setting please check the name and try again. `,
// data: [],
// status: 400,
// });
// }
// manage the actual setting. we will still do an upsert just in case we strangely get past everything
if (req.body?.value !== undefined) {
updates.value = req.body.value;
}
if (req.body?.description !== undefined) {
updates.description = req.body.description;
}
if (req.body?.moduleName !== undefined) {
updates.moduleName = req.body.moduleName;
}
if (req.body?.active !== undefined) {
updates.active = req.body.active === "true";
}
if (req.body?.roles !== undefined) {
updates.roles = req.body.roles;
}
if (req.body?.settingType !== undefined) {
updates.settingType = req.body.settingType;
}
updates.upd_user = req.user?.username || "lst_user";
updates.upd_date = sql`NOW()`;
return apiReturn(res, {
success: true,
level: "info",
module: "system",
subModule: "settings",
message: `Setting "${name}" Was just updated. `,
data: [updates],
status: 400,
});
});
export default r;

View File

@@ -1,9 +1,14 @@
import { requireAuth } from "backend/middleware/auth.middleware.js";
import type { Express } from "express";
import getSettings from "./settings.route.js";
import updSetting from "./settingsUpdate.route.js";
import stats from "./stats.route.js";
export const setupSystemRoutes = (baseUrl: string, app: Express) => {
//stats will be like this as we dont need to change this
app.use(`${baseUrl}/api/stats`, stats);
app.use(`${baseUrl}/api/settings`, getSettings);
app.use(`${baseUrl}/api/settings`, requireAuth, updSetting);
// all other system should be under /api/system/*
};

View File

@@ -4,12 +4,12 @@ import {
admin,
apiKey,
createAuthMiddleware,
customSession,
//customSession,
jwt,
lastLoginMethod,
username,
} from "better-auth/plugins";
import { eq } from "drizzle-orm";
//import { eq } from "drizzle-orm";
import { db } from "../db/db.controller.js";
import * as rawSchema from "../db/schema/auth.schema.js";
import { allowedOrigins } from "./cors.utils.js";
@@ -31,20 +31,15 @@ export const auth = betterAuth({
provider: "pg",
schema,
}),
user: {
additionalFields: {
role: {
type: "string",
required: false,
input: false,
},
lastLogin: {
type: "date",
required: true,
input: false,
},
},
},
// user: {
// additionalFields: {
// role: {
// type: "string",
// //required: false,
// input: false,
// },
// },
// },
plugins: [
jwt({ jwt: { expirationTime: "1h" } }),
apiKey(),
@@ -53,26 +48,27 @@ export const auth = betterAuth({
username({
minUsernameLength: 5,
usernameValidator: (username) => {
if (username === "admin") {
if (username === "admin" || username === "root") {
return false;
}
return true;
},
}),
customSession(async ({ user, session }) => {
const roles = await db
.select({ roles: rawSchema.user.role })
.from(rawSchema.user)
.where(eq(rawSchema.user.id, session.id));
return {
roles,
user: {
...user,
//newField: "newField",
},
session,
};
}),
// customSession(async ({ user, session }) => {
// const roles = await db
// .select({ roles: rawSchema.user.role })
// .from(rawSchema.user)
// .where(eq(rawSchema.user.id, session.id));
// return {
// roles,
// user: {
// ...user,
// //newField: "newField",
// },
// session,
// };
// }),
],
trustedOrigins: allowedOrigins,

View File

@@ -1,7 +1,7 @@
import type { Response } from "express";
import { createLogger } from "../logger/logger.controller.js";
interface Data {
interface Data<T = unknown[]> {
success: boolean;
module: "system" | "ocp" | "routes" | "datamart" | "utils" | "opendock";
subModule:
@@ -14,10 +14,11 @@ interface Data {
| "auth"
| "datamart"
| "jobs"
| "apt";
| "apt"
| "settings";
level: "info" | "error" | "debug" | "fatal";
message: string;
data?: unknown[];
data?: T;
notify?: boolean;
}

33
brunoApi/auth/Login.bru Normal file
View File

@@ -0,0 +1,33 @@
meta {
name: Login
type: http
seq: 1
}
post {
url: {{url}}/lst/api/authentication/login
body: json
auth: inherit
}
body:json {
{
"username": "matthes01",
"password": "nova0511"
}
}
script:post-response {
// // grab the raw Set-Cookie header
// const cookies = res.headers["set-cookie"];
// const sessionCookie = cookies[0].split(";")[0];
// // Save it as an environment variable
// bru.setEnvVar("session_cookie", sessionCookie);
}
settings {
encodeUrl: true
timeout: 0
}

8
brunoApi/auth/folder.bru Normal file
View File

@@ -0,0 +1,8 @@
meta {
name: auth
seq: 5
}
auth {
mode: inherit
}

0
brunoApi/collection.bru Normal file
View File

View File

@@ -1,3 +1,4 @@
vars {
url: http://localhost:3000
~session_cookie:
}

View File

@@ -0,0 +1,16 @@
meta {
name: Get Settings
type: http
seq: 3
}
get {
url: {{url}}/lst/api/settings
body: none
auth: inherit
}
settings {
encodeUrl: true
timeout: 0
}

View File

@@ -0,0 +1,23 @@
meta {
name: updateSetting
type: http
seq: 2
}
patch {
url: {{url}}/lst/api/settings/something
body: json
auth: inherit
}
body:json {
{
"value" : "true",
"active": "false"
}
}
settings {
encodeUrl: true
timeout: 0
}