diff --git a/apiDocs/lstV2/api/get modules.bru b/apiDocs/lstV2/api/get modules.bru new file mode 100644 index 0000000..6822594 --- /dev/null +++ b/apiDocs/lstV2/api/get modules.bru @@ -0,0 +1,18 @@ +meta { + name: get modules + type: http + seq: 2 +} + +post { + url: http://localhost:3000/api/hits + body: json + auth: none +} + +body:json { + { + "name": "something", + "arrayData": "{someghjdsfsd}" + } +} diff --git a/frontend/src/components/providers/Providers.tsx b/frontend/src/components/providers/Providers.tsx index a5856f0..67bdb1f 100644 --- a/frontend/src/components/providers/Providers.tsx +++ b/frontend/src/components/providers/Providers.tsx @@ -1,7 +1,14 @@ import {QueryClient, QueryClientProvider} from "@tanstack/react-query"; +import {useModuleStore} from "../../lib/store/useModuleStore"; +import {useEffect} from "react"; const queryClient = new QueryClient(); export const SessionProvider = ({children}: {children: React.ReactNode}) => { + const {fetchModules} = useModuleStore(); + + useEffect(() => { + fetchModules(); + }, []); return {children}; }; diff --git a/frontend/src/lib/store/useModuleStore.ts b/frontend/src/lib/store/useModuleStore.ts new file mode 100644 index 0000000..5f0bc64 --- /dev/null +++ b/frontend/src/lib/store/useModuleStore.ts @@ -0,0 +1,45 @@ +import {create} from "zustand"; +//import useSWR from "swr"; + +interface Modules { + module_id: string; + name: string; + active: boolean; + roles: string; + add_user: string; + add_date: Date; + upd_user: string; + upd_date: Date; +} + +interface SettingState { + modules: Modules[]; + + fetchModules: () => Promise; + setModules: (modules: Modules[]) => void; +} +interface FetchModulesResponse { + data: Modules[]; +} + +export const useModuleStore = create()((set) => ({ + modules: [], + setModules: (modules) => set({modules}), + fetchModules: async () => { + try { + //const response = await axios.get<{data: Setting[]}>(`${process.env.NEXT_PUBLIC_URL}/api/settings/client`); + const response = await fetch(`/api/server/modules`, { + method: "GET", + headers: { + "Content-Type": "application/json", + // You can add other headers here if necessary + }, + }); + const data: FetchModulesResponse = await response.json(); + //console.log(data); + set({modules: data.data}); + } catch (error) { + console.error("Failed to fetch settings:", error); + } + }, +})); diff --git a/frontend/src/utils/moduleActive.ts b/frontend/src/utils/moduleActive.ts index ce270fa..1673970 100644 --- a/frontend/src/utils/moduleActive.ts +++ b/frontend/src/utils/moduleActive.ts @@ -1,20 +1,23 @@ -type Feature = string; -interface Module { - id: number; - name: string; - features: Feature[]; - active: boolean; -} +// type Feature = string; +// interface Module { +// id: number; +// name: string; +// features: Feature[]; +// active: boolean; +// } -const modules: Module[] = [ - {id: 1, name: "production", active: true, features: ["view", "edit", "approve"]}, - {id: 2, name: "logistics", active: true, features: ["view", "assign", "track"]}, - {id: 3, name: "quality", active: false, features: ["view", "audit", "approve"]}, - {id: 4, name: "forklift", active: true, features: ["view", "request", "operate"]}, - {id: 5, name: "admin", active: true, features: ["view", "manage_users", "view_logs", "settings"]}, -]; +import {useModuleStore} from "../lib/store/useModuleStore"; + +// const modules: Module[] = [ +// {id: 1, name: "production", active: true, features: ["view", "edit", "approve"]}, +// {id: 2, name: "logistics", active: true, features: ["view", "assign", "track"]}, +// {id: 3, name: "quality", active: false, features: ["view", "audit", "approve"]}, +// {id: 4, name: "forklift", active: true, features: ["view", "request", "operate"]}, +// {id: 5, name: "admin", active: true, features: ["view", "manage_users", "view_logs", "settings"]}, +// ]; export function moduleActive(moduleName: string): boolean { - const module = modules.find((m) => m.name === moduleName); + const {modules} = useModuleStore(); + const module = modules.find((m: any) => m.name === moduleName); return module ? module.active : false; } diff --git a/server/database/migrations/0002_bitter_oracle.sql b/server/database/migrations/0002_bitter_oracle.sql new file mode 100644 index 0000000..e1303c0 --- /dev/null +++ b/server/database/migrations/0002_bitter_oracle.sql @@ -0,0 +1,36 @@ +CREATE TABLE "modules" ( + "module_id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "name" text NOT NULL, + "active" boolean DEFAULT false, + "add_User" text DEFAULT 'LST_System' NOT NULL, + "add_Date" timestamp DEFAULT now(), + "upd_User" text DEFAULT 'LST_System' NOT NULL, + "upd_date" timestamp DEFAULT now() +); +--> statement-breakpoint +CREATE TABLE "roles" ( + "role_id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "name" text NOT NULL, + "add_User" text DEFAULT 'LST_System' NOT NULL, + "add_Date" timestamp DEFAULT now(), + "upd_User" text DEFAULT 'LST_System' NOT NULL, + "upd_date" timestamp DEFAULT now() +); +--> statement-breakpoint +CREATE TABLE "userRoles" ( + "user_id" uuid NOT NULL, + "role_id" uuid NOT NULL, + "module_id" uuid NOT NULL, + "roles" text NOT NULL, + "add_User" text DEFAULT 'LST_System' NOT NULL, + "add_Date" timestamp DEFAULT now(), + "upd_User" text DEFAULT 'LST_System' NOT NULL, + "upd_date" timestamp DEFAULT now() +); +--> statement-breakpoint +ALTER TABLE "userRoles" ADD CONSTRAINT "userRoles_user_id_users_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("user_id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "userRoles" ADD CONSTRAINT "userRoles_role_id_roles_role_id_fk" FOREIGN KEY ("role_id") REFERENCES "public"."roles"("role_id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "userRoles" ADD CONSTRAINT "userRoles_module_id_modules_module_id_fk" FOREIGN KEY ("module_id") REFERENCES "public"."modules"("module_id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +CREATE UNIQUE INDEX "module_name" ON "modules" USING btree ("name");--> statement-breakpoint +CREATE UNIQUE INDEX "role_name" ON "roles" USING btree ("name");--> statement-breakpoint +CREATE UNIQUE INDEX "user_module_unique" ON "userRoles" USING btree ("user_id","module_id"); \ No newline at end of file diff --git a/server/database/migrations/0003_luxuriant_namorita.sql b/server/database/migrations/0003_luxuriant_namorita.sql new file mode 100644 index 0000000..a26eb18 --- /dev/null +++ b/server/database/migrations/0003_luxuriant_namorita.sql @@ -0,0 +1,2 @@ +ALTER TABLE "userRoles" RENAME COLUMN "roles" TO "role";--> statement-breakpoint +ALTER TABLE "modules" ADD COLUMN "roles" text NOT NULL; \ No newline at end of file diff --git a/server/database/migrations/0004_quick_mandrill.sql b/server/database/migrations/0004_quick_mandrill.sql new file mode 100644 index 0000000..bb8201a --- /dev/null +++ b/server/database/migrations/0004_quick_mandrill.sql @@ -0,0 +1 @@ +ALTER TABLE "modules" ALTER COLUMN "roles" SET DEFAULT '["view", "systemAdmin"]'; \ No newline at end of file diff --git a/server/database/migrations/meta/0002_snapshot.json b/server/database/migrations/meta/0002_snapshot.json new file mode 100644 index 0000000..3b6ffd6 --- /dev/null +++ b/server/database/migrations/meta/0002_snapshot.json @@ -0,0 +1,407 @@ +{ + "id": "9a2ddf11-fd30-4dd9-bf2d-259fe7aed201", + "prevId": "d6c99236-0eea-49f1-817b-13139c0f42f5", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.modules": { + "name": "modules", + "schema": "", + "columns": { + "module_id": { + "name": "module_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "add_User": { + "name": "add_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "add_Date": { + "name": "add_Date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "upd_User": { + "name": "upd_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "upd_date": { + "name": "upd_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "module_name": { + "name": "module_name", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.roles": { + "name": "roles", + "schema": "", + "columns": { + "role_id": { + "name": "role_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "add_User": { + "name": "add_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "add_Date": { + "name": "add_Date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "upd_User": { + "name": "upd_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "upd_date": { + "name": "upd_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "role_name": { + "name": "role_name", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.userRoles": { + "name": "userRoles", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role_id": { + "name": "role_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "module_id": { + "name": "module_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "roles": { + "name": "roles", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "add_User": { + "name": "add_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "add_Date": { + "name": "add_Date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "upd_User": { + "name": "upd_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "upd_date": { + "name": "upd_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "user_module_unique": { + "name": "user_module_unique", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "module_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "userRoles_user_id_users_user_id_fk": { + "name": "userRoles_user_id_users_user_id_fk", + "tableFrom": "userRoles", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "user_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "userRoles_role_id_roles_role_id_fk": { + "name": "userRoles_role_id_roles_role_id_fk", + "tableFrom": "userRoles", + "tableTo": "roles", + "columnsFrom": [ + "role_id" + ], + "columnsTo": [ + "role_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "userRoles_module_id_modules_module_id_fk": { + "name": "userRoles_module_id_modules_module_id_fk", + "tableFrom": "userRoles", + "tableTo": "modules", + "columnsFrom": [ + "module_id" + ], + "columnsTo": [ + "module_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "passwordToken": { + "name": "passwordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "passwordTokenExpires": { + "name": "passwordTokenExpires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "pingcode": { + "name": "pingcode", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "lastLogin": { + "name": "lastLogin", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "add_User": { + "name": "add_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "add_Date": { + "name": "add_Date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "upd_User": { + "name": "upd_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "upd_date": { + "name": "upd_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "username": { + "name": "username", + "columns": [ + { + "expression": "username", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/server/database/migrations/meta/0003_snapshot.json b/server/database/migrations/meta/0003_snapshot.json new file mode 100644 index 0000000..bffce7e --- /dev/null +++ b/server/database/migrations/meta/0003_snapshot.json @@ -0,0 +1,413 @@ +{ + "id": "ad424668-721e-414f-ad0b-a8e7301e50c6", + "prevId": "9a2ddf11-fd30-4dd9-bf2d-259fe7aed201", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.modules": { + "name": "modules", + "schema": "", + "columns": { + "module_id": { + "name": "module_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "roles": { + "name": "roles", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "add_User": { + "name": "add_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "add_Date": { + "name": "add_Date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "upd_User": { + "name": "upd_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "upd_date": { + "name": "upd_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "module_name": { + "name": "module_name", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.roles": { + "name": "roles", + "schema": "", + "columns": { + "role_id": { + "name": "role_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "add_User": { + "name": "add_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "add_Date": { + "name": "add_Date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "upd_User": { + "name": "upd_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "upd_date": { + "name": "upd_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "role_name": { + "name": "role_name", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.userRoles": { + "name": "userRoles", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role_id": { + "name": "role_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "module_id": { + "name": "module_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "add_User": { + "name": "add_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "add_Date": { + "name": "add_Date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "upd_User": { + "name": "upd_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "upd_date": { + "name": "upd_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "user_module_unique": { + "name": "user_module_unique", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "module_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "userRoles_user_id_users_user_id_fk": { + "name": "userRoles_user_id_users_user_id_fk", + "tableFrom": "userRoles", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "user_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "userRoles_role_id_roles_role_id_fk": { + "name": "userRoles_role_id_roles_role_id_fk", + "tableFrom": "userRoles", + "tableTo": "roles", + "columnsFrom": [ + "role_id" + ], + "columnsTo": [ + "role_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "userRoles_module_id_modules_module_id_fk": { + "name": "userRoles_module_id_modules_module_id_fk", + "tableFrom": "userRoles", + "tableTo": "modules", + "columnsFrom": [ + "module_id" + ], + "columnsTo": [ + "module_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "passwordToken": { + "name": "passwordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "passwordTokenExpires": { + "name": "passwordTokenExpires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "pingcode": { + "name": "pingcode", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "lastLogin": { + "name": "lastLogin", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "add_User": { + "name": "add_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "add_Date": { + "name": "add_Date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "upd_User": { + "name": "upd_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "upd_date": { + "name": "upd_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "username": { + "name": "username", + "columns": [ + { + "expression": "username", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/server/database/migrations/meta/0004_snapshot.json b/server/database/migrations/meta/0004_snapshot.json new file mode 100644 index 0000000..f54b8a9 --- /dev/null +++ b/server/database/migrations/meta/0004_snapshot.json @@ -0,0 +1,414 @@ +{ + "id": "313590a8-2068-45b5-96fc-cfa5d2b32b56", + "prevId": "ad424668-721e-414f-ad0b-a8e7301e50c6", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.modules": { + "name": "modules", + "schema": "", + "columns": { + "module_id": { + "name": "module_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "roles": { + "name": "roles", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'[\"view\", \"systemAdmin\"]'" + }, + "add_User": { + "name": "add_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "add_Date": { + "name": "add_Date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "upd_User": { + "name": "upd_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "upd_date": { + "name": "upd_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "module_name": { + "name": "module_name", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.roles": { + "name": "roles", + "schema": "", + "columns": { + "role_id": { + "name": "role_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "add_User": { + "name": "add_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "add_Date": { + "name": "add_Date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "upd_User": { + "name": "upd_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "upd_date": { + "name": "upd_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "role_name": { + "name": "role_name", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.userRoles": { + "name": "userRoles", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role_id": { + "name": "role_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "module_id": { + "name": "module_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "add_User": { + "name": "add_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "add_Date": { + "name": "add_Date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "upd_User": { + "name": "upd_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "upd_date": { + "name": "upd_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "user_module_unique": { + "name": "user_module_unique", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "module_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "userRoles_user_id_users_user_id_fk": { + "name": "userRoles_user_id_users_user_id_fk", + "tableFrom": "userRoles", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "user_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "userRoles_role_id_roles_role_id_fk": { + "name": "userRoles_role_id_roles_role_id_fk", + "tableFrom": "userRoles", + "tableTo": "roles", + "columnsFrom": [ + "role_id" + ], + "columnsTo": [ + "role_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "userRoles_module_id_modules_module_id_fk": { + "name": "userRoles_module_id_modules_module_id_fk", + "tableFrom": "userRoles", + "tableTo": "modules", + "columnsFrom": [ + "module_id" + ], + "columnsTo": [ + "module_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "passwordToken": { + "name": "passwordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "passwordTokenExpires": { + "name": "passwordTokenExpires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "pingcode": { + "name": "pingcode", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "lastLogin": { + "name": "lastLogin", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "add_User": { + "name": "add_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "add_Date": { + "name": "add_Date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "upd_User": { + "name": "upd_User", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'LST_System'" + }, + "upd_date": { + "name": "upd_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "username": { + "name": "username", + "columns": [ + { + "expression": "username", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/server/database/migrations/meta/_journal.json b/server/database/migrations/meta/_journal.json index 132eb27..ec56e92 100644 --- a/server/database/migrations/meta/_journal.json +++ b/server/database/migrations/meta/_journal.json @@ -15,6 +15,27 @@ "when": 1740161259149, "tag": "0001_sharp_pet_avengers", "breakpoints": true + }, + { + "idx": 2, + "version": "7", + "when": 1740361491818, + "tag": "0002_bitter_oracle", + "breakpoints": true + }, + { + "idx": 3, + "version": "7", + "when": 1740362541964, + "tag": "0003_luxuriant_namorita", + "breakpoints": true + }, + { + "idx": 4, + "version": "7", + "when": 1740364483791, + "tag": "0004_quick_mandrill", + "breakpoints": true } ] } \ No newline at end of file diff --git a/server/database/schema/modules.ts b/server/database/schema/modules.ts new file mode 100644 index 0000000..fbd7592 --- /dev/null +++ b/server/database/schema/modules.ts @@ -0,0 +1,28 @@ +import {text, pgTable, numeric, index, timestamp, boolean, uuid, uniqueIndex} from "drizzle-orm/pg-core"; +import {createInsertSchema, createSelectSchema} from "drizzle-zod"; +import {z} from "zod"; + +export const modules = pgTable( + "modules", + { + module_id: uuid("module_id").defaultRandom().primaryKey(), + name: text("name").notNull(), + active: boolean("active").default(false), + roles: text("roles").notNull().default(`["view", "systemAdmin"]`), // ["view", "technician", "supervisor","manager", "admin","systemAdmin"] + add_User: text("add_User").default("LST_System").notNull(), + add_Date: timestamp("add_Date").defaultNow(), + upd_user: text("upd_User").default("LST_System").notNull(), + upd_date: timestamp("upd_date").defaultNow(), + }, + (table) => [ + // uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`), + uniqueIndex("module_name").on(table.name), + ] +); + +// Schema for inserting a user - can be used to validate API requests +// export const insertModuleSchema = createInsertSchema(modules, { +// name: z.string().min(3, {message: "Module name should be longer than 3 letters"}), +// }); +// Schema for selecting a Expenses - can be used to validate API responses +export const selectModuleSchema = createSelectSchema(modules); diff --git a/server/database/schema/roles.ts b/server/database/schema/roles.ts new file mode 100644 index 0000000..ac6c700 --- /dev/null +++ b/server/database/schema/roles.ts @@ -0,0 +1,26 @@ +import {text, pgTable, numeric, index, timestamp, boolean, uuid, uniqueIndex} from "drizzle-orm/pg-core"; +import {createInsertSchema, createSelectSchema} from "drizzle-zod"; +import {z} from "zod"; + +export const roles = pgTable( + "roles", + { + role_id: uuid("role_id").defaultRandom().primaryKey(), + name: text("name").notNull(), + add_User: text("add_User").default("LST_System").notNull(), + add_Date: timestamp("add_Date").defaultNow(), + upd_user: text("upd_User").default("LST_System").notNull(), + upd_date: timestamp("upd_date").defaultNow(), + }, + (table) => [ + // uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`), + uniqueIndex("role_name").on(table.name), + ] +); + +// Schema for inserting a user - can be used to validate API requests +// export const insertRolesSchema = createInsertSchema(roles, { +// name: z.string().min(3, {message: "Role name must be more than 3 letters"}), +// }); +// Schema for selecting a Expenses - can be used to validate API responses +export const selectRolesSchema = createSelectSchema(roles); diff --git a/server/database/schema/userRoles.ts b/server/database/schema/userRoles.ts new file mode 100644 index 0000000..bd74363 --- /dev/null +++ b/server/database/schema/userRoles.ts @@ -0,0 +1,45 @@ +import {text, pgTable, numeric, index, timestamp, boolean, uuid, uniqueIndex} from "drizzle-orm/pg-core"; +import {createInsertSchema, createSelectSchema} from "drizzle-zod"; +import {z} from "zod"; +import {users} from "./users"; +import {roles} from "./roles"; +import {modules} from "./modules"; + +/* +we will add the user +the module they have access to +and there role for this module. default will be user role, and view for production. + +systemAdmin will just get admin to all modules. +*/ + +export const userRoles = pgTable( + "userRoles", + { + user_id: uuid("user_id") + .notNull() + .references(() => users.user_id), + role_id: uuid("role_id") + .notNull() + .references(() => roles.role_id), + module_id: uuid("module_id") + .notNull() + .references(() => modules.module_id), + role: text("role").notNull(), // "view", "technician", "supervisor","manager", "admin", "systemAdmin" + add_User: text("add_User").default("LST_System").notNull(), + add_Date: timestamp("add_Date").defaultNow(), + upd_user: text("upd_User").default("LST_System").notNull(), + upd_date: timestamp("upd_date").defaultNow(), + }, + (table) => { + // ensures only one user gets permissions to one role + return [uniqueIndex("user_module_unique").on(table.user_id, table.module_id)]; + } +); + +// Schema for inserting a user - can be used to validate API requests +export const insertUserRolesSchema = createInsertSchema(userRoles, { + role: z.string().min(3, {message: "Role must be at least 3 characters"}), +}); +// Schema for selecting a Expenses - can be used to validate API responses +export const selectUserRolesSchema = createSelectSchema(userRoles); diff --git a/server/database/schema/users.ts b/server/database/schema/users.ts index 3d0f0d7..7d02c3c 100644 --- a/server/database/schema/users.ts +++ b/server/database/schema/users.ts @@ -32,4 +32,4 @@ export const insertUsersSchema = createInsertSchema(users, { password: z.string().min(8, {message: "Password must be at least 8 characters"}), }); // Schema for selecting a Expenses - can be used to validate API responses -export const selectExpensesSchema = createSelectSchema(users); +export const selectUsersSchema = createSelectSchema(users); diff --git a/server/src/app.ts b/server/src/app.ts index e1e7923..353bfb2 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -8,6 +8,7 @@ import {OpenAPIHono} from "@hono/zod-openapi"; import auth from "./services/auth/authService"; import scalar from "./services/general/route/scalar"; import apiHits from "./services/general/route/apitHits"; +import getModules from "./services/general/route/getModules"; // services import {ocmeService} from "./services/ocme/ocmeServer"; @@ -39,7 +40,7 @@ app.all("/ocme/*", async (c) => { return ocmeService(c); }); -const routes = [scalar, auth, apiHits] as const; +const routes = [scalar, auth, apiHits, getModules] as const; routes.forEach((route) => { app.route("/api/", route); diff --git a/server/src/services/auth/controllers/getUserAccess.ts b/server/src/services/auth/controllers/getUserAccess.ts new file mode 100644 index 0000000..ed351b7 --- /dev/null +++ b/server/src/services/auth/controllers/getUserAccess.ts @@ -0,0 +1,15 @@ +/* +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"; +import {userRoles} from "../../../../database/schema/userRoles"; + +export const roleCheck = async (user_id: any) => { + // get the user roles by the user_id + const roles = await db.select().from(userRoles).where(eq(userRoles.user_id, user_id)); + + return roles; +}; diff --git a/server/src/services/auth/controllers/login.ts b/server/src/services/auth/controllers/login.ts index cd3484a..3bd400d 100644 --- a/server/src/services/auth/controllers/login.ts +++ b/server/src/services/auth/controllers/login.ts @@ -3,6 +3,7 @@ import {db} from "../../../../database/dbClient"; import {users} from "../../../../database/schema/users"; import {eq} from "drizzle-orm"; import {checkPassword} from "../utils/checkPassword"; +import {roleCheck} from "./getUserAccess"; /** * Authenticate a user and return a JWT. @@ -26,14 +27,17 @@ export async function login( // Create a JWT const secret: string = process.env.JWT_SECRET! || "bnghsjhsd"; - const expiresIn: string = process.env.JWT_EXPIRES! || "1h"; + 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, }; - const token = sign({user: userData}, secret, {expiresIn: 60 * 60}); + const token = sign({user: userData}, secret, {expiresIn: expiresIn * 60}); return {token, user: {user_id: user[0].user_id, username: user[0].username}}; } diff --git a/server/src/services/general/route/apitHits.ts b/server/src/services/general/route/apitHits.ts index 3d45a80..68ca06d 100644 --- a/server/src/services/general/route/apitHits.ts +++ b/server/src/services/general/route/apitHits.ts @@ -41,7 +41,7 @@ app.openapi( async (c) => { const data = await c.req.json(); - apiHit(data); + //apiHit(data); // Return response with the received data return c.json({ diff --git a/server/src/services/general/route/getModules.ts b/server/src/services/general/route/getModules.ts new file mode 100644 index 0000000..3828e68 --- /dev/null +++ b/server/src/services/general/route/getModules.ts @@ -0,0 +1,70 @@ +import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi"; +import {modules} from "../../../../database/schema/modules"; +import {db} from "../../../../database/dbClient"; +import {eq} from "drizzle-orm"; + +// Define the request body schema +const requestSchema = z.object({ + ip: z.string().optional(), + endpoint: z.string().optional(), + action: z.string().optional(), + stats: z.string().optional(), +}); + +// Define the response schema +const responseSchema = z.object({ + message: z.string().optional(), + module_id: z.string().openapi({example: "6c922c6c-7de3-4ec4-acb0-f068abdc"}).optional(), + name: z.string().openapi({example: "Production"}).optional(), + active: z.boolean().openapi({example: true}).optional(), + roles: z.string().openapi({example: `["viewer","technician"]`}).optional(), +}); + +const app = new OpenAPIHono(); + +app.openapi( + createRoute({ + tags: ["server"], + summary: "Returns all modules in the server", + method: "get", + path: "/server/modules", + responses: { + 200: { + content: { + "application/json": {schema: responseSchema}, + }, + description: "Response message", + }, + }, + }), + async (c) => { + //const data = await c.req.json(); + + //apiHit(data); + + // get the modules that are active + let module: any = []; + try { + module = await db.select().from(modules).where(eq(modules.active, true)); + } catch (error) { + console.log(error); + module = []; + } + + // parse the roles + const updateModules = module.map((m: any) => { + if (m.roles) { + return {...m, roles: JSON.parse(m?.roles)}; + } + return m; + }); //JSON.parse(module[0]?.roles); + + // Return response with the received data + return c.json({ + message: `All active modules`, + data: updateModules, + }); + } +); + +export default app;