Complete implementation of multi-server Oracle database support: Backend: - Multi-pool Oracle with lazy loading per server - Email-to-server cache for automatic server discovery - JWT tokens include server_id claim - /auth/check-identity and /auth/check-email endpoints - /auth/my-servers endpoint for listing user's accessible servers - Server switch with password re-authentication Frontend: - New ServerSelector component for header dropdown - Multi-step login flow (identity → server → password) - Server switching from header with password modal - Mobile drawer menu with server selection - Dark mode support for all new components - URL bookmark support with ?server= query param Scripts: - Unified start.sh replacing start-prod.sh/start-test.sh - Unified ssh-tunnel.sh with multi-server support - Updated status.sh for new architecture Tests: - E2E tests for multi-server and single-server login flows - Backend unit tests for all new endpoints - Oracle multi-pool integration tests Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
7.8 KiB
CLAUDE.md
This file provides guidance to Claude Code when working with code in this repository.
Project Overview
ROA2WEB - Modern ERP Application with ultrathin monolith architecture:
- Reports Module (
backend/modules/reports/) - Read-only reports from Oracle - Data Entry Module (
backend/modules/data_entry/) - Data input with approval workflow - Telegram Bot Module (
backend/modules/telegram/) - Telegram bot integration
Main Branch: main | Setup & Commands: See README.md
Module-Specific Instructions
Important
: Read module documentation FIRST before working on a module!
| Module | Documentation | Key Points |
|---|---|---|
| Data Entry | docs/data-entry/DATA-ENTRY-MODULE.md |
SQLModel, SQLite, workflow states |
| Reports | This file + README.md |
Oracle read-only, caching |
| Telegram | docs/telegram/README.md |
Bot commands, formatting |
Quick Reference
Architecture
See docs/ARCHITECTURE-DECISIONS.md for:
- Single worker (
--workers 1) - required for Telegram bot - Ultrathin monolith rationale
- IIS deployment strategy
Starting Services
./start.sh prod # Backend :8000 + Frontend :3000 (PROD)
./start.sh prod stop # Stop all services
./start.sh test # Start in TEST mode
./start.sh test stop # Stop TEST services
./ssh-tunnel.sh # Oracle DB tunnel (for servers with SSH access)
./status.sh # Check services
Playwright Testing
# Pentru testare UI cu Playwright:
./start.sh test # Pornește în mod TEST
./start.sh test stop # Oprește serverele
# Credențiale TEST:
# User: MARIUS M
# Pass: 123
# Firma: MARIUSM AUTO
Database
- Schema:
CONTAFIN_ORACLE - Connection: SSH tunnel required (Linux dev)
- Env:
backend/.env(seebackend/ENV-SETUP.md)
Key Rules (ALWAYS FOLLOW)
Git Commits
- NU da commit automat! Întreabă utilizatorul ÎNAINTE de a face commit
- Arată ce fișiere s-au modificat (
git status) și propune mesajul de commit - Așteaptă confirmarea explicită înainte de
git commit
Frontend Development
Before writing CSS: Read docs/ONBOARDING_CSS.md (5 min)
CSS Design Tokens - OBLIGATORIU ⚠️
- CITEȘTE ÎNTÂI:
docs/DESIGN_TOKENS.mdînainte de a scrie CSS - Folosește DOAR design tokens, NICIODATĂ valori hardcodate:
- Spacing:
var(--space-xs)(4px),var(--space-sm)(8px),var(--space-md)(16px) - Font weight:
var(--font-medium)(500),var(--font-semibold)(600),var(--font-bold)(700) - Colors:
var(--green-50),var(--blue-50),var(--surface-card), etc. - Radius:
var(--radius-sm)(4px),var(--radius-md)(8px)
- Spacing:
- ❌ GREȘIT:
padding: 8px,font-weight: 500,background: #f0fdf4 - ✅ CORECT:
padding: var(--space-sm),font-weight: var(--font-medium),background: var(--green-50)
Alte reguli CSS
- Use shared CSS from
src/assets/css/- NEVER duplicate - Check
docs/CSS_PATTERNS.mdfor existing patterns - Theme toggle: App has 3 modes (auto/light/dark) - test BOTH themes!
Tables:
- Use separate columns (Debit | Credit, not stacked)
- Filter buttons on separate row below filters
- Export ALL data, not just current page
Mobile Development Rules ⚠️
Before writing mobile UI: Read docs/MOBILE_PATTERNS.md
Required Components
All mobile pages MUST use these shared components:
- MobileTopBar: Required for all mobile views (title, actions, back navigation)
- MobileBottomNav: Required for all mobile views (persistent navigation)
- BottomSheet: Required for filters on mobile (NEVER inline filters)
- MobileSelectionFooter: Required when items are selectable (batch actions)
// Import from shared components
import MobileTopBar from '@shared/components/mobile/MobileTopBar.vue'
import MobileBottomNav from '@shared/components/mobile/MobileBottomNav.vue'
import BottomSheet from '@shared/components/mobile/BottomSheet.vue'
import MobileSelectionFooter from '@shared/components/mobile/MobileSelectionFooter.vue'
Layout Rules
- Filters → BottomSheet: Pe mobil, filtrele se pun ÎNTOTDEAUNA în BottomSheet, NU inline
- Selection Actions → Footer: Când sunt elemente selectate, acțiunile apar în MobileSelectionFooter, NU în header
- Touch Targets: Minimum 44x44px pentru toate elementele interactive (butoane, linkuri, checkbox-uri)
- Content Padding: Conținutul trebuie să aibă
padding-top: 56pxșipadding-bottom: 56pxpentru barele fixe
Mobile Code Review Checklist ✓
□ Folosește MobileTopBar cu props corecte (title, showBack/showMenu, actions)
□ Folosește MobileBottomNav pentru navigare
□ Filtrele sunt în BottomSheet (nu inline pe pagină)
□ Selecția afișează MobileSelectionFooter (nu butoane în header)
□ Touch targets ≥ 44x44px
□ Padding pentru bare fixe (top: 56px, bottom: 56px)
□ Testat în dark mode
□ Folosește design tokens (nu valori hardcodate)
□ Nu duplică CSS din componentele mobile
Backend Development
- Place logic in services, not routers
- Use
@cacheddecorator for ALL database queries - Cache schema lookups separately (24h TTL)
Service pattern:
from app.cache.decorators import cached
class YourService:
@staticmethod
@cached(cache_type='your_data', key_params=['company_id', 'username'])
async def get_data(company_id: int, username: str):
# Query Oracle here
Authentication
- JWT structure:
username,user_id,companies[],permissions[],exp,iat,type - Middleware:
shared/auth/middleware.py(auto-injectsrequest.state.user) - Rate limiting: 5 req/5 min for
/auth/*
Documentation Index
| Topic | File | Description |
|---|---|---|
| Setup | README.md |
Commands, testing, deployment |
| Architecture | docs/ARCHITECTURE-DECISIONS.md |
Critical decisions |
| CSS Quick Start | docs/ONBOARDING_CSS.md |
START HERE for frontend |
| CSS Patterns | docs/CSS_PATTERNS.md |
Cards, forms, buttons, tables |
| Design Tokens | docs/DESIGN_TOKENS.md |
Colors, spacing, dark mode |
| Mobile Patterns | docs/MOBILE_PATTERNS.md |
Mobile components, touch UX |
| Data Entry | docs/data-entry/DATA-ENTRY-MODULE.md |
SQLModel, workflow |
| Telegram | docs/telegram/README.md |
Bot development |
| Deployment | deployment/linux/README.md |
Deploy from LXC |
| Troubleshooting | docs/troubleshooting/ |
Debugging guides, memory leaks |
Learned Patterns & Known Issues
Patterns/Gotchas from previous sessions: Auto-loaded from
.claude/rules/auto-build-memory.md
Known Issues & Fixes
Mobile File Upload ERR_NETWORK (Android/iOS)
Problem: File uploads fail with ERR_NETWORK (status 0) on mobile browsers.
Cause: Android/iOS browsers invalidate File object references after accessing properties due to SnapshotState in W3C File API. See Chromium Bug #40703873.
Solution: Clone the file into memory immediately after selection:
const handleFile = async (file) => {
// FIX: Clone file to memory to avoid SnapshotState invalidation
const arrayBuffer = await file.arrayBuffer()
const clonedFile = new File([arrayBuffer], file.name, {
type: file.type,
lastModified: file.lastModified
})
selectedFile.value = clonedFile
}
Applied in: src/modules/data-entry/components/ocr/OCRUploadZone.vue
Security
- Never commit
.envfiles - SSH keys in
ssh-tunnel/secrets/(gitignored) - Rate limiting in auth middleware
Tech Stack
Backend: FastAPI, python-oracledb, JWT, Pydantic Frontend: Vue.js 3, PrimeVue, Pinia, Vite Telegram: python-telegram-bot, SQLite, httpx Infra: Oracle DB, SSH Tunnel, IIS (Windows prod)