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:
@@ -19,7 +19,6 @@
|
||||
rounded
|
||||
size="small"
|
||||
@click="$emit('collapse')"
|
||||
v-tooltip="'Minimizeaza'"
|
||||
class="collapse-btn"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user