create-fluxstack 1.8.1 → 1.8.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.
Files changed (132) hide show
  1. package/.env.example +19 -0
  2. package/README.md +6 -3
  3. package/app/client/SIMPLIFICATION.md +140 -0
  4. package/app/client/frontend-only.ts +1 -1
  5. package/app/client/src/App.tsx +148 -283
  6. package/app/client/src/index.css +5 -20
  7. package/app/client/src/lib/eden-api.ts +53 -220
  8. package/app/client/src/main.tsx +2 -3
  9. package/app/server/controllers/users.controller.ts +57 -31
  10. package/app/server/index.ts +5 -2
  11. package/app/server/live/register-components.ts +18 -7
  12. package/app/server/routes/env-test.ts +53 -2
  13. package/app/server/routes/index.ts +1 -8
  14. package/app/server/routes/users.routes.ts +192 -91
  15. package/config/fluxstack.config.ts +2 -2
  16. package/config/plugins.config.ts +22 -1
  17. package/core/build/flux-plugins-generator.ts +5 -5
  18. package/core/build/live-components-generator.ts +15 -12
  19. package/core/cli/command-registry.ts +4 -14
  20. package/core/cli/commands/plugin-deps.ts +8 -8
  21. package/core/cli/generators/component.ts +3 -3
  22. package/core/cli/generators/controller.ts +4 -4
  23. package/core/cli/generators/index.ts +8 -8
  24. package/core/cli/generators/interactive.ts +4 -4
  25. package/core/cli/generators/plugin.ts +3 -3
  26. package/core/cli/generators/prompts.ts +1 -1
  27. package/core/cli/generators/route.ts +27 -11
  28. package/core/cli/generators/service.ts +5 -5
  29. package/core/cli/generators/template-engine.ts +1 -1
  30. package/core/cli/generators/types.ts +1 -1
  31. package/core/cli/index.ts +158 -193
  32. package/core/cli/plugin-discovery.ts +3 -3
  33. package/core/client/hooks/index.ts +2 -2
  34. package/core/client/hooks/state-validator.ts +1 -1
  35. package/core/client/hooks/useAuth.ts +1 -1
  36. package/core/client/hooks/useChunkedUpload.ts +1 -1
  37. package/core/client/hooks/useHybridLiveComponent.ts +1 -1
  38. package/core/client/hooks/useWebSocket.ts +1 -1
  39. package/core/config/env.ts +1 -1
  40. package/core/config/runtime-config.ts +5 -5
  41. package/core/config/schema.ts +9 -0
  42. package/core/framework/server.ts +30 -15
  43. package/core/framework/types.ts +2 -2
  44. package/core/live/ComponentRegistry.ts +1 -1
  45. package/core/plugins/built-in/live-components/commands/create-live-component.ts +1 -1
  46. package/core/plugins/built-in/live-components/index.ts +1 -1
  47. package/core/plugins/built-in/monitoring/index.ts +65 -161
  48. package/core/plugins/built-in/static/index.ts +18 -47
  49. package/core/plugins/built-in/swagger/index.ts +301 -231
  50. package/core/plugins/built-in/vite/index.ts +74 -109
  51. package/core/plugins/config.ts +2 -2
  52. package/core/plugins/dependency-manager.ts +2 -2
  53. package/core/plugins/discovery.ts +1 -1
  54. package/core/plugins/executor.ts +2 -2
  55. package/core/plugins/manager.ts +19 -4
  56. package/core/plugins/module-resolver.ts +1 -1
  57. package/core/plugins/registry.ts +3 -3
  58. package/core/plugins/types.ts +147 -5
  59. package/core/server/framework.ts +2 -2
  60. package/core/server/live/ComponentRegistry.ts +9 -26
  61. package/core/server/live/FileUploadManager.ts +1 -1
  62. package/core/server/live/auto-generated-components.ts +26 -0
  63. package/core/server/live/websocket-plugin.ts +211 -19
  64. package/core/server/middleware/errorHandling.ts +1 -1
  65. package/core/server/middleware/index.ts +4 -4
  66. package/core/server/plugins/database.ts +1 -2
  67. package/core/server/plugins/static-files-plugin.ts +259 -231
  68. package/core/server/plugins/swagger.ts +1 -1
  69. package/core/server/services/BaseService.ts +1 -1
  70. package/core/server/services/ServiceContainer.ts +1 -1
  71. package/core/server/services/index.ts +4 -4
  72. package/core/server/standalone.ts +16 -1
  73. package/core/testing/index.ts +1 -1
  74. package/core/testing/setup.ts +1 -1
  75. package/core/utils/logger/startup-banner.ts +7 -33
  76. package/core/utils/version.ts +6 -6
  77. package/create-fluxstack.ts +68 -25
  78. package/package.json +2 -2
  79. package/plugins/crypto-auth/index.ts +52 -47
  80. package/plugins/crypto-auth/server/AuthMiddleware.ts +1 -1
  81. package/plugins/crypto-auth/server/middlewares/helpers.ts +16 -1
  82. package/vitest.config.ts +11 -2
  83. package/app/client/src/App.css +0 -883
  84. package/app/client/src/components/ErrorBoundary.tsx +0 -107
  85. package/app/client/src/components/ErrorDisplay.css +0 -365
  86. package/app/client/src/components/ErrorDisplay.tsx +0 -258
  87. package/app/client/src/components/FluxStackConfig.tsx +0 -1321
  88. package/app/client/src/components/HybridLiveCounter.tsx +0 -140
  89. package/app/client/src/components/LiveClock.tsx +0 -286
  90. package/app/client/src/components/MainLayout.tsx +0 -388
  91. package/app/client/src/components/SidebarNavigation.tsx +0 -391
  92. package/app/client/src/components/StateDemo.tsx +0 -178
  93. package/app/client/src/components/SystemMonitor.tsx +0 -1044
  94. package/app/client/src/components/UserProfile.tsx +0 -809
  95. package/app/client/src/hooks/useAuth.ts +0 -39
  96. package/app/client/src/hooks/useNotifications.ts +0 -56
  97. package/app/client/src/lib/errors.ts +0 -340
  98. package/app/client/src/lib/hooks/useErrorHandler.ts +0 -258
  99. package/app/client/src/lib/index.ts +0 -45
  100. package/app/client/src/pages/ApiDocs.tsx +0 -182
  101. package/app/client/src/pages/CryptoAuthPage.tsx +0 -394
  102. package/app/client/src/pages/Demo.tsx +0 -174
  103. package/app/client/src/pages/HybridLive.tsx +0 -263
  104. package/app/client/src/pages/Overview.tsx +0 -155
  105. package/app/client/src/store/README.md +0 -43
  106. package/app/client/src/store/index.ts +0 -16
  107. package/app/client/src/store/slices/uiSlice.ts +0 -151
  108. package/app/client/src/store/slices/userSlice.ts +0 -161
  109. package/app/client/src/test/README.md +0 -257
  110. package/app/client/src/test/setup.ts +0 -70
  111. package/app/client/src/test/types.ts +0 -12
  112. package/app/server/live/CounterComponent.ts +0 -191
  113. package/app/server/live/FluxStackConfig.ts +0 -534
  114. package/app/server/live/SidebarNavigation.ts +0 -157
  115. package/app/server/live/SystemMonitor.ts +0 -595
  116. package/app/server/live/SystemMonitorIntegration.ts +0 -151
  117. package/app/server/live/UserProfileComponent.ts +0 -141
  118. package/app/server/middleware/auth.ts +0 -136
  119. package/app/server/middleware/errorHandling.ts +0 -252
  120. package/app/server/middleware/index.ts +0 -10
  121. package/app/server/middleware/rateLimit.ts +0 -193
  122. package/app/server/middleware/requestLogging.ts +0 -215
  123. package/app/server/middleware/validation.ts +0 -270
  124. package/app/server/routes/config.ts +0 -145
  125. package/app/server/routes/crypto-auth-demo.routes.ts +0 -167
  126. package/app/server/routes/example-with-crypto-auth.routes.ts +0 -235
  127. package/app/server/routes/exemplo-posts.routes.ts +0 -161
  128. package/app/server/routes/upload.ts +0 -92
  129. package/app/server/services/NotificationService.ts +0 -302
  130. package/app/server/services/UserService.ts +0 -222
  131. package/app/server/services/index.ts +0 -46
  132. package/app/server/types/index.ts +0 -1
@@ -1,92 +0,0 @@
1
- import { Elysia, t } from 'elysia'
2
- import { writeFile, mkdir } from 'fs/promises'
3
- import { existsSync } from 'fs'
4
- import { join, extname } from 'path'
5
-
6
- export const uploadRoutes = new Elysia({ prefix: '/upload' })
7
- .post('/avatar', async ({ body }: { body: { file: File } }) => {
8
- try {
9
- const { file } = body
10
-
11
- if (!file) {
12
- throw new Error('No file provided')
13
- }
14
-
15
- // Validate file type
16
- const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp', 'image/gif']
17
- if (!allowedTypes.includes(file.type)) {
18
- throw new Error('Invalid file type. Only JPEG, PNG, WebP and GIF are allowed.')
19
- }
20
-
21
- // Validate file size (5MB max)
22
- const maxSize = 5 * 1024 * 1024 // 5MB
23
- if (file.size > maxSize) {
24
- throw new Error('File too large. Maximum size is 5MB.')
25
- }
26
-
27
- // Create uploads directory if it doesn't exist
28
- const uploadsDir = join(process.cwd(), 'uploads', 'avatars')
29
- if (!existsSync(uploadsDir)) {
30
- await mkdir(uploadsDir, { recursive: true })
31
- }
32
-
33
- // Generate unique filename
34
- const timestamp = Date.now()
35
- const randomId = Math.random().toString(36).substring(2, 8)
36
- const extension = extname(file.name) || '.jpg'
37
- const filename = `avatar-${timestamp}-${randomId}${extension}`
38
- const filepath = join(uploadsDir, filename)
39
-
40
- // Convert file to buffer and save
41
- const buffer = await file.arrayBuffer()
42
- await writeFile(filepath, new Uint8Array(buffer))
43
-
44
- // Return the URL path for the uploaded file
45
- const imageUrl = `/uploads/avatars/${filename}`
46
-
47
- console.log('📸 Avatar uploaded successfully:', {
48
- filename,
49
- size: file.size,
50
- type: file.type,
51
- url: imageUrl
52
- })
53
-
54
- return {
55
- success: true,
56
- message: 'Avatar uploaded successfully',
57
- imageUrl,
58
- filename,
59
- size: file.size,
60
- type: file.type
61
- }
62
-
63
- } catch (error: any) {
64
- console.error('❌ Avatar upload failed:', error.message)
65
-
66
- return {
67
- success: false,
68
- error: error.message || 'Upload failed',
69
- imageUrl: null
70
- }
71
- }
72
- }, {
73
- body: t.Object({
74
- file: t.File({
75
- type: ['image/jpeg', 'image/jpg', 'image/png', 'image/webp', 'image/gif'],
76
- maxSize: 5 * 1024 * 1024 // 5MB
77
- })
78
- }),
79
- response: {
80
- 200: t.Object({
81
- success: t.Boolean(),
82
- message: t.Optional(t.String()),
83
- error: t.Optional(t.String()),
84
- imageUrl: t.Union([t.String(), t.Null()]),
85
- filename: t.Optional(t.String()),
86
- size: t.Optional(t.Number()),
87
- type: t.Optional(t.String())
88
- })
89
- }
90
- })
91
-
92
- // Note: File serving is now handled by the static-files plugin at /uploads/*
@@ -1,302 +0,0 @@
1
- /**
2
- * Notification Service
3
- * Handles application notifications and messaging
4
- */
5
-
6
- import { BaseService } from '../../../core/server/services/index.js'
7
-
8
- export interface Notification {
9
- id: string
10
- userId?: number
11
- type: 'info' | 'success' | 'warning' | 'error'
12
- title: string
13
- message: string
14
- data?: Record<string, any>
15
- read: boolean
16
- createdAt: string
17
- expiresAt?: string
18
- }
19
-
20
- export interface CreateNotificationData {
21
- userId?: number
22
- type: 'info' | 'success' | 'warning' | 'error'
23
- title: string
24
- message: string
25
- data?: Record<string, any>
26
- expiresIn?: number // seconds
27
- }
28
-
29
- export class NotificationService extends BaseService {
30
- private notifications: Notification[] = []
31
-
32
- async initialize(): Promise<void> {
33
- await super.initialize()
34
-
35
- // Start cleanup interval for expired notifications
36
- setInterval(() => {
37
- this.cleanupExpiredNotifications()
38
- }, 60000) // Check every minute
39
-
40
- this.logger.info('NotificationService initialized')
41
- }
42
-
43
- /**
44
- * Create a new notification
45
- */
46
- async createNotification(data: CreateNotificationData): Promise<Notification> {
47
- return this.executeWithLogging('createNotification', async () => {
48
- this.validateRequired(data, ['type', 'title', 'message'])
49
-
50
- const notification: Notification = {
51
- id: this.generateId(),
52
- userId: data.userId,
53
- type: data.type,
54
- title: data.title.trim(),
55
- message: data.message.trim(),
56
- data: data.data,
57
- read: false,
58
- createdAt: new Date().toISOString(),
59
- expiresAt: data.expiresIn
60
- ? new Date(Date.now() + data.expiresIn * 1000).toISOString()
61
- : undefined
62
- }
63
-
64
- this.notifications.push(notification)
65
-
66
- this.logger.info('Notification created', {
67
- notificationId: notification.id,
68
- userId: notification.userId,
69
- type: notification.type
70
- })
71
-
72
- return notification
73
- }, { userId: data.userId, type: data.type })
74
- }
75
-
76
- /**
77
- * Get notifications for a user
78
- */
79
- async getUserNotifications(
80
- userId: number,
81
- options: {
82
- unreadOnly?: boolean
83
- limit?: number
84
- offset?: number
85
- } = {}
86
- ): Promise<{
87
- notifications: Notification[]
88
- total: number
89
- unreadCount: number
90
- }> {
91
- return this.executeWithLogging('getUserNotifications', async () => {
92
- let userNotifications = this.notifications.filter(n => n.userId === userId)
93
-
94
- if (options.unreadOnly) {
95
- userNotifications = userNotifications.filter(n => !n.read)
96
- }
97
-
98
- // Sort by creation date (newest first)
99
- userNotifications.sort((a, b) =>
100
- new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
101
- )
102
-
103
- const total = userNotifications.length
104
- const unreadCount = this.notifications.filter(n =>
105
- n.userId === userId && !n.read
106
- ).length
107
-
108
- // Apply pagination
109
- const offset = options.offset || 0
110
- const limit = options.limit || 50
111
- const paginatedNotifications = userNotifications.slice(offset, offset + limit)
112
-
113
- return {
114
- notifications: paginatedNotifications,
115
- total,
116
- unreadCount
117
- }
118
- }, { userId, ...options })
119
- }
120
-
121
- /**
122
- * Get global notifications (not user-specific)
123
- */
124
- async getGlobalNotifications(options: {
125
- limit?: number
126
- offset?: number
127
- } = {}): Promise<{
128
- notifications: Notification[]
129
- total: number
130
- }> {
131
- return this.executeWithLogging('getGlobalNotifications', async () => {
132
- let globalNotifications = this.notifications.filter(n => !n.userId)
133
-
134
- // Sort by creation date (newest first)
135
- globalNotifications.sort((a, b) =>
136
- new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
137
- )
138
-
139
- const total = globalNotifications.length
140
-
141
- // Apply pagination
142
- const offset = options.offset || 0
143
- const limit = options.limit || 50
144
- const paginatedNotifications = globalNotifications.slice(offset, offset + limit)
145
-
146
- return {
147
- notifications: paginatedNotifications,
148
- total
149
- }
150
- }, options)
151
- }
152
-
153
- /**
154
- * Mark notification as read
155
- */
156
- async markAsRead(notificationId: string, userId?: number): Promise<boolean> {
157
- return this.executeWithLogging('markAsRead', async () => {
158
- const notification = this.notifications.find(n => n.id === notificationId)
159
-
160
- if (!notification) {
161
- return false
162
- }
163
-
164
- // Check if user has permission to mark this notification as read
165
- if (notification.userId && notification.userId !== userId) {
166
- throw new Error('Permission denied')
167
- }
168
-
169
- notification.read = true
170
-
171
- this.logger.info('Notification marked as read', {
172
- notificationId,
173
- userId: notification.userId
174
- })
175
-
176
- return true
177
- }, { notificationId, userId })
178
- }
179
-
180
- /**
181
- * Mark all notifications as read for a user
182
- */
183
- async markAllAsRead(userId: number): Promise<number> {
184
- return this.executeWithLogging('markAllAsRead', async () => {
185
- const userNotifications = this.notifications.filter(n =>
186
- n.userId === userId && !n.read
187
- )
188
-
189
- userNotifications.forEach(notification => {
190
- notification.read = true
191
- })
192
-
193
- const count = userNotifications.length
194
-
195
- this.logger.info('All notifications marked as read', { userId, count })
196
-
197
- return count
198
- }, { userId })
199
- }
200
-
201
- /**
202
- * Delete a notification
203
- */
204
- async deleteNotification(notificationId: string, userId?: number): Promise<boolean> {
205
- return this.executeWithLogging('deleteNotification', async () => {
206
- const index = this.notifications.findIndex(n => n.id === notificationId)
207
-
208
- if (index === -1) {
209
- return false
210
- }
211
-
212
- const notification = this.notifications[index]
213
-
214
- // Check if user has permission to delete this notification
215
- if (notification.userId && notification.userId !== userId) {
216
- throw new Error('Permission denied')
217
- }
218
-
219
- this.notifications.splice(index, 1)
220
-
221
- this.logger.info('Notification deleted', {
222
- notificationId,
223
- userId: notification.userId
224
- })
225
-
226
- return true
227
- }, { notificationId, userId })
228
- }
229
-
230
- /**
231
- * Broadcast notification to all users
232
- */
233
- async broadcastNotification(data: Omit<CreateNotificationData, 'userId'>): Promise<Notification> {
234
- return this.executeWithLogging('broadcastNotification', async () => {
235
- return this.createNotification(data)
236
- }, { type: data.type })
237
- }
238
-
239
- /**
240
- * Get notification statistics
241
- */
242
- async getNotificationStats(): Promise<{
243
- total: number
244
- unread: number
245
- byType: Record<string, number>
246
- recentCount: number
247
- }> {
248
- return this.executeWithLogging('getNotificationStats', async () => {
249
- const total = this.notifications.length
250
- const unread = this.notifications.filter(n => !n.read).length
251
-
252
- // Count by type
253
- const byType: Record<string, number> = {}
254
- this.notifications.forEach(n => {
255
- byType[n.type] = (byType[n.type] || 0) + 1
256
- })
257
-
258
- // Recent notifications (last 24 hours)
259
- const dayAgo = new Date()
260
- dayAgo.setDate(dayAgo.getDate() - 1)
261
- const recentCount = this.notifications.filter(n =>
262
- new Date(n.createdAt) > dayAgo
263
- ).length
264
-
265
- return {
266
- total,
267
- unread,
268
- byType,
269
- recentCount
270
- }
271
- })
272
- }
273
-
274
- /**
275
- * Cleanup expired notifications
276
- */
277
- private async cleanupExpiredNotifications(): Promise<void> {
278
- const now = new Date()
279
- const initialCount = this.notifications.length
280
-
281
- this.notifications = this.notifications.filter(notification => {
282
- if (!notification.expiresAt) {
283
- return true
284
- }
285
-
286
- return new Date(notification.expiresAt) > now
287
- })
288
-
289
- const removedCount = initialCount - this.notifications.length
290
-
291
- if (removedCount > 0) {
292
- this.logger.info(`Cleaned up ${removedCount} expired notifications`)
293
- }
294
- }
295
-
296
- /**
297
- * Generate unique notification ID
298
- */
299
- private generateId(): string {
300
- return `notif_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
301
- }
302
- }
@@ -1,222 +0,0 @@
1
- /**
2
- * User Service
3
- * Handles user-related business logic
4
- */
5
-
6
- import { BaseService } from '../../../core/server/services/index.js'
7
-
8
- export interface User {
9
- id: number
10
- name: string
11
- email: string
12
- createdAt: string
13
- updatedAt?: string
14
- }
15
-
16
- export interface CreateUserData {
17
- name: string
18
- email: string
19
- }
20
-
21
- export interface UpdateUserData {
22
- name?: string
23
- email?: string
24
- }
25
-
26
- export class UserService extends BaseService {
27
- private users: User[] = []
28
- private nextId = 1
29
-
30
- async initialize(): Promise<void> {
31
- await super.initialize()
32
-
33
- // Initialize with some sample data
34
- this.users = [
35
- {
36
- id: this.nextId++,
37
- name: 'John Doe',
38
- email: 'john@example.com',
39
- createdAt: new Date().toISOString()
40
- },
41
- {
42
- id: this.nextId++,
43
- name: 'Jane Smith',
44
- email: 'jane@example.com',
45
- createdAt: new Date().toISOString()
46
- }
47
- ]
48
-
49
- this.logger.info(`UserService initialized with ${this.users.length} users`)
50
- }
51
-
52
- /**
53
- * Get all users
54
- */
55
- async getAllUsers(): Promise<User[]> {
56
- return this.executeWithLogging('getAllUsers', async () => {
57
- return [...this.users]
58
- })
59
- }
60
-
61
- /**
62
- * Get user by ID
63
- */
64
- async getUserById(id: number): Promise<User | null> {
65
- return this.executeWithLogging('getUserById', async () => {
66
- const user = this.users.find(u => u.id === id)
67
- return user || null
68
- }, { userId: id })
69
- }
70
-
71
- /**
72
- * Get user by email
73
- */
74
- async getUserByEmail(email: string): Promise<User | null> {
75
- return this.executeWithLogging('getUserByEmail', async () => {
76
- const user = this.users.find(u => u.email === email)
77
- return user || null
78
- }, { email })
79
- }
80
-
81
- /**
82
- * Create a new user
83
- */
84
- async createUser(data: CreateUserData): Promise<User> {
85
- return this.executeWithLogging('createUser', async () => {
86
- this.validateRequired(data, ['name', 'email'])
87
-
88
- // Check if email already exists
89
- const existingUser = await this.getUserByEmail(data.email)
90
- if (existingUser) {
91
- throw new Error(`User with email '${data.email}' already exists`)
92
- }
93
-
94
- const user: User = {
95
- id: this.nextId++,
96
- name: data.name.trim(),
97
- email: data.email.trim().toLowerCase(),
98
- createdAt: new Date().toISOString()
99
- }
100
-
101
- this.users.push(user)
102
-
103
- this.logger.info('User created', { userId: user.id, email: user.email })
104
-
105
- return user
106
- }, { email: data.email })
107
- }
108
-
109
- /**
110
- * Update an existing user
111
- */
112
- async updateUser(id: number, data: UpdateUserData): Promise<User> {
113
- return this.executeWithLogging('updateUser', async () => {
114
- const userIndex = this.users.findIndex(u => u.id === id)
115
- if (userIndex === -1) {
116
- throw new Error(`User with ID ${id} not found`)
117
- }
118
-
119
- // Check email uniqueness if email is being updated
120
- if (data.email) {
121
- const existingUser = await this.getUserByEmail(data.email)
122
- if (existingUser && existingUser.id !== id) {
123
- throw new Error(`User with email '${data.email}' already exists`)
124
- }
125
- }
126
-
127
- const updatedUser: User = {
128
- ...this.users[userIndex],
129
- ...data,
130
- updatedAt: new Date().toISOString()
131
- }
132
-
133
- if (data.email) {
134
- updatedUser.email = data.email.trim().toLowerCase()
135
- }
136
-
137
- if (data.name) {
138
- updatedUser.name = data.name.trim()
139
- }
140
-
141
- this.users[userIndex] = updatedUser
142
-
143
- this.logger.info('User updated', { userId: id })
144
-
145
- return updatedUser
146
- }, { userId: id })
147
- }
148
-
149
- /**
150
- * Delete a user
151
- */
152
- async deleteUser(id: number): Promise<boolean> {
153
- return this.executeWithLogging('deleteUser', async () => {
154
- const userIndex = this.users.findIndex(u => u.id === id)
155
- if (userIndex === -1) {
156
- return false
157
- }
158
-
159
- this.users.splice(userIndex, 1)
160
-
161
- this.logger.info('User deleted', { userId: id })
162
-
163
- return true
164
- }, { userId: id })
165
- }
166
-
167
- /**
168
- * Search users by name or email
169
- */
170
- async searchUsers(query: string): Promise<User[]> {
171
- return this.executeWithLogging('searchUsers', async () => {
172
- if (!query || query.trim().length === 0) {
173
- return []
174
- }
175
-
176
- const searchTerm = query.trim().toLowerCase()
177
-
178
- return this.users.filter(user =>
179
- user.name.toLowerCase().includes(searchTerm) ||
180
- user.email.toLowerCase().includes(searchTerm)
181
- )
182
- }, { query })
183
- }
184
-
185
- /**
186
- * Get user statistics
187
- */
188
- async getUserStats(): Promise<{
189
- totalUsers: number
190
- recentUsers: number
191
- topDomains: Array<{ domain: string; count: number }>
192
- }> {
193
- return this.executeWithLogging('getUserStats', async () => {
194
- const totalUsers = this.users.length
195
-
196
- // Users created in the last 7 days
197
- const weekAgo = new Date()
198
- weekAgo.setDate(weekAgo.getDate() - 7)
199
- const recentUsers = this.users.filter(user =>
200
- new Date(user.createdAt) > weekAgo
201
- ).length
202
-
203
- // Top email domains
204
- const domainCounts = new Map<string, number>()
205
- this.users.forEach(user => {
206
- const domain = user.email.split('@')[1]
207
- domainCounts.set(domain, (domainCounts.get(domain) || 0) + 1)
208
- })
209
-
210
- const topDomains = Array.from(domainCounts.entries())
211
- .map(([domain, count]) => ({ domain, count }))
212
- .sort((a, b) => b.count - a.count)
213
- .slice(0, 5)
214
-
215
- return {
216
- totalUsers,
217
- recentUsers,
218
- topDomains
219
- }
220
- })
221
- }
222
- }
@@ -1,46 +0,0 @@
1
- /**
2
- * Service Registry
3
- * Configures and exports all application services
4
- */
5
-
6
- import type { FluxStackConfig } from '../../../core/config'
7
- import type { Logger } from '../../../core/utils/logger/index'
8
- import { ServiceContainer } from '../../../core/server/services/index.js'
9
- import { UserService } from './UserService'
10
- import { NotificationService } from './NotificationService'
11
-
12
- /**
13
- * Create and configure the service container
14
- */
15
- export function createServiceContainer(config: FluxStackConfig, logger: Logger): ServiceContainer {
16
- const container = new ServiceContainer(logger)
17
-
18
- // Register all services
19
- container.registerMany([
20
- {
21
- name: 'userService',
22
- constructor: UserService,
23
- singleton: true
24
- },
25
- {
26
- name: 'notificationService',
27
- constructor: NotificationService,
28
- dependencies: [], // Could depend on userService if needed
29
- singleton: true
30
- }
31
- ])
32
-
33
- return container
34
- }
35
-
36
- // Re-export service classes and types
37
- export { BaseService, ServiceContainer } from '../../../core/server/services/index.js'
38
- export { UserService } from './UserService'
39
- export { NotificationService } from './NotificationService'
40
-
41
- export type { ServiceContext, ServiceDefinition } from '../../../core/server/services/index.js'
42
- export type { User, CreateUserData, UpdateUserData } from './UserService'
43
- export type {
44
- Notification,
45
- CreateNotificationData
46
- } from './NotificationService'
@@ -1 +0,0 @@
1
- export * from '../../shared/types'