feat: multi-Oracle server support with runtime switching

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>
This commit is contained in:
Claude Agent
2026-01-26 22:39:06 +00:00
parent 5f99ee2fd0
commit b137e80b71
102 changed files with 9398 additions and 2787 deletions

View File

@@ -19,7 +19,11 @@
:user="authStore.user"
:companies-store="companyStore"
:period-store="periodStore"
:available-servers="authStore.availableServers"
:current-server-id="authStore.selectedServerId"
:auth-store="authStore"
@logout="handleLogout"
@server-switched="handleServerSwitched"
/>
<!-- US-109: Filter BottomSheet for mobile -->
@@ -425,6 +429,16 @@ const handleLogout = async () => {
router.push('/login');
};
// Handle server switch completed - reload data for new server
const handleServerSwitched = async (newServerId) => {
// Server switch already completed in MobileDrawerMenu modal
// Reload data for the new server
await companyStore.loadCompanies();
if (companyStore.selectedCompany?.id_firma) {
await periodStore.loadPeriods(companyStore.selectedCompany.id_firma);
}
};
// US-501: Mobile TopBar actions (filter, reset, export dropdown)
const mobileTopBarActions = computed(() => [
{