create-fluxstack 1.0.13 → 1.0.15
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/.env.example +29 -29
- package/app/client/README.md +69 -69
- package/app/client/index.html +14 -13
- package/app/client/src/App.tsx +157 -524
- package/app/client/src/components/ErrorBoundary.tsx +107 -0
- package/app/client/src/components/ErrorDisplay.css +365 -0
- package/app/client/src/components/ErrorDisplay.tsx +258 -0
- package/app/client/src/components/FluxStackConfig.tsx +1321 -0
- package/app/client/src/components/HybridLiveCounter.tsx +140 -0
- package/app/client/src/components/LiveClock.tsx +286 -0
- package/app/client/src/components/MainLayout.tsx +390 -0
- package/app/client/src/components/SidebarNavigation.tsx +391 -0
- package/app/client/src/components/StateDemo.tsx +178 -0
- package/app/client/src/components/SystemMonitor.tsx +1038 -0
- package/app/client/src/components/Teste.tsx +104 -0
- package/app/client/src/components/UserProfile.tsx +809 -0
- package/app/client/src/hooks/useAuth.ts +39 -0
- package/app/client/src/hooks/useNotifications.ts +56 -0
- package/app/client/src/lib/eden-api.ts +189 -53
- package/app/client/src/lib/errors.ts +340 -0
- package/app/client/src/lib/hooks/useErrorHandler.ts +258 -0
- package/app/client/src/lib/index.ts +45 -0
- package/app/client/src/main.tsx +3 -2
- package/app/client/src/pages/ApiDocs.tsx +182 -0
- package/app/client/src/pages/Demo.tsx +174 -0
- package/app/client/src/pages/HybridLive.tsx +263 -0
- package/app/client/src/pages/Overview.tsx +155 -0
- package/app/client/src/store/README.md +43 -0
- package/app/client/src/store/index.ts +16 -0
- package/app/client/src/store/slices/uiSlice.ts +151 -0
- package/app/client/src/store/slices/userSlice.ts +161 -0
- package/app/client/src/test/README.md +257 -0
- package/app/client/src/test/setup.ts +70 -0
- package/app/client/src/test/types.ts +12 -0
- package/app/client/src/vite-env.d.ts +1 -1
- package/app/client/tsconfig.app.json +44 -43
- package/app/client/tsconfig.json +7 -7
- package/app/client/tsconfig.node.json +25 -25
- package/app/client/zustand-setup.md +65 -0
- package/app/server/controllers/users.controller.ts +68 -68
- package/app/server/index.ts +9 -1
- package/app/server/live/CounterComponent.ts +191 -0
- package/app/server/live/FluxStackConfig.ts +529 -0
- package/app/server/live/LiveClockComponent.ts +214 -0
- package/app/server/live/SidebarNavigation.ts +156 -0
- package/app/server/live/SystemMonitor.ts +594 -0
- package/app/server/live/SystemMonitorIntegration.ts +151 -0
- package/app/server/live/TesteComponent.ts +87 -0
- package/app/server/live/UserProfileComponent.ts +135 -0
- package/app/server/live/register-components.ts +28 -0
- package/app/server/middleware/auth.ts +136 -0
- package/app/server/middleware/errorHandling.ts +250 -0
- package/app/server/middleware/index.ts +10 -0
- package/app/server/middleware/rateLimit.ts +193 -0
- package/app/server/middleware/requestLogging.ts +215 -0
- package/app/server/middleware/validation.ts +270 -0
- package/app/server/routes/index.ts +14 -2
- package/app/server/routes/upload.ts +92 -0
- package/app/server/routes/users.routes.ts +2 -9
- package/app/server/services/NotificationService.ts +302 -0
- package/app/server/services/UserService.ts +222 -0
- package/app/server/services/index.ts +46 -0
- package/core/cli/commands/plugin-deps.ts +263 -0
- package/core/cli/generators/README.md +339 -0
- package/core/cli/generators/component.ts +770 -0
- package/core/cli/generators/controller.ts +299 -0
- package/core/cli/generators/index.ts +144 -0
- package/core/cli/generators/interactive.ts +228 -0
- package/core/cli/generators/prompts.ts +83 -0
- package/core/cli/generators/route.ts +513 -0
- package/core/cli/generators/service.ts +465 -0
- package/core/cli/generators/template-engine.ts +154 -0
- package/core/cli/generators/types.ts +71 -0
- package/core/cli/generators/utils.ts +192 -0
- package/core/cli/index.ts +69 -0
- package/core/cli/plugin-discovery.ts +16 -85
- package/core/client/fluxstack.ts +17 -0
- package/core/client/hooks/index.ts +7 -0
- package/core/client/hooks/state-validator.ts +130 -0
- package/core/client/hooks/useAuth.ts +49 -0
- package/core/client/hooks/useChunkedUpload.ts +258 -0
- package/core/client/hooks/useHybridLiveComponent.ts +967 -0
- package/core/client/hooks/useWebSocket.ts +373 -0
- package/core/client/index.ts +47 -0
- package/core/client/state/createStore.ts +193 -0
- package/core/client/state/index.ts +15 -0
- package/core/config/env-dynamic.ts +1 -1
- package/core/config/env.ts +2 -1
- package/core/config/runtime-config.ts +3 -3
- package/core/config/schema.ts +84 -49
- package/core/framework/server.ts +30 -0
- package/core/index.ts +25 -0
- package/core/live/ComponentRegistry.ts +399 -0
- package/core/live/types.ts +164 -0
- package/core/plugins/built-in/live-components/commands/create-live-component.ts +1201 -0
- package/core/plugins/built-in/live-components/index.ts +27 -0
- package/core/plugins/built-in/logger/index.ts +1 -1
- package/core/plugins/built-in/monitoring/index.ts +1 -1
- package/core/plugins/built-in/static/index.ts +1 -1
- package/core/plugins/built-in/swagger/index.ts +1 -1
- package/core/plugins/built-in/vite/index.ts +1 -1
- package/core/plugins/dependency-manager.ts +384 -0
- package/core/plugins/index.ts +5 -1
- package/core/plugins/manager.ts +7 -3
- package/core/plugins/registry.ts +88 -10
- package/core/plugins/types.ts +11 -11
- package/core/server/framework.ts +43 -0
- package/core/server/index.ts +11 -1
- package/core/server/live/ComponentRegistry.ts +1017 -0
- package/core/server/live/FileUploadManager.ts +272 -0
- package/core/server/live/LiveComponentPerformanceMonitor.ts +930 -0
- package/core/server/live/SingleConnectionManager.ts +0 -0
- package/core/server/live/StateSignature.ts +644 -0
- package/core/server/live/WebSocketConnectionManager.ts +688 -0
- package/core/server/live/websocket-plugin.ts +435 -0
- package/core/server/middleware/errorHandling.ts +141 -0
- package/core/server/middleware/index.ts +16 -0
- package/core/server/plugins/static-files-plugin.ts +232 -0
- package/core/server/services/BaseService.ts +95 -0
- package/core/server/services/ServiceContainer.ts +144 -0
- package/core/server/services/index.ts +9 -0
- package/core/templates/create-project.ts +196 -33
- package/core/testing/index.ts +10 -0
- package/core/testing/setup.ts +74 -0
- package/core/types/build.ts +38 -14
- package/core/types/types.ts +319 -0
- package/core/utils/env-runtime.ts +7 -0
- package/core/utils/errors/handlers.ts +264 -39
- package/core/utils/errors/index.ts +528 -18
- package/core/utils/errors/middleware.ts +114 -0
- package/core/utils/logger/formatters.ts +222 -0
- package/core/utils/logger/index.ts +167 -48
- package/core/utils/logger/middleware.ts +253 -0
- package/core/utils/logger/performance.ts +384 -0
- package/core/utils/logger/transports.ts +365 -0
- package/create-fluxstack.ts +296 -296
- package/fluxstack.config.ts +17 -1
- package/package-template.json +66 -66
- package/package.json +31 -6
- package/public/README.md +16 -0
- package/vite.config.ts +29 -14
- package/.claude/settings.local.json +0 -74
- package/.github/workflows/ci-build-tests.yml +0 -480
- package/.github/workflows/dependency-management.yml +0 -324
- package/.github/workflows/release-validation.yml +0 -355
- package/.kiro/specs/fluxstack-architecture-optimization/design.md +0 -700
- package/.kiro/specs/fluxstack-architecture-optimization/requirements.md +0 -127
- package/.kiro/specs/fluxstack-architecture-optimization/tasks.md +0 -330
- package/CLAUDE.md +0 -200
- package/Dockerfile +0 -58
- package/Dockerfile.backend +0 -52
- package/Dockerfile.frontend +0 -54
- package/README-Docker.md +0 -85
- package/ai-context/00-QUICK-START.md +0 -86
- package/ai-context/README.md +0 -88
- package/ai-context/development/eden-treaty-guide.md +0 -362
- package/ai-context/development/patterns.md +0 -382
- package/ai-context/development/plugins-guide.md +0 -572
- package/ai-context/examples/crud-complete.md +0 -626
- package/ai-context/project/architecture.md +0 -399
- package/ai-context/project/overview.md +0 -213
- package/ai-context/recent-changes/eden-treaty-refactor.md +0 -281
- package/ai-context/recent-changes/type-inference-fix.md +0 -223
- package/ai-context/reference/environment-vars.md +0 -384
- package/ai-context/reference/troubleshooting.md +0 -407
- package/app/client/src/components/TestPage.tsx +0 -453
- package/bun.lock +0 -1063
- package/bunfig.toml +0 -16
- package/core/__tests__/integration.test.ts +0 -227
- package/core/build/index.ts +0 -186
- package/core/config/__tests__/config-loader.test.ts +0 -554
- package/core/config/__tests__/config-merger.test.ts +0 -657
- package/core/config/__tests__/env-converter.test.ts +0 -372
- package/core/config/__tests__/env-processor.test.ts +0 -431
- package/core/config/__tests__/env.test.ts +0 -452
- package/core/config/__tests__/integration.test.ts +0 -418
- package/core/config/__tests__/loader.test.ts +0 -331
- package/core/config/__tests__/schema.test.ts +0 -129
- package/core/config/__tests__/validator.test.ts +0 -318
- package/core/framework/__tests__/server.test.ts +0 -233
- package/core/plugins/__tests__/built-in.test.ts.disabled +0 -366
- package/core/plugins/__tests__/manager.test.ts +0 -398
- package/core/plugins/__tests__/monitoring.test.ts +0 -401
- package/core/plugins/__tests__/registry.test.ts +0 -335
- package/core/utils/__tests__/errors.test.ts +0 -139
- package/core/utils/__tests__/helpers.test.ts +0 -297
- package/core/utils/__tests__/logger.test.ts +0 -141
- package/create-test-app.ts +0 -156
- package/docker-compose.microservices.yml +0 -75
- package/docker-compose.simple.yml +0 -57
- package/docker-compose.yml +0 -71
- package/eslint.config.js +0 -23
- package/flux-cli.ts +0 -214
- package/nginx-lb.conf +0 -37
- package/publish.sh +0 -63
- package/run-clean.ts +0 -26
- package/run-env-tests.ts +0 -313
- package/tailwind.config.js +0 -34
- package/tests/__mocks__/api.ts +0 -56
- package/tests/fixtures/users.ts +0 -69
- package/tests/integration/api/users.routes.test.ts +0 -221
- package/tests/setup.ts +0 -29
- package/tests/unit/app/client/App-simple.test.tsx +0 -56
- package/tests/unit/app/client/App.test.tsx.skip +0 -237
- package/tests/unit/app/client/eden-api.test.ts +0 -186
- package/tests/unit/app/client/simple.test.tsx +0 -23
- package/tests/unit/app/controllers/users.controller.test.ts +0 -150
- package/tests/unit/core/create-project.test.ts.skip +0 -95
- package/tests/unit/core/framework.test.ts +0 -144
- package/tests/unit/core/plugins/logger.test.ts.skip +0 -268
- package/tests/unit/core/plugins/vite.test.ts.disabled +0 -188
- package/tests/utils/test-helpers.ts +0 -61
- package/vitest.config.ts +0 -50
- package/workspace.json +0 -6
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { writeFile, mkdir, unlink } from 'fs/promises'
|
|
2
|
+
import { existsSync } from 'fs'
|
|
3
|
+
import { join, extname } from 'path'
|
|
4
|
+
import type {
|
|
5
|
+
ActiveUpload,
|
|
6
|
+
FileUploadStartMessage,
|
|
7
|
+
FileUploadChunkMessage,
|
|
8
|
+
FileUploadCompleteMessage,
|
|
9
|
+
FileUploadProgressResponse,
|
|
10
|
+
FileUploadCompleteResponse
|
|
11
|
+
} from '../../types/types'
|
|
12
|
+
|
|
13
|
+
export class FileUploadManager {
|
|
14
|
+
private activeUploads = new Map<string, ActiveUpload>()
|
|
15
|
+
private readonly maxUploadSize = 50 * 1024 * 1024 // 50MB max
|
|
16
|
+
private readonly chunkTimeout = 30000 // 30 seconds timeout per chunk
|
|
17
|
+
private readonly allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp', 'image/gif']
|
|
18
|
+
|
|
19
|
+
constructor() {
|
|
20
|
+
// Cleanup stale uploads every 5 minutes
|
|
21
|
+
setInterval(() => this.cleanupStaleUploads(), 5 * 60 * 1000)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async startUpload(message: FileUploadStartMessage): Promise<{ success: boolean; error?: string }> {
|
|
25
|
+
try {
|
|
26
|
+
const { uploadId, componentId, filename, fileType, fileSize, chunkSize = 64 * 1024 } = message
|
|
27
|
+
|
|
28
|
+
// Validate file type
|
|
29
|
+
if (!this.allowedTypes.includes(fileType)) {
|
|
30
|
+
throw new Error(`Invalid file type: ${fileType}. Allowed: ${this.allowedTypes.join(', ')}`)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Validate file size
|
|
34
|
+
if (fileSize > this.maxUploadSize) {
|
|
35
|
+
throw new Error(`File too large: ${fileSize} bytes. Max: ${this.maxUploadSize} bytes`)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Check if upload already exists
|
|
39
|
+
if (this.activeUploads.has(uploadId)) {
|
|
40
|
+
throw new Error(`Upload ${uploadId} already in progress`)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Calculate total chunks
|
|
44
|
+
const totalChunks = Math.ceil(fileSize / chunkSize)
|
|
45
|
+
|
|
46
|
+
// Create upload record
|
|
47
|
+
const upload: ActiveUpload = {
|
|
48
|
+
uploadId,
|
|
49
|
+
componentId,
|
|
50
|
+
filename,
|
|
51
|
+
fileType,
|
|
52
|
+
fileSize,
|
|
53
|
+
totalChunks,
|
|
54
|
+
receivedChunks: new Map(),
|
|
55
|
+
startTime: Date.now(),
|
|
56
|
+
lastChunkTime: Date.now()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
this.activeUploads.set(uploadId, upload)
|
|
60
|
+
|
|
61
|
+
console.log('📤 Upload started:', {
|
|
62
|
+
uploadId,
|
|
63
|
+
componentId,
|
|
64
|
+
filename,
|
|
65
|
+
fileType,
|
|
66
|
+
fileSize,
|
|
67
|
+
totalChunks
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
return { success: true }
|
|
71
|
+
|
|
72
|
+
} catch (error: any) {
|
|
73
|
+
console.error('❌ Upload start failed:', error.message)
|
|
74
|
+
return { success: false, error: error.message }
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async receiveChunk(message: FileUploadChunkMessage, ws: any): Promise<FileUploadProgressResponse | null> {
|
|
79
|
+
try {
|
|
80
|
+
const { uploadId, chunkIndex, totalChunks, data } = message
|
|
81
|
+
|
|
82
|
+
const upload = this.activeUploads.get(uploadId)
|
|
83
|
+
if (!upload) {
|
|
84
|
+
throw new Error(`Upload ${uploadId} not found`)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Validate chunk index
|
|
88
|
+
if (chunkIndex < 0 || chunkIndex >= totalChunks) {
|
|
89
|
+
throw new Error(`Invalid chunk index: ${chunkIndex}`)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Check if chunk already received
|
|
93
|
+
if (upload.receivedChunks.has(chunkIndex)) {
|
|
94
|
+
console.log(`📦 Chunk ${chunkIndex} already received for upload ${uploadId}`)
|
|
95
|
+
} else {
|
|
96
|
+
// Store chunk data
|
|
97
|
+
upload.receivedChunks.set(chunkIndex, data)
|
|
98
|
+
upload.lastChunkTime = Date.now()
|
|
99
|
+
|
|
100
|
+
console.log(`📦 Received chunk ${chunkIndex + 1}/${totalChunks} for upload ${uploadId}`)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Calculate progress
|
|
104
|
+
const progress = (upload.receivedChunks.size / totalChunks) * 100
|
|
105
|
+
const bytesUploaded = upload.receivedChunks.size * (upload.fileSize / totalChunks)
|
|
106
|
+
|
|
107
|
+
// Check if upload is complete
|
|
108
|
+
if (upload.receivedChunks.size === totalChunks) {
|
|
109
|
+
await this.finalizeUpload(upload)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
type: 'FILE_UPLOAD_PROGRESS',
|
|
114
|
+
componentId: upload.componentId,
|
|
115
|
+
uploadId: upload.uploadId,
|
|
116
|
+
chunkIndex,
|
|
117
|
+
totalChunks,
|
|
118
|
+
bytesUploaded: Math.min(bytesUploaded, upload.fileSize),
|
|
119
|
+
totalBytes: upload.fileSize,
|
|
120
|
+
progress: Math.min(progress, 100),
|
|
121
|
+
timestamp: Date.now()
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
} catch (error: any) {
|
|
125
|
+
console.error(`❌ Chunk receive failed for upload ${message.uploadId}:`, error.message)
|
|
126
|
+
throw error
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private async finalizeUpload(upload: ActiveUpload): Promise<void> {
|
|
131
|
+
try {
|
|
132
|
+
console.log(`✅ Upload completed: ${upload.uploadId}`)
|
|
133
|
+
|
|
134
|
+
// Assemble file from chunks
|
|
135
|
+
const fileUrl = await this.assembleFile(upload)
|
|
136
|
+
|
|
137
|
+
// Cleanup
|
|
138
|
+
this.activeUploads.delete(upload.uploadId)
|
|
139
|
+
|
|
140
|
+
} catch (error: any) {
|
|
141
|
+
console.error(`❌ Upload finalization failed for ${upload.uploadId}:`, error.message)
|
|
142
|
+
throw error
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async completeUpload(message: FileUploadCompleteMessage): Promise<FileUploadCompleteResponse> {
|
|
147
|
+
try {
|
|
148
|
+
const { uploadId } = message
|
|
149
|
+
|
|
150
|
+
const upload = this.activeUploads.get(uploadId)
|
|
151
|
+
if (!upload) {
|
|
152
|
+
throw new Error(`Upload ${uploadId} not found`)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
console.log(`✅ Upload completed: ${uploadId}`)
|
|
156
|
+
|
|
157
|
+
// Check for missing chunks
|
|
158
|
+
const missingChunks: number[] = []
|
|
159
|
+
for (let i = 0; i < upload.totalChunks; i++) {
|
|
160
|
+
if (!upload.receivedChunks.has(i)) {
|
|
161
|
+
missingChunks.push(i)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (missingChunks.length > 0) {
|
|
166
|
+
throw new Error(`Missing chunks: ${missingChunks.join(', ')}`)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Assemble file from chunks
|
|
170
|
+
const fileUrl = await this.assembleFile(upload)
|
|
171
|
+
|
|
172
|
+
// Cleanup
|
|
173
|
+
this.activeUploads.delete(uploadId)
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
type: 'FILE_UPLOAD_COMPLETE',
|
|
177
|
+
componentId: upload.componentId,
|
|
178
|
+
uploadId: upload.uploadId,
|
|
179
|
+
success: true,
|
|
180
|
+
filename: upload.filename,
|
|
181
|
+
fileUrl,
|
|
182
|
+
timestamp: Date.now()
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
} catch (error: any) {
|
|
186
|
+
console.error(`❌ Upload completion failed for ${message.uploadId}:`, error.message)
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
type: 'FILE_UPLOAD_COMPLETE',
|
|
190
|
+
componentId: '',
|
|
191
|
+
uploadId: message.uploadId,
|
|
192
|
+
success: false,
|
|
193
|
+
error: error.message,
|
|
194
|
+
timestamp: Date.now()
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private async assembleFile(upload: ActiveUpload): Promise<string> {
|
|
200
|
+
try {
|
|
201
|
+
// Create uploads directory if it doesn't exist
|
|
202
|
+
const uploadsDir = './uploads'
|
|
203
|
+
if (!existsSync(uploadsDir)) {
|
|
204
|
+
await mkdir(uploadsDir, { recursive: true })
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Generate unique filename
|
|
208
|
+
const timestamp = Date.now()
|
|
209
|
+
const extension = extname(upload.filename)
|
|
210
|
+
const baseName = upload.filename.replace(extension, '')
|
|
211
|
+
const safeFilename = `${baseName}_${timestamp}${extension}`
|
|
212
|
+
const filePath = join(uploadsDir, safeFilename)
|
|
213
|
+
|
|
214
|
+
// Assemble chunks in order
|
|
215
|
+
const chunks: Buffer[] = []
|
|
216
|
+
for (let i = 0; i < upload.totalChunks; i++) {
|
|
217
|
+
const chunkData = upload.receivedChunks.get(i)
|
|
218
|
+
if (chunkData) {
|
|
219
|
+
chunks.push(Buffer.from(chunkData, 'base64'))
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Write assembled file
|
|
224
|
+
const fileBuffer = Buffer.concat(chunks)
|
|
225
|
+
await writeFile(filePath, fileBuffer)
|
|
226
|
+
|
|
227
|
+
console.log(`📁 File assembled: ${filePath}`)
|
|
228
|
+
return `/uploads/${safeFilename}`
|
|
229
|
+
|
|
230
|
+
} catch (error) {
|
|
231
|
+
console.error('❌ File assembly failed:', error)
|
|
232
|
+
throw error
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
private cleanupStaleUploads(): void {
|
|
237
|
+
const now = Date.now()
|
|
238
|
+
const staleUploads: string[] = []
|
|
239
|
+
|
|
240
|
+
for (const [uploadId, upload] of this.activeUploads) {
|
|
241
|
+
const timeSinceLastChunk = now - upload.lastChunkTime
|
|
242
|
+
|
|
243
|
+
if (timeSinceLastChunk > this.chunkTimeout * 2) {
|
|
244
|
+
staleUploads.push(uploadId)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
for (const uploadId of staleUploads) {
|
|
249
|
+
this.activeUploads.delete(uploadId)
|
|
250
|
+
console.log(`🧹 Cleaned up stale upload: ${uploadId}`)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (staleUploads.length > 0) {
|
|
254
|
+
console.log(`🧹 Cleaned up ${staleUploads.length} stale uploads`)
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
getUploadStatus(uploadId: string): ActiveUpload | null {
|
|
259
|
+
return this.activeUploads.get(uploadId) || null
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
getStats() {
|
|
263
|
+
return {
|
|
264
|
+
activeUploads: this.activeUploads.size,
|
|
265
|
+
maxUploadSize: this.maxUploadSize,
|
|
266
|
+
allowedTypes: this.allowedTypes
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Global instance
|
|
272
|
+
export const fileUploadManager = new FileUploadManager()
|