# Two-Tier IIS Deployment Architecture

## Overview

ROA2WEB uses a **2-tier IIS architecture** for production deployment:

```
Internet
    ↓
Public IIS Server (roa2web.romfast.ro)
    ↓ HTTPS reverse proxy
Internal IIS Server (10.0.20.36)
    ↓ API proxy
Backend Service (localhost:8000)
    ↓
Oracle Database
```

---

## Architecture Components

### Tier 1: Public IIS Server (Edge/Gateway)

**Hostname**: `roa2web.romfast.ro`
**IP Address**: `10.0.20.122`
**Role**: Public-facing reverse proxy
**Location**: DMZ/Public network

**Responsibilities**:
- SSL/TLS termination (HTTPS)
- Reverse proxy to internal server
- Security headers
- Public DNS endpoint

**Configuration** (`web.config` pe serverul 10.0.20.122):

```xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <!-- Redirect root to /roa2web/ -->
        <rule name="Root to ROA2WEB" stopProcessing="true">
          <match url="^$" />
          <action type="Redirect" url="/roa2web/" redirectType="Permanent" />
        </rule>

        <!-- Reverse Proxy to internal server -->
        <rule name="ROA2WEB Reverse Proxy to HTTPS Backend" stopProcessing="true">
          <match url="(.*)" />
          <action type="Rewrite" url="https://10.0.20.36/{R:1}" />
          <serverVariables>
            <set name="HTTP_X_FORWARDED_PROTO" value="https" />
            <set name="HTTP_X_FORWARDED_HOST" value="{HTTP_HOST}" />
            <set name="HTTP_X_REAL_IP" value="{REMOTE_ADDR}" />
          </serverVariables>
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>
```

**Key Features**:
- Root redirect: `https://roa2web.romfast.ro/` → `https://roa2web.romfast.ro/roa2web/`
- All requests proxied to: `https://10.0.20.36/{REQUEST_PATH}`
- Forwards client IP and protocol headers

---

### Tier 2: Internal IIS Server (Application Server)

**IP Address**: `10.0.20.36`
**Role**: Application host + API proxy
**Location**: Internal network

**Responsibilities**:
- Serve Vue.js frontend static files
- Proxy API requests to backend service
- Handle uploads
- IIS sub-application at `/roa2web`

**Configuration** (`web.config` pe serverul 10.0.20.36 - `C:\inetpub\wwwroot\roa2web\web.config`):

```xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <!-- Proxy all API requests to unified backend -->
        <rule name="Proxy Unified API" stopProcessing="true">
          <match url="^roa2web/api/(.*)" />
          <action type="Rewrite" url="http://localhost:8000/api/{R:1}" />
        </rule>

        <!-- Proxy uploads to unified backend -->
        <rule name="Proxy Uploads" stopProcessing="true">
          <match url="^roa2web/uploads/(.*)" />
          <action type="Rewrite" url="http://localhost:8000/uploads/{R:1}" />
        </rule>

        <!-- SPA fallback - all other routes serve index.html -->
        <rule name="SPA Fallback" stopProcessing="true">
          <match url="^roa2web/.*" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
          </conditions>
          <action type="Rewrite" url="/roa2web/index.html" />
        </rule>
      </rules>
    </rewrite>

    <!-- Static content configuration -->
    <staticContent>
      <mimeMap fileExtension=".webmanifest" mimeType="application/manifest+json" />
      <mimeMap fileExtension=".js" mimeType="application/javascript" />
      <mimeMap fileExtension=".json" mimeType="application/json" />
    </staticContent>

    <!-- Client cache for static assets (1 year) -->
    <httpProtocol>
      <customHeaders>
        <add name="Cache-Control" value="public, max-age=31536000" />
      </customHeaders>
    </httpProtocol>
  </system.webServer>
</configuration>
```

**CRITICAL**: The internal server web.config must handle the `/roa2web/` prefix since requests arrive as:
- `https://10.0.20.36/roa2web/api/auth/login` (NOT `/api/auth/login`)

---

### Backend Service (FastAPI)

**Host**: `localhost` (internal server)
**Port**: `8000`
**Type**: Windows Service (NSSM)
**Name**: `ROA2WEB-Backend`

**Configuration** (`.env`):
```env
HOST=127.0.0.1
PORT=8000
ENVIRONMENT=production
```

**Base Path**: `/api` (NOT `/roa2web/api`)

The backend serves:
- `/api/auth/login`
- `/api/companies`
- `/api/calendar`
- etc.

---

## Request Flow Example

### Login Request Flow

1. **Client Browser** → `POST https://roa2web.romfast.ro/roa2web/api/auth/login`

2. **Public IIS** (roa2web.romfast.ro):
   - Receives: `/roa2web/api/auth/login`
   - Proxies to: `https://10.0.20.36/roa2web/api/auth/login`
   - Sets headers: `X-Forwarded-Proto: https`, `X-Forwarded-Host: roa2web.romfast.ro`

3. **Internal IIS** (10.0.20.36):
   - Receives: `/roa2web/api/auth/login`
   - Matches rule: `^roa2web/api/(.*)`
   - Extracts: `auth/login`
   - Proxies to: `http://localhost:8000/api/auth/login`

4. **Backend Service** (localhost:8000):
   - Receives: `/api/auth/login`
   - Processes request
   - Returns response

---

## Frontend Configuration

### Vite Build Configuration (`vite.config.js`)

```javascript
export default defineConfig({
  // Base path for IIS sub-application
  base: process.env.NODE_ENV === 'production' ? '/roa2web/' : '/',

  // Development proxy (NOT used in production)
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:8000',
        changeOrigin: true
      }
    }
  }
})
```

**IMPORTANT**: In production, `base: '/roa2web/'` ensures:
- All asset paths: `/roa2web/assets/...`
- Router base: `/roa2web/`
- API calls: `/roa2web/api/...` (via axios baseURL)

### API Service Configuration (`src/App.vue`)

```javascript
const authApi = axios.create({
  baseURL: import.meta.env.BASE_URL + 'api',  // Results in: '/roa2web/api'
  headers: { 'Content-Type': 'application/json' }
})
```

---

## Common Issues & Troubleshooting

### Issue: 404 on API calls

**Symptoms**:
- Frontend loads correctly
- API calls return 404
- Browser console: `POST https://roa2web.romfast.ro/roa2web/api/auth/login 404`

**Possible Causes**:

1. **Internal server web.config missing `/roa2web/` prefix in match rules**

   ❌ **WRONG**:
   ```xml
   <match url="^api/(.*)" />
   ```

   ✅ **CORRECT**:
   ```xml
   <match url="^roa2web/api/(.*)" />
   ```

2. **Backend service not running**

   Check on internal server (10.0.20.36):
   ```powershell
   Get-Service ROA2WEB-Backend
   Invoke-WebRequest http://localhost:8000/health
   ```

3. **IIS ARR not enabled**

   On internal server (10.0.20.36):
   ```powershell
   # Install ARR
   Install-WindowsFeature -Name Web-ARR

   # Enable proxy
   Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" `
       -Filter "system.webServer/proxy" `
       -Name "enabled" `
       -Value "True"
   ```

4. **IIS sub-application not configured at `/roa2web`**

   The frontend must be deployed as IIS sub-application at path `/roa2web`, NOT as root site.

### Issue: Frontend loads but shows blank page

**Symptoms**:
- Browser shows white screen
- Console error: `Failed to load module script`
- Assets return 404

**Solution**: Check `base` in `vite.config.js` matches IIS sub-application path.

### Issue: CORS errors

**Symptoms**:
- API calls blocked by CORS policy
- Console: `Access-Control-Allow-Origin` error

**Solution**: Backend should see requests as same-origin (via IIS proxy), so CORS shouldn't apply. If you see CORS errors, the proxy is misconfigured.

---

## Deployment Checklist

### Public Server (roa2web.romfast.ro)

- [ ] SSL certificate installed and valid
- [ ] IIS ARR (Application Request Routing) installed
- [ ] web.config configured with reverse proxy to 10.0.20.36
- [ ] Server variables enabled in IIS
- [ ] Firewall allows HTTPS outbound to 10.0.20.36

### Internal Server (10.0.20.36)

- [ ] IIS installed and running
- [ ] IIS ARR installed
- [ ] IIS URL Rewrite module installed
- [ ] Sub-application created at `/roa2web`
- [ ] Frontend files deployed to `C:\inetpub\wwwroot\roa2web\`
- [ ] web.config includes `/roa2web/` prefix in match rules
- [ ] Backend service (ROA2WEB-Backend) running
- [ ] Backend accessible at `http://localhost:8000/health`
- [ ] Firewall allows HTTPS inbound from public server

### Backend Service

- [ ] Windows Service created (NSSM)
- [ ] Service set to auto-start
- [ ] `.env` configured with correct Oracle credentials
- [ ] Logs directory exists and writable
- [ ] Health check returns 200 OK

---

## Testing Procedure

### 1. Test Backend Directly (on 10.0.20.36)

```powershell
# Health check
Invoke-WebRequest http://localhost:8000/health

# API test (without auth)
Invoke-WebRequest http://localhost:8000/api/health
```

### 2. Test Internal IIS Proxy (on 10.0.20.36)

```powershell
# Should proxy to backend
Invoke-WebRequest https://localhost/roa2web/api/health

# Should serve frontend
Invoke-WebRequest https://localhost/roa2web/
```

### 3. Test Public Access (from any client)

```powershell
# Frontend
Invoke-WebRequest https://roa2web.romfast.ro/roa2web/

# API (will fail without auth, but should return 401 not 404)
Invoke-WebRequest https://roa2web.romfast.ro/roa2web/api/health
```

### 4. Test with Playwright (comprehensive)

```bash
# Use Playwright to test full login flow
./start-playwright.sh
```

---

## Monitoring & Logs

### Public Server Logs

```powershell
# IIS logs
Get-Content C:\inetpub\logs\LogFiles\W3SVC*\*.log -Tail 50

# Failed Request Tracing (if enabled)
Get-ChildItem C:\inetpub\logs\FailedReqLogFiles
```

### Internal Server Logs

```powershell
# IIS logs
Get-Content C:\inetpub\logs\LogFiles\W3SVC*\*.log -Tail 50

# Backend service logs
Get-Content C:\inetpub\wwwroot\roa2web\logs\backend-stdout.log -Tail 50 -Wait
Get-Content C:\inetpub\wwwroot\roa2web\logs\backend-stderr.log -Tail 50
```

### Backend Application Logs

```powershell
# Application log
Get-Content C:\inetpub\wwwroot\roa2web\backend\logs\app.log -Tail 100
```

---

## Security Considerations

### SSL/TLS

- Public server handles SSL termination
- Internal communication can use HTTPS (current) or HTTP (simpler)
- Certificate management only needed on public server

### Firewall Rules

**Public Server**:
- Allow inbound: 443 (HTTPS)
- Allow outbound: 443 to 10.0.20.36

**Internal Server**:
- Allow inbound: 443 from public server IP only
- No need to expose port 8000 externally (backend is localhost-only)

### Headers

Public server sets forwarding headers:
- `X-Forwarded-Proto: https` - Original protocol
- `X-Forwarded-Host: roa2web.romfast.ro` - Original hostname
- `X-Real-IP: {CLIENT_IP}` - Client IP address

Backend can use these for logging and security.

---

## Version History

| Version | Date | Changes |
|---------|------|---------|
| 1.0.0 | 2025-12-30 | Initial documentation |

---

*Last Updated: 2025-12-30*
*ROA2WEB Two-Tier IIS Deployment Architecture*
