feat(vmis): 租戶自動開通完整流程 + Admin Portal SSO + NC 行事曆訂閱

Backend:
- schedule_tenant: NC 新容器自動 pgsql 安裝 (_nc_db_check 全新容器處理)
- schedule_tenant: NC 初始化加入 Redis + APCu memcache 設定 (修正 OIDC invalid_state)
- schedule_tenant: 新租戶 KC realm 自動設定 accessCodeLifespan=600s (修正 authentication_expired)
- schedule_account: NC Mail 帳號自動設定 (nc_mail_result/nc_mail_done_at)
- schedule_account: NC 台灣國定假日行事曆自動訂閱 (CalDAV MKCALENDAR)
- nextcloud_client: 新增 subscribe_calendar() CalDAV 訂閱方法
- settings: 新增系統設定 API (site_title/version/timezone/SSO/Keycloak)
- models/result: 新增 nc_mail_result, nc_mail_done_at 欄位
- alembic: 遷移 002(system_settings) 003(keycloak_admin) 004(nc_mail_result)

Frontend (Admin Portal):
- 新增完整管理後台 (index/tenants/accounts/servers/schedules/logs/settings/system-status)
- api.js: Keycloak JS Adapter SSO 整合 (PKCE/S256, fallback KC JS 來源, 自動 token 更新)
- index.html: Promise.allSettled 取代 Promise.all,防止單一 API 失敗影響整頁
- 所有頁面加入 try/catch + toast 錯誤處理
- 新增品牌 LOGO 與 favicon

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
VMIS Developer
2026-03-15 15:31:37 +08:00
parent 42d1420f9c
commit 62baadb06f
53 changed files with 5638 additions and 195 deletions

249
docker/radicale/README.md Normal file
View File

@@ -0,0 +1,249 @@
# Radicale CalDAV/CardDAV Server
Virtual MIS 日曆與聯絡人服務後端。
## 服務資訊
- **服務名稱**: Radicale CalDAV/CardDAV Server
- **容器名稱**: vmis-radicale
- **內部埠號**: 5232
- **外部埠號**: 5232 (開發環境)
- **認證方式**: HTTP Header (X-Remote-User) - 由 Keycloak OAuth2 整合
- **資料儲存**: File System (/data/collections)
## 功能
- ✅ CalDAV - 日曆同步 (RFC 4791)
- ✅ CardDAV - 聯絡人同步 (RFC 6352)
- ✅ HTTP Header 認證
- ✅ 多使用者支援
- ✅ Web 管理介面
## 目錄結構
```
radicale/
├── config/
│ └── config # Radicale 配置檔
├── data/ # 資料目錄(自動建立)
│ └── collections/ # 日曆與聯絡人資料
├── docker-compose.yml # Docker Compose 配置
└── README.md # 本文件
```
## 部署步驟
### 1. 建立資料目錄
```bash
mkdir -p data/collections
```
### 2. 設定權限
```bash
# 確保資料目錄權限正確UID=1000, GID=1000
sudo chown -R 1000:1000 data/
chmod -R 750 data/
```
### 3. 啟動服務
```bash
# 啟動
docker-compose up -d
# 查看日誌
docker-compose logs -f
# 停止
docker-compose down
```
### 4. 驗證服務
```bash
# 檢查服務狀態
docker-compose ps
# 測試連線
curl -I http://localhost:5232
# 預期回應: HTTP/1.1 200 OK
```
## 整合架構
### Virtual MIS 整合
```
Virtual MIS Backend
↓ caldav_service.py
Radicale (CalDAV/CardDAV)
↓ File System
/data/collections/
├── {username}/
│ ├── {calendar-uuid}/ # 日曆
│ └── {addressbook-uuid}/ # 通訊錄
```
### 認證流程
```
1. 使用者登入 Keycloak SSO
2. Virtual MIS Backend 取得 username
3. Backend 使用 X-Remote-User Header 呼叫 Radicale
4. Radicale 信任 Header直接存取對應使用者資料
```
## 資料結構
### 日曆資料 (CalDAV)
```
/data/collections/{username}/{calendar-uuid}/
├── event-1.ics
├── event-2.ics
└── ...
```
### 聯絡人資料 (CardDAV)
```
/data/collections/{username}/{addressbook-uuid}/
├── contact-1.vcf
├── contact-2.vcf
└── ...
```
## 客戶端配置
### iOS / macOS
**日曆 (CalDAV)**:
```
伺服器: 10.1.0.254:5232 (開發環境)
使用者名稱: {Keycloak username}
密碼: (由 Backend 處理)
使用 SSL: 否 (開發環境) / 是 (正式環境)
```
**聯絡人 (CardDAV)**:
```
伺服器: 10.1.0.254:5232
使用者名稱: {Keycloak username}
密碼: (由 Backend 處理)
使用 SSL: 否 (開發環境) / 是 (正式環境)
```
### Android (DAVx⁵)
```
基礎 URL: http://10.1.0.254:5232/
使用者名稱: {Keycloak username}
密碼: (由 Backend 處理)
```
### Thunderbird
**日曆**:
```
位置: http://10.1.0.254:5232/{username}/{calendar-uuid}/
```
**聯絡人** (需要 CardBook 擴充套件):
```
URL: http://10.1.0.254:5232/{username}/{addressbook-uuid}/
```
## 監控與維護
### 查看日誌
```bash
# 即時日誌
docker-compose logs -f radicale
# 最近 100 行
docker-compose logs --tail=100 radicale
```
### 備份策略
```bash
#!/bin/bash
# 備份腳本
BACKUP_DIR="/backups/radicale"
DATE=$(date +%Y%m%d_%H%M%S)
# 備份資料
tar -czf ${BACKUP_DIR}/radicale_data_${DATE}.tar.gz \
./data/
# 保留最近 30 天
find ${BACKUP_DIR} -name "radicale_data_*.tar.gz" -mtime +30 -delete
```
### 效能監控
```bash
# 查看儲存空間
du -sh data/
# 查看容器資源使用
docker stats vmis-radicale
```
## 故障排除
### 問題 1: 無法連線
**檢查**:
```bash
# 檢查容器狀態
docker-compose ps
# 檢查埠號占用
netstat -tulpn | grep 5232
# 檢查防火牆
sudo ufw status
```
### 問題 2: 認證失敗
**檢查**:
- X-Remote-User Header 是否正確傳遞
- Radicale 配置檔中 auth.type 是否為 http_x_remote_user
- Backend 呼叫時是否正確設定 Header
### 問題 3: 資料無法寫入
**檢查**:
```bash
# 檢查目錄權限
ls -la data/
# 修正權限
sudo chown -R 1000:1000 data/
chmod -R 750 data/
```
## 安全建議
1. **認證**: 使用 Keycloak SSO 統一認證
2. **網路隔離**: 僅允許 Backend 存取 Radicale
3. **HTTPS**: 正式環境必須使用 HTTPS
4. **備份**: 定期備份 data/ 目錄
5. **權限**: 嚴格控制檔案系統權限
## 參考文件
- [Radicale 官方文件](https://radicale.org/v3.html)
- [CalDAV 協議 (RFC 4791)](https://tools.ietf.org/html/rfc4791)
- [CardDAV 協議 (RFC 6352)](https://tools.ietf.org/html/rfc6352)
## 更新記錄
- **2026-03-02**: 建立 Radicale Docker Compose 配置
- **2026-03-02**: 整合 Virtual MIS Calendar & Contacts

View File

@@ -0,0 +1,34 @@
[server]
# 監聽所有介面
hosts = 0.0.0.0:5232
# 最大連線數
max_connections = 100
# 逾時設定
timeout = 60
[auth]
# 使用 HTTP Header 認證 (由 Keycloak OAuth2 Proxy 提供)
type = http_x_remote_user
[storage]
# 檔案系統儲存
type = multifilesystem
# 資料目錄
filesystem_folder = /data/collections
# Hook for changes (optional - for git versioning)
# hook = git add -A && git commit -m "Changes by %(user)s"
[web]
# 啟用 Web 介面
type = internal
[logging]
# 日誌等級
level = info
# 日誌格式
# debug 模式可設為 debug

View File

@@ -0,0 +1,44 @@
version: '3.8'
services:
radicale:
image: tomsquest/docker-radicale:latest
container_name: vmis-radicale
restart: unless-stopped
# 環境變數
environment:
# 設定時區
- TZ=Asia/Taipei
# UID/GID (與 porsche 使用者相同)
- UID=1000
- GID=1000
# 資料卷
volumes:
# 配置檔
- ./config:/config:ro
# 資料目錄
- ./data:/data
# 埠號
ports:
- "5232:5232"
# 網路
networks:
- vmis-network
# 健康檢查
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5232/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
vmis-network:
external: true