mbkauthe 2.5.0 → 3.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/LICENSE +339 -373
- package/README.md +106 -285
- package/docs/api.md +139 -1
- package/docs/db.md +1 -1
- package/docs/error-messages.md +557 -0
- package/index.d.ts +233 -0
- package/index.js +43 -32
- package/lib/config/cookies.js +52 -0
- package/lib/{config.js → config/index.js} +15 -95
- package/lib/config/security.js +8 -0
- package/lib/{pool.js → database/pool.js} +1 -1
- package/lib/main.js +27 -1041
- package/lib/{validateSessionAndRole.js → middleware/auth.js} +5 -3
- package/lib/middleware/index.js +106 -0
- package/lib/routes/auth.js +521 -0
- package/lib/routes/misc.js +272 -0
- package/lib/routes/oauth.js +325 -0
- package/lib/utils/errors.js +257 -0
- package/lib/utils/response.js +21 -0
- package/package.json +5 -2
- package/public/main.js +4 -4
- package/views/Error/dError.handlebars +1 -1
- package/views/errorCodes.handlebars +341 -0
- package/views/showmessage.handlebars +7 -6
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized error messages and error codes for mbkauthe
|
|
3
|
+
* Provides consistent, user-friendly error messages across the application
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Error codes for different scenarios
|
|
7
|
+
export const ErrorCodes = {
|
|
8
|
+
// Authentication errors (600-699)
|
|
9
|
+
INVALID_CREDENTIALS: 601,
|
|
10
|
+
USER_NOT_FOUND: 602,
|
|
11
|
+
INCORRECT_PASSWORD: 603,
|
|
12
|
+
ACCOUNT_INACTIVE: 604,
|
|
13
|
+
APP_NOT_AUTHORIZED: 605,
|
|
14
|
+
|
|
15
|
+
// 2FA errors (700-799)
|
|
16
|
+
TWO_FA_REQUIRED: 701,
|
|
17
|
+
TWO_FA_INVALID_TOKEN: 702,
|
|
18
|
+
TWO_FA_NOT_CONFIGURED: 703,
|
|
19
|
+
TWO_FA_EXPIRED: 704,
|
|
20
|
+
|
|
21
|
+
// Session errors (800-899)
|
|
22
|
+
SESSION_EXPIRED: 801,
|
|
23
|
+
SESSION_INVALID: 802,
|
|
24
|
+
SESSION_NOT_FOUND: 803,
|
|
25
|
+
|
|
26
|
+
// Authorization errors (900-999)
|
|
27
|
+
INSUFFICIENT_PERMISSIONS: 901,
|
|
28
|
+
ROLE_NOT_ALLOWED: 902,
|
|
29
|
+
|
|
30
|
+
// Input validation errors (1000-1099)
|
|
31
|
+
MISSING_REQUIRED_FIELD: 1001,
|
|
32
|
+
INVALID_USERNAME_FORMAT: 1002,
|
|
33
|
+
INVALID_PASSWORD_LENGTH: 1003,
|
|
34
|
+
INVALID_TOKEN_FORMAT: 1004,
|
|
35
|
+
|
|
36
|
+
// Rate limiting (1100-1199)
|
|
37
|
+
RATE_LIMIT_EXCEEDED: 1101,
|
|
38
|
+
|
|
39
|
+
// Server errors (1200-1299)
|
|
40
|
+
INTERNAL_SERVER_ERROR: 1201,
|
|
41
|
+
DATABASE_ERROR: 1202,
|
|
42
|
+
CONFIGURATION_ERROR: 1203,
|
|
43
|
+
|
|
44
|
+
// GitHub OAuth errors (1300-1399)
|
|
45
|
+
GITHUB_NOT_LINKED: 1301,
|
|
46
|
+
GITHUB_AUTH_FAILED: 1302,
|
|
47
|
+
OAUTH_STATE_MISMATCH: 1303,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// User-friendly error messages
|
|
51
|
+
export const ErrorMessages = {
|
|
52
|
+
// Authentication
|
|
53
|
+
[ErrorCodes.INVALID_CREDENTIALS]: {
|
|
54
|
+
message: "Invalid username or password",
|
|
55
|
+
userMessage: "The username or password you entered is incorrect. Please try again.",
|
|
56
|
+
hint: "Check your spelling and make sure Caps Lock is off"
|
|
57
|
+
},
|
|
58
|
+
[ErrorCodes.USER_NOT_FOUND]: {
|
|
59
|
+
message: "User account not found",
|
|
60
|
+
userMessage: "We couldn't find an account with that username.",
|
|
61
|
+
hint: "Please check the username and try again"
|
|
62
|
+
},
|
|
63
|
+
[ErrorCodes.INCORRECT_PASSWORD]: {
|
|
64
|
+
message: "Incorrect password",
|
|
65
|
+
userMessage: "The password you entered is incorrect.",
|
|
66
|
+
hint: "Make sure you're using the correct password for this account"
|
|
67
|
+
},
|
|
68
|
+
[ErrorCodes.ACCOUNT_INACTIVE]: {
|
|
69
|
+
message: "Account is inactive",
|
|
70
|
+
userMessage: "Your account has been deactivated.",
|
|
71
|
+
hint: "Please contact your administrator to reactivate your account"
|
|
72
|
+
},
|
|
73
|
+
[ErrorCodes.APP_NOT_AUTHORIZED]: {
|
|
74
|
+
message: "Not authorized for this application",
|
|
75
|
+
userMessage: "You don't have permission to access this application.",
|
|
76
|
+
hint: "Contact your administrator if you believe this is an error"
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
// 2FA
|
|
80
|
+
[ErrorCodes.TWO_FA_REQUIRED]: {
|
|
81
|
+
message: "Two-factor authentication required",
|
|
82
|
+
userMessage: "Please enter your 6-digit authentication code.",
|
|
83
|
+
hint: "Check your authenticator app for the code"
|
|
84
|
+
},
|
|
85
|
+
[ErrorCodes.TWO_FA_INVALID_TOKEN]: {
|
|
86
|
+
message: "Invalid 2FA code",
|
|
87
|
+
userMessage: "The authentication code you entered is incorrect.",
|
|
88
|
+
hint: "Make sure you're using the latest code from your authenticator app"
|
|
89
|
+
},
|
|
90
|
+
[ErrorCodes.TWO_FA_NOT_CONFIGURED]: {
|
|
91
|
+
message: "2FA not configured",
|
|
92
|
+
userMessage: "Two-factor authentication is not set up for your account.",
|
|
93
|
+
hint: "Contact your administrator to enable 2FA"
|
|
94
|
+
},
|
|
95
|
+
[ErrorCodes.TWO_FA_EXPIRED]: {
|
|
96
|
+
message: "2FA code expired",
|
|
97
|
+
userMessage: "The authentication code has expired.",
|
|
98
|
+
hint: "Please use a fresh code from your authenticator app"
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
// Session
|
|
102
|
+
[ErrorCodes.SESSION_EXPIRED]: {
|
|
103
|
+
message: "Session expired",
|
|
104
|
+
userMessage: "Your session has expired. Please log in again.",
|
|
105
|
+
hint: "This happens when you've been inactive for too long"
|
|
106
|
+
},
|
|
107
|
+
[ErrorCodes.SESSION_INVALID]: {
|
|
108
|
+
message: "Invalid session",
|
|
109
|
+
userMessage: "Your session is no longer valid. Please log in again.",
|
|
110
|
+
hint: "This may happen if you logged in from another device"
|
|
111
|
+
},
|
|
112
|
+
[ErrorCodes.SESSION_NOT_FOUND]: {
|
|
113
|
+
message: "Session not found",
|
|
114
|
+
userMessage: "Please log in to continue.",
|
|
115
|
+
hint: "You need to be logged in to access this page"
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
// Authorization
|
|
119
|
+
[ErrorCodes.INSUFFICIENT_PERMISSIONS]: {
|
|
120
|
+
message: "Insufficient permissions",
|
|
121
|
+
userMessage: "You don't have permission to perform this action.",
|
|
122
|
+
hint: "Contact your administrator if you need access"
|
|
123
|
+
},
|
|
124
|
+
[ErrorCodes.ROLE_NOT_ALLOWED]: {
|
|
125
|
+
message: "Role not allowed",
|
|
126
|
+
userMessage: "Your account role doesn't have access to this feature.",
|
|
127
|
+
hint: "This feature requires a different permission level"
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
// Input Validation
|
|
131
|
+
[ErrorCodes.MISSING_REQUIRED_FIELD]: {
|
|
132
|
+
message: "Required field missing",
|
|
133
|
+
userMessage: "Please fill in all required fields.",
|
|
134
|
+
hint: "Username and password are required"
|
|
135
|
+
},
|
|
136
|
+
[ErrorCodes.INVALID_USERNAME_FORMAT]: {
|
|
137
|
+
message: "Invalid username format",
|
|
138
|
+
userMessage: "Please enter a valid username.",
|
|
139
|
+
hint: "Username must be 1-255 characters"
|
|
140
|
+
},
|
|
141
|
+
[ErrorCodes.INVALID_PASSWORD_LENGTH]: {
|
|
142
|
+
message: "Invalid password length",
|
|
143
|
+
userMessage: "Password must be at least 8 characters long.",
|
|
144
|
+
hint: "Please use a password with 8 or more characters"
|
|
145
|
+
},
|
|
146
|
+
[ErrorCodes.INVALID_TOKEN_FORMAT]: {
|
|
147
|
+
message: "Invalid token format",
|
|
148
|
+
userMessage: "Please enter a valid 6-digit code.",
|
|
149
|
+
hint: "The code should be 6 numbers from your authenticator app"
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
// Rate Limiting
|
|
153
|
+
[ErrorCodes.RATE_LIMIT_EXCEEDED]: {
|
|
154
|
+
message: "Too many requests",
|
|
155
|
+
userMessage: "Too many attempts. Please try again later.",
|
|
156
|
+
hint: "Wait a few minutes before trying again"
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
// Server Errors
|
|
160
|
+
[ErrorCodes.INTERNAL_SERVER_ERROR]: {
|
|
161
|
+
message: "Internal server error",
|
|
162
|
+
userMessage: "Something went wrong on our end.",
|
|
163
|
+
hint: "Please try again later or contact support if the problem persists"
|
|
164
|
+
},
|
|
165
|
+
[ErrorCodes.DATABASE_ERROR]: {
|
|
166
|
+
message: "Database error",
|
|
167
|
+
userMessage: "We're experiencing technical difficulties.",
|
|
168
|
+
hint: "Please try again in a few moments"
|
|
169
|
+
},
|
|
170
|
+
[ErrorCodes.CONFIGURATION_ERROR]: {
|
|
171
|
+
message: "Configuration error",
|
|
172
|
+
userMessage: "The service is temporarily unavailable.",
|
|
173
|
+
hint: "Please contact your administrator"
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
// GitHub OAuth
|
|
177
|
+
[ErrorCodes.GITHUB_NOT_LINKED]: {
|
|
178
|
+
message: "GitHub account not linked",
|
|
179
|
+
userMessage: "Your GitHub account is not linked to any user account.",
|
|
180
|
+
hint: "Please link your GitHub account in your profile settings first"
|
|
181
|
+
},
|
|
182
|
+
[ErrorCodes.GITHUB_AUTH_FAILED]: {
|
|
183
|
+
message: "GitHub authentication failed",
|
|
184
|
+
userMessage: "We couldn't authenticate you with GitHub.",
|
|
185
|
+
hint: "Please try again or use username/password login"
|
|
186
|
+
},
|
|
187
|
+
[ErrorCodes.OAUTH_STATE_MISMATCH]: {
|
|
188
|
+
message: "OAuth state mismatch",
|
|
189
|
+
userMessage: "Authentication verification failed.",
|
|
190
|
+
hint: "Please try logging in again"
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Get error details by error code
|
|
196
|
+
* @param {number} errorCode - The error code
|
|
197
|
+
* @param {Object} customData - Optional custom data to merge with error
|
|
198
|
+
* @returns {Object} Error details with message, userMessage, and hint
|
|
199
|
+
*/
|
|
200
|
+
export function getErrorByCode(errorCode, customData = {}) {
|
|
201
|
+
const errorDetails = ErrorMessages[errorCode] || {
|
|
202
|
+
message: "An error occurred",
|
|
203
|
+
userMessage: "An unexpected error occurred. Please try again.",
|
|
204
|
+
hint: "Contact support if this problem continues"
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
return {
|
|
208
|
+
errorCode,
|
|
209
|
+
...errorDetails,
|
|
210
|
+
...customData
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Create a standardized error response
|
|
216
|
+
* @param {number} statusCode - HTTP status code
|
|
217
|
+
* @param {number} errorCode - Application error code
|
|
218
|
+
* @param {Object} customData - Optional custom data
|
|
219
|
+
* @returns {Object} Standardized error response
|
|
220
|
+
*/
|
|
221
|
+
export function createErrorResponse(statusCode, errorCode, customData = {}) {
|
|
222
|
+
const error = getErrorByCode(errorCode, customData);
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
success: false,
|
|
226
|
+
statusCode,
|
|
227
|
+
errorCode: error.errorCode,
|
|
228
|
+
message: error.userMessage || error.message,
|
|
229
|
+
hint: error.hint,
|
|
230
|
+
timestamp: new Date().toISOString(),
|
|
231
|
+
...customData
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Log error with consistent format
|
|
237
|
+
* @param {string} context - Context where error occurred
|
|
238
|
+
* @param {number} errorCode - Error code
|
|
239
|
+
* @param {Object} additionalInfo - Additional info to log
|
|
240
|
+
*/
|
|
241
|
+
export function logError(context, errorCode, additionalInfo = {}) {
|
|
242
|
+
const error = getErrorByCode(errorCode);
|
|
243
|
+
console.error(`[mbkauthe] ${context}:`, {
|
|
244
|
+
errorCode,
|
|
245
|
+
message: error.message,
|
|
246
|
+
...additionalInfo,
|
|
247
|
+
timestamp: new Date().toISOString()
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export default {
|
|
252
|
+
ErrorCodes,
|
|
253
|
+
ErrorMessages,
|
|
254
|
+
getErrorByCode,
|
|
255
|
+
createErrorResponse,
|
|
256
|
+
logError
|
|
257
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { mbkautheVar, packageJson } from "../config/index.js";
|
|
2
|
+
|
|
3
|
+
// Helper function to render error pages consistently
|
|
4
|
+
export const renderError = (res, { code, error, message, page, pagename, details }) => {
|
|
5
|
+
res.status(code);
|
|
6
|
+
const renderData = {
|
|
7
|
+
layout: false,
|
|
8
|
+
code,
|
|
9
|
+
error,
|
|
10
|
+
message,
|
|
11
|
+
page,
|
|
12
|
+
pagename,
|
|
13
|
+
app: mbkautheVar.APP_NAME,
|
|
14
|
+
version: packageJson.version
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Add optional parameters if provided
|
|
18
|
+
if (details !== undefined) renderData.details = details;
|
|
19
|
+
|
|
20
|
+
return res.render("Error/dError.handlebars", renderData);
|
|
21
|
+
};
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mbkauthe",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "MBKTech's reusable authentication system for Node.js applications.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
7
|
+
"types": "index.d.ts",
|
|
7
8
|
"scripts": {
|
|
8
9
|
"dev": "cross-env test=dev nodemon index.js"
|
|
9
10
|
},
|
|
@@ -20,7 +21,7 @@
|
|
|
20
21
|
"security"
|
|
21
22
|
],
|
|
22
23
|
"author": "Muhammad Bin Khalid <support@mbktech.org>",
|
|
23
|
-
"license": "
|
|
24
|
+
"license": "GPL-2.0",
|
|
24
25
|
"bugs": {
|
|
25
26
|
"url": "https://github.com/MIbnEKhalid/mbkauthe/issues"
|
|
26
27
|
},
|
|
@@ -49,6 +50,8 @@
|
|
|
49
50
|
"url": "^0.11.4"
|
|
50
51
|
},
|
|
51
52
|
"devDependencies": {
|
|
53
|
+
"@types/express": "^5.0.6",
|
|
54
|
+
"@types/node": "^22.19.1",
|
|
52
55
|
"cross-env": "^7.0.3",
|
|
53
56
|
"nodemon": "^3.1.11"
|
|
54
57
|
}
|
package/public/main.js
CHANGED
|
@@ -25,7 +25,7 @@ async function logout() {
|
|
|
25
25
|
alert(result.message);
|
|
26
26
|
}
|
|
27
27
|
} catch (error) {
|
|
28
|
-
console.error("Error during logout:", error);
|
|
28
|
+
console.error("[mbkauthe] Error during logout:", error);
|
|
29
29
|
alert(`Logout failed: ${error.message}`);
|
|
30
30
|
}
|
|
31
31
|
}
|
|
@@ -64,7 +64,7 @@ async function nuclearCacheClear() {
|
|
|
64
64
|
return Promise.resolve();
|
|
65
65
|
}));
|
|
66
66
|
} catch (error) {
|
|
67
|
-
console.error("Error clearing IndexedDB:", error);
|
|
67
|
+
console.error("[mbkauthe] Error clearing IndexedDB:", error);
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
@@ -84,7 +84,7 @@ async function nuclearCacheClear() {
|
|
|
84
84
|
window.location.reload();
|
|
85
85
|
|
|
86
86
|
} catch (error) {
|
|
87
|
-
console.error('Nuclear cache clear failed:', error);
|
|
87
|
+
console.error('[mbkauthe] Nuclear cache clear failed:', error);
|
|
88
88
|
window.location.reload(true);
|
|
89
89
|
}
|
|
90
90
|
}
|
|
@@ -103,7 +103,7 @@ function checkSession() {
|
|
|
103
103
|
window.location.reload(); // Reload the page to update the session status
|
|
104
104
|
}
|
|
105
105
|
})
|
|
106
|
-
.catch((error) => console.error("Error checking session:", error));
|
|
106
|
+
.catch((error) => console.error("[mbkauthe] Error checking session:", error));
|
|
107
107
|
}
|
|
108
108
|
// Call validateSession every 2 minutes (120000 milliseconds)
|
|
109
109
|
// setInterval(checkSession, validateSessionInterval);
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
{{> head}}
|
|
5
|
+
|
|
6
|
+
<body>
|
|
7
|
+
{{> header}}
|
|
8
|
+
|
|
9
|
+
<style>
|
|
10
|
+
.container {
|
|
11
|
+
max-width: 1200px;
|
|
12
|
+
margin: 0 auto;
|
|
13
|
+
padding: 2rem;
|
|
14
|
+
padding-top: 100px;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.page-header {
|
|
18
|
+
text-align: center;
|
|
19
|
+
padding: 3rem 0 2rem;
|
|
20
|
+
border-bottom: 2px solid rgba(0, 184, 148, 0.3);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.page-header h1 {
|
|
24
|
+
font-size: 2.5rem;
|
|
25
|
+
color: var(--accent);
|
|
26
|
+
margin-bottom: 0.5rem;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.page-header p {
|
|
30
|
+
font-size: 1.1rem;
|
|
31
|
+
color: var(--text-light);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.search-box {
|
|
35
|
+
margin-bottom: 2rem;
|
|
36
|
+
position: sticky;
|
|
37
|
+
top: 70px;
|
|
38
|
+
background: transparent;
|
|
39
|
+
backdrop-filter: blur(10px);
|
|
40
|
+
padding: 1rem 0;
|
|
41
|
+
z-index: 100;
|
|
42
|
+
border-bottom: 1px solid rgba(0, 184, 148, 0.2);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.search-input {
|
|
46
|
+
width: 100%;
|
|
47
|
+
padding: 1rem 1.5rem;
|
|
48
|
+
background: rgba(255, 255, 255, 0.05);
|
|
49
|
+
border: 2px solid rgba(0, 184, 148, 0.3);
|
|
50
|
+
border-radius: 8px;
|
|
51
|
+
color: #e0f7fa;
|
|
52
|
+
font-size: 1rem;
|
|
53
|
+
transition: all 0.3s ease;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.search-input:focus {
|
|
57
|
+
outline: none;
|
|
58
|
+
border-color: #00b894;
|
|
59
|
+
background: rgba(255, 255, 255, 0.08);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.search-input::placeholder {
|
|
63
|
+
color: #b2dfdb;
|
|
64
|
+
opacity: 0.6;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.category {
|
|
68
|
+
margin-bottom: 3rem;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.category-header {
|
|
72
|
+
display: flex;
|
|
73
|
+
align-items: center;
|
|
74
|
+
gap: 1rem;
|
|
75
|
+
margin-bottom: 1.5rem;
|
|
76
|
+
padding: 1rem;
|
|
77
|
+
background: rgba(0, 184, 148, 0.1);
|
|
78
|
+
border-left: 4px solid #00b894;
|
|
79
|
+
border-radius: 4px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.category-header h2 {
|
|
83
|
+
font-size: 1.8rem;
|
|
84
|
+
color: var(--accent);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.category-header .range {
|
|
88
|
+
font-size: 1rem;
|
|
89
|
+
color: var(--text-light);
|
|
90
|
+
font-weight: normal;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.error-card {
|
|
94
|
+
background: rgba(10, 20, 20, 0.8);
|
|
95
|
+
border: 1px solid rgba(0, 184, 148, 0.2);
|
|
96
|
+
border-radius: 8px;
|
|
97
|
+
padding: 1.5rem;
|
|
98
|
+
margin-bottom: 1.5rem;
|
|
99
|
+
transition: all 0.3s ease;
|
|
100
|
+
scroll-margin-top: 100px;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.error-card:hover {
|
|
104
|
+
border-color: #00b894;
|
|
105
|
+
box-shadow: 0 4px 20px rgba(0, 184, 148, 0.2);
|
|
106
|
+
transform: translateY(-2px);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.error-header {
|
|
110
|
+
display: flex;
|
|
111
|
+
align-items: center;
|
|
112
|
+
gap: 1rem;
|
|
113
|
+
margin-bottom: 1rem;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.error-code {
|
|
117
|
+
background: var(--warning);
|
|
118
|
+
color: var(--darker);
|
|
119
|
+
padding: 0.5rem 1rem;
|
|
120
|
+
border-radius: 6px;
|
|
121
|
+
font-weight: bold;
|
|
122
|
+
font-size: 1.1rem;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.error-name {
|
|
126
|
+
color: var(--accent);
|
|
127
|
+
font-size: 1.3rem;
|
|
128
|
+
font-weight: 600;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.error-message {
|
|
132
|
+
color: var(--text);
|
|
133
|
+
font-size: 1.1rem;
|
|
134
|
+
margin-bottom: 0.75rem;
|
|
135
|
+
padding-left: 1rem;
|
|
136
|
+
border-left: 3px solid rgba(0, 184, 148, 0.3);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.error-hint {
|
|
140
|
+
background: rgba(255, 209, 102, 0.1);
|
|
141
|
+
color: var(--warning);
|
|
142
|
+
padding: 0.75rem 1rem;
|
|
143
|
+
border-radius: 6px;
|
|
144
|
+
border-left: 3px solid var(--warning);
|
|
145
|
+
font-size: 0.95rem;
|
|
146
|
+
margin-top: 0.75rem;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
.no-results {
|
|
152
|
+
text-align: center;
|
|
153
|
+
padding: 3rem;
|
|
154
|
+
color: var(--text-light);
|
|
155
|
+
font-size: 1.2rem;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.back-to-top {
|
|
159
|
+
position: fixed;
|
|
160
|
+
bottom: 2rem;
|
|
161
|
+
right: 2rem;
|
|
162
|
+
background: var(--accent);
|
|
163
|
+
color: var(--darker);
|
|
164
|
+
width: 50px;
|
|
165
|
+
height: 50px;
|
|
166
|
+
border-radius: 50%;
|
|
167
|
+
display: flex;
|
|
168
|
+
align-items: center;
|
|
169
|
+
justify-content: center;
|
|
170
|
+
font-size: 1.5rem;
|
|
171
|
+
cursor: pointer;
|
|
172
|
+
box-shadow: 0 4px 20px rgba(0, 184, 148, 0.3);
|
|
173
|
+
transition: all 0.3s ease;
|
|
174
|
+
opacity: 0;
|
|
175
|
+
pointer-events: none;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.back-to-top.visible {
|
|
179
|
+
opacity: 1;
|
|
180
|
+
pointer-events: all;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.back-to-top:hover {
|
|
184
|
+
transform: translateY(-5px);
|
|
185
|
+
box-shadow: 0 6px 25px rgba(0, 184, 148, 0.4);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
@media (max-width: 768px) {
|
|
189
|
+
.page-header h1 {
|
|
190
|
+
font-size: 2rem;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.category-header h2 {
|
|
194
|
+
font-size: 1.5rem;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.error-card {
|
|
198
|
+
padding: 1rem;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.error-header {
|
|
202
|
+
flex-direction: column;
|
|
203
|
+
align-items: flex-start;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
</style>
|
|
207
|
+
|
|
208
|
+
<div class="container">
|
|
209
|
+
<div class="page-header">
|
|
210
|
+
<h1>🔐 MBKAuthe Error Codes</h1>
|
|
211
|
+
<p>Complete reference guide for all error codes and their solutions</p>
|
|
212
|
+
</div>
|
|
213
|
+
|
|
214
|
+
<div class="search-box">
|
|
215
|
+
<input type="text" class="search-input" id="searchInput" placeholder="Search by error code, name, or description...">
|
|
216
|
+
</div>
|
|
217
|
+
|
|
218
|
+
<div id="errorContainer">
|
|
219
|
+
{{#each errorCategories}}
|
|
220
|
+
<div class="category" data-category="{{this.category}}">
|
|
221
|
+
<div class="category-header">
|
|
222
|
+
<h2>{{this.icon}} {{this.name}}</h2>
|
|
223
|
+
<span class="range">{{this.range}}</span>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
{{#each this.errors}}
|
|
227
|
+
<div class="error-card" id="error-{{this.code}}" data-search="{{this.code}} {{this.name}} {{this.message}} {{this.userMessage}} {{this.hint}}">
|
|
228
|
+
<div class="error-header">
|
|
229
|
+
<span class="error-code">{{this.code}}</span>
|
|
230
|
+
<span class="error-name">{{this.name}}</span>
|
|
231
|
+
</div>
|
|
232
|
+
<div class="error-message">
|
|
233
|
+
{{this.userMessage}}
|
|
234
|
+
</div>
|
|
235
|
+
<div class="error-hint">
|
|
236
|
+
💡 {{this.hint}}
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
{{/each}}
|
|
240
|
+
</div>
|
|
241
|
+
{{/each}}
|
|
242
|
+
</div>
|
|
243
|
+
|
|
244
|
+
<div class="no-results" id="noResults" style="display: none;">
|
|
245
|
+
No error codes found matching your search.
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
|
|
249
|
+
<div class="back-to-top" id="backToTop">↑</div>
|
|
250
|
+
|
|
251
|
+
<script>
|
|
252
|
+
// Search functionality
|
|
253
|
+
const searchInput = document.getElementById('searchInput');
|
|
254
|
+
const errorCards = document.querySelectorAll('.error-card');
|
|
255
|
+
const categories = document.querySelectorAll('.category');
|
|
256
|
+
const noResults = document.getElementById('noResults');
|
|
257
|
+
|
|
258
|
+
searchInput.addEventListener('input', function() {
|
|
259
|
+
const searchTerm = this.value.toLowerCase().trim();
|
|
260
|
+
let hasResults = false;
|
|
261
|
+
|
|
262
|
+
if (!searchTerm) {
|
|
263
|
+
// Show all cards and categories
|
|
264
|
+
errorCards.forEach(card => card.style.display = 'block');
|
|
265
|
+
categories.forEach(cat => cat.style.display = 'block');
|
|
266
|
+
noResults.style.display = 'none';
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Search through error cards
|
|
271
|
+
errorCards.forEach(card => {
|
|
272
|
+
const searchData = card.getAttribute('data-search').toLowerCase();
|
|
273
|
+
if (searchData.includes(searchTerm)) {
|
|
274
|
+
card.style.display = 'block';
|
|
275
|
+
hasResults = true;
|
|
276
|
+
} else {
|
|
277
|
+
card.style.display = 'none';
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// Hide/show categories based on visible cards
|
|
282
|
+
categories.forEach(category => {
|
|
283
|
+
const visibleCards = category.querySelectorAll('.error-card[style="display: block;"]');
|
|
284
|
+
if (visibleCards.length > 0) {
|
|
285
|
+
category.style.display = 'block';
|
|
286
|
+
} else {
|
|
287
|
+
category.style.display = 'none';
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
noResults.style.display = hasResults ? 'none' : 'block';
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// Back to top button
|
|
295
|
+
const backToTop = document.getElementById('backToTop');
|
|
296
|
+
|
|
297
|
+
window.addEventListener('scroll', function() {
|
|
298
|
+
if (window.pageYOffset > 300) {
|
|
299
|
+
backToTop.classList.add('visible');
|
|
300
|
+
} else {
|
|
301
|
+
backToTop.classList.remove('visible');
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
backToTop.addEventListener('click', function() {
|
|
306
|
+
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// Handle hash navigation
|
|
310
|
+
function scrollToError() {
|
|
311
|
+
if (window.location.hash) {
|
|
312
|
+
let targetId = window.location.hash.substring(1);
|
|
313
|
+
|
|
314
|
+
// If hash doesn't start with "error-", add it
|
|
315
|
+
if (!targetId.startsWith('error-') && /^\d+$/.test(targetId)) {
|
|
316
|
+
targetId = 'error-' + targetId;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const targetElement = document.getElementById(targetId);
|
|
320
|
+
if (targetElement) {
|
|
321
|
+
setTimeout(() => {
|
|
322
|
+
targetElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
323
|
+
targetElement.style.background = 'rgba(0, 184, 148, 0.2)';
|
|
324
|
+
targetElement.style.transition = 'background 0.3s ease';
|
|
325
|
+
setTimeout(() => {
|
|
326
|
+
targetElement.style.background = '';
|
|
327
|
+
}, 2000);
|
|
328
|
+
}, 100);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Scroll on page load
|
|
334
|
+
scrollToError();
|
|
335
|
+
|
|
336
|
+
// Scroll when hash changes
|
|
337
|
+
window.addEventListener('hashchange', scrollToError);
|
|
338
|
+
</script>
|
|
339
|
+
</body>
|
|
340
|
+
|
|
341
|
+
</html>
|