fiberx-backend-toolkit 0.0.55 → 0.0.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,3 +28,5 @@ export declare const ALPHABET_CORPUS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
28
28
  export declare const DEFAULT_TOTP_STEP = 30;
29
29
  export declare const DEFAULT_TOTP_DIGITS = 6;
30
30
  export declare const DEFAULT_TOTP_WINDOW = 1;
31
+ export declare const DEFAULT_RBAC_CACHE_KEY = "RBAC_SNAPSHOT";
32
+ export declare const DEFAULT_RBAC_CACHE_TTL: number;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.DEFAULT_TOTP_WINDOW = exports.DEFAULT_TOTP_DIGITS = exports.DEFAULT_TOTP_STEP = exports.ALPHABET_CORPUS = exports.CHARACTER_CORPUS = exports.REQUEST_RATE_LIMITTER_OPTIONS = exports.CORS_MAX_AGE_IN_MICRO_SECONDS = exports.CORS_MAX_AGE_IN_SECONDS = exports.CORS_ALLOWED_HEADERS = exports.CORS_ALLOWED_METHODS = exports.DEVICE_ID_HEADERS_NAME = exports.DEVICE_ID_COOKIE_NAME = exports.DEVICE_ID_COOKIE_MAX_AGE = exports.REQUEST_ID_HEADERS_NAME = exports.REQUEST_ID_COOKIE_NAME = exports.REQUEST_ID_COOKIE_MAX_AGE = exports.SEQUELIZE_SEEDER_META_TABLE_NAME = exports.SEQUELIZE_META_TABLE_NAME = exports.SEEDERS_DIR = exports.MIGRATIONS_DIR = exports.MODELS_DIR = exports.SCHEMA_SNAPSHOTS_DIR = exports.SCHEMAS_DIR = exports.ENV_VAR_DIR = exports.LOG_DIR = exports.BASE_DIR = void 0;
6
+ exports.DEFAULT_RBAC_CACHE_TTL = exports.DEFAULT_RBAC_CACHE_KEY = exports.DEFAULT_TOTP_WINDOW = exports.DEFAULT_TOTP_DIGITS = exports.DEFAULT_TOTP_STEP = exports.ALPHABET_CORPUS = exports.CHARACTER_CORPUS = exports.REQUEST_RATE_LIMITTER_OPTIONS = exports.CORS_MAX_AGE_IN_MICRO_SECONDS = exports.CORS_MAX_AGE_IN_SECONDS = exports.CORS_ALLOWED_HEADERS = exports.CORS_ALLOWED_METHODS = exports.DEVICE_ID_HEADERS_NAME = exports.DEVICE_ID_COOKIE_NAME = exports.DEVICE_ID_COOKIE_MAX_AGE = exports.REQUEST_ID_HEADERS_NAME = exports.REQUEST_ID_COOKIE_NAME = exports.REQUEST_ID_COOKIE_MAX_AGE = exports.SEQUELIZE_SEEDER_META_TABLE_NAME = exports.SEQUELIZE_META_TABLE_NAME = exports.SEEDERS_DIR = exports.MIGRATIONS_DIR = exports.MODELS_DIR = exports.SCHEMA_SNAPSHOTS_DIR = exports.SCHEMAS_DIR = exports.ENV_VAR_DIR = exports.LOG_DIR = exports.BASE_DIR = void 0;
7
7
  const path_1 = __importDefault(require("path"));
8
8
  // Database constants
9
9
  exports.BASE_DIR = process.cwd();
@@ -38,3 +38,5 @@ exports.ALPHABET_CORPUS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
38
38
  exports.DEFAULT_TOTP_STEP = 30;
39
39
  exports.DEFAULT_TOTP_DIGITS = 6;
40
40
  exports.DEFAULT_TOTP_WINDOW = 1;
41
+ exports.DEFAULT_RBAC_CACHE_KEY = "RBAC_SNAPSHOT";
42
+ exports.DEFAULT_RBAC_CACHE_TTL = (1000 * 60 * 60 * 24); // 24 hours
@@ -0,0 +1,2 @@
1
+ import RBACLoaderUtil from "../rbac/rbac_loader_util";
2
+ export { RBACLoaderUtil };
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.RBACLoaderUtil = void 0;
7
+ const rbac_loader_util_1 = __importDefault(require("../rbac/rbac_loader_util"));
8
+ exports.RBACLoaderUtil = rbac_loader_util_1.default;
@@ -0,0 +1,37 @@
1
+ import { RBACDataProvider, RBACOptionsInterface } from "../types/rbac_type";
2
+ declare class RBACLoaderUtil<TSafeRole> {
3
+ private readonly provider;
4
+ private static instance;
5
+ private readonly logger;
6
+ private readonly cache;
7
+ private readonly RBAC_CACHE_KEY;
8
+ private readonly DEFAULT_TTL;
9
+ private loading_promise;
10
+ private constructor();
11
+ static initialize<TSafeRole>(provider: RBACDataProvider<TSafeRole>, options?: RBACOptionsInterface): RBACLoaderUtil<TSafeRole>;
12
+ static getInstance<TSafeRole>(): RBACLoaderUtil<TSafeRole>;
13
+ /**
14
+ * ===============================
15
+ * LOAD RBAC
16
+ * ===============================
17
+ */
18
+ load(force_refresh?: boolean): Promise<void>;
19
+ /**
20
+ * ===============================
21
+ * BUILD SNAPSHOT
22
+ * ===============================
23
+ */
24
+ private buildSnapshot;
25
+ /**
26
+ * ===============================
27
+ * ACCESSORS
28
+ * ===============================
29
+ */
30
+ private getSnapshot;
31
+ getRoleById(role_id: number): TSafeRole | undefined;
32
+ getRoleBySymbol(symbol: string): TSafeRole | undefined;
33
+ getPermissionsForRole(role_id: number): string[];
34
+ roleHasPermission(role_id: number, permission_key: string): boolean;
35
+ refresh(): Promise<void>;
36
+ }
37
+ export default RBACLoaderUtil;
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const main_1 = require("../utils/main");
4
+ const constants_1 = require("../config/constants");
5
+ class RBACLoaderUtil {
6
+ provider;
7
+ static instance = null;
8
+ logger = new main_1.LoggerUtil("rbac_loader_util");
9
+ cache = new main_1.InMemoryCacheUtil();
10
+ RBAC_CACHE_KEY;
11
+ DEFAULT_TTL;
12
+ loading_promise = null;
13
+ constructor(provider, options) {
14
+ this.provider = provider;
15
+ this.RBAC_CACHE_KEY = options?.rbac_cache_key ?? constants_1.DEFAULT_RBAC_CACHE_KEY;
16
+ this.DEFAULT_TTL = options?.cache_ttl ?? constants_1.DEFAULT_RBAC_CACHE_TTL;
17
+ }
18
+ //Method to Initialize singleton (MUST be called once)
19
+ static initialize(provider, options) {
20
+ if (this.instance) {
21
+ throw new Error("RBACLoaderUtil already initialized.");
22
+ }
23
+ this.instance = new RBACLoaderUtil(provider, options);
24
+ return this.instance;
25
+ }
26
+ // Method to Get singleton instance
27
+ static getInstance() {
28
+ if (!this.instance) {
29
+ throw new Error("RBACLoaderUtil not initialized.");
30
+ }
31
+ return this.instance;
32
+ }
33
+ /**
34
+ * ===============================
35
+ * LOAD RBAC
36
+ * ===============================
37
+ */
38
+ async load(force_refresh = false) {
39
+ if (!force_refresh && this.cache.has(this.RBAC_CACHE_KEY)) {
40
+ return;
41
+ }
42
+ if (this.loading_promise) {
43
+ return this.loading_promise;
44
+ }
45
+ this.loading_promise = (async () => {
46
+ try {
47
+ const snapshot = await this.buildSnapshot();
48
+ this.cache.set(this.RBAC_CACHE_KEY, snapshot, this.DEFAULT_TTL);
49
+ }
50
+ catch (error) {
51
+ this.logger.error("Failed to load RBAC snapshot", { error });
52
+ throw error;
53
+ }
54
+ finally {
55
+ this.loading_promise = null;
56
+ }
57
+ })();
58
+ return this.loading_promise;
59
+ }
60
+ /**
61
+ * ===============================
62
+ * BUILD SNAPSHOT
63
+ * ===============================
64
+ */
65
+ async buildSnapshot() {
66
+ const roles = await this.provider.fetchRoles();
67
+ const permissions = await this.provider.fetchPermissions();
68
+ const role_permissions = await this.provider.fetchRolePermissions();
69
+ const role_by_id = new Map();
70
+ const role_by_symbol = new Map();
71
+ const permission_by_id = new Map();
72
+ const permissions_by_role_id = new Map();
73
+ // Roles
74
+ for (const role of roles) {
75
+ const safe = this.provider.mapRoleToSafe(role);
76
+ const id = this.provider.getRoleId(role);
77
+ const symbol = this.provider.getRoleSymbol(role);
78
+ role_by_id.set(id, safe);
79
+ role_by_symbol.set(symbol, safe);
80
+ }
81
+ // Permissions
82
+ for (const permission of permissions) {
83
+ const id = this.provider.getPermissionId(permission);
84
+ const key = this.provider.getPermissionKey(permission);
85
+ permission_by_id.set(id, key);
86
+ }
87
+ // RolePermissions
88
+ for (const rp of role_permissions) {
89
+ const role_id = this.provider.getRolePermissionRoleId(rp);
90
+ const permission_id = this.provider.getRolePermissionPermissionId(rp);
91
+ const permission_key = permission_by_id.get(permission_id);
92
+ if (!permission_key) {
93
+ continue;
94
+ }
95
+ if (!permissions_by_role_id.has(role_id)) {
96
+ permissions_by_role_id.set(role_id, new Set());
97
+ }
98
+ permissions_by_role_id.get(role_id).add(permission_key);
99
+ }
100
+ return {
101
+ role_by_id,
102
+ role_by_symbol,
103
+ permission_by_id,
104
+ permissions_by_role_id
105
+ };
106
+ }
107
+ /**
108
+ * ===============================
109
+ * ACCESSORS
110
+ * ===============================
111
+ */
112
+ getSnapshot() {
113
+ const snapshot = this.cache.get(this.RBAC_CACHE_KEY);
114
+ if (!snapshot) {
115
+ throw new Error("RBAC snapshot not loaded. Call load() first.");
116
+ }
117
+ return snapshot;
118
+ }
119
+ getRoleById(role_id) {
120
+ return this.getSnapshot().role_by_id.get(role_id);
121
+ }
122
+ getRoleBySymbol(symbol) {
123
+ return this.getSnapshot().role_by_symbol.get(symbol);
124
+ }
125
+ getPermissionsForRole(role_id) {
126
+ return Array.from(this.getSnapshot().permissions_by_role_id.get(role_id) ?? []);
127
+ }
128
+ roleHasPermission(role_id, permission_key) {
129
+ return (this.getSnapshot()
130
+ .permissions_by_role_id
131
+ .get(role_id)
132
+ ?.has(permission_key) ?? false);
133
+ }
134
+ async refresh() {
135
+ await this.load(true);
136
+ }
137
+ }
138
+ exports.default = RBACLoaderUtil;
@@ -5,3 +5,4 @@ export * from "./middle_ware_type";
5
5
  export * from "./database_type";
6
6
  export * from "./module_type";
7
7
  export * from "./express_decelaration";
8
+ export * from "./rbac_type";
@@ -21,3 +21,4 @@ __exportStar(require("./middle_ware_type"), exports);
21
21
  __exportStar(require("./database_type"), exports);
22
22
  __exportStar(require("./module_type"), exports);
23
23
  __exportStar(require("./express_decelaration"), exports);
24
+ __exportStar(require("./rbac_type"), exports);
@@ -0,0 +1,28 @@
1
+ export interface RBACOptionsInterface {
2
+ rbac_cache_key?: string;
3
+ cache_ttl?: number;
4
+ }
5
+ /**
6
+ * Generic RBAC snapshot
7
+ */
8
+ export type RBACSnapshot<TSafeRole> = {
9
+ role_by_id: Map<number, TSafeRole>;
10
+ role_by_symbol: Map<string, TSafeRole>;
11
+ permission_by_id: Map<number, string>;
12
+ permissions_by_role_id: Map<number, Set<string>>;
13
+ };
14
+ /**
15
+ * Provider contract (each app implements this)
16
+ */
17
+ export interface RBACDataProvider<TSafeRole> {
18
+ fetchRoles(): Promise<any[]>;
19
+ fetchPermissions(): Promise<any[]>;
20
+ fetchRolePermissions(): Promise<any[]>;
21
+ mapRoleToSafe(role: any): TSafeRole;
22
+ getRoleId(role: any): number;
23
+ getRoleSymbol(role: any): string;
24
+ getPermissionId(permission: any): number;
25
+ getPermissionKey(permission: any): string;
26
+ getRolePermissionRoleId(rp: any): number;
27
+ getRolePermissionPermissionId(rp: any): number;
28
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fiberx-backend-toolkit",
3
- "version": "0.0.55",
3
+ "version": "0.0.57",
4
4
  "description": "A TypeScript backend toolkit providing shared domain logic, infrastructure helpers, and utilities for FiberX server-side applications and services.",
5
5
  "type": "commonjs",
6
6
  "main": "./dist/index.js",