feat: Migrate to ultrathin monolith architecture

Consolidate 3 separate applications (reports-app, data-entry-app, telegram-bot) into a unified
architecture with single backend and frontend:

Backend Changes:
- Unified FastAPI backend at backend/ with modular structure
- Modules: reports, data_entry, telegram in backend/modules/
- Centralized config.py and main.py with all routers registered
- Single worker mode (--workers 1) for Telegram bot compatibility
- Shared Oracle connection pool and JWT authentication
- Unified requirements.txt and environment configuration

Frontend Changes:
- Single Vue.js SPA with module-based routing
- Unified frontend at src/ with modules in src/modules/{reports,data-entry}/
- Shared components and stores in src/shared/
- Error boundaries for module isolation
- Dual API proxy in Vite for module communication

Infrastructure:
- New unified startup scripts: start-prod.sh, start-test.sh, start-backend.sh
- Environment templates: .env.dev.example, .env.test.example, .env.prod.example
- Updated deployment scripts for Windows IIS
- Simplified SSH tunnel management

Documentation:
- Comprehensive CLAUDE.md with architecture overview
- Module-specific docs in docs/{data-entry,telegram}/
- Architecture decision records in docs/ARCHITECTURE-DECISIONS.md
- Deployment guides consolidated in deployment/windows/docs/

This migration reduces complexity, improves maintainability, and enables easier
deployment while maintaining all existing functionality.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-29 23:48:14 +02:00
parent 2a101f1ef5
commit c5e051ad80
378 changed files with 7566 additions and 73730 deletions

View File

@@ -19,7 +19,6 @@
rounded
size="small"
@click="$emit('collapse')"
v-tooltip="'Minimizeaza'"
class="collapse-btn"
/>
</div>

View File

@@ -29,21 +29,6 @@
<i class="pi pi-check-circle" style="font-size: 1.75rem; color: #22c55e;"></i>
<p class="file-name">{{ selectedFile.name }}</p>
<p class="file-size">{{ formatFileSize(selectedFile.size) }}</p>
<div class="file-actions">
<Button
label="Schimba"
icon="pi pi-refresh"
severity="secondary"
size="small"
@click.stop="triggerFileInput"
/>
<Button
label="Proceseaza OCR"
icon="pi pi-cog"
size="small"
@click.stop="processOCR"
/>
</div>
</div>
<div v-else class="empty-state">
@@ -58,6 +43,23 @@
</div>
</div>
<!-- Action Buttons (below dropzone) -->
<div v-if="selectedFile && !processing" class="action-buttons">
<Button
label="Schimba"
icon="pi pi-refresh"
severity="secondary"
size="small"
@click="triggerFileInput"
/>
<Button
label="Proceseaza OCR"
icon="pi pi-cog"
size="small"
@click="processOCR"
/>
</div>
<!-- OCR Error Message -->
<Message v-if="error" severity="error" :closable="true" @close="error = null">
{{ error }}
@@ -133,8 +135,9 @@ const processOCR = async () => {
const formData = new FormData()
formData.append('file', selectedFile.value)
// Don't set Content-Type header - let browser set it with boundary for multipart/form-data
// The API interceptor will add Authorization header automatically
const response = await api.post('/ocr/extract', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
timeout: 60000, // 60 second timeout for OCR
})
@@ -253,10 +256,12 @@ defineExpose({ reset, processOCR })
margin: 0;
}
.file-actions {
/* Action buttons (below dropzone) */
.action-buttons {
display: flex;
gap: 0.5rem;
margin-top: 0.5rem;
margin-top: 0.75rem;
justify-content: center;
}
/* Processing state */