frontend-hamroun 1.1.90 → 1.2.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.
Files changed (92) hide show
  1. package/dist/{src/backend → backend}/api-utils.d.ts +2 -2
  2. package/dist/backend/api-utils.js +135 -0
  3. package/dist/backend/auth.js +387 -0
  4. package/dist/{src/backend → backend}/database.d.ts +1 -1
  5. package/dist/backend/database.js +91 -0
  6. package/dist/{src/backend → backend}/model.d.ts +2 -2
  7. package/dist/backend/model.js +176 -0
  8. package/dist/{src/backend → backend}/router.d.ts +1 -1
  9. package/dist/backend/router.js +137 -0
  10. package/dist/backend/server.js +268 -0
  11. package/dist/batch.js +22 -0
  12. package/dist/cli/index.js +1 -0
  13. package/dist/{src/component.d.ts → component.d.ts} +1 -1
  14. package/dist/component.js +84 -0
  15. package/dist/components/Counter.js +2 -0
  16. package/dist/context.js +20 -0
  17. package/dist/frontend-hamroun.es.js +1680 -0
  18. package/dist/frontend-hamroun.es.js.map +1 -0
  19. package/dist/frontend-hamroun.umd.js +2 -0
  20. package/dist/frontend-hamroun.umd.js.map +1 -0
  21. package/dist/hooks.js +164 -0
  22. package/dist/index.d.ts +46 -0
  23. package/dist/index.js +52 -355
  24. package/dist/jsx-runtime/index.d.ts +9 -0
  25. package/dist/jsx-runtime/index.js +16 -0
  26. package/dist/jsx-runtime/jsx-dev-runtime.js +1 -0
  27. package/dist/jsx-runtime/jsx-runtime.js +91 -0
  28. package/dist/{src/jsx-runtime.d.ts → jsx-runtime.d.ts} +1 -1
  29. package/dist/jsx-runtime.js +192 -0
  30. package/dist/renderer.js +51 -0
  31. package/dist/{src/server-renderer.d.ts → server-renderer.d.ts} +3 -0
  32. package/dist/server-renderer.js +102 -0
  33. package/dist/vdom.js +27 -0
  34. package/package.json +38 -52
  35. package/scripts/generate.js +134 -0
  36. package/src/backend/api-utils.ts +178 -0
  37. package/src/backend/auth.ts +543 -0
  38. package/src/backend/database.ts +104 -0
  39. package/src/backend/model.ts +196 -0
  40. package/src/backend/router.ts +176 -0
  41. package/src/backend/server.ts +330 -0
  42. package/src/backend/types.ts +257 -0
  43. package/src/batch.ts +24 -0
  44. package/src/cli/index.js +22 -40
  45. package/src/component.ts +98 -0
  46. package/src/components/Counter.tsx +4 -0
  47. package/src/context.ts +32 -0
  48. package/src/hooks.ts +211 -0
  49. package/src/index.ts +113 -0
  50. package/src/jsx-runtime/index.ts +24 -0
  51. package/src/jsx-runtime/jsx-dev-runtime.ts +0 -0
  52. package/src/jsx-runtime/jsx-runtime.ts +99 -0
  53. package/src/jsx-runtime.ts +226 -0
  54. package/src/renderer.ts +55 -0
  55. package/src/server-renderer.ts +114 -0
  56. package/src/types/bcrypt.d.ts +30 -0
  57. package/src/types/jsonwebtoken.d.ts +55 -0
  58. package/src/types.d.ts +26 -0
  59. package/src/types.ts +21 -0
  60. package/src/vdom.ts +34 -0
  61. package/templates/basic-app/package.json +17 -15
  62. package/templates/basic-app/postcss.config.js +1 -0
  63. package/templates/basic-app/src/App.tsx +65 -0
  64. package/templates/basic-app/src/api.ts +58 -0
  65. package/templates/basic-app/src/components/Counter.tsx +26 -0
  66. package/templates/basic-app/src/components/Header.tsx +9 -0
  67. package/templates/basic-app/src/components/TodoList.tsx +90 -0
  68. package/templates/basic-app/src/main.ts +20 -0
  69. package/templates/basic-app/src/server.ts +99 -0
  70. package/templates/basic-app/tailwind.config.js +23 -2
  71. package/bin/cli.js +0 -371
  72. package/dist/index.js.map +0 -1
  73. package/dist/index.mjs +0 -139269
  74. package/dist/index.mjs.map +0 -1
  75. package/dist/src/index.d.ts +0 -16
  76. package/dist/test/setupTests.d.ts +0 -4
  77. /package/dist/{src/backend → backend}/auth.d.ts +0 -0
  78. /package/dist/{src/backend → backend}/server.d.ts +0 -0
  79. /package/dist/{src/backend → backend}/types.d.ts +0 -0
  80. /package/dist/{test/backend.test.d.ts → backend/types.js} +0 -0
  81. /package/dist/{src/batch.d.ts → batch.d.ts} +0 -0
  82. /package/dist/{src/cli → cli}/index.d.ts +0 -0
  83. /package/dist/{src/components → components}/Counter.d.ts +0 -0
  84. /package/dist/{src/context.d.ts → context.d.ts} +0 -0
  85. /package/dist/{src/hooks.d.ts → hooks.d.ts} +0 -0
  86. /package/dist/{src/jsx-runtime → jsx-runtime}/jsx-dev-runtime.d.ts +0 -0
  87. /package/dist/{src/jsx-runtime → jsx-runtime}/jsx-runtime.d.ts +0 -0
  88. /package/dist/{src/renderer.d.ts → renderer.d.ts} +0 -0
  89. /package/dist/{src/types.d.ts → types.d.ts} +0 -0
  90. /package/dist/{test/mockTest.d.ts → types.js} +0 -0
  91. /package/dist/{src/vdom.d.ts → vdom.d.ts} +0 -0
  92. /package/{dist/test/mongooseSetup.d.ts → src/cli/index.ts} +0 -0
@@ -27,7 +27,7 @@ export declare function validateRequest(schema: any): (req: Request, res: Respon
27
27
  /**
28
28
  * Create a new router with common middleware and options
29
29
  */
30
- export declare function createApiRouter(options?: RouterOptions): Router;
30
+ export declare function createApiRouter(options?: RouterOptions, auth?: any): Router;
31
31
  /**
32
32
  * Wrap an async route handler to catch errors
33
33
  */
@@ -35,4 +35,4 @@ export declare function asyncHandler(fn: (req: Request, res: Response, next: Nex
35
35
  /**
36
36
  * Create standard REST endpoints for a model
37
37
  */
38
- export declare function createRestEndpoints<T>(model: any): import('express-serve-static-core').Router;
38
+ export declare function createRestEndpoints<T>(model: any): import("express-serve-static-core").Router;
@@ -0,0 +1,135 @@
1
+ import { Router } from 'express';
2
+ /**
3
+ * Creates a consistent API response structure
4
+ */
5
+ export function apiResponse(success, data, message, error, meta) {
6
+ const response = { success };
7
+ if (data !== undefined)
8
+ response.data = data;
9
+ if (message)
10
+ response.message = message;
11
+ if (error)
12
+ response.error = error;
13
+ if (meta)
14
+ response.meta = meta;
15
+ return response;
16
+ }
17
+ /**
18
+ * Helper to send successful responses
19
+ */
20
+ export function sendSuccess(res, data, message, statusCode = 200, meta) {
21
+ res.status(statusCode).json(apiResponse(true, data, message, undefined, meta));
22
+ }
23
+ /**
24
+ * Helper to send error responses
25
+ */
26
+ export function sendError(res, error, statusCode = 400, meta) {
27
+ const errorMessage = error instanceof Error ? error.message : error;
28
+ res.status(statusCode).json(apiResponse(false, undefined, undefined, errorMessage, meta));
29
+ }
30
+ /**
31
+ * Helper to extract pagination parameters from request
32
+ */
33
+ export function getPaginationParams(req) {
34
+ const page = parseInt(req.query.page) || 1;
35
+ const limit = parseInt(req.query.limit) || 10;
36
+ const sort = req.query.sort || 'createdAt';
37
+ const order = req.query.order === 'asc' ? 'asc' : 'desc';
38
+ return { page, limit, sort, order };
39
+ }
40
+ /**
41
+ * Middleware to extract and validate pagination parameters
42
+ */
43
+ export function paginationMiddleware(req, res, next) {
44
+ req.pagination = getPaginationParams(req);
45
+ next();
46
+ }
47
+ /**
48
+ * Middleware for basic request validation
49
+ */
50
+ export function validateRequest(schema) {
51
+ return (req, res, next) => {
52
+ try {
53
+ const { error, value } = schema.validate(req.body);
54
+ if (error) {
55
+ sendError(res, `Validation error: ${error.message}`, 400);
56
+ return;
57
+ }
58
+ // Replace request body with validated value
59
+ req.body = value;
60
+ next();
61
+ }
62
+ catch (err) {
63
+ sendError(res, 'Validation error', 400);
64
+ }
65
+ };
66
+ }
67
+ /**
68
+ * Create a new router with common middleware and options
69
+ */
70
+ export function createApiRouter(options = {}, auth) {
71
+ const router = Router();
72
+ // Add authentication middleware if required
73
+ if (options.requireAuth && auth) {
74
+ router.use(auth.authenticate);
75
+ // Add role check if specified
76
+ if (options.requiredRole) {
77
+ router.use(auth.hasRole(options.requiredRole));
78
+ }
79
+ }
80
+ // Add rate limiting if specified
81
+ if (options.rateLimit) {
82
+ console.warn('Rate limiting is disabled: express-rate-limit dependency is not installed');
83
+ // Note: To use rate limiting, install express-rate-limit: npm install express-rate-limit
84
+ }
85
+ return router;
86
+ }
87
+ /**
88
+ * Wrap an async route handler to catch errors
89
+ */
90
+ export function asyncHandler(fn) {
91
+ return (req, res, next) => {
92
+ fn(req, res, next).catch(next);
93
+ };
94
+ }
95
+ /**
96
+ * Create standard REST endpoints for a model
97
+ */
98
+ export function createRestEndpoints(model) {
99
+ const router = Router();
100
+ // GET all with pagination
101
+ router.get('/', paginationMiddleware, asyncHandler(async (req, res) => {
102
+ const result = await model.getAll(req.pagination);
103
+ sendSuccess(res, result);
104
+ }));
105
+ // GET by ID
106
+ router.get('/:id', asyncHandler(async (req, res) => {
107
+ const item = await model.getById(req.params.id);
108
+ if (!item) {
109
+ return sendError(res, 'Item not found', 404);
110
+ }
111
+ sendSuccess(res, item);
112
+ }));
113
+ // POST new item
114
+ router.post('/', asyncHandler(async (req, res) => {
115
+ const newItem = await model.create(req.body);
116
+ sendSuccess(res, newItem, 'Item created successfully', 201);
117
+ }));
118
+ // PUT update item
119
+ router.put('/:id', asyncHandler(async (req, res) => {
120
+ const updatedItem = await model.update(req.params.id, req.body);
121
+ if (!updatedItem) {
122
+ return sendError(res, 'Item not found', 404);
123
+ }
124
+ sendSuccess(res, updatedItem, 'Item updated successfully');
125
+ }));
126
+ // DELETE item
127
+ router.delete('/:id', asyncHandler(async (req, res) => {
128
+ const result = await model.delete(req.params.id);
129
+ if (!result) {
130
+ return sendError(res, 'Item not found', 404);
131
+ }
132
+ sendSuccess(res, null, 'Item deleted successfully');
133
+ }));
134
+ return router;
135
+ }
@@ -0,0 +1,387 @@
1
+ import jwt from 'jsonwebtoken';
2
+ import bcrypt from 'bcrypt';
3
+ import crypto from 'crypto';
4
+ /**
5
+ * Authentication service
6
+ */
7
+ export class Auth {
8
+ constructor(options) {
9
+ this.loginAttempts = new Map();
10
+ /**
11
+ * Login middleware to authenticate users
12
+ */
13
+ this.login = async (req, res) => {
14
+ try {
15
+ const { username, password } = req.body;
16
+ const ip = req.ip || req.connection.remoteAddress || '';
17
+ // Check rate limiting
18
+ if (!this.checkRateLimit(ip)) {
19
+ res.status(429).json({
20
+ success: false,
21
+ message: 'Too many login attempts. Please try again later.'
22
+ });
23
+ return;
24
+ }
25
+ if (!username || !password) {
26
+ res.status(400).json({
27
+ success: false,
28
+ message: 'Username and password are required'
29
+ });
30
+ return;
31
+ }
32
+ // Use custom find user function if provided
33
+ if (!this.options.findUser) {
34
+ res.status(500).json({
35
+ success: false,
36
+ message: 'User finder function not configured'
37
+ });
38
+ return;
39
+ }
40
+ const user = await this.options.findUser(username);
41
+ if (!user) {
42
+ res.status(401).json({
43
+ success: false,
44
+ message: 'Invalid credentials'
45
+ });
46
+ return;
47
+ }
48
+ // Verify password
49
+ const isPasswordValid = await this.options.verifyPassword(password, user.password);
50
+ if (!isPasswordValid) {
51
+ res.status(401).json({
52
+ success: false,
53
+ message: 'Invalid credentials'
54
+ });
55
+ return;
56
+ }
57
+ // Create a sanitized user object (without password)
58
+ const userWithoutPassword = { ...user };
59
+ delete userWithoutPassword.password;
60
+ // Generate tokens
61
+ const tokenPair = this.generateTokenPair({
62
+ id: user.id || user._id,
63
+ username: user.username,
64
+ role: user.role || 'user'
65
+ });
66
+ // Save refresh token if the function is provided
67
+ if (this.options.saveRefreshToken) {
68
+ const refreshExpiry = new Date();
69
+ refreshExpiry.setSeconds(refreshExpiry.getSeconds() +
70
+ this.getExpirationSeconds(this.options.refreshExpiration || '7d'));
71
+ await this.options.saveRefreshToken(user.id || user._id, tokenPair.refreshToken, refreshExpiry);
72
+ }
73
+ // Set authentication cookies if using cookie-based auth
74
+ if (req.body.useCookies) {
75
+ this.setAuthCookies(res, tokenPair);
76
+ }
77
+ res.json({
78
+ success: true,
79
+ message: 'Authentication successful',
80
+ tokens: tokenPair,
81
+ user: userWithoutPassword
82
+ });
83
+ }
84
+ catch (error) {
85
+ console.error('Authentication error:', error);
86
+ res.status(500).json({
87
+ success: false,
88
+ message: 'Authentication failed',
89
+ error: process.env.NODE_ENV !== 'production' ? error.message : undefined
90
+ });
91
+ }
92
+ };
93
+ /**
94
+ * Refresh token handler
95
+ */
96
+ this.refreshToken = async (req, res) => {
97
+ try {
98
+ // Get refresh token from cookies or request body
99
+ const refreshToken = req.cookies?.refreshToken || req.body.refreshToken;
100
+ if (!refreshToken) {
101
+ res.status(401).json({
102
+ success: false,
103
+ message: 'Refresh token required'
104
+ });
105
+ return;
106
+ }
107
+ // Verify the refresh token
108
+ const decoded = this.verifyToken(refreshToken, 'refresh');
109
+ // Check if token is still valid in database if verification function provided
110
+ if (this.options.verifyRefreshToken) {
111
+ const isValid = await this.options.verifyRefreshToken(decoded.id, refreshToken);
112
+ if (!isValid) {
113
+ this.clearAuthCookies(res);
114
+ res.status(401).json({
115
+ success: false,
116
+ message: 'Invalid refresh token'
117
+ });
118
+ return;
119
+ }
120
+ }
121
+ // Generate new token pair
122
+ const tokenPair = this.generateTokenPair({
123
+ id: decoded.id,
124
+ // We need to fetch the user data again for complete payload
125
+ ...(this.options.findUser ? await this.options.findUser(decoded.id) : {})
126
+ });
127
+ // Update refresh token in database if save function provided
128
+ if (this.options.saveRefreshToken) {
129
+ const refreshExpiry = new Date();
130
+ refreshExpiry.setSeconds(refreshExpiry.getSeconds() +
131
+ this.getExpirationSeconds(this.options.refreshExpiration || '7d'));
132
+ await this.options.saveRefreshToken(decoded.id, tokenPair.refreshToken, refreshExpiry);
133
+ }
134
+ // Set cookies if cookie-based auth is used
135
+ const useCookies = req.cookies?.accessToken || req.body.useCookies;
136
+ if (useCookies) {
137
+ this.setAuthCookies(res, tokenPair);
138
+ }
139
+ res.json({
140
+ success: true,
141
+ message: 'Token refreshed successfully',
142
+ tokens: tokenPair
143
+ });
144
+ }
145
+ catch (error) {
146
+ this.clearAuthCookies(res);
147
+ res.status(401).json({
148
+ success: false,
149
+ message: 'Invalid or expired refresh token',
150
+ error: process.env.NODE_ENV !== 'production' ? error.message : undefined
151
+ });
152
+ }
153
+ };
154
+ /**
155
+ * Logout handler
156
+ */
157
+ this.logout = async (req, res) => {
158
+ try {
159
+ // Clear cookies
160
+ this.clearAuthCookies(res);
161
+ // Invalidate refresh token if verification function provided
162
+ const refreshToken = req.cookies?.refreshToken || req.body.refreshToken;
163
+ if (refreshToken && this.options.saveRefreshToken) {
164
+ try {
165
+ const decoded = this.verifyToken(refreshToken, 'refresh');
166
+ // Check if invalidateToken function exists, otherwise we just remove it from storage
167
+ if (typeof this.options.saveRefreshToken === 'function') {
168
+ // We pass null as the token to indicate deletion/invalidation
169
+ await this.options.saveRefreshToken(decoded.id, '', new Date());
170
+ }
171
+ }
172
+ catch (e) {
173
+ // Token already invalid, nothing to do
174
+ }
175
+ }
176
+ res.json({ success: true, message: 'Logged out successfully' });
177
+ }
178
+ catch (error) {
179
+ console.error('Logout error:', error);
180
+ res.status(500).json({ success: false, message: 'Logout failed', error: error.message });
181
+ }
182
+ };
183
+ /**
184
+ * Middleware to verify user is authenticated
185
+ */
186
+ this.authenticate = (req, res, next) => {
187
+ try {
188
+ // Get token from Authorization header or cookies
189
+ let token = req.cookies?.accessToken;
190
+ if (!token) {
191
+ const authHeader = req.headers.authorization;
192
+ if (authHeader && authHeader.startsWith('Bearer ')) {
193
+ token = authHeader.split(' ')[1];
194
+ }
195
+ }
196
+ if (!token) {
197
+ res.status(401).json({
198
+ success: false,
199
+ message: 'Authentication required'
200
+ });
201
+ return;
202
+ }
203
+ const decoded = this.verifyToken(token, 'access');
204
+ // Add user info to request
205
+ req.user = decoded;
206
+ next();
207
+ }
208
+ catch (error) {
209
+ res.status(401).json({
210
+ success: false,
211
+ message: 'Invalid or expired token',
212
+ error: process.env.NODE_ENV !== 'production' ? error.message : undefined
213
+ });
214
+ }
215
+ };
216
+ /**
217
+ * Middleware to check if user has required role
218
+ */
219
+ this.hasRole = (role) => {
220
+ return (req, res, next) => {
221
+ const user = req.user;
222
+ if (!user) {
223
+ res.status(401).json({
224
+ success: false,
225
+ message: 'Authentication required'
226
+ });
227
+ return;
228
+ }
229
+ const roles = Array.isArray(role) ? role : [role];
230
+ if (roles.includes(user.role)) {
231
+ next();
232
+ }
233
+ else {
234
+ res.status(403).json({
235
+ success: false,
236
+ message: 'Insufficient permissions'
237
+ });
238
+ }
239
+ };
240
+ };
241
+ this.options = {
242
+ tokenExpiration: '15m',
243
+ refreshExpiration: '7d',
244
+ saltRounds: 10,
245
+ secureCookies: process.env.NODE_ENV === 'production',
246
+ httpOnlyCookies: true,
247
+ rateLimit: true,
248
+ ...options,
249
+ refreshSecret: options.refreshSecret || options.jwtSecret
250
+ };
251
+ if (!options.jwtSecret) {
252
+ throw new Error('JWT secret is required for authentication');
253
+ }
254
+ // Set default password verification if not provided
255
+ if (!this.options.verifyPassword) {
256
+ this.options.verifyPassword = this.verifyPasswordWithBcrypt;
257
+ }
258
+ }
259
+ /**
260
+ * Hash a password using bcrypt
261
+ */
262
+ async hashPassword(password) {
263
+ return await bcrypt.hash(password, this.options.saltRounds || 10);
264
+ }
265
+ /**
266
+ * Verify a password against a hash using bcrypt
267
+ */
268
+ async verifyPasswordWithBcrypt(password, hashedPassword) {
269
+ return await bcrypt.compare(password, hashedPassword);
270
+ }
271
+ /**
272
+ * Generate a cryptographically secure random token
273
+ */
274
+ generateSecureToken(length = 32) {
275
+ return crypto.randomBytes(length).toString('hex');
276
+ }
277
+ /**
278
+ * Generate token pair (access token + refresh token)
279
+ */
280
+ generateTokenPair(payload) {
281
+ // Calculate expiration times
282
+ const expiresIn = this.getExpirationSeconds(this.options.tokenExpiration || '15m');
283
+ // Generate the access token
284
+ const accessToken = jwt.sign({ ...payload, type: 'access' }, this.options.jwtSecret, { expiresIn: this.options.tokenExpiration });
285
+ // Generate the refresh token
286
+ const refreshToken = jwt.sign({ id: payload.id, type: 'refresh' }, this.options.refreshSecret, { expiresIn: this.options.refreshExpiration });
287
+ return {
288
+ accessToken,
289
+ refreshToken,
290
+ expiresIn
291
+ };
292
+ }
293
+ /**
294
+ * Convert JWT expiration time to seconds
295
+ */
296
+ getExpirationSeconds(expiration) {
297
+ const unit = expiration.charAt(expiration.length - 1);
298
+ const value = parseInt(expiration.slice(0, -1));
299
+ switch (unit) {
300
+ case 's': return value;
301
+ case 'm': return value * 60;
302
+ case 'h': return value * 60 * 60;
303
+ case 'd': return value * 60 * 60 * 24;
304
+ default: return 3600; // Default 1 hour
305
+ }
306
+ }
307
+ /**
308
+ * Verify a JWT token
309
+ */
310
+ verifyToken(token, type = 'access') {
311
+ try {
312
+ const secret = type === 'access' ? this.options.jwtSecret : this.options.refreshSecret;
313
+ const decoded = jwt.verify(token, secret);
314
+ // Verify token type matches expected type
315
+ if (typeof decoded === 'object' && decoded.type !== type) {
316
+ throw new Error('Invalid token type');
317
+ }
318
+ return decoded;
319
+ }
320
+ catch (error) {
321
+ throw new Error('Invalid or expired token');
322
+ }
323
+ }
324
+ /**
325
+ * Set authentication cookies
326
+ */
327
+ setAuthCookies(res, tokens) {
328
+ // Set access token cookie
329
+ res.cookie('accessToken', tokens.accessToken, {
330
+ httpOnly: this.options.httpOnlyCookies,
331
+ secure: this.options.secureCookies,
332
+ domain: this.options.cookieDomain,
333
+ sameSite: 'strict',
334
+ maxAge: tokens.expiresIn * 1000
335
+ });
336
+ // Set refresh token cookie with longer expiration
337
+ const refreshExpiresIn = this.getExpirationSeconds(this.options.refreshExpiration || '7d');
338
+ res.cookie('refreshToken', tokens.refreshToken, {
339
+ httpOnly: true, // Always HTTP only for refresh tokens
340
+ secure: this.options.secureCookies,
341
+ domain: this.options.cookieDomain,
342
+ sameSite: 'strict',
343
+ maxAge: refreshExpiresIn * 1000,
344
+ path: '/api/auth/refresh' // Restrict to refresh endpoint
345
+ });
346
+ }
347
+ /**
348
+ * Clear authentication cookies
349
+ */
350
+ clearAuthCookies(res) {
351
+ res.clearCookie('accessToken');
352
+ res.clearCookie('refreshToken', { path: '/api/auth/refresh' });
353
+ }
354
+ /**
355
+ * Check and handle rate limiting
356
+ */
357
+ checkRateLimit(ip) {
358
+ if (!this.options.rateLimit) {
359
+ return true;
360
+ }
361
+ const now = Date.now();
362
+ const attempt = this.loginAttempts.get(ip);
363
+ if (!attempt) {
364
+ this.loginAttempts.set(ip, { count: 1, resetTime: now + 3600000 });
365
+ return true;
366
+ }
367
+ // Reset if time expired
368
+ if (now > attempt.resetTime) {
369
+ this.loginAttempts.set(ip, { count: 1, resetTime: now + 3600000 });
370
+ return true;
371
+ }
372
+ // Check attempts
373
+ if (attempt.count >= 5) {
374
+ return false;
375
+ }
376
+ // Increment counter
377
+ attempt.count++;
378
+ this.loginAttempts.set(ip, attempt);
379
+ return true;
380
+ }
381
+ }
382
+ /**
383
+ * Create an authentication service
384
+ */
385
+ export function createAuth(options) {
386
+ return new Auth(options);
387
+ }
@@ -1,4 +1,4 @@
1
- import { default as mongoose } from 'mongoose';
1
+ import mongoose from 'mongoose';
2
2
  import { DatabaseOptions } from './types';
3
3
  /**
4
4
  * Database connector for MongoDB using Mongoose
@@ -0,0 +1,91 @@
1
+ import mongoose from 'mongoose';
2
+ /**
3
+ * Database connector for MongoDB using Mongoose
4
+ */
5
+ export class DatabaseConnector {
6
+ constructor(options) {
7
+ this.connection = null;
8
+ this._connected = false; // Renamed from isConnected to _connected
9
+ this.options = {
10
+ retryAttempts: 3,
11
+ retryDelay: 1000,
12
+ connectionTimeout: 10000,
13
+ autoIndex: true,
14
+ ...options
15
+ };
16
+ }
17
+ /**
18
+ * Connect to the database
19
+ */
20
+ async connect() {
21
+ try {
22
+ if (this._connected && this.connection) {
23
+ return this.connection;
24
+ }
25
+ // Set Mongoose options
26
+ mongoose.set('strictQuery', true);
27
+ // Connect with retry logic
28
+ let attempts = 0;
29
+ while (attempts < (this.options.retryAttempts || 3)) {
30
+ try {
31
+ await mongoose.connect(this.options.uri, {
32
+ dbName: this.options.name,
33
+ connectTimeoutMS: this.options.connectionTimeout,
34
+ autoIndex: this.options.autoIndex,
35
+ ...this.options.options
36
+ });
37
+ break;
38
+ }
39
+ catch (error) {
40
+ attempts++;
41
+ if (attempts >= (this.options.retryAttempts || 3)) {
42
+ throw error;
43
+ }
44
+ console.log(`Connection attempt ${attempts} failed. Retrying in ${this.options.retryDelay}ms...`);
45
+ await new Promise(resolve => setTimeout(resolve, this.options.retryDelay));
46
+ }
47
+ }
48
+ this.connection = mongoose.connection;
49
+ this._connected = true;
50
+ // Log successful connection
51
+ console.log(`Connected to MongoDB at ${this.options.uri}/${this.options.name}`);
52
+ // Handle connection events
53
+ this.connection.on('error', (err) => {
54
+ console.error('MongoDB connection error:', err);
55
+ this._connected = false;
56
+ });
57
+ this.connection.on('disconnected', () => {
58
+ console.log('MongoDB disconnected');
59
+ this._connected = false;
60
+ });
61
+ return this.connection;
62
+ }
63
+ catch (error) {
64
+ console.error('Failed to connect to MongoDB:', error);
65
+ throw error;
66
+ }
67
+ }
68
+ /**
69
+ * Disconnect from the database
70
+ */
71
+ async disconnect() {
72
+ if (this.connection) {
73
+ await mongoose.disconnect();
74
+ this._connected = false;
75
+ this.connection = null;
76
+ console.log('Disconnected from MongoDB');
77
+ }
78
+ }
79
+ /**
80
+ * Check if connected to the database
81
+ */
82
+ isConnected() {
83
+ return this._connected;
84
+ }
85
+ /**
86
+ * Get the mongoose connection
87
+ */
88
+ getConnection() {
89
+ return this.connection;
90
+ }
91
+ }
@@ -24,12 +24,12 @@ export declare const FieldTypes: {
24
24
  type: DateConstructor;
25
25
  };
26
26
  ObjectId: {
27
- type: typeof Schema.Types.ObjectId;
27
+ type: StringConstructor;
28
28
  };
29
29
  Required: (fieldType: any) => any;
30
30
  Unique: (fieldType: any) => any;
31
31
  Ref: (model: string) => {
32
- type: typeof Schema.Types.ObjectId;
32
+ type: StringConstructor;
33
33
  ref: string;
34
34
  };
35
35
  Enum: (values: any[]) => {