Problem Summary
The WEMS v2 Electron application suffers from severe performance issues at startup, crashes, memory leaks, security vulnerabilities, and numerous code quality problems. This is not production-ready.
🚨 CRITICAL ISSUES (Must Fix)
1. Startup Performance & Crashes
Issue #166 - App takes 2 minutes to launch and crashes
2. SECURITY: postMessage Wildcard Origin
File: src/preload/index.ts:76, 100
window.postMessage({ type: "main-ready" }, "*");
window.postMessage({ type: "orpc-port-ready" }, "*", [port]);
Problem: Using "*" as target origin allows ANY website to capture these messages.
3. CRITICAL Memory Leaks
| Location |
Issue |
src/core/ipc/manager.ts:41-49 |
window.addEventListener never removed |
src/core/ipc/manager.ts:68-96 |
Polling setTimeout never stops |
src/renderer/src/app.tsx:22-38 |
window.addEventListener never removed |
src/main/index.ts:224-226 |
Lock watcher cleanup never called |
4. DATABASE: Column Name Mismatch (CRITICAL)
File: src/core/db/schema/notes.ts:13
isCompleted: integer("isCompleted", { mode: "boolean" })
But migration 0004_notes.sql creates is_completed (snake_case).
5. DATABASE: N+1 Query Problem (CRITICAL)
File: src/core/ipc/database/handlers.ts:1692-1862
getAllDrivingAuthorizationStatuses() makes 1 + (N × 5) queries.
With 100 employees = 501 queries.
🔒 SECURITY ISSUES
| Severity |
File |
Issue |
| CRITICAL |
src/preload/index.ts:76,100 |
postMessage with "*" origin |
| MODERATE |
src/preload/index.ts:58-77 |
ipcRenderer.on listeners never removed |
| HIGH |
src/main/index.ts:121-127 |
serverPort never closed on shutdown |
| MODERATE |
src/main/index.ts:255-258 |
Uncaught exception doesn't graceful shutdown |
🗄️ DATABASE ISSUES
| Severity |
Issue |
Location |
| CRITICAL |
Column mismatch isCompleted vs is_completed |
notes.ts:13 vs 0004_notes.sql |
| HIGH |
trainingProvider nullable mismatch |
online-trainings.ts:16 vs migration |
| HIGH |
N+1 queries (501 queries for 100 employees) |
handlers.ts:1692-1862 |
| HIGH |
createEmployee manual rollback (not atomic) |
handlers.ts:367-379 |
| MEDIUM |
Missing indexes on employee_id FKs |
Multiple schema files |
| MEDIUM |
Missing indexes on deleted_at |
Most soft-delete tables |
⚡ OPTIMISTIC UPDATES ANALYSIS
Summary Table
| Hook |
Optimistic |
Rollback |
Invalidation |
Temp ID |
Status |
| use-employees.ts |
|
|
|
|
|
| useCreateEmployee |
✅ |
✅ |
⚠️ Partial |
Date.now() |
⚠️ |
| useUpdateEmployee |
✅ |
✅ |
⚠️ Partial |
N/A |
⚠️ |
| useDeleteEmployee |
✅ |
✅ |
✅ |
N/A |
✅ |
| use-contracts.ts |
|
|
|
|
|
| useCreateContract |
✅ |
❌ BUG |
⚠️ Partial |
Date.now() |
❌ |
| useUpdateContract |
✅ |
✅ |
⚠️ Partial |
N/A |
⚠️ |
| useDeleteContract |
✅ |
✅ |
⚠️ Partial |
N/A |
⚠️ |
| use-caces.ts |
|
|
|
|
|
| useCreateCaces |
✅ |
✅ |
✅ |
Date.now() |
✅ |
| useUpdateCaces |
✅ |
✅ |
✅ |
N/A |
✅ |
| useDeleteCaces |
✅ |
✅ |
✅ |
N/A |
✅ |
| use-medical-visits.ts |
|
|
|
|
|
| useCreateMedicalVisit |
✅ |
✅ |
✅ |
Date.now() |
✅ |
| useUpdateMedicalVisit |
✅ |
✅ |
⚠️ Partial |
N/A |
⚠️ |
| useDeleteMedicalVisit |
✅ |
✅ |
✅ |
N/A |
✅ |
| use-driving-authorizations.ts |
|
|
|
|
|
| useCreateDrivingAuthorization |
❌ MISSING |
N/A |
N/A |
N/A |
❌ |
| useUpdateDrivingAuthorization |
❌ MISSING |
N/A |
N/A |
N/A |
❌ |
| useDeleteDrivingAuthorization |
✅ |
✅ |
⚠️ Partial |
N/A |
⚠️ |
| use-online-trainings.ts |
|
|
|
|
|
| useCreateOnlineTraining |
❌ MISSING |
N/A |
N/A |
N/A |
❌ |
| useUpdateOnlineTraining |
❌ MISSING |
N/A |
N/A |
N/A |
❌ |
| useDeleteOnlineTraining |
✅ |
✅ |
⚠️ Partial |
N/A |
⚠️ |
| use-positions-worklocations.ts |
|
|
|
|
|
| useCreateDepartment |
✅ |
✅ |
⚠️ Partial |
Date.now() |
⚠️ |
| useUpdateDepartment |
❌ MISSING |
N/A |
N/A |
N/A |
❌ |
| useDeleteDepartment |
✅ |
✅ |
⚠️ Partial |
N/A |
⚠️ |
| useCreateContractType |
✅ |
✅ |
⚠️ Partial |
Date.now() |
⚠️ |
| useUpdateContractType |
❌ MISSING |
N/A |
N/A |
N/A |
❌ |
| useDeleteContractType |
✅ |
✅ |
⚠️ Partial |
N/A |
⚠️ |
| useCreatePosition |
✅ |
✅ |
⚠️ Partial |
Date.now() |
⚠️ |
| useUpdatePosition |
❌ MISSING |
N/A |
N/A |
N/A |
❌ |
| useDeletePosition |
✅ |
⚠️ |
⚠️ Partial |
N/A |
⚠️ |
| useCreateWorkLocation |
✅ |
✅ |
⚠️ Partial |
Date.now() |
⚠️ |
| useUpdateWorkLocation |
❌ MISSING |
N/A |
N/A |
N/A |
❌ |
| useDeleteWorkLocation |
✅ |
✅ |
⚠️ Partial |
N/A |
⚠️ |
| use-notes.ts |
|
|
|
|
|
| useCreateNote |
✅ |
✅ |
⚠️ Partial |
Date.now() |
⚠️ |
| useUpdateNote |
✅ |
✅ |
⚠️ Partial |
N/A |
⚠️ |
| useDeleteNote |
✅ |
✅ |
⚠️ Partial |
N/A |
⚠️ |
| use-agencies.ts |
|
|
|
|
|
| useCreateAgency |
✅ |
✅ |
⚠️ Partial |
Date.now() |
⚠️ |
| useUpdateAgency |
✅ |
✅ |
⚠️ Partial |
N/A |
⚠️ |
| useDeleteAgency |
✅ |
✅ |
⚠️ Partial |
N/A |
⚠️ |
| use-documents.ts |
|
|
|
|
|
| useCreateDocument |
✅ |
✅ |
⚠️ Partial |
temp-${Date.now()} |
⚠️ |
| useUpdateDocument |
✅ |
✅ |
⚠️ Partial |
N/A |
⚠️ |
| useDeleteDocument |
✅ |
✅ |
⚠️ Partial |
N/A |
⚠️ |
❌ CRITICAL BUG: useCreateContract Broken Rollback
File: src/renderer/src/hooks/use-contracts.ts:89-104
// onMutate saves BOTH queries:
const previousContracts = queryClient.getQueryData(queryKeys.contracts.lists());
const previousByEmployee = queryClient.getQueryData(
queryKeys.contracts.byEmployee(newContract.employeeId)
);
// But onError ONLY restores the first:
if (context?.previousContracts) {
queryClient.setQueryData(queryKeys.contracts.lists(), context.previousContracts);
}
// previousByEmployee is NEVER restored!
Impact: If contract creation fails, contracts.byEmployee cache is left in inconsistent state.
❌ MISSING Optimistic Updates (9 mutations)
| Hook |
File |
Lines |
useCreateDrivingAuthorization |
use-driving-authorizations.ts |
29-56 |
useUpdateDrivingAuthorization |
use-driving-authorizations.ts |
59-87 |
useCreateOnlineTraining |
use-online-trainings.ts |
28-58 |
useUpdateOnlineTraining |
use-online-trainings.ts |
60-90 |
useUpdateDepartment |
use-positions-worklocations.ts |
72-89 |
useUpdateContractType |
use-positions-worklocations.ts |
199-216 |
useUpdatePosition |
use-positions-worklocations.ts |
333-349 |
useUpdateWorkLocation |
use-positions-worklocations.ts |
490-508 |
⚠️ Partial Issues
Temp ID Collision Risk
Most create mutations use Date.now() which can collide on rapid creation:
id: Date.now(), // Can collide!
Better: crypto.randomUUID() or temp-${Date.now()}-${Math.random()}.
Missing Toast on Error
useUpdateContract:155 - No error toast
useDeleteContract:203 - No error toast
useDeleteDrivingAuthorization:113 - No description toast
useDeleteOnlineTraining:116 - No description toast
useDeletePosition Uses Wrong Pattern
File: use-positions-worklocations.ts:367-373
Instead of filtering out deleted items, it sets deletedAt:
// WRONG - shows "deleted" items in UI
old.map((p) => p.id === data.id ? { ...p, deletedAt: ... } : p)
Pattern Inconsistency
- Pattern A (Single): Used in contracts, notes, onlineTrainings, drivingAuthorizations
- Pattern B (Map): Used in employees, caces, medicalVisits, agencies, documents, positions
Pattern B is superior - captures ALL matching queries.
Missing Employee-Scoped Invalidation
Create/Delete for employee-tied entities don't invalidate byEmployee queries:
useCreateContract → should invalidate contracts.byEmployee(employeeId)
useCreateCaces → should invalidate caces.byEmployee(employeeId)
useCreateMedicalVisit → should invalidate medicalVisits.byEmployee(employeeId)
useCreateDrivingAuthorization → should invalidate drivingAuthorizations.byEmployee(employeeId)
useCreateOnlineTraining → should invalidate onlineTrainings.byEmployee(employeeId)
📝 CODE QUALITY ISSUES
TypeScript: 80+ Uses of any
| File |
Lines |
Issue |
actions/database.ts |
221, 229, 270, 278 |
data: any parameters |
pages/employees-page.tsx |
37, 108, 112, 238 |
useState<any>, handleEditClick(employee: any) |
pages/employee-detail-page.tsx |
135, 140, 335, 438+ |
Multiple any in callbacks |
React Anti-patterns: 12+ useEffect Missing Dependencies
| File |
Line |
Issue |
contracts-page.tsx |
167-169 |
Empty deps array |
EditEmployeeDialog.tsx |
135-156 |
Missing contract in deps |
app-sidebar.tsx |
97-100 |
getAppVersion not in deps |
useMemo Used as Side Effect (WRONG)
File: src/renderer/src/pages/caces-page.tsx:178-180
// WRONG - useMemo is for computed values, not side effects
useMemo(() => {
setCurrentPage(1);
}, []);
State Management Bug: toggleNoteComplete
File: src/renderer/src/stores/notes-store.ts:37-42
toggleNoteComplete: (id) =>
set((state) => ({
notes: state.notes.map((note) =>
note.id === id ? { ...note, badges: note.badges } : note // BUG: doesn't change isCompleted!
),
})),
🏗️ ARCHITECTURE ISSUES
| File |
Size |
Issue |
handlers.ts |
108KB |
60+ handlers in one file |
database.ts (actions) |
1046 lines |
100+ functions |
| Page components |
1500-2000+ lines |
Should be split |
| All mutation hooks |
Duplicated |
Should extract useOptimisticMutation |
- Double migration on startup
- Hardcoded window dimensions (800x600)
- No error boundary at app level
📊 TOTAL SUMMARY
| Category |
Count |
Critical |
| Memory Leaks |
4 |
🚨 |
| Security Issues |
4 |
🚨 |
| Database Issues |
6 |
🚨 |
| Optimistic Update Bugs |
1 |
🚨 |
| Missing Optimistic Updates |
9 |
❌ |
TypeScript any |
80+ |
⚠️ |
| useEffect Missing Deps |
12+ |
⚠️ |
| Rollback/Invalidation Issues |
20+ |
⚠️ |
Total Issues: 140+
Environment
- Electron: 40
- Node: 25+
- Platform: Windows 11 Enterprise
- Build: electron-builder 26.7 with NSIS installer
Labels: bug, priority:critical, performance, tech-debt, security, database
Problem Summary
The WEMS v2 Electron application suffers from severe performance issues at startup, crashes, memory leaks, security vulnerabilities, and numerous code quality problems. This is not production-ready.
🚨 CRITICAL ISSUES (Must Fix)
1. Startup Performance & Crashes
Issue #166 - App takes 2 minutes to launch and crashes
2. SECURITY: postMessage Wildcard Origin
File:
src/preload/index.ts:76, 100Problem: Using
"*"as target origin allows ANY website to capture these messages.3. CRITICAL Memory Leaks
src/core/ipc/manager.ts:41-49window.addEventListenernever removedsrc/core/ipc/manager.ts:68-96setTimeoutnever stopssrc/renderer/src/app.tsx:22-38window.addEventListenernever removedsrc/main/index.ts:224-2264. DATABASE: Column Name Mismatch (CRITICAL)
File:
src/core/db/schema/notes.ts:13But migration
0004_notes.sqlcreatesis_completed(snake_case).5. DATABASE: N+1 Query Problem (CRITICAL)
File:
src/core/ipc/database/handlers.ts:1692-1862getAllDrivingAuthorizationStatuses()makes 1 + (N × 5) queries.With 100 employees = 501 queries.
🔒 SECURITY ISSUES
src/preload/index.ts:76,100postMessagewith"*"originsrc/preload/index.ts:58-77ipcRenderer.onlisteners never removedsrc/main/index.ts:121-127serverPortnever closed on shutdownsrc/main/index.ts:255-258🗄️ DATABASE ISSUES
isCompletedvsis_completednotes.ts:13vs0004_notes.sqltrainingProvidernullable mismatchonline-trainings.ts:16vs migrationhandlers.ts:1692-1862createEmployeemanual rollback (not atomic)handlers.ts:367-379employee_idFKsdeleted_at⚡ OPTIMISTIC UPDATES ANALYSIS
Summary Table
Date.now()Date.now()Date.now()Date.now()Date.now()Date.now()Date.now()Date.now()Date.now()Date.now()temp-${Date.now()}❌ CRITICAL BUG: useCreateContract Broken Rollback
File:
src/renderer/src/hooks/use-contracts.ts:89-104Impact: If contract creation fails,
contracts.byEmployeecache is left in inconsistent state.❌ MISSING Optimistic Updates (9 mutations)
useCreateDrivingAuthorizationuseUpdateDrivingAuthorizationuseCreateOnlineTraininguseUpdateOnlineTraininguseUpdateDepartmentuseUpdateContractTypeuseUpdatePositionuseUpdateWorkLocationTemp ID Collision Risk
Most create mutations use
Date.now()which can collide on rapid creation:Better:
crypto.randomUUID()ortemp-${Date.now()}-${Math.random()}.Missing Toast on Error
useUpdateContract:155- No error toastuseDeleteContract:203- No error toastuseDeleteDrivingAuthorization:113- No description toastuseDeleteOnlineTraining:116- No description toastuseDeletePosition Uses Wrong Pattern
File:
use-positions-worklocations.ts:367-373Instead of filtering out deleted items, it sets
deletedAt:Pattern Inconsistency
Pattern B is superior - captures ALL matching queries.
Missing Employee-Scoped Invalidation
Create/Delete for employee-tied entities don't invalidate
byEmployeequeries:useCreateContract→ should invalidatecontracts.byEmployee(employeeId)useCreateCaces→ should invalidatecaces.byEmployee(employeeId)useCreateMedicalVisit→ should invalidatemedicalVisits.byEmployee(employeeId)useCreateDrivingAuthorization→ should invalidatedrivingAuthorizations.byEmployee(employeeId)useCreateOnlineTraining→ should invalidateonlineTrainings.byEmployee(employeeId)📝 CODE QUALITY ISSUES
TypeScript: 80+ Uses of
anyactions/database.tsdata: anyparameterspages/employees-page.tsxuseState<any>,handleEditClick(employee: any)pages/employee-detail-page.tsxanyin callbacksReact Anti-patterns: 12+ useEffect Missing Dependencies
contracts-page.tsxEditEmployeeDialog.tsxcontractin depsapp-sidebar.tsxgetAppVersionnot in depsuseMemo Used as Side Effect (WRONG)
File:
src/renderer/src/pages/caces-page.tsx:178-180State Management Bug: toggleNoteComplete
File:
src/renderer/src/stores/notes-store.ts:37-42🏗️ ARCHITECTURE ISSUES
handlers.tsdatabase.ts(actions)useOptimisticMutation📊 TOTAL SUMMARY
anyTotal Issues: 140+
Environment
Labels: bug, priority:critical, performance, tech-debt, security, database