3a-ecommerce-utils 1.0.0
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 +163 -0
- package/dist/chunk-PEAZVBSD.mjs +597 -0
- package/dist/client-DYGi_pyp.d.mts +87 -0
- package/dist/client-DYGi_pyp.d.ts +87 -0
- package/dist/index.d.mts +496 -0
- package/dist/index.d.ts +496 -0
- package/dist/index.js +17707 -0
- package/dist/index.mjs +17043 -0
- package/dist/validation/server.d.mts +50 -0
- package/dist/validation/server.d.ts +50 -0
- package/dist/validation/server.js +518 -0
- package/dist/validation/server.mjs +168 -0
- package/package.json +69 -0
- package/src/api/address.queries.ts +96 -0
- package/src/api/category.queries.ts +85 -0
- package/src/api/coupon.queries.ts +120 -0
- package/src/api/dashboard.queries.ts +35 -0
- package/src/api/errorHandler.ts +164 -0
- package/src/api/graphqlClient.ts +113 -0
- package/src/api/index.ts +10 -0
- package/src/api/logger.client.ts +89 -0
- package/src/api/logger.ts +135 -0
- package/src/api/order.queries.ts +211 -0
- package/src/api/product.queries.ts +144 -0
- package/src/api/review.queries.ts +56 -0
- package/src/api/user.queries.ts +232 -0
- package/src/assets/3A.png +0 -0
- package/src/assets/index.ts +1 -0
- package/src/assets.d.ts +29 -0
- package/src/auth.ts +176 -0
- package/src/config/jest.backend.config.js +42 -0
- package/src/config/jest.frontend.config.js +50 -0
- package/src/config/postcss.config.js +6 -0
- package/src/config/tailwind.config.ts +70 -0
- package/src/config/tsconfig.base.json +36 -0
- package/src/config/vite.config.ts +86 -0
- package/src/config/vitest.base.config.ts +74 -0
- package/src/config/webpack.base.config.ts +126 -0
- package/src/constants/index.ts +312 -0
- package/src/cookies.ts +104 -0
- package/src/helpers.ts +400 -0
- package/src/index.ts +32 -0
- package/src/validation/client.ts +287 -0
- package/src/validation/index.ts +3 -0
- package/src/validation/server.ts +32 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
export const GET_PRODUCTS_QUERY = `
|
|
2
|
+
query GetProducts($page: Int, $limit: Int, $search: String, $category: String, $minPrice: Float, $maxPrice: Float, $sortBy: String, $sortOrder: String, $featured: Boolean, $includeInactive: Boolean) {
|
|
3
|
+
products(page: $page, limit: $limit, search: $search, category: $category, minPrice: $minPrice, maxPrice: $maxPrice, sortBy: $sortBy, sortOrder: $sortOrder, featured: $featured, includeInactive: $includeInactive) {
|
|
4
|
+
products {
|
|
5
|
+
id
|
|
6
|
+
name
|
|
7
|
+
description
|
|
8
|
+
price
|
|
9
|
+
stock
|
|
10
|
+
category
|
|
11
|
+
sellerId
|
|
12
|
+
isActive
|
|
13
|
+
imageUrl
|
|
14
|
+
tags
|
|
15
|
+
rating
|
|
16
|
+
reviewCount
|
|
17
|
+
createdAt
|
|
18
|
+
updatedAt
|
|
19
|
+
}
|
|
20
|
+
pagination {
|
|
21
|
+
page
|
|
22
|
+
limit
|
|
23
|
+
total
|
|
24
|
+
pages
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
export const GET_PRODUCT_QUERY = `
|
|
31
|
+
query GetProduct($id: ID!) {
|
|
32
|
+
product(id: $id) {
|
|
33
|
+
id
|
|
34
|
+
name
|
|
35
|
+
description
|
|
36
|
+
price
|
|
37
|
+
stock
|
|
38
|
+
category
|
|
39
|
+
sellerId
|
|
40
|
+
seller {
|
|
41
|
+
id
|
|
42
|
+
name
|
|
43
|
+
email
|
|
44
|
+
}
|
|
45
|
+
isActive
|
|
46
|
+
imageUrl
|
|
47
|
+
tags
|
|
48
|
+
rating
|
|
49
|
+
reviewCount
|
|
50
|
+
createdAt
|
|
51
|
+
updatedAt
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
export const GET_PRODUCTS_BY_SELLER_QUERY = `
|
|
57
|
+
query GetProductsBySeller($sellerId: String!) {
|
|
58
|
+
productsBySeller(sellerId: $sellerId) {
|
|
59
|
+
id
|
|
60
|
+
name
|
|
61
|
+
description
|
|
62
|
+
price
|
|
63
|
+
stock
|
|
64
|
+
category
|
|
65
|
+
sellerId
|
|
66
|
+
isActive
|
|
67
|
+
imageUrl
|
|
68
|
+
tags
|
|
69
|
+
rating
|
|
70
|
+
reviewCount
|
|
71
|
+
createdAt
|
|
72
|
+
updatedAt
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
`;
|
|
76
|
+
|
|
77
|
+
export const CREATE_PRODUCT_MUTATION = `
|
|
78
|
+
mutation CreateProduct($input: CreateProductInput!) {
|
|
79
|
+
createProduct(input: $input) {
|
|
80
|
+
id
|
|
81
|
+
name
|
|
82
|
+
description
|
|
83
|
+
price
|
|
84
|
+
stock
|
|
85
|
+
category
|
|
86
|
+
sellerId
|
|
87
|
+
isActive
|
|
88
|
+
imageUrl
|
|
89
|
+
tags
|
|
90
|
+
rating
|
|
91
|
+
reviewCount
|
|
92
|
+
createdAt
|
|
93
|
+
updatedAt
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
`;
|
|
97
|
+
|
|
98
|
+
export const UPDATE_PRODUCT_MUTATION = `
|
|
99
|
+
mutation UpdateProduct($id: ID!, $input: UpdateProductInput!) {
|
|
100
|
+
updateProduct(id: $id, input: $input) {
|
|
101
|
+
id
|
|
102
|
+
name
|
|
103
|
+
description
|
|
104
|
+
price
|
|
105
|
+
stock
|
|
106
|
+
category
|
|
107
|
+
sellerId
|
|
108
|
+
isActive
|
|
109
|
+
imageUrl
|
|
110
|
+
tags
|
|
111
|
+
rating
|
|
112
|
+
reviewCount
|
|
113
|
+
createdAt
|
|
114
|
+
updatedAt
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
`;
|
|
118
|
+
|
|
119
|
+
export const DELETE_PRODUCT_MUTATION = `
|
|
120
|
+
mutation DeleteProduct($id: ID!) {
|
|
121
|
+
deleteProduct(id: $id)
|
|
122
|
+
}
|
|
123
|
+
`;
|
|
124
|
+
|
|
125
|
+
export const SEARCH_PRODUCTS_QUERY = `
|
|
126
|
+
query SearchProducts($search: String!, $limit: Int) {
|
|
127
|
+
searchProducts(search: $search, limit: $limit) {
|
|
128
|
+
id
|
|
129
|
+
name
|
|
130
|
+
description
|
|
131
|
+
price
|
|
132
|
+
stock
|
|
133
|
+
category
|
|
134
|
+
sellerId
|
|
135
|
+
isActive
|
|
136
|
+
imageUrl
|
|
137
|
+
tags
|
|
138
|
+
rating
|
|
139
|
+
reviewCount
|
|
140
|
+
createdAt
|
|
141
|
+
updatedAt
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
`;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export const GET_PRODUCT_REVIEWS_QUERY = `
|
|
2
|
+
query GetProductReviews($productId: ID!, $page: Int, $limit: Int) {
|
|
3
|
+
productReviews(productId: $productId, page: $page, limit: $limit) {
|
|
4
|
+
reviews {
|
|
5
|
+
id
|
|
6
|
+
productId
|
|
7
|
+
userId
|
|
8
|
+
userName
|
|
9
|
+
rating
|
|
10
|
+
comment
|
|
11
|
+
helpful
|
|
12
|
+
createdAt
|
|
13
|
+
}
|
|
14
|
+
pagination {
|
|
15
|
+
page
|
|
16
|
+
limit
|
|
17
|
+
total
|
|
18
|
+
pages
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
export const CREATE_REVIEW_MUTATION = `
|
|
25
|
+
mutation CreateReview($productId: ID!, $input: CreateReviewInput!) {
|
|
26
|
+
createReview(productId: $productId, input: $input) {
|
|
27
|
+
success
|
|
28
|
+
message
|
|
29
|
+
review {
|
|
30
|
+
id
|
|
31
|
+
productId
|
|
32
|
+
userId
|
|
33
|
+
userName
|
|
34
|
+
rating
|
|
35
|
+
comment
|
|
36
|
+
helpful
|
|
37
|
+
createdAt
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
export const MARK_REVIEW_HELPFUL_MUTATION = `
|
|
44
|
+
mutation MarkReviewHelpful($reviewId: ID!) {
|
|
45
|
+
markReviewHelpful(reviewId: $reviewId) {
|
|
46
|
+
id
|
|
47
|
+
helpful
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
export const DELETE_REVIEW_MUTATION = `
|
|
53
|
+
mutation DeleteReview($reviewId: ID!) {
|
|
54
|
+
deleteReview(reviewId: $reviewId)
|
|
55
|
+
}
|
|
56
|
+
`;
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
export const GET_USERS_QUERY = `
|
|
2
|
+
query GetUsers($page: Int, $limit: Int, $search: String, $role: String) {
|
|
3
|
+
users(page: $page, limit: $limit, search: $search, role: $role) {
|
|
4
|
+
users {
|
|
5
|
+
id
|
|
6
|
+
name
|
|
7
|
+
email
|
|
8
|
+
role
|
|
9
|
+
isActive
|
|
10
|
+
emailVerified
|
|
11
|
+
createdAt
|
|
12
|
+
lastLogin
|
|
13
|
+
}
|
|
14
|
+
pagination {
|
|
15
|
+
page
|
|
16
|
+
limit
|
|
17
|
+
total
|
|
18
|
+
pages
|
|
19
|
+
sellerCount
|
|
20
|
+
adminCount
|
|
21
|
+
customerCount
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
export const GET_USER_QUERY = `
|
|
28
|
+
query GetUser($id: ID!) {
|
|
29
|
+
user(id: $id) {
|
|
30
|
+
id
|
|
31
|
+
name
|
|
32
|
+
email
|
|
33
|
+
role
|
|
34
|
+
isActive
|
|
35
|
+
emailVerified
|
|
36
|
+
createdAt
|
|
37
|
+
lastLogin
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
export const GET_USER_BY_ID_QUERY = `
|
|
43
|
+
query GetUserById($id: ID!) {
|
|
44
|
+
getUserById(id: $id) {
|
|
45
|
+
user {
|
|
46
|
+
id
|
|
47
|
+
name
|
|
48
|
+
email
|
|
49
|
+
role
|
|
50
|
+
isActive
|
|
51
|
+
emailVerified
|
|
52
|
+
createdAt
|
|
53
|
+
lastLogin
|
|
54
|
+
}
|
|
55
|
+
accessToken
|
|
56
|
+
refreshToken
|
|
57
|
+
tokenExpiry
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
export const GET_ME_QUERY = `
|
|
63
|
+
query GetMe {
|
|
64
|
+
me {
|
|
65
|
+
id
|
|
66
|
+
name
|
|
67
|
+
email
|
|
68
|
+
role
|
|
69
|
+
isActive
|
|
70
|
+
emailVerified
|
|
71
|
+
createdAt
|
|
72
|
+
lastLogin
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
`;
|
|
76
|
+
|
|
77
|
+
export const LOGIN_MUTATION = `
|
|
78
|
+
mutation Login($input: LoginInput!) {
|
|
79
|
+
login(input: $input) {
|
|
80
|
+
user {
|
|
81
|
+
id
|
|
82
|
+
name
|
|
83
|
+
email
|
|
84
|
+
role
|
|
85
|
+
isActive
|
|
86
|
+
emailVerified
|
|
87
|
+
createdAt
|
|
88
|
+
}
|
|
89
|
+
accessToken
|
|
90
|
+
refreshToken
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
`;
|
|
94
|
+
|
|
95
|
+
export const REGISTER_MUTATION = `
|
|
96
|
+
mutation Register($input: RegisterInput!) {
|
|
97
|
+
register(input: $input) {
|
|
98
|
+
user {
|
|
99
|
+
id
|
|
100
|
+
name
|
|
101
|
+
email
|
|
102
|
+
role
|
|
103
|
+
isActive
|
|
104
|
+
emailVerified
|
|
105
|
+
createdAt
|
|
106
|
+
}
|
|
107
|
+
accessToken
|
|
108
|
+
refreshToken
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
`;
|
|
112
|
+
|
|
113
|
+
export const GOOGLE_AUTH_MUTATION = `
|
|
114
|
+
mutation GoogleAuth($input: GoogleAuthInput!) {
|
|
115
|
+
googleAuth(input: $input) {
|
|
116
|
+
user {
|
|
117
|
+
id
|
|
118
|
+
name
|
|
119
|
+
email
|
|
120
|
+
role
|
|
121
|
+
isActive
|
|
122
|
+
emailVerified
|
|
123
|
+
createdAt
|
|
124
|
+
profilePicture
|
|
125
|
+
}
|
|
126
|
+
accessToken
|
|
127
|
+
refreshToken
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
`;
|
|
131
|
+
|
|
132
|
+
export const LOGOUT_MUTATION = `
|
|
133
|
+
mutation Logout {
|
|
134
|
+
logout
|
|
135
|
+
}
|
|
136
|
+
`;
|
|
137
|
+
|
|
138
|
+
export const UPDATE_USER_ROLE_MUTATION = `
|
|
139
|
+
mutation UpdateUserRole($id: ID!, $role: String!) {
|
|
140
|
+
updateUserRole(id: $id, role: $role) {
|
|
141
|
+
id
|
|
142
|
+
name
|
|
143
|
+
email
|
|
144
|
+
role
|
|
145
|
+
isActive
|
|
146
|
+
emailVerified
|
|
147
|
+
createdAt
|
|
148
|
+
lastLogin
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
`;
|
|
152
|
+
|
|
153
|
+
export const DELETE_USER_MUTATION = `
|
|
154
|
+
mutation DeleteUser($id: ID!) {
|
|
155
|
+
deleteUser(id: $id)
|
|
156
|
+
}
|
|
157
|
+
`;
|
|
158
|
+
|
|
159
|
+
export const SEND_VERIFICATION_EMAIL_MUTATION = `
|
|
160
|
+
mutation SendVerificationEmail($source: String) {
|
|
161
|
+
sendVerificationEmail(source: $source) {
|
|
162
|
+
success
|
|
163
|
+
message
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
`;
|
|
167
|
+
|
|
168
|
+
export const VERIFY_EMAIL_MUTATION = `
|
|
169
|
+
mutation VerifyEmail {
|
|
170
|
+
verifyEmail {
|
|
171
|
+
success
|
|
172
|
+
message
|
|
173
|
+
user {
|
|
174
|
+
id
|
|
175
|
+
name
|
|
176
|
+
email
|
|
177
|
+
role
|
|
178
|
+
isActive
|
|
179
|
+
emailVerified
|
|
180
|
+
createdAt
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
`;
|
|
185
|
+
|
|
186
|
+
export const FORGOT_PASSWORD_MUTATION = `
|
|
187
|
+
mutation ForgotPassword($email: String!, $role: String!) {
|
|
188
|
+
forgotPassword(email: $email, role: $role) {
|
|
189
|
+
success
|
|
190
|
+
message
|
|
191
|
+
resetToken
|
|
192
|
+
resetUrl
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
`;
|
|
196
|
+
|
|
197
|
+
export const RESET_PASSWORD_MUTATION = `
|
|
198
|
+
mutation ResetPassword($token: String!, $password: String!, $confirmPassword: String!) {
|
|
199
|
+
resetPassword(token: $token, password: $password, confirmPassword: $confirmPassword) {
|
|
200
|
+
success
|
|
201
|
+
message
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
`;
|
|
205
|
+
|
|
206
|
+
export const VALIDATE_RESET_TOKEN_QUERY = `
|
|
207
|
+
query ValidateResetToken($token: String!) {
|
|
208
|
+
validateResetToken(token: $token) {
|
|
209
|
+
success
|
|
210
|
+
message
|
|
211
|
+
email
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
`;
|
|
215
|
+
|
|
216
|
+
export const UPDATE_PROFILE_MUTATION = `
|
|
217
|
+
mutation UpdateProfile($input: UpdateProfileInput!) {
|
|
218
|
+
updateProfile(input: $input) {
|
|
219
|
+
success
|
|
220
|
+
message
|
|
221
|
+
user {
|
|
222
|
+
id
|
|
223
|
+
name
|
|
224
|
+
email
|
|
225
|
+
role
|
|
226
|
+
isActive
|
|
227
|
+
emailVerified
|
|
228
|
+
createdAt
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
`;
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/src/assets.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
declare module '*.png' {
|
|
2
|
+
const content: string;
|
|
3
|
+
export default content;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
declare module '*.jpg' {
|
|
7
|
+
const content: string;
|
|
8
|
+
export default content;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
declare module '*.jpeg' {
|
|
12
|
+
const content: string;
|
|
13
|
+
export default content;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
declare module '*.svg' {
|
|
17
|
+
const content: string;
|
|
18
|
+
export default content;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
declare module '*.gif' {
|
|
22
|
+
const content: string;
|
|
23
|
+
export default content;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
declare module '*.webp' {
|
|
27
|
+
const content: string;
|
|
28
|
+
export default content;
|
|
29
|
+
}
|
package/src/auth.ts
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { SHELL_APP_URL } from './constants';
|
|
2
|
+
import {
|
|
3
|
+
setCookie,
|
|
4
|
+
getCookie,
|
|
5
|
+
removeCookie,
|
|
6
|
+
AUTH_COOKIE_NAMES,
|
|
7
|
+
} from './cookies';
|
|
8
|
+
|
|
9
|
+
export interface AuthTokens {
|
|
10
|
+
accessToken: string;
|
|
11
|
+
refreshToken?: string;
|
|
12
|
+
expiresIn?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface StoredAuth {
|
|
16
|
+
user: any;
|
|
17
|
+
token: string;
|
|
18
|
+
expiresIn: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Cookie expiry in days
|
|
22
|
+
const ACCESS_TOKEN_EXPIRY = 1; // 1 day
|
|
23
|
+
const REFRESH_TOKEN_EXPIRY = 7; // 7 days
|
|
24
|
+
|
|
25
|
+
export const storeAuth = (data: {
|
|
26
|
+
user: any;
|
|
27
|
+
accessToken: string;
|
|
28
|
+
refreshToken?: string;
|
|
29
|
+
expiresIn?: number;
|
|
30
|
+
}) => {
|
|
31
|
+
// Store user data
|
|
32
|
+
setCookie(AUTH_COOKIE_NAMES.USER, JSON.stringify(data.user), {
|
|
33
|
+
expires: REFRESH_TOKEN_EXPIRY,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Store access token
|
|
37
|
+
setCookie(AUTH_COOKIE_NAMES.ACCESS_TOKEN, data.accessToken, {
|
|
38
|
+
expires: ACCESS_TOKEN_EXPIRY,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Store refresh token if provided
|
|
42
|
+
if (data.refreshToken) {
|
|
43
|
+
setCookie(AUTH_COOKIE_NAMES.REFRESH_TOKEN, data.refreshToken, {
|
|
44
|
+
expires: REFRESH_TOKEN_EXPIRY,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Calculate and store token expiry time
|
|
49
|
+
const expiresIn = data.expiresIn || 3600;
|
|
50
|
+
const expiryTime = new Date().getTime() + expiresIn * 1000;
|
|
51
|
+
setCookie(AUTH_COOKIE_NAMES.TOKEN_EXPIRY, expiryTime.toString(), {
|
|
52
|
+
expires: ACCESS_TOKEN_EXPIRY,
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const getStoredAuth = (): StoredAuth | null => {
|
|
57
|
+
const userStr = getCookie(AUTH_COOKIE_NAMES.USER);
|
|
58
|
+
const token = getCookie(AUTH_COOKIE_NAMES.ACCESS_TOKEN);
|
|
59
|
+
const expiryStr = getCookie(AUTH_COOKIE_NAMES.TOKEN_EXPIRY);
|
|
60
|
+
|
|
61
|
+
if (!userStr || !token) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const user = JSON.parse(userStr);
|
|
67
|
+
const expiresIn = expiryStr ? parseInt(expiryStr) - new Date().getTime() : 0;
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
user,
|
|
71
|
+
token,
|
|
72
|
+
expiresIn: Math.max(0, expiresIn),
|
|
73
|
+
};
|
|
74
|
+
} catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const isTokenExpired = (): boolean => {
|
|
80
|
+
const tokenExpiry = getCookie(AUTH_COOKIE_NAMES.TOKEN_EXPIRY);
|
|
81
|
+
|
|
82
|
+
if (!tokenExpiry) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const now = new Date().getTime();
|
|
87
|
+
return now > parseInt(tokenExpiry);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export const willTokenExpireSoon = (): boolean => {
|
|
91
|
+
const tokenExpiry = getCookie(AUTH_COOKIE_NAMES.TOKEN_EXPIRY);
|
|
92
|
+
|
|
93
|
+
if (!tokenExpiry) {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const now = new Date().getTime();
|
|
98
|
+
const expiryTime = parseInt(tokenExpiry);
|
|
99
|
+
const timeUntilExpiry = expiryTime - now;
|
|
100
|
+
|
|
101
|
+
return timeUntilExpiry < 300000;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export const clearAuth = () => {
|
|
105
|
+
removeCookie(AUTH_COOKIE_NAMES.USER);
|
|
106
|
+
removeCookie(AUTH_COOKIE_NAMES.ACCESS_TOKEN);
|
|
107
|
+
removeCookie(AUTH_COOKIE_NAMES.REFRESH_TOKEN);
|
|
108
|
+
removeCookie(AUTH_COOKIE_NAMES.TOKEN_EXPIRY);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export const validateUserRole = (requiredRole: string): boolean => {
|
|
112
|
+
const userStr = getCookie(AUTH_COOKIE_NAMES.USER);
|
|
113
|
+
|
|
114
|
+
if (!userStr) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
const user = JSON.parse(userStr);
|
|
120
|
+
return user.role === requiredRole;
|
|
121
|
+
} catch {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export const getCurrentUser = () => {
|
|
127
|
+
const userStr = getCookie(AUTH_COOKIE_NAMES.USER);
|
|
128
|
+
|
|
129
|
+
if (!userStr) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
return JSON.parse(userStr);
|
|
135
|
+
} catch {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
export const getAccessToken = (): string | null => {
|
|
141
|
+
return getCookie(AUTH_COOKIE_NAMES.ACCESS_TOKEN);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export const getRefreshToken = (): string | null => {
|
|
145
|
+
return getCookie(AUTH_COOKIE_NAMES.REFRESH_TOKEN);
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
export const updateAccessToken = (newToken: string, expiresIn?: number) => {
|
|
149
|
+
setCookie(AUTH_COOKIE_NAMES.ACCESS_TOKEN, newToken, {
|
|
150
|
+
expires: ACCESS_TOKEN_EXPIRY,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
if (expiresIn) {
|
|
154
|
+
const expiryTime = new Date().getTime() + expiresIn * 1000;
|
|
155
|
+
setCookie(AUTH_COOKIE_NAMES.TOKEN_EXPIRY, expiryTime.toString(), {
|
|
156
|
+
expires: ACCESS_TOKEN_EXPIRY,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export const setupAutoRefresh = (refreshFn: () => Promise<void>) => {
|
|
162
|
+
const checkAndRefresh = async () => {
|
|
163
|
+
if (willTokenExpireSoon() && !isTokenExpired()) {
|
|
164
|
+
try {
|
|
165
|
+
await refreshFn();
|
|
166
|
+
} catch (error) {
|
|
167
|
+
clearAuth();
|
|
168
|
+
window.location.href = SHELL_APP_URL;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const interval = setInterval(checkAndRefresh, 5 * 60 * 1000);
|
|
174
|
+
|
|
175
|
+
return () => clearInterval(interval);
|
|
176
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Jest configuration for backend Node.js services
|
|
3
|
+
* @type {import('jest').Config}
|
|
4
|
+
*/
|
|
5
|
+
module.exports = {
|
|
6
|
+
preset: 'ts-jest',
|
|
7
|
+
testEnvironment: 'node',
|
|
8
|
+
roots: ['<rootDir>/src', '<rootDir>/tests'],
|
|
9
|
+
testMatch: ['**/*.test.ts', '**/*.spec.ts'],
|
|
10
|
+
transform: {
|
|
11
|
+
'^.+\\.tsx?$': [
|
|
12
|
+
'ts-jest',
|
|
13
|
+
{
|
|
14
|
+
tsconfig: 'tsconfig.json',
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
|
|
19
|
+
collectCoverageFrom: [
|
|
20
|
+
'src/**/*.ts',
|
|
21
|
+
'!src/**/*.d.ts',
|
|
22
|
+
'!src/swagger/**',
|
|
23
|
+
'!src/index.ts',
|
|
24
|
+
],
|
|
25
|
+
coverageDirectory: 'coverage',
|
|
26
|
+
coverageReporters: ['text', 'lcov', 'html'],
|
|
27
|
+
coverageThreshold: {
|
|
28
|
+
global: {
|
|
29
|
+
branches: 60,
|
|
30
|
+
functions: 70,
|
|
31
|
+
lines: 70,
|
|
32
|
+
statements: 70,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
setupFilesAfterEnv: ['<rootDir>/tests/setup.ts'],
|
|
36
|
+
testTimeout: 10000,
|
|
37
|
+
verbose: true,
|
|
38
|
+
moduleNameMapper: {
|
|
39
|
+
'^@e-commerce/utils/server$': '<rootDir>/tests/__mocks__/utils.ts',
|
|
40
|
+
'^@e-commerce/types$': '<rootDir>/../../packages/types/src/index.ts',
|
|
41
|
+
},
|
|
42
|
+
};
|