@htlkg/core 0.0.2 → 0.0.3
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.
- package/README.md +51 -0
- package/dist/index.d.ts +219 -0
- package/dist/index.js +121 -0
- package/dist/index.js.map +1 -1
- package/package.json +30 -8
- package/src/amplify-astro-adapter/amplify-astro-adapter.md +167 -0
- package/src/amplify-astro-adapter/createCookieStorageAdapterFromAstroContext.test.ts +296 -0
- package/src/amplify-astro-adapter/createCookieStorageAdapterFromAstroContext.ts +97 -0
- package/src/amplify-astro-adapter/createRunWithAmplifyServerContext.ts +84 -0
- package/src/amplify-astro-adapter/errors.test.ts +115 -0
- package/src/amplify-astro-adapter/errors.ts +105 -0
- package/src/amplify-astro-adapter/globalSettings.test.ts +78 -0
- package/src/amplify-astro-adapter/globalSettings.ts +16 -0
- package/src/amplify-astro-adapter/index.ts +14 -0
- package/src/amplify-astro-adapter/types.ts +55 -0
- package/src/auth/auth.md +178 -0
- package/src/auth/index.test.ts +180 -0
- package/src/auth/index.ts +294 -0
- package/src/constants/constants.md +132 -0
- package/src/constants/index.test.ts +116 -0
- package/src/constants/index.ts +98 -0
- package/src/core-exports.property.test.ts +186 -0
- package/src/errors/errors.md +177 -0
- package/src/errors/index.test.ts +153 -0
- package/src/errors/index.ts +134 -0
- package/src/index.ts +65 -0
- package/src/routes/index.ts +225 -0
- package/src/routes/routes.md +189 -0
- package/src/types/index.ts +94 -0
- package/src/types/types.md +144 -0
- package/src/utils/index.test.ts +257 -0
- package/src/utils/index.ts +112 -0
- package/src/utils/logger.ts +88 -0
- package/src/utils/utils.md +199 -0
- package/src/workspace.property.test.ts +235 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
hasAccessToBrand,
|
|
4
|
+
hasAccessToAccount,
|
|
5
|
+
isAdminUser,
|
|
6
|
+
isSuperAdminUser,
|
|
7
|
+
type User,
|
|
8
|
+
} from "./index";
|
|
9
|
+
|
|
10
|
+
describe("Auth Module", () => {
|
|
11
|
+
describe("hasAccessToBrand", () => {
|
|
12
|
+
it("should return false for null user", () => {
|
|
13
|
+
expect(hasAccessToBrand(null, 1)).toBe(false);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("should return true for admin user", () => {
|
|
17
|
+
const user: User = {
|
|
18
|
+
username: "admin",
|
|
19
|
+
email: "admin@test.com",
|
|
20
|
+
brandIds: [],
|
|
21
|
+
accountIds: [],
|
|
22
|
+
isAdmin: true,
|
|
23
|
+
isSuperAdmin: false,
|
|
24
|
+
roles: ["ADMINS"],
|
|
25
|
+
};
|
|
26
|
+
expect(hasAccessToBrand(user, 1)).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should return true if user has brand access", () => {
|
|
30
|
+
const user: User = {
|
|
31
|
+
username: "user",
|
|
32
|
+
email: "user@test.com",
|
|
33
|
+
brandIds: [1, 2, 3],
|
|
34
|
+
accountIds: [],
|
|
35
|
+
isAdmin: false,
|
|
36
|
+
isSuperAdmin: false,
|
|
37
|
+
roles: [],
|
|
38
|
+
};
|
|
39
|
+
expect(hasAccessToBrand(user, 2)).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("should return false if user does not have brand access", () => {
|
|
43
|
+
const user: User = {
|
|
44
|
+
username: "user",
|
|
45
|
+
email: "user@test.com",
|
|
46
|
+
brandIds: [1, 2, 3],
|
|
47
|
+
accountIds: [],
|
|
48
|
+
isAdmin: false,
|
|
49
|
+
isSuperAdmin: false,
|
|
50
|
+
roles: [],
|
|
51
|
+
};
|
|
52
|
+
expect(hasAccessToBrand(user, 5)).toBe(false);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe("hasAccessToAccount", () => {
|
|
57
|
+
it("should return false for null user", () => {
|
|
58
|
+
expect(hasAccessToAccount(null, 1)).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("should return true for admin user", () => {
|
|
62
|
+
const user: User = {
|
|
63
|
+
username: "admin",
|
|
64
|
+
email: "admin@test.com",
|
|
65
|
+
brandIds: [],
|
|
66
|
+
accountIds: [],
|
|
67
|
+
isAdmin: true,
|
|
68
|
+
isSuperAdmin: false,
|
|
69
|
+
roles: ["ADMINS"],
|
|
70
|
+
};
|
|
71
|
+
expect(hasAccessToAccount(user, 1)).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("should return true if user has account access", () => {
|
|
75
|
+
const user: User = {
|
|
76
|
+
username: "user",
|
|
77
|
+
email: "user@test.com",
|
|
78
|
+
brandIds: [],
|
|
79
|
+
accountIds: [10, 20, 30],
|
|
80
|
+
isAdmin: false,
|
|
81
|
+
isSuperAdmin: false,
|
|
82
|
+
roles: [],
|
|
83
|
+
};
|
|
84
|
+
expect(hasAccessToAccount(user, 20)).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("should return false if user does not have account access", () => {
|
|
88
|
+
const user: User = {
|
|
89
|
+
username: "user",
|
|
90
|
+
email: "user@test.com",
|
|
91
|
+
brandIds: [],
|
|
92
|
+
accountIds: [10, 20, 30],
|
|
93
|
+
isAdmin: false,
|
|
94
|
+
isSuperAdmin: false,
|
|
95
|
+
roles: [],
|
|
96
|
+
};
|
|
97
|
+
expect(hasAccessToAccount(user, 50)).toBe(false);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("isAdminUser", () => {
|
|
102
|
+
it("should return false for null user", () => {
|
|
103
|
+
expect(isAdminUser(null)).toBe(false);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("should return true for admin user", () => {
|
|
107
|
+
const user: User = {
|
|
108
|
+
username: "admin",
|
|
109
|
+
email: "admin@test.com",
|
|
110
|
+
brandIds: [],
|
|
111
|
+
accountIds: [],
|
|
112
|
+
isAdmin: true,
|
|
113
|
+
isSuperAdmin: false,
|
|
114
|
+
roles: ["ADMINS"],
|
|
115
|
+
};
|
|
116
|
+
expect(isAdminUser(user)).toBe(true);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("should return false for non-admin user", () => {
|
|
120
|
+
const user: User = {
|
|
121
|
+
username: "user",
|
|
122
|
+
email: "user@test.com",
|
|
123
|
+
brandIds: [],
|
|
124
|
+
accountIds: [],
|
|
125
|
+
isAdmin: false,
|
|
126
|
+
isSuperAdmin: false,
|
|
127
|
+
roles: [],
|
|
128
|
+
};
|
|
129
|
+
expect(isAdminUser(user)).toBe(false);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe("isSuperAdminUser", () => {
|
|
134
|
+
it("should return false for null user", () => {
|
|
135
|
+
expect(isSuperAdminUser(null)).toBe(false);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("should return true for super admin user", () => {
|
|
139
|
+
const user: User = {
|
|
140
|
+
username: "superadmin",
|
|
141
|
+
email: "superadmin@test.com",
|
|
142
|
+
brandIds: [],
|
|
143
|
+
accountIds: [],
|
|
144
|
+
isAdmin: true,
|
|
145
|
+
isSuperAdmin: true,
|
|
146
|
+
roles: ["SUPER_ADMINS"],
|
|
147
|
+
};
|
|
148
|
+
expect(isSuperAdminUser(user)).toBe(true);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("should return false for regular admin user", () => {
|
|
152
|
+
const user: User = {
|
|
153
|
+
username: "admin",
|
|
154
|
+
email: "admin@test.com",
|
|
155
|
+
brandIds: [],
|
|
156
|
+
accountIds: [],
|
|
157
|
+
isAdmin: true,
|
|
158
|
+
isSuperAdmin: false,
|
|
159
|
+
roles: ["ADMINS"],
|
|
160
|
+
};
|
|
161
|
+
expect(isSuperAdminUser(user)).toBe(false);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("should return false for non-admin user", () => {
|
|
165
|
+
const user: User = {
|
|
166
|
+
username: "user",
|
|
167
|
+
email: "user@test.com",
|
|
168
|
+
brandIds: [],
|
|
169
|
+
accountIds: [],
|
|
170
|
+
isAdmin: false,
|
|
171
|
+
isSuperAdmin: false,
|
|
172
|
+
roles: [],
|
|
173
|
+
};
|
|
174
|
+
expect(isSuperAdminUser(user)).toBe(false);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Note: getClientUser tests are skipped because they require mocking AWS Amplify
|
|
179
|
+
// which is complex in a unit test environment. These should be tested in integration tests.
|
|
180
|
+
});
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @htlkg/core/auth
|
|
3
|
+
* Core authentication functions and utilities
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { APIContext } from "astro";
|
|
7
|
+
import { Amplify } from "aws-amplify";
|
|
8
|
+
import { fetchAuthSession } from "aws-amplify/auth";
|
|
9
|
+
import {
|
|
10
|
+
fetchAuthSession as fetchServerAuthSession,
|
|
11
|
+
getCurrentUser as getServerCurrentUser,
|
|
12
|
+
} from "aws-amplify/auth/server";
|
|
13
|
+
import { createRunWithAmplifyServerContext, globalSettings } from "../amplify-astro-adapter";
|
|
14
|
+
|
|
15
|
+
// Re-export types
|
|
16
|
+
export type { APIContext } from "astro";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* User interface representing an authenticated user
|
|
20
|
+
*/
|
|
21
|
+
export interface User {
|
|
22
|
+
username: string;
|
|
23
|
+
email: string;
|
|
24
|
+
brandIds: number[];
|
|
25
|
+
accountIds: number[];
|
|
26
|
+
isAdmin: boolean;
|
|
27
|
+
isSuperAdmin: boolean;
|
|
28
|
+
roles: string[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Parse comma-separated IDs from Cognito custom attributes
|
|
33
|
+
*/
|
|
34
|
+
function parseIds(ids: string | undefined): number[] {
|
|
35
|
+
if (!ids) return [];
|
|
36
|
+
return ids
|
|
37
|
+
.split(",")
|
|
38
|
+
.map((id) => Number.parseInt(id.trim(), 10))
|
|
39
|
+
.filter((id) => !Number.isNaN(id));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Track if Amplify has been configured
|
|
43
|
+
let amplifyConfigured = false;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Configure Amplify on first use (server-side)
|
|
47
|
+
* This must be called before any auth operations
|
|
48
|
+
*/
|
|
49
|
+
function ensureAmplifyConfigured(): void {
|
|
50
|
+
if (amplifyConfigured) return;
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
// Amplify should already be configured by the middleware
|
|
54
|
+
// This is just a safety check
|
|
55
|
+
const config = Amplify.getConfig();
|
|
56
|
+
if (!config.Auth) {
|
|
57
|
+
throw new Error("Amplify Auth not configured");
|
|
58
|
+
}
|
|
59
|
+
amplifyConfigured = true;
|
|
60
|
+
} catch (error) {
|
|
61
|
+
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
62
|
+
console.error(`[Auth] Amplify not configured: ${errorMsg}`);
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get current authenticated user (server-side)
|
|
69
|
+
* Reads Amplify auth cookies from the request and validates the session
|
|
70
|
+
*/
|
|
71
|
+
export async function getUser(context: APIContext): Promise<User | null> {
|
|
72
|
+
try {
|
|
73
|
+
ensureAmplifyConfigured();
|
|
74
|
+
|
|
75
|
+
// Get the current Amplify configuration
|
|
76
|
+
const amplifyConfig = Amplify.getConfig();
|
|
77
|
+
|
|
78
|
+
// Create the server context runner - pass globalSettings explicitly
|
|
79
|
+
// to ensure the same instance is used across all modules
|
|
80
|
+
const runWithAmplifyServerContext = createRunWithAmplifyServerContext({
|
|
81
|
+
config: amplifyConfig,
|
|
82
|
+
globalSettings,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Create Astro server context
|
|
86
|
+
const astroServerContext = {
|
|
87
|
+
cookies: context.cookies,
|
|
88
|
+
request: context.request,
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// Run in Amplify server context
|
|
92
|
+
const user = await runWithAmplifyServerContext({
|
|
93
|
+
astroServerContext,
|
|
94
|
+
operation: async (contextSpec) => {
|
|
95
|
+
try {
|
|
96
|
+
const currentUser = await getServerCurrentUser(contextSpec as any);
|
|
97
|
+
const session = await fetchServerAuthSession(contextSpec as any);
|
|
98
|
+
|
|
99
|
+
if (!session.tokens?.accessToken) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const accessPayload = session.tokens.accessToken.payload;
|
|
104
|
+
const idPayload = session.tokens.idToken?.payload;
|
|
105
|
+
|
|
106
|
+
// Parse user attributes - email is typically in ID token, not access token
|
|
107
|
+
const email =
|
|
108
|
+
(idPayload?.email as string) ||
|
|
109
|
+
(accessPayload.email as string) ||
|
|
110
|
+
"";
|
|
111
|
+
const brandIds = parseIds(
|
|
112
|
+
accessPayload["custom:brand_ids"] as string,
|
|
113
|
+
);
|
|
114
|
+
const accountIds = parseIds(
|
|
115
|
+
accessPayload["custom:account_ids"] as string,
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// Check admin status
|
|
119
|
+
const groups = (accessPayload["cognito:groups"] as string[]) || [];
|
|
120
|
+
const isAdmin =
|
|
121
|
+
groups.includes("admin") ||
|
|
122
|
+
groups.includes("ADMINS") ||
|
|
123
|
+
groups.includes("SUPER_ADMINS");
|
|
124
|
+
const isSuperAdmin = groups.includes("SUPER_ADMINS");
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
username: currentUser.username,
|
|
128
|
+
email,
|
|
129
|
+
brandIds,
|
|
130
|
+
accountIds,
|
|
131
|
+
isAdmin,
|
|
132
|
+
isSuperAdmin,
|
|
133
|
+
roles: groups,
|
|
134
|
+
};
|
|
135
|
+
} catch (error) {
|
|
136
|
+
// UserUnAuthenticatedException is expected when user is not logged in
|
|
137
|
+
// Only log other errors without sensitive data
|
|
138
|
+
if (
|
|
139
|
+
error instanceof Error &&
|
|
140
|
+
error.name !== "UserUnAuthenticatedException"
|
|
141
|
+
) {
|
|
142
|
+
// Log error name and message without stack trace or sensitive data
|
|
143
|
+
console.error(
|
|
144
|
+
"[Auth] Error in server context:",
|
|
145
|
+
error.name,
|
|
146
|
+
"-",
|
|
147
|
+
error.message.replace(/token[=:]\s*[^\s,}]+/gi, "token=***"),
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
return user;
|
|
156
|
+
} catch (error) {
|
|
157
|
+
// Log error without exposing sensitive information
|
|
158
|
+
if (error instanceof Error) {
|
|
159
|
+
console.error(
|
|
160
|
+
"[Auth] Server-side auth error:",
|
|
161
|
+
error.name,
|
|
162
|
+
"-",
|
|
163
|
+
error.message.replace(/token[=:]\s*[^\s,}]+/gi, "token=***"),
|
|
164
|
+
);
|
|
165
|
+
} else {
|
|
166
|
+
console.error("[Auth] Server-side auth error: Unknown error");
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Get current authenticated user (client-side)
|
|
174
|
+
*/
|
|
175
|
+
export async function getClientUser(): Promise<User | null> {
|
|
176
|
+
try {
|
|
177
|
+
const session = await fetchAuthSession();
|
|
178
|
+
|
|
179
|
+
if (!session.tokens?.accessToken) {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const accessPayload = session.tokens.accessToken.payload;
|
|
184
|
+
const idPayload = session.tokens.idToken?.payload;
|
|
185
|
+
|
|
186
|
+
const email =
|
|
187
|
+
(idPayload?.email as string) || (accessPayload.email as string) || "";
|
|
188
|
+
const brandIds = parseIds(accessPayload["custom:brand_ids"] as string);
|
|
189
|
+
const accountIds = parseIds(accessPayload["custom:account_ids"] as string);
|
|
190
|
+
|
|
191
|
+
const groups = (accessPayload["cognito:groups"] as string[]) || [];
|
|
192
|
+
const isAdmin =
|
|
193
|
+
groups.includes("admin") ||
|
|
194
|
+
groups.includes("ADMINS") ||
|
|
195
|
+
groups.includes("SUPER_ADMINS");
|
|
196
|
+
const isSuperAdmin = groups.includes("SUPER_ADMINS");
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
username: (accessPayload.username as string) || "",
|
|
200
|
+
email,
|
|
201
|
+
brandIds,
|
|
202
|
+
accountIds,
|
|
203
|
+
isAdmin,
|
|
204
|
+
isSuperAdmin,
|
|
205
|
+
roles: groups,
|
|
206
|
+
};
|
|
207
|
+
} catch (error) {
|
|
208
|
+
if (error instanceof Error) {
|
|
209
|
+
console.error(
|
|
210
|
+
"[Auth] Client auth error:",
|
|
211
|
+
error.name,
|
|
212
|
+
"-",
|
|
213
|
+
error.message.replace(/token[=:]\s*[^\s,}]+/gi, "token=***"),
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Check if user has access to a specific brand
|
|
222
|
+
*/
|
|
223
|
+
export function hasAccessToBrand(
|
|
224
|
+
user: User | null,
|
|
225
|
+
brandId: number,
|
|
226
|
+
): boolean {
|
|
227
|
+
if (!user) return false;
|
|
228
|
+
if (user.isAdmin) return true;
|
|
229
|
+
return user.brandIds.includes(brandId);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Check if user has access to a specific account
|
|
234
|
+
*/
|
|
235
|
+
export function hasAccessToAccount(
|
|
236
|
+
user: User | null,
|
|
237
|
+
accountId: number,
|
|
238
|
+
): boolean {
|
|
239
|
+
if (!user) return false;
|
|
240
|
+
if (user.isAdmin) return true;
|
|
241
|
+
return user.accountIds.includes(accountId);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Check if user is an admin
|
|
246
|
+
*/
|
|
247
|
+
export function isAdminUser(user: User | null): boolean {
|
|
248
|
+
return user?.isAdmin || false;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Check if user is a super admin
|
|
253
|
+
*/
|
|
254
|
+
export function isSuperAdminUser(user: User | null): boolean {
|
|
255
|
+
return user?.isSuperAdmin || false;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Placeholder for requireAuth - will be implemented in @htlkg/integrations
|
|
260
|
+
* This is here for type exports and documentation
|
|
261
|
+
*/
|
|
262
|
+
export async function requireAuth(
|
|
263
|
+
context: APIContext,
|
|
264
|
+
loginUrl?: string,
|
|
265
|
+
): Promise<{ success: boolean; user?: User; redirectTo?: string }> {
|
|
266
|
+
throw new Error(
|
|
267
|
+
"requireAuth should be imported from @htlkg/astro/middleware",
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Placeholder for requireAdminAccess - will be implemented in @htlkg/integrations
|
|
273
|
+
*/
|
|
274
|
+
export async function requireAdminAccess(
|
|
275
|
+
context: APIContext,
|
|
276
|
+
loginUrl?: string,
|
|
277
|
+
): Promise<{ success: boolean; user?: User; redirectTo?: string }> {
|
|
278
|
+
throw new Error(
|
|
279
|
+
"requireAdminAccess should be imported from @htlkg/astro/middleware",
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Placeholder for requireBrandAccess - will be implemented in @htlkg/integrations
|
|
285
|
+
*/
|
|
286
|
+
export async function requireBrandAccess(
|
|
287
|
+
context: APIContext,
|
|
288
|
+
brandId: number,
|
|
289
|
+
loginUrl?: string,
|
|
290
|
+
): Promise<{ success: boolean; user?: User; redirectTo?: string }> {
|
|
291
|
+
throw new Error(
|
|
292
|
+
"requireBrandAccess should be imported from @htlkg/astro/middleware",
|
|
293
|
+
);
|
|
294
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Constants Module
|
|
2
|
+
|
|
3
|
+
Centralized constants for routes, products, permissions, and user roles.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { ROUTES, PRODUCTS, PERMISSIONS, USER_ROLES } from '@htlkg/core/constants';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## ROUTES
|
|
12
|
+
|
|
13
|
+
Application route definitions.
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
// Admin routes
|
|
17
|
+
ROUTES.ADMIN.HOME // "/admin"
|
|
18
|
+
ROUTES.ADMIN.BRANDS // "/admin/brands"
|
|
19
|
+
ROUTES.ADMIN.ACCOUNTS // "/admin/accounts"
|
|
20
|
+
ROUTES.ADMIN.USERS // "/admin/users"
|
|
21
|
+
ROUTES.ADMIN.PRODUCTS // "/admin/products"
|
|
22
|
+
ROUTES.ADMIN.SETTINGS // "/admin/settings"
|
|
23
|
+
|
|
24
|
+
// Brand routes (with brandId parameter)
|
|
25
|
+
ROUTES.BRAND.HOME(brandId) // "/{brandId}"
|
|
26
|
+
ROUTES.BRAND.ADMIN(brandId) // "/{brandId}/admin"
|
|
27
|
+
ROUTES.BRAND.TEMPLATES(brandId) // "/{brandId}/admin/templates"
|
|
28
|
+
ROUTES.BRAND.SETTINGS(brandId) // "/{brandId}/admin/settings"
|
|
29
|
+
|
|
30
|
+
// Auth routes
|
|
31
|
+
ROUTES.AUTH.LOGIN // "/login"
|
|
32
|
+
ROUTES.AUTH.LOGOUT // "/logout"
|
|
33
|
+
ROUTES.AUTH.SIGNUP // "/signup"
|
|
34
|
+
ROUTES.AUTH.FORGOT_PASSWORD // "/forgot-password"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## PRODUCTS
|
|
38
|
+
|
|
39
|
+
Product definitions for the platform.
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
PRODUCTS.WIFI_PORTAL
|
|
43
|
+
// { id: "wifi-portal", name: "WiFi Portal", description: "..." }
|
|
44
|
+
|
|
45
|
+
PRODUCTS.WHATSAPP_CRM
|
|
46
|
+
// { id: "whatsapp-crm", name: "WhatsApp CRM", description: "..." }
|
|
47
|
+
|
|
48
|
+
PRODUCTS.ANALYTICS
|
|
49
|
+
// { id: "analytics", name: "Analytics", description: "..." }
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## PERMISSIONS
|
|
53
|
+
|
|
54
|
+
Permission string constants for authorization.
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// Brand permissions
|
|
58
|
+
PERMISSIONS.BRAND_VIEW // "brand:view"
|
|
59
|
+
PERMISSIONS.BRAND_EDIT // "brand:edit"
|
|
60
|
+
PERMISSIONS.BRAND_DELETE // "brand:delete"
|
|
61
|
+
PERMISSIONS.BRAND_CREATE // "brand:create"
|
|
62
|
+
|
|
63
|
+
// Account permissions
|
|
64
|
+
PERMISSIONS.ACCOUNT_VIEW // "account:view"
|
|
65
|
+
PERMISSIONS.ACCOUNT_EDIT // "account:edit"
|
|
66
|
+
PERMISSIONS.ACCOUNT_DELETE // "account:delete"
|
|
67
|
+
PERMISSIONS.ACCOUNT_CREATE // "account:create"
|
|
68
|
+
|
|
69
|
+
// User permissions
|
|
70
|
+
PERMISSIONS.USER_VIEW // "user:view"
|
|
71
|
+
PERMISSIONS.USER_EDIT // "user:edit"
|
|
72
|
+
PERMISSIONS.USER_DELETE // "user:delete"
|
|
73
|
+
PERMISSIONS.USER_CREATE // "user:create"
|
|
74
|
+
|
|
75
|
+
// Product permissions
|
|
76
|
+
PERMISSIONS.PRODUCT_VIEW // "product:view"
|
|
77
|
+
PERMISSIONS.PRODUCT_EDIT // "product:edit"
|
|
78
|
+
PERMISSIONS.PRODUCT_ENABLE // "product:enable"
|
|
79
|
+
PERMISSIONS.PRODUCT_DISABLE // "product:disable"
|
|
80
|
+
|
|
81
|
+
// Admin permissions
|
|
82
|
+
PERMISSIONS.ADMIN_ACCESS // "admin:access"
|
|
83
|
+
PERMISSIONS.SUPER_ADMIN_ACCESS // "super_admin:access"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## USER_ROLES
|
|
87
|
+
|
|
88
|
+
Cognito group names for user roles.
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
USER_ROLES.SUPER_ADMIN // "SUPER_ADMINS"
|
|
92
|
+
USER_ROLES.ADMIN // "ADMINS"
|
|
93
|
+
USER_ROLES.BRAND_ADMIN // "BRAND_ADMINS"
|
|
94
|
+
USER_ROLES.BRAND_USER // "BRAND_USERS"
|
|
95
|
+
USER_ROLES.USER // "USERS"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Usage Examples
|
|
99
|
+
|
|
100
|
+
### Route Navigation
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { ROUTES } from '@htlkg/core/constants';
|
|
104
|
+
|
|
105
|
+
// Static routes
|
|
106
|
+
window.location.href = ROUTES.AUTH.LOGIN;
|
|
107
|
+
|
|
108
|
+
// Dynamic routes
|
|
109
|
+
const brandId = '123';
|
|
110
|
+
window.location.href = ROUTES.BRAND.ADMIN(brandId);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Permission Checks
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { PERMISSIONS } from '@htlkg/core/constants';
|
|
117
|
+
|
|
118
|
+
function canEditBrand(userPermissions: string[]): boolean {
|
|
119
|
+
return userPermissions.includes(PERMISSIONS.BRAND_EDIT);
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Role Checks
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { USER_ROLES } from '@htlkg/core/constants';
|
|
127
|
+
|
|
128
|
+
function isAdmin(userRoles: string[]): boolean {
|
|
129
|
+
return userRoles.includes(USER_ROLES.ADMIN) ||
|
|
130
|
+
userRoles.includes(USER_ROLES.SUPER_ADMIN);
|
|
131
|
+
}
|
|
132
|
+
```
|