diff --git a/.vscode/settings.json b/.vscode/settings.json index 3c3e745..a18517a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -60,7 +60,8 @@ "opendock", "opendocks", "ppoo", - "prodlabels" + "prodlabels", + "trycatch" ], "gitea.token": "8456def90e1c651a761a8711763d6ef225d6b2db", "gitea.instanceURL": "https://git.tuffraid.net", diff --git a/backend/auth/login.route.ts b/backend/auth/login.route.ts index 5edfb32..ed90051 100644 --- a/backend/auth/login.route.ts +++ b/backend/auth/login.route.ts @@ -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); + } }); } diff --git a/backend/middleware/auth.middleware.ts b/backend/middleware/auth.middleware.ts index 0a01c21..2adb856 100644 --- a/backend/middleware/auth.middleware.ts +++ b/backend/middleware/auth.middleware.ts @@ -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; + 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" }); diff --git a/backend/prodSql/prodSqlQuery.controller.ts b/backend/prodSql/prodSqlQuery.controller.ts index 2d6a3ea..fb492ae 100644 --- a/backend/prodSql/prodSqlQuery.controller.ts +++ b/backend/prodSql/prodSqlQuery.controller.ts @@ -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; diff --git a/backend/system/settings.route.ts b/backend/system/settings.route.ts new file mode 100644 index 0000000..f92f6e3 --- /dev/null +++ b/backend/system/settings.route.ts @@ -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; diff --git a/backend/system/settingsBase.controller.ts b/backend/system/settingsBase.controller.ts new file mode 100644 index 0000000..e69de29 diff --git a/backend/system/settingsUpdate.controller.ts b/backend/system/settingsUpdate.controller.ts deleted file mode 100644 index 5239ad4..0000000 --- a/backend/system/settingsUpdate.controller.ts +++ /dev/null @@ -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. -}; diff --git a/backend/system/settingsUpdate.route.ts b/backend/system/settingsUpdate.route.ts new file mode 100644 index 0000000..00d3c73 --- /dev/null +++ b/backend/system/settingsUpdate.route.ts @@ -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 = {}; + // 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; diff --git a/backend/system/system.routes.ts b/backend/system/system.routes.ts index bf5f39c..927f13e 100644 --- a/backend/system/system.routes.ts +++ b/backend/system/system.routes.ts @@ -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/* }; diff --git a/backend/utils/auth.utils.ts b/backend/utils/auth.utils.ts index cd9464f..7125f9c 100644 --- a/backend/utils/auth.utils.ts +++ b/backend/utils/auth.utils.ts @@ -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, diff --git a/backend/utils/returnHelper.utils.ts b/backend/utils/returnHelper.utils.ts index ddbf2b8..3b5b60b 100644 --- a/backend/utils/returnHelper.utils.ts +++ b/backend/utils/returnHelper.utils.ts @@ -1,7 +1,7 @@ import type { Response } from "express"; import { createLogger } from "../logger/logger.controller.js"; -interface Data { +interface Data { 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; } diff --git a/brunoApi/auth/Login.bru b/brunoApi/auth/Login.bru new file mode 100644 index 0000000..06f5c05 --- /dev/null +++ b/brunoApi/auth/Login.bru @@ -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 +} diff --git a/brunoApi/auth/folder.bru b/brunoApi/auth/folder.bru new file mode 100644 index 0000000..bdc4699 --- /dev/null +++ b/brunoApi/auth/folder.bru @@ -0,0 +1,8 @@ +meta { + name: auth + seq: 5 +} + +auth { + mode: inherit +} diff --git a/brunoApi/collection.bru b/brunoApi/collection.bru new file mode 100644 index 0000000..e69de29 diff --git a/brunoApi/environments/lstv3.bru b/brunoApi/environments/lstv3.bru index 5c0f373..6385759 100644 --- a/brunoApi/environments/lstv3.bru +++ b/brunoApi/environments/lstv3.bru @@ -1,3 +1,4 @@ vars { url: http://localhost:3000 + ~session_cookie: } diff --git a/brunoApi/system/Get Settings.bru b/brunoApi/system/Get Settings.bru new file mode 100644 index 0000000..ecb645c --- /dev/null +++ b/brunoApi/system/Get Settings.bru @@ -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 +} diff --git a/brunoApi/system/updateSetting.bru b/brunoApi/system/updateSetting.bru new file mode 100644 index 0000000..87ca359 --- /dev/null +++ b/brunoApi/system/updateSetting.bru @@ -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 +}