VisionFlow 提供完整的 REST API 套件,支援攝影機管理、物件辨識和通知服務。本文檔詳述增強版 API 端點,包含改進的錯誤處理和安全功能。
| 服務 | 基礎 URL | 用途 | 狀態 |
|---|---|---|---|
| 🌐 Web Service | http://localhost:5000 |
主要 API 服務 | ✅ Active |
| 📹 Camera Controller | http://localhost:8000 |
攝影機控制服務 | ✅ Active |
| 🤖 Object Recognition | Internal Service |
AI 辨識服務 | 🔒 Internal |
所有受保護的端點都需要 JWT 認證。請在請求標頭中包含令牌:
Authorization: Bearer <your-jwt-token>🔑 支援的認證方式:
- 🎯 JWT Bearer Token (主要方式)
- 🔄 Refresh Token (令牌更新)
- 🔐 API Key (服務間通信)
所有 API 錯誤都遵循一致的回應格式:
{
"success": false,
"error": "Authentication",
"message": "Invalid or expired token",
"code": "AUTH_TOKEN_INVALID",
"timestamp": "2024-01-20T17:45:00Z",
"request_id": "req_abc123def456",
"documentation_url": "https://docs.visionflow.com/errors/AUTH_TOKEN_INVALID"
}🏷️ 錯誤類別:
Authentication- 認證相關錯誤Validation- 資料驗證錯誤Permission- 權限不足錯誤Resource- 資源不存在錯誤System- 系統內部錯誤
安全的使用者認證與授權管理
🔑 使用者註冊
創建新的使用者帳戶,支援角色權限分配。
📋 請求參數:
{
"username": "string", // 3-20 字元,唯一
"email": "string", // 有效的電子郵件格式
"password": "string", // 最少 8 字元,需包含數字和字母
"confirm_password": "string", // 確認密碼需一致
"role": "user" // 可選: user, admin, operator
}✅ 成功回應:
{
"success": true,
"data": {
"user_id": 1,
"username": "example_user",
"email": "user@example.com",
"role": "user",
"created_at": "2024-01-20T10:30:00Z",
"profile": {
"status": "active",
"last_login": null
}
},
"message": "使用者註冊成功"
}❌ 錯誤代碼:
| 代碼 | 說明 | HTTP 狀態 |
|---|---|---|
VALIDATION_ERROR |
輸入資料格式錯誤 | 400 |
USER_EXISTS |
使用者名稱或信箱已存在 | 409 |
PASSWORD_MISMATCH |
密碼確認不一致 | 400 |
EMAIL_INVALID |
電子郵件格式無效 | 400 |
PASSWORD_WEAK |
密碼強度不足 | 400 |
🚪 使用者登入
驗證使用者憑證並取得存取令牌。
📋 請求參數:
{
"username": "string", // 使用者名稱或電子郵件
"password": "string", // 使用者密碼
"remember_me": false // 可選: 延長令牌有效期
}✅ 成功回應:
{
"success": true,
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "def123abc456...",
"token_type": "Bearer",
"expires_in": 3600,
"user": {
"id": 1,
"username": "admin",
"email": "admin@visionflow.com",
"role": "admin",
"permissions": ["camera:read", "camera:write", "user:manage"]
}
},
"message": "登入成功"
}❌ 錯誤代碼:
| 代碼 | 說明 | HTTP 狀態 |
|---|---|---|
INVALID_CREDENTIALS |
使用者名稱或密碼錯誤 | 401 |
ACCOUNT_LOCKED |
帳戶已被鎖定 | 423 |
ACCOUNT_DISABLED |
帳戶已停用 | 403 |
TOO_MANY_ATTEMPTS |
登入嘗試次數過多 | 429 |
🔄 令牌管理
使用 Refresh Token 取得新的存取令牌。
📋 請求參數:
{
"refresh_token": "string"
}登出並撤銷令牌。
🔒 Headers: Authorization: Bearer <token>
✅ 成功回應:
{
"success": true,
"message": "成功登出"
}取得當前使用者資料。
🔒 Headers: Authorization: Bearer <token>
✅ 成功回應:
{
"success": true,
"data": {
"id": 1,
"username": "admin",
"email": "admin@visionflow.com",
"role": "admin",
"created_at": "2024-01-01T00:00:00Z",
"last_login": "2024-01-20T10:30:00Z",
"settings": {
"language": "zh-TW",
"timezone": "Asia/Taipei",
"notifications": true
}
}
}Authenticate and receive access token.
Request Body:
{
"username": "string",
"password": "string"
}Response:
{
"success": true,
"data": {
"access_token": "jwt-token-here",
"user": {
"id": 1,
"username": "example_user",
"email": "user@example.com"
}
},
"expires_in": 3600
}Error Codes:
INVALID_CREDENTIALS: Wrong username or passwordACCOUNT_DISABLED: User account is disabled
Logout and invalidate token.
Headers: Authorization: Bearer <token>
Response:
{
"success": true,
"message": "Logged out successfully"
}Get all camera status information.
Headers: Authorization: Bearer <token>
Response:
{
"success": true,
"data": {
"camera_1": {
"status": "active",
"fps": 25.5,
"last_frame": "2024-01-01T00:00:00Z",
"resolution": "1920x1080"
}
},
"count": 1
}Get list of configured cameras.
Headers: Authorization: Bearer <token>
Query Parameters:
active_only(boolean): Only return active cameraspage(integer): Page number for paginationlimit(integer): Items per page
Response:
{
"success": true,
"data": [
{
"id": 1,
"name": "Front Door Camera",
"url": "rtsp://camera1.local/stream",
"status": "active",
"created_at": "2024-01-01T00:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 1,
"pages": 1
}
}完整的攝影機控制與監控系統
📊 攝影機狀態查詢
取得所有攝影機的即時狀態資訊。
🔒 Headers: Authorization: Bearer <token>
🔍 查詢參數:
| 參數 | 類型 | 必填 | 說明 |
|---|---|---|---|
active_only |
boolean | ❌ | 僅顯示啟用的攝影機 |
include_stats |
boolean | ❌ | 包含詳細統計資料 |
✅ 成功回應:
{
"success": true,
"data": [
{
"id": 1,
"name": "前門攝影機",
"url": "rtsp://camera1.local/stream",
"status": "active",
"health": {
"fps": 25.5,
"resolution": "1920x1080",
"latency": 120,
"quality": "excellent"
},
"statistics": {
"uptime": "99.8%",
"total_detections": 1247,
"last_detection": "2024-01-20T14:30:00Z"
},
"settings": {
"auto_recording": true,
"motion_detection": true,
"audio_enabled": false
}
}
],
"summary": {
"total_cameras": 4,
"active_cameras": 3,
"total_uptime": "99.2%"
}
}➕ 新增攝影機
新增攝影機配置到系統中。
🔒 Headers: Authorization: Bearer <token>
📋 請求參數:
{
"name": "string", // 攝影機名稱(必填)
"url": "string", // RTSP/HTTP 串流 URL(必填)
"description": "string", // 攝影機描述(可選)
"location": { // 位置資訊(可選)
"building": "主辦公大樓",
"floor": "1F",
"area": "前門入口"
},
"settings": {
"resolution": "1920x1080", // 解析度設定
"fps": 30, // 影格率
"codec": "h264", // 編碼格式
"quality": "high", // 畫質設定
"night_vision": true // 夜視模式
},
"detection_config": {
"enabled": true,
"sensitivity": 0.7,
"classes": ["person", "vehicle"],
"alert_zones": []
}
}✅ 成功回應:
{
"success": true,
"data": {
"id": 5,
"name": "後門攝影機",
"url": "rtsp://camera5.local/stream",
"status": "connecting",
"created_at": "2024-01-20T15:00:00Z",
"test_result": {
"connection": "success",
"stream_quality": "good",
"estimated_bandwidth": "2.5 Mbps"
}
},
"message": "攝影機新增成功,正在建立連線"
}❌ 錯誤代碼:
| 代碼 | 說明 | HTTP 狀態 |
|---|---|---|
INVALID_URL |
攝影機 URL 格式錯誤 | 400 |
DUPLICATE_CAMERA |
此 URL 的攝影機已存在 | 409 |
CONNECTION_FAILED |
無法連線到攝影機 | 422 |
UNSUPPORTED_CODEC |
不支援的影像編碼 | 400 |
BANDWIDTH_EXCEEDED |
頻寬使用超過限制 | 507 |
✏️ 更新攝影機設定
更新指定攝影機的配置設定。
🔒 Headers: Authorization: Bearer <token>
📋 請求參數:
{
"name": "string", // 攝影機名稱(可選)
"url": "string", // 串流 URL(可選)
"description": "string", // 描述(可選)
"settings": {
"resolution": "1920x1080",
"fps": 25,
"quality": "medium"
},
"detection_config": {
"enabled": true,
"sensitivity": 0.8,
"classes": ["person", "vehicle", "package"]
}
}✅ 成功回應:
{
"success": true,
"data": {
"id": 1,
"name": "前門攝影機(已更新)",
"changes_applied": [
"resolution updated to 1920x1080",
"detection sensitivity increased to 0.8",
"added package detection class"
],
"restart_required": false
},
"message": "攝影機設定更新成功"
}🗑️ 移除攝影機
從系統中移除攝影機配置。
🔒 Headers: Authorization: Bearer <token>
🔍 查詢參數:
| 參數 | 類型 | 必填 | 說明 |
|---|---|---|---|
force |
boolean | ❌ | 強制刪除(即使攝影機正在錄影) |
backup_data |
boolean | ❌ | 在刪除前備份歷史資料 |
✅ 成功回應:
{
"success": true,
"data": {
"deleted_camera_id": 3,
"backup_created": true,
"backup_location": "/backups/camera_3_2024-01-20.zip",
"cleanup_completed": true
},
"message": "攝影機已成功移除"
}📸 即時快照與串流
取得攝影機的最新快照圖像。
🔒 Headers: Authorization: Bearer <token>
🔍 查詢參數:
| 參數 | 類型 | 必填 | 說明 |
|---|---|---|---|
quality |
string | ❌ | 圖像品質:low, medium, high |
format |
string | ❌ | 圖像格式:jpeg, png |
width |
integer | ❌ | 圖像寬度(像素) |
height |
integer | ❌ | 圖像高度(像素) |
✅ 成功回應:
- Content-Type:
image/jpeg或image/png - X-Frame-Timestamp:
2024-01-20T15:30:00Z - X-Camera-Status:
active
取得攝影機的即時影像串流。
🔒 Headers: Authorization: Bearer <token>
🔍 查詢參數:
| 參數 | 類型 | 必填 | 說明 |
|---|---|---|---|
format |
string | ❌ | 串流格式:mjpeg, webm, hls |
quality |
string | ❌ | 串流品質:low, medium, high |
✅ 成功回應:
- Content-Type:
multipart/x-mixed-replace(MJPEG) - X-Stream-Info: 包含串流的詳細資訊
❌ 錯誤代碼:
| 代碼 | 說明 | HTTP 狀態 |
|---|---|---|
CAMERA_NOT_FOUND |
攝影機 ID 不存在 | 404 |
NO_FRAME_AVAILABLE |
無可用的影像框架 | 503 |
CAMERA_OFFLINE |
攝影機離線中 | 503 |
STREAM_LIMIT_EXCEEDED |
串流連線數超過限制 | 429 |
智慧偵測區域配置與多邊形管理
📍 查詢偵測區域
取得指定攝影機的所有偵測區域設定。
🔒 Headers: Authorization: Bearer <token>
🔍 查詢參數:
| 參數 | 類型 | 必填 | 說明 |
|---|---|---|---|
active_only |
boolean | ❌ | 僅顯示啟用的偵測區域 |
include_stats |
boolean | ❌ | 包含偵測統計資料 |
✅ 成功回應:
{
"success": true,
"data": [
{
"id": 1,
"name": "主要入口區域",
"description": "監控主要出入口的人員活動",
"type": "polygon",
"points": [
{"x": 100, "y": 100},
{"x": 300, "y": 100},
{"x": 300, "y": 250},
{"x": 100, "y": 250}
],
"properties": {
"active": true,
"sensitivity": 0.8,
"min_object_size": 50,
"detection_classes": ["person", "vehicle"],
"color": "#FF0000",
"line_width": 2
},
"statistics": {
"total_detections": 156,
"last_detection": "2024-01-20T14:45:00Z",
"average_daily_detections": 12.3
},
"schedule": {
"enabled": true,
"time_ranges": [
{
"start": "08:00",
"end": "18:00",
"days": ["monday", "tuesday", "wednesday", "thursday", "friday"]
}
]
}
},
{
"id": 2,
"name": "停車區監控",
"description": "監控停車場車輛進出",
"type": "polygon",
"points": [
{"x": 400, "y": 200},
{"x": 600, "y": 200},
{"x": 600, "y": 400},
{"x": 400, "y": 400}
],
"properties": {
"active": true,
"sensitivity": 0.6,
"detection_classes": ["car", "truck", "motorcycle"],
"color": "#00FF00"
}
}
],
"camera_info": {
"camera_id": 1,
"resolution": "1920x1080",
"total_areas": 2,
"active_areas": 2
}
}➕ 建立偵測區域
為指定攝影機建立新的偵測區域。
🔒 Headers: Authorization: Bearer <token>
📋 請求參數:
{
"name": "string", // 區域名稱(必填)
"description": "string", // 區域描述(可選)
"type": "polygon", // 區域類型:polygon, rectangle, circle
"points": [ // 多邊形座標點(必填)
{"x": 150, "y": 150},
{"x": 250, "y": 150},
{"x": 250, "y": 300},
{"x": 150, "y": 300}
],
"properties": {
"active": true, // 是否啟用偵測
"sensitivity": 0.7, // 偵測靈敏度 (0.0-1.0)
"min_object_size": 30, // 最小物件大小(像素)
"max_object_size": 500, // 最大物件大小(像素)
"detection_classes": [ // 偵測物件類別
"person",
"vehicle",
"package"
],
"visual": {
"color": "#FF6B35", // 區域邊框顏色
"line_width": 3, // 邊框寬度
"fill_opacity": 0.2 // 填充透明度
}
},
"alerts": {
"enabled": true, // 啟用警報
"cooldown": 300, // 警報冷卻時間(秒)
"notification_types": [ // 通知類型
"email",
"line",
"webhook"
]
},
"schedule": { // 偵測時間排程
"enabled": true,
"time_ranges": [
{
"start": "06:00",
"end": "22:00",
"days": ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"]
}
],
"timezone": "Asia/Taipei"
}
}✅ 成功回應:
{
"success": true,
"data": {
"id": 3,
"name": "新建偵測區域",
"camera_id": 1,
"status": "active",
"created_at": "2024-01-20T16:00:00Z",
"validation": {
"geometry_valid": true,
"coverage_percentage": 12.5,
"overlaps_with": []
}
},
"message": "偵測區域建立成功"
}❌ 錯誤代碼:
| 代碼 | 說明 | HTTP 狀態 |
|---|---|---|
INVALID_POLYGON |
多邊形座標格式錯誤 | 400 |
AREA_TOO_SMALL |
偵測區域過小 | 400 |
AREA_TOO_LARGE |
偵測區域過大 | 400 |
OVERLAPPING_AREAS |
與現有區域重疊過多 | 409 |
MAX_AREAS_EXCEEDED |
超過最大區域數量限制 | 429 |
✏️ 更新偵測區域
更新指定偵測區域的設定。
🔒 Headers: Authorization: Bearer <token>
📋 請求參數:
{
"name": "更新的區域名稱",
"properties": {
"active": true,
"sensitivity": 0.9,
"detection_classes": ["person", "bicycle", "package"]
},
"schedule": {
"enabled": true,
"time_ranges": [
{
"start": "07:00",
"end": "19:00",
"days": ["monday", "tuesday", "wednesday", "thursday", "friday"]
}
]
}
}✅ 成功回應:
{
"success": true,
"data": {
"id": 1,
"changes_applied": [
"sensitivity updated to 0.9",
"added bicycle detection class",
"schedule time range updated"
],
"restart_required": false,
"estimated_impact": "improved detection accuracy by 15%"
},
"message": "偵測區域設定更新成功"
}🗑️ 刪除偵測區域
刪除指定的偵測區域配置。
🔒 Headers: Authorization: Bearer <token>
🔍 查詢參數:
| 參數 | 類型 | 必填 | 說明 |
|---|---|---|---|
backup_data |
boolean | ❌ | 在刪除前備份區域資料 |
✅ 成功回應:
{
"success": true,
"data": {
"deleted_area_id": 2,
"backup_created": true,
"affected_schedules": 1,
"cleanup_completed": true
},
"message": "偵測區域已成功刪除"
}📊 區域偵測統計
取得偵測區域的詳細統計資料。
🔒 Headers: Authorization: Bearer <token>
🔍 查詢參數:
| 參數 | 類型 | 必填 | 說明 |
|---|---|---|---|
period |
string | ❌ | 統計週期:hour, day, week, month |
start_date |
string | ❌ | 開始日期 (ISO 8601) |
end_date |
string | ❌ | 結束日期 (ISO 8601) |
✅ 成功回應:
{
"success": true,
"data": {
"area_info": {
"id": 1,
"name": "主要入口區域",
"camera_id": 1
},
"statistics": {
"total_detections": 1247,
"detection_rate": 3.2,
"accuracy": 0.94,
"false_positives": 23,
"object_breakdown": {
"person": 1156,
"vehicle": 68,
"package": 23
},
"hourly_distribution": [
{"hour": 8, "count": 45},
{"hour": 9, "count": 78},
{"hour": 10, "count": 92}
],
"peak_detection_time": "09:30-10:30",
"average_confidence": 0.87
},
"performance": {
"detection_latency": "120ms",
"processing_time": "45ms",
"system_load": "12%"
}
}
}多元化通知管理與訊息分發系統
⚙️ 通知設定管理
取得當前的通知系統設定。
🔒 Headers: Authorization: Bearer <token>
✅ 成功回應:
{
"success": true,
"data": {
"general": {
"enabled": true,
"global_cooldown": 300,
"max_notifications_per_hour": 50,
"priority_override": true
},
"channels": {
"email": {
"enabled": true,
"smtp_configured": true,
"daily_limit": 100,
"template": "professional"
},
"line": {
"enabled": true,
"tokens_configured": 3,
"rate_limit": "30/minute"
},
"slack": {
"enabled": false,
"webhook_configured": false
},
"webhook": {
"enabled": true,
"endpoints": 2,
"retry_attempts": 3
}
},
"notification_schedule": {
"enabled": true,
"quiet_hours": {
"start": "22:00",
"end": "06:00",
"timezone": "Asia/Taipei"
},
"active_days": ["monday", "tuesday", "wednesday", "thursday", "friday"],
"emergency_override": true
},
"filtering": {
"min_confidence": 0.7,
"detection_types": {
"person": true,
"vehicle": true,
"package": false,
"animal": false
},
"area_specific": true
}
}
}更新通知系統設定。
🔒 Headers: Authorization: Bearer <token>
📋 請求參數:
{
"general": {
"enabled": true,
"global_cooldown": 180,
"max_notifications_per_hour": 75
},
"channels": {
"email": {
"enabled": true,
"template": "minimal",
"include_snapshots": true
},
"line": {
"enabled": true,
"message_format": "detailed"
}
},
"notification_schedule": {
"enabled": true,
"quiet_hours": {
"start": "23:00",
"end": "07:00"
},
"active_days": ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday"]
},
"filtering": {
"min_confidence": 0.8,
"detection_types": {
"person": true,
"vehicle": true,
"package": true
}
}
}✅ 成功回應:
{
"success": true,
"data": {
"updated_settings": [
"global_cooldown reduced to 180 seconds",
"email template changed to minimal",
"added package detection notifications",
"extended active days to include Saturday"
],
"restart_required": false,
"estimated_notification_increase": "15%"
},
"message": "通知設定更新成功"
}📋 通知歷史查詢
查詢通知發送歷史記錄。
🔒 Headers: Authorization: Bearer <token>
🔍 查詢參數:
| 參數 | 類型 | 必填 | 說明 |
|---|---|---|---|
start_date |
string | ❌ | 開始日期 (ISO 8601) |
end_date |
string | ❌ | 結束日期 (ISO 8601) |
type |
string | ❌ | 通知類型:email, line, slack, webhook |
status |
string | ❌ | 發送狀態:sent, failed, pending |
camera_id |
integer | ❌ | 特定攝影機 ID |
page |
integer | ❌ | 頁碼(預設: 1) |
limit |
integer | ❌ | 每頁筆數(預設: 20,最大: 100) |
✅ 成功回應:
{
"success": true,
"data": [
{
"id": 1001,
"type": "object_detected",
"channel": "email",
"camera_id": 1,
"camera_name": "前門攝影機",
"detection_info": {
"object_class": "person",
"confidence": 0.92,
"detection_area": "主要入口區域",
"snapshot_url": "https://api.visionflow.com/snapshots/1001.jpg"
},
"message": "在主要入口區域偵測到人員活動",
"recipients": [
{
"address": "security@company.com",
"status": "delivered",
"delivered_at": "2024-01-20T14:32:05Z"
}
],
"sent_at": "2024-01-20T14:32:00Z",
"status": "delivered",
"delivery_time": "4.2s",
"metadata": {
"priority": "normal",
"retry_count": 0,
"template_used": "detection_alert"
}
},
{
"id": 1002,
"type": "system_alert",
"channel": "line",
"message": "攝影機 3 連線中斷",
"status": "failed",
"error": "LINE token expired",
"sent_at": "2024-01-20T14:25:00Z",
"retry_scheduled": "2024-01-20T14:35:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 156,
"pages": 8,
"has_next": true,
"has_prev": false
},
"statistics": {
"total_notifications": 156,
"delivery_rate": 94.2,
"average_delivery_time": "3.8s",
"channel_breakdown": {
"email": 89,
"line": 45,
"webhook": 22
}
}
}📧 電子郵件收件人管理
取得電子郵件收件人清單。
🔒 Headers: Authorization: Bearer <token>
🔍 查詢參數:
| 參數 | 類型 | 必填 | 說明 |
|---|---|---|---|
active_only |
boolean | ❌ | 僅顯示啟用的收件人 |
group |
string | ❌ | 依群組篩選 |
✅ 成功回應:
{
"success": true,
"data": [
{
"id": 1,
"email": "security@company.com",
"name": "安全管理員",
"active": true,
"group": "security",
"notification_types": ["detection", "system_alert", "maintenance"],
"schedule": {
"enabled": true,
"time_ranges": [
{
"start": "08:00",
"end": "18:00",
"days": ["monday", "tuesday", "wednesday", "thursday", "friday"]
}
]
},
"statistics": {
"total_sent": 245,
"delivery_rate": 98.4,
"last_sent": "2024-01-20T14:30:00Z"
},
"created_at": "2024-01-01T00:00:00Z"
}
],
"summary": {
"total_recipients": 8,
"active_recipients": 6,
"groups": ["security", "management", "it"]
}
}新增電子郵件收件人。
🔒 Headers: Authorization: Bearer <token>
📋 請求參數:
{
"email": "manager@company.com", // 電子郵件地址(必填)
"name": "部門主管", // 收件人姓名(必填)
"group": "management", // 群組分類(可選)
"active": true, // 是否啟用(預設: true)
"notification_types": [ // 接收的通知類型
"detection",
"system_alert"
],
"schedule": { // 接收時間排程
"enabled": true,
"time_ranges": [
{
"start": "09:00",
"end": "17:00",
"days": ["monday", "tuesday", "wednesday", "thursday", "friday"]
}
],
"timezone": "Asia/Taipei"
},
"preferences": {
"include_snapshots": true,
"digest_mode": false,
"priority_only": false
}
}更新收件人設定。
刪除收件人。
📱 LINE 通知管理
取得 LINE Notify 令牌清單。
🔒 Headers: Authorization: Bearer <token>
✅ 成功回應:
{
"success": true,
"data": [
{
"id": 1,
"name": "安全群組",
"token": "***************abc123", // 部分遮蔽的令牌
"description": "主要安全人員通知群組",
"active": true,
"target_type": "group",
"last_used": "2024-01-20T14:30:00Z",
"statistics": {
"total_sent": 89,
"success_rate": 100,
"last_error": null
},
"created_at": "2024-01-01T00:00:00Z"
}
],
"summary": {
"total_tokens": 3,
"active_tokens": 3,
"daily_usage": 23
}
}新增 LINE Notify 令牌。
🔒 Headers: Authorization: Bearer <token>
📋 請求參數:
{
"token": "abcdef123456789...", // LINE Notify 令牌(必填)
"name": "IT 部門群組", // 令牌名稱(必填)
"description": "IT 部門系統通知", // 描述(可選)
"target_type": "group", // 目標類型:user, group
"notification_types": [ // 接收的通知類型
"system_alert",
"maintenance",
"detection"
],
"schedule": {
"enabled": true,
"quiet_hours": {
"start": "22:00",
"end": "08:00"
}
}
}✅ 成功回應:
{
"success": true,
"data": {
"id": 4,
"name": "IT 部門群組",
"token_preview": "***************789",
"validation": {
"token_valid": true,
"target_accessible": true,
"rate_limit": "1000/hour"
},
"test_message_sent": true
},
"message": "LINE 令牌新增成功,測試訊息已發送"
}更新 LINE 令牌設定。
刪除 LINE 令牌。
系統健康狀態監控與效能診斷
💊 基礎健康檢查
基礎系統健康檢查端點(無需認證)。
🔓 無需認證
✅ 成功回應:
{
"success": true,
"data": {
"status": "healthy",
"timestamp": "2024-01-20T16:45:00Z",
"uptime": "7 days, 14 hours, 23 minutes",
"version": "2.1.0",
"environment": "production",
"services": {
"database": {
"status": "healthy",
"response_time": "12ms",
"connections": "5/50"
},
"redis": {
"status": "healthy",
"response_time": "3ms",
"memory_usage": "245MB"
},
"camera_controller": {
"status": "healthy",
"active_streams": 4,
"processing_load": "23%"
},
"object_recognition": {
"status": "healthy",
"model_loaded": true,
"gpu_usage": "67%",
"queue_size": 0
},
"notification_service": {
"status": "healthy",
"pending_notifications": 0,
"delivery_rate": "98.5%"
}
},
"quick_stats": {
"total_cameras": 4,
"active_cameras": 4,
"detections_today": 147,
"system_load": "low"
}
}
}{
"success": true,
"data": {
"status": "degraded",
"timestamp": "2024-01-20T16:45:00Z",
"services": {
"database": {
"status": "healthy",
"response_time": "15ms"
},
"redis": {
"status": "warning",
"response_time": "45ms",
"issue": "high memory usage",
"memory_usage": "890MB"
},
"camera_controller": {
"status": "error",
"error": "Camera 3 connection lost",
"active_streams": 3,
"failed_streams": 1
}
},
"alerts": [
{
"level": "warning",
"service": "redis",
"message": "Memory usage approaching limit"
},
{
"level": "error",
"service": "camera_controller",
"message": "Camera 3 offline for 5 minutes"
}
]
}
}🔬 詳細健康檢查
取得系統的詳細健康狀態資訊。
🔒 Headers: Authorization: Bearer <token>
🔍 查詢參數:
| 參數 | 類型 | 必填 | 說明 |
|---|---|---|---|
include_metrics |
boolean | ❌ | 包含效能指標 |
include_logs |
boolean | ❌ | 包含最近錯誤日誌 |
✅ 成功回應:
{
"success": true,
"data": {
"system": {
"status": "healthy",
"uptime": 604823,
"boot_time": "2024-01-13T02:22:00Z",
"load_average": [0.23, 0.25, 0.28],
"cpu_count": 8,
"architecture": "x86_64"
},
"resources": {
"memory": {
"total": "16GB",
"used": "8.2GB",
"free": "7.8GB",
"percentage": 51.2,
"swap_used": "0MB"
},
"disk": {
"total": "500GB",
"used": "125GB",
"free": "375GB",
"percentage": 25.0,
"mount_point": "/"
},
"network": {
"interfaces": {
"eth0": {
"status": "up",
"ip": "192.168.1.100",
"rx_bytes": "2.3TB",
"tx_bytes": "1.8TB"
}
},
"active_connections": 28
}
},
"services_detailed": {
"web_server": {
"status": "healthy",
"process_id": 1234,
"memory_usage": "256MB",
"cpu_usage": "12%",
"active_requests": 3,
"total_requests": 89247,
"average_response_time": "45ms"
},
"database": {
"status": "healthy",
"version": "PostgreSQL 14.9",
"connections": {
"active": 5,
"idle": 15,
"max": 50
},
"performance": {
"queries_per_second": 23.5,
"slow_queries": 0,
"cache_hit_ratio": 0.94
},
"storage": {
"size": "2.3GB",
"tables": 15,
"indexes": 28
}
},
"redis": {
"status": "healthy",
"version": "7.0.5",
"memory_usage": "245MB",
"memory_peak": "312MB",
"keys": 1247,
"operations_per_second": 156,
"keyspace_hits": 98.7
},
"ai_service": {
"status": "healthy",
"model_info": {
"name": "YOLOv8n",
"version": "8.0.196",
"loaded_at": "2024-01-20T08:30:00Z"
},
"gpu": {
"device": "NVIDIA RTX 4090",
"memory_used": "6.2GB",
"memory_total": "24GB",
"utilization": 67,
"temperature": "72°C"
},
"processing": {
"frames_processed": 234567,
"average_fps": 28.5,
"queue_size": 0,
"inference_time": "34ms"
}
}
},
"cameras": {
"total": 4,
"active": 4,
"status_breakdown": {
"healthy": 3,
"warning": 1,
"error": 0
},
"performance": {
"total_fps": 98.5,
"average_latency": "125ms",
"dropped_frames": 0.02
}
},
"recent_errors": [
{
"timestamp": "2024-01-20T15:30:00Z",
"level": "warning",
"service": "camera_controller",
"message": "Camera 2 frame rate dropped to 20 FPS",
"resolved": true
}
],
"alerts": {
"active": 0,
"last_24h": 3,
"severity_breakdown": {
"critical": 0,
"warning": 0,
"info": 0
}
}
}
}📊 系統效能指標
取得系統效能指標和統計資料。
🔒 Headers: Authorization: Bearer <token>
🔍 查詢參數:
| 參數 | 類型 | 必填 | 說明 |
|---|---|---|---|
period |
string | ❌ | 統計週期:1h, 6h, 24h, 7d |
format |
string | ❌ | 回應格式:json, prometheus |
✅ 成功回應:
{
"success": true,
"data": {
"collection_time": "2024-01-20T16:45:00Z",
"period": "24h",
"system_metrics": {
"cpu": {
"average_usage": 23.5,
"peak_usage": 67.2,
"cores": [
{"core": 0, "usage": 25.3},
{"core": 1, "usage": 21.7}
]
},
"memory": {
"average_usage": 52.1,
"peak_usage": 78.9,
"swap_usage": 0.0
},
"disk": {
"read_iops": 125.3,
"write_iops": 89.7,
"read_throughput": "15.2 MB/s",
"write_throughput": "8.9 MB/s"
},
"network": {
"rx_bandwidth": "50.2 Mbps",
"tx_bandwidth": "32.1 Mbps",
"packet_loss": 0.01,
"latency": "1.2ms"
}
},
"application_metrics": {
"api": {
"total_requests": 89247,
"requests_per_minute": 156.7,
"average_response_time": "45ms",
"error_rate": 0.02,
"status_codes": {
"200": 87456,
"400": 789,
"401": 234,
"500": 12
}
},
"detections": {
"total_objects": 5678,
"objects_per_hour": 236.6,
"confidence_distribution": {
"high (>0.9)": 4234,
"medium (0.7-0.9)": 1234,
"low (<0.7)": 210
},
"class_breakdown": {
"person": 4567,
"vehicle": 890,
"package": 123,
"animal": 98
}
},
"notifications": {
"total_sent": 234,
"success_rate": 98.3,
"channel_breakdown": {
"email": 145,
"line": 67,
"webhook": 22
},
"average_delivery_time": "3.8s"
}
},
"camera_metrics": [
{
"camera_id": 1,
"name": "前門攝影機",
"fps": 25.2,
"resolution": "1920x1080",
"bitrate": "2.3 Mbps",
"dropped_frames": 0.01,
"latency": "120ms",
"detections": 89,
"uptime": 99.8
}
]
}
}⚡ 即時系統狀態
取得即時系統狀態資訊(適用於監控儀表板)。
🔒 Headers: Authorization: Bearer <token>
✅ 成功回應:
{
"success": true,
"data": {
"timestamp": "2024-01-20T16:45:23Z",
"overall_status": "healthy",
"live_metrics": {
"cpu_usage": 24.7,
"memory_usage": 52.3,
"active_cameras": 4,
"total_fps": 98.2,
"detection_rate": 3.2,
"notification_queue": 0,
"api_response_time": "42ms"
},
"service_status": {
"web_service": "✅ healthy",
"database": "✅ healthy",
"redis": "✅ healthy",
"ai_service": "✅ healthy",
"camera_controller": "⚠️ degraded",
"notification_service": "✅ healthy"
},
"recent_activity": [
{
"time": "16:45:20",
"event": "Person detected",
"camera": "前門攝影機",
"confidence": 0.94
},
{
"time": "16:45:15",
"event": "Email sent",
"recipient": "security@company.com",
"status": "delivered"
}
],
"alerts": [
{
"level": "warning",
"message": "Camera 3 frame rate below threshold",
"since": "16:40:00"
}
]
}
}API 呼叫頻率限制:
| 端點類別 | 限制 | 時間窗口 | 超出處理 |
|---|---|---|---|
| 🔐 認證端點 | 5 requests | per minute | 429 Too Many Requests |
| 📹 攝影機管理 | 30 requests | per minute | Rate limit header 提示 |
| 🎯 偵測區域 | 20 requests | per minute | 自動延遲重試 |
| 🔔 通知端點 | 60 requests | per minute | 佇列處理 |
| 📸 串流端點 | 無限制 | - | 頻寬管理 |
| 🏥 健康檢查 | 100 requests | per minute | 無限制 |
{
"success": false,
"error": "RateLimit",
"message": "API rate limit exceeded",
"code": "RATE_LIMIT_EXCEEDED",
"details": {
"limit": 30,
"period": "minute",
"reset_at": "2024-01-20T17:01:00Z",
"retry_after": 15
}
}即時雙向通訊與事件推送系統
🔌 WebSocket 連線
使用 JWT 令牌建立 WebSocket 連線以接收即時事件。
連線 URL:
ws://localhost:5000/ws?token=<your-jwt-token>
JavaScript 範例:
const token = 'your-jwt-token-here';
const ws = new WebSocket(`ws://localhost:5000/ws?token=${token}`);
ws.onopen = function(event) {
console.log('✅ WebSocket 連線已建立');
// 發送訂閱請求
ws.send(JSON.stringify({
action: 'subscribe',
events: ['camera_status', 'object_detection', 'system_alerts']
}));
};
ws.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log('📨 收到事件:', data);
handleRealtimeEvent(data);
};
ws.onclose = function(event) {
console.log('❌ WebSocket 連線已關閉:', event.code, event.reason);
if (event.code !== 1000) {
// 異常關閉,嘗試重新連線
setTimeout(reconnectWebSocket, 3000);
}
};
ws.onerror = function(error) {
console.error('🔥 WebSocket 錯誤:', error);
};連線狀態管理:
class VisionFlowWebSocket {
constructor(token) {
this.token = token;
this.ws = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.reconnectInterval = 3000;
this.subscriptions = [];
}
connect() {
try {
this.ws = new WebSocket(`ws://localhost:5000/ws?token=${this.token}`);
this.setupEventHandlers();
} catch (error) {
console.error('WebSocket 連線失敗:', error);
this.scheduleReconnect();
}
}
setupEventHandlers() {
this.ws.onopen = () => {
console.log('✅ WebSocket 已連線');
this.reconnectAttempts = 0;
this.resubscribe();
};
this.ws.onmessage = (event) => {
const data = JSON.parse(event.data);
this.handleEvent(data);
};
this.ws.onclose = (event) => {
console.log('❌ WebSocket 已斷線');
if (event.code !== 1000) {
this.scheduleReconnect();
}
};
}
subscribe(events) {
this.subscriptions = events;
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({
action: 'subscribe',
events: events
}));
}
}
scheduleReconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
setTimeout(() => this.connect(), this.reconnectInterval);
}
}
}📡 支援的事件類型
攝影機狀態變更時觸發。
事件結構:
{
"event": "camera_status_update",
"timestamp": "2024-01-20T17:00:00Z",
"data": {
"camera_id": 1,
"camera_name": "前門攝影機",
"previous_status": "inactive",
"current_status": "active",
"health_metrics": {
"fps": 25.5,
"resolution": "1920x1080",
"latency": 120,
"quality": "excellent"
},
"error_info": null
}
}物件偵測事件觸發。
事件結構:
{
"event": "object_detected",
"timestamp": "2024-01-20T17:00:15Z",
"data": {
"detection_id": "det_abc123",
"camera_id": 1,
"camera_name": "前門攝影機",
"detection_area": {
"id": 1,
"name": "主要入口區域"
},
"objects": [
{
"id": "obj_001",
"class": "person",
"confidence": 0.95,
"bbox": {
"x": 100,
"y": 100,
"width": 80,
"height": 200
},
"attributes": {
"age_group": "adult",
"gender": "unknown",
"clothing_color": "blue"
}
}
],
"frame_info": {
"frame_id": "frame_789",
"timestamp": "2024-01-20T17:00:15.123Z",
"size": "1920x1080"
},
"snapshot_url": "https://api.visionflow.com/snapshots/det_abc123.jpg",
"notification_sent": true
}
}系統警報事件。
事件結構:
{
"event": "system_alert",
"timestamp": "2024-01-20T17:05:00Z",
"data": {
"alert_id": "alert_456",
"level": "warning",
"category": "performance",
"source": "camera_controller",
"title": "攝影機效能警告",
"message": "攝影機 3 的影格率降至 15 FPS,低於設定閾值",
"details": {
"camera_id": 3,
"metric": "fps",
"current_value": 15,
"threshold": 20,
"duration": "5 minutes"
},
"suggested_actions": [
"檢查網路連線品質",
"重新啟動攝影機串流",
"檢查攝影機硬體狀態"
],
"auto_resolved": false
}
}通知發送狀態更新。
事件結構:
{
"event": "notification_update",
"timestamp": "2024-01-20T17:00:20Z",
"data": {
"notification_id": 1001,
"type": "object_detected",
"channel": "email",
"status": "delivered",
"recipient": "security@company.com",
"delivery_time": "4.2s",
"retry_count": 0,
"related_detection": "det_abc123"
}
}錄影狀態變更事件。
事件結構:
{
"event": "recording_status",
"timestamp": "2024-01-20T17:10:00Z",
"data": {
"camera_id": 1,
"recording_id": "rec_789",
"status": "started",
"trigger": "motion_detected",
"estimated_duration": 300,
"storage_path": "/recordings/2024/01/20/camera_1_17-10-00.mp4",
"quality": "high"
}
}🎛️ 事件訂閱管理
// 訂閱所有事件
ws.send(JSON.stringify({
action: 'subscribe',
events: ['*']
}));
// 訂閱特定事件
ws.send(JSON.stringify({
action: 'subscribe',
events: [
'camera_status_update',
'object_detected',
'system_alert'
]
}));
// 訂閱特定攝影機的事件
ws.send(JSON.stringify({
action: 'subscribe',
events: ['object_detected'],
filters: {
camera_ids: [1, 2, 3]
}
}));ws.send(JSON.stringify({
action: 'unsubscribe',
events: ['system_alert']
}));ws.send(JSON.stringify({
action: 'get_subscriptions'
}));
// 回應
{
"action": "subscriptions_info",
"data": {
"active_subscriptions": [
"camera_status_update",
"object_detected"
],
"filters": {
"camera_ids": [1, 2]
}
}
}多語言 SDK 與實用程式碼範例
🐍 Python SDK
import requests
import websocket
import json
import threading
from typing import Dict, List, Optional, Callable
from datetime import datetime
class VisionFlowClient:
"""VisionFlow API Python 客戶端"""
def __init__(self, base_url: str, token: str):
self.base_url = base_url.rstrip('/')
self.token = token
self.headers = {
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
self.ws = None
self.event_handlers = {}
def _make_request(self, method: str, endpoint: str, **kwargs) -> Dict:
"""發送 HTTP 請求的通用方法"""
url = f"{self.base_url}{endpoint}"
response = requests.request(method, url, headers=self.headers, **kwargs)
if response.status_code == 429:
raise Exception(f"Rate limit exceeded. Retry after: {response.headers.get('Retry-After')}s")
response.raise_for_status()
return response.json()
# 認證相關方法
def get_profile(self) -> Dict:
"""取得使用者資料"""
return self._make_request('GET', '/auth/profile')
def refresh_token(self, refresh_token: str) -> Dict:
"""更新存取令牌"""
return self._make_request('POST', '/auth/refresh',
json={'refresh_token': refresh_token})
# 攝影機管理
def get_camera_status(self, active_only: bool = False) -> Dict:
"""取得攝影機狀態"""
params = {'active_only': active_only} if active_only else {}
return self._make_request('GET', '/camera/status', params=params)
def add_camera(self, name: str, url: str, **kwargs) -> Dict:
"""新增攝影機"""
data = {'name': name, 'url': url, **kwargs}
return self._make_request('POST', '/camera/add', json=data)
def update_camera(self, camera_id: int, **kwargs) -> Dict:
"""更新攝影機設定"""
return self._make_request('PUT', f'/camera/{camera_id}', json=kwargs)
def delete_camera(self, camera_id: int, force: bool = False) -> Dict:
"""刪除攝影機"""
params = {'force': force} if force else {}
return self._make_request('DELETE', f'/camera/{camera_id}', params=params)
def get_camera_snapshot(self, camera_id: int, quality: str = 'high') -> bytes:
"""取得攝影機快照"""
params = {'quality': quality}
response = requests.get(
f"{self.base_url}/camera/{camera_id}/snapshot",
headers=self.headers,
params=params
)
response.raise_for_status()
return response.content
# 偵測區域管理
def get_detection_areas(self, camera_id: int) -> Dict:
"""取得偵測區域"""
return self._make_request('GET', f'/camera/{camera_id}/areas')
def create_detection_area(self, camera_id: int, name: str, points: List[Dict], **kwargs) -> Dict:
"""建立偵測區域"""
data = {'name': name, 'points': points, **kwargs}
return self._make_request('POST', f'/camera/{camera_id}/areas', json=data)
def update_detection_area(self, camera_id: int, area_id: int, **kwargs) -> Dict:
"""更新偵測區域"""
return self._make_request('PUT', f'/camera/{camera_id}/areas/{area_id}', json=kwargs)
def delete_detection_area(self, camera_id: int, area_id: int) -> Dict:
"""刪除偵測區域"""
return self._make_request('DELETE', f'/camera/{camera_id}/areas/{area_id}')
# 通知管理
def get_notification_settings(self) -> Dict:
"""取得通知設定"""
return self._make_request('GET', '/notification/settings')
def update_notification_settings(self, **kwargs) -> Dict:
"""更新通知設定"""
return self._make_request('PUT', '/notification/settings', json=kwargs)
def get_notification_history(self, **kwargs) -> Dict:
"""取得通知歷史"""
return self._make_request('GET', '/notification/history', params=kwargs)
# 健康檢查
def get_system_health(self, detailed: bool = False) -> Dict:
"""取得系統健康狀態"""
endpoint = '/health/detailed' if detailed else '/health'
return self._make_request('GET', endpoint)
# WebSocket 相關方法
def connect_websocket(self, event_handlers: Dict[str, Callable] = None):
"""建立 WebSocket 連線"""
if event_handlers:
self.event_handlers = event_handlers
ws_url = f"ws://{self.base_url.replace('http://', '').replace('https://', '')}/ws?token={self.token}"
def on_message(ws, message):
data = json.loads(message)
event_type = data.get('event')
if event_type in self.event_handlers:
self.event_handlers[event_type](data)
def on_error(ws, error):
print(f"WebSocket 錯誤: {error}")
def on_close(ws, close_status_code, close_msg):
print("WebSocket 連線已關閉")
def on_open(ws):
print("WebSocket 連線已建立")
self.ws = websocket.WebSocketApp(
ws_url,
on_open=on_open,
on_message=on_message,
on_error=on_error,
on_close=on_close
)
# 在背景執行緒中運行
ws_thread = threading.Thread(target=self.ws.run_forever)
ws_thread.daemon = True
ws_thread.start()
def subscribe_events(self, events: List[str], camera_ids: List[int] = None):
"""訂閱事件"""
if self.ws:
message = {
'action': 'subscribe',
'events': events
}
if camera_ids:
message['filters'] = {'camera_ids': camera_ids}
self.ws.send(json.dumps(message))
def disconnect_websocket(self):
"""斷開 WebSocket 連線"""
if self.ws:
self.ws.close()
# 使用範例
if __name__ == "__main__":
# 初始化客戶端
client = VisionFlowClient('http://localhost:5000', 'your-token-here')
try:
# 取得系統健康狀態
health = client.get_system_health()
print(f"系統狀態: {health['data']['status']}")
# 取得攝影機狀態
cameras = client.get_camera_status()
print(f"攝影機數量: {len(cameras['data'])}")
# 新增攝影機
new_camera = client.add_camera(
name="測試攝影機",
url="rtsp://test.local/stream",
description="API 測試攝影機"
)
print(f"新增攝影機 ID: {new_camera['data']['id']}")
# 建立偵測區域
area = client.create_detection_area(
camera_id=1,
name="測試區域",
points=[
{"x": 100, "y": 100},
{"x": 300, "y": 100},
{"x": 300, "y": 300},
{"x": 100, "y": 300}
],
properties={
"active": True,
"sensitivity": 0.8,
"detection_classes": ["person", "vehicle"]
}
)
print(f"建立偵測區域 ID: {area['data']['id']}")
# 設定 WebSocket 事件處理器
def handle_detection(data):
detection = data['data']
print(f"偵測到物件: {detection['objects'][0]['class']}")
print(f"信心度: {detection['objects'][0]['confidence']}")
def handle_camera_status(data):
status = data['data']
print(f"攝影機 {status['camera_id']} 狀態變更: {status['current_status']}")
# 建立 WebSocket 連線
client.connect_websocket({
'object_detected': handle_detection,
'camera_status_update': handle_camera_status
})
# 訂閱事件
client.subscribe_events(['object_detected', 'camera_status_update'])
# 保持程式運行
input("按 Enter 鍵結束程式...")
except requests.exceptions.HTTPError as e:
print(f"HTTP 錯誤: {e}")
except Exception as e:
print(f"錯誤: {e}")
finally:
client.disconnect_websocket()import requests
from datetime import datetime
class SimpleVisionFlowClient:
"""簡化版 VisionFlow 客戶端"""
def __init__(self, base_url: str, username: str, password: str):
self.base_url = base_url.rstrip('/')
self.token = self._login(username, password)
self.headers = {'Authorization': f'Bearer {self.token}'}
def _login(self, username: str, password: str) -> str:
"""登入並取得令牌"""
response = requests.post(
f"{self.base_url}/auth/login",
json={'username': username, 'password': password}
)
response.raise_for_status()
return response.json()['data']['access_token']
def get_cameras(self):
"""取得所有攝影機"""
response = requests.get(
f"{self.base_url}/camera/status",
headers=self.headers
)
response.raise_for_status()
return response.json()['data']
def capture_snapshot(self, camera_id: int, filename: str = None):
"""擷取快照"""
response = requests.get(
f"{self.base_url}/camera/{camera_id}/snapshot",
headers=self.headers
)
response.raise_for_status()
if not filename:
filename = f"camera_{camera_id}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg"
with open(filename, 'wb') as f:
f.write(response.content)
print(f"快照已儲存至: {filename}")
return filename
# 使用範例
client = SimpleVisionFlowClient(
'http://localhost:5000',
'admin',
'password'
)
cameras = client.get_cameras()
for camera in cameras:
print(f"攝影機: {camera['name']} - 狀態: {camera['status']}")
if camera['status'] == 'active':
client.capture_snapshot(camera['id'])🟨 JavaScript/Node.js SDK
const axios = require('axios');
const WebSocket = require('ws');
const EventEmitter = require('events');
const fs = require('fs');
class VisionFlowClient extends EventEmitter {
constructor(baseUrl, token) {
super();
this.baseUrl = baseUrl.replace(/\/$/, '');
this.token = token;
this.headers = {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
};
this.ws = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
}
// HTTP 請求的通用方法
async _request(method, endpoint, options = {}) {
const config = {
method,
url: `${this.baseUrl}${endpoint}`,
headers: this.headers,
...options
};
try {
const response = await axios(config);
return response.data;
} catch (error) {
if (error.response?.status === 429) {
const retryAfter = error.response.headers['retry-after'];
throw new Error(`Rate limit exceeded. Retry after: ${retryAfter}s`);
}
throw error;
}
}
// 認證相關
async getProfile() {
return this._request('GET', '/auth/profile');
}
async refreshToken(refreshToken) {
return this._request('POST', '/auth/refresh', {
data: { refresh_token: refreshToken }
});
}
// 攝影機管理
async getCameraStatus(activeOnly = false) {
const params = activeOnly ? { active_only: true } : {};
return this._request('GET', '/camera/status', { params });
}
async addCamera(name, url, options = {}) {
const data = { name, url, ...options };
return this._request('POST', '/camera/add', { data });
}
async updateCamera(cameraId, options) {
return this._request('PUT', `/camera/${cameraId}`, { data: options });
}
async deleteCamera(cameraId, force = false) {
const params = force ? { force: true } : {};
return this._request('DELETE', `/camera/${cameraId}`, { params });
}
async getCameraSnapshot(cameraId, quality = 'high') {
const response = await axios({
method: 'GET',
url: `${this.baseUrl}/camera/${cameraId}/snapshot`,
headers: this.headers,
params: { quality },
responseType: 'arraybuffer'
});
return response.data;
}
async saveCameraSnapshot(cameraId, filename, quality = 'high') {
const imageData = await this.getCameraSnapshot(cameraId, quality);
fs.writeFileSync(filename, imageData);
console.log(`快照已儲存至: ${filename}`);
return filename;
}
// 偵測區域管理
async getDetectionAreas(cameraId) {
return this._request('GET', `/camera/${cameraId}/areas`);
}
async createDetectionArea(cameraId, name, points, options = {}) {
const data = { name, points, ...options };
return this._request('POST', `/camera/${cameraId}/areas`, { data });
}
async updateDetectionArea(cameraId, areaId, options) {
return this._request('PUT', `/camera/${cameraId}/areas/${areaId}`, { data: options });
}
async deleteDetectionArea(cameraId, areaId) {
return this._request('DELETE', `/camera/${cameraId}/areas/${areaId}`);
}
// 通知管理
async getNotificationSettings() {
return this._request('GET', '/notification/settings');
}
async updateNotificationSettings(settings) {
return this._request('PUT', '/notification/settings', { data: settings });
}
async getNotificationHistory(options = {}) {
return this._request('GET', '/notification/history', { params: options });
}
// WebSocket 相關
connectWebSocket() {
const wsUrl = `ws://${this.baseUrl.replace(/^https?:\/\//, '')}/ws?token=${this.token}`;
this.ws = new WebSocket(wsUrl);
this.ws.on('open', () => {
console.log('✅ WebSocket 連線已建立');
this.reconnectAttempts = 0;
this.emit('connected');
});
this.ws.on('message', (data) => {
try {
const message = JSON.parse(data);
this.emit('message', message);
this.emit(message.event, message.data);
} catch (error) {
console.error('解析 WebSocket 訊息失敗:', error);
}
});
this.ws.on('close', (code, reason) => {
console.log(`❌ WebSocket 連線已關閉: ${code} ${reason}`);
this.emit('disconnected', { code, reason });
if (code !== 1000 && this.reconnectAttempts < this.maxReconnectAttempts) {
this.scheduleReconnect();
}
});
this.ws.on('error', (error) => {
console.error('🔥 WebSocket 錯誤:', error);
this.emit('error', error);
});
}
scheduleReconnect() {
this.reconnectAttempts++;
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
console.log(`嘗試重新連線... (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
setTimeout(() => {
this.connectWebSocket();
}, delay);
}
subscribeEvents(events, filters = {}) {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
const message = {
action: 'subscribe',
events,
...filters
};
this.ws.send(JSON.stringify(message));
}
}
unsubscribeEvents(events) {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
const message = {
action: 'unsubscribe',
events
};
this.ws.send(JSON.stringify(message));
}
}
disconnectWebSocket() {
if (this.ws) {
this.ws.close(1000, 'Client disconnecting');
this.ws = null;
}
}
// 工具方法
async waitForCameraActive(cameraId, timeout = 30000) {
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
try {
const status = await this.getCameraStatus();
const camera = status.data.find(c => c.id === cameraId);
if (camera && camera.status === 'active') {
return true;
}
await new Promise(resolve => setTimeout(resolve, 1000));
} catch (error) {
console.error('檢查攝影機狀態時發生錯誤:', error);
}
}
throw new Error(`攝影機 ${cameraId} 在 ${timeout}ms 內未變為啟用狀態`);
}
async monitorDetections(cameraIds = [], callback) {
this.connectWebSocket();
this.on('connected', () => {
const filters = cameraIds.length > 0 ? { filters: { camera_ids: cameraIds } } : {};
this.subscribeEvents(['object_detected'], filters);
});
this.on('object_detected', (data) => {
if (callback) {
callback(data);
}
});
}
}
// 使用範例
async function example() {
const client = new VisionFlowClient('http://localhost:5000', 'your-token-here');
try {
// 取得攝影機狀態
const cameras = await client.getCameraStatus();
console.log(`發現 ${cameras.data.length} 台攝影機`);
// 監控物件偵測
await client.monitorDetections([1, 2], (detection) => {
console.log(`偵測到 ${detection.objects[0].class},信心度: ${detection.objects[0].confidence}`);
});
// 擷取所有啟用攝影機的快照
for (const camera of cameras.data) {
if (camera.status === 'active') {
const filename = `snapshot_${camera.id}_${Date.now()}.jpg`;
await client.saveCameraSnapshot(camera.id, filename);
}
}
} catch (error) {
console.error('執行時發生錯誤:', error);
}
}
module.exports = VisionFlowClient;import { useState, useEffect, useCallback } from 'react';
export function useVisionFlow(baseUrl, token) {
const [client, setClient] = useState(null);
const [connected, setConnected] = useState(false);
const [cameras, setCameras] = useState([]);
const [detections, setDetections] = useState([]);
useEffect(() => {
if (!token) return;
const vfClient = new VisionFlowClient(baseUrl, token);
setClient(vfClient);
// 連接 WebSocket
vfClient.connectWebSocket();
vfClient.on('connected', () => {
setConnected(true);
vfClient.subscribeEvents(['object_detected', 'camera_status_update']);
});
vfClient.on('disconnected', () => {
setConnected(false);
});
vfClient.on('object_detected', (detection) => {
setDetections(prev => [detection, ...prev.slice(0, 99)]); // 保留最新100筆
});
vfClient.on('camera_status_update', (status) => {
setCameras(prev => prev.map(camera =>
camera.id === status.camera_id
? { ...camera, status: status.current_status }
: camera
));
});
// 初始載入攝影機資料
vfClient.getCameraStatus().then(response => {
setCameras(response.data);
});
return () => {
vfClient.disconnectWebSocket();
};
}, [baseUrl, token]);
const addCamera = useCallback(async (name, url, options) => {
if (!client) return;
try {
const result = await client.addCamera(name, url, options);
const updatedCameras = await client.getCameraStatus();
setCameras(updatedCameras.data);
return result;
} catch (error) {
throw error;
}
}, [client]);
const captureSnapshot = useCallback(async (cameraId) => {
if (!client) return;
return await client.getCameraSnapshot(cameraId);
}, [client]);
return {
client,
connected,
cameras,
detections,
addCamera,
captureSnapshot
};
}
// React 元件使用範例
function CameraMonitor() {
const { connected, cameras, detections, addCamera } = useVisionFlow(
'http://localhost:5000',
localStorage.getItem('visionflow_token')
);
return (
<div>
<h1>攝影機監控 {connected ? '🟢' : '🔴'}</h1>
<div>
<h2>攝影機清單</h2>
{cameras.map(camera => (
<div key={camera.id}>
{camera.name} - {camera.status}
</div>
))}
</div>
<div>
<h2>最新偵測</h2>
{detections.slice(0, 5).map((detection, index) => (
<div key={index}>
{new Date(detection.timestamp).toLocaleTimeString()} -
攝影機 {detection.camera_name}: {detection.objects[0].class}
</div>
))}
</div>
</div>
);
}API 測試工具與除錯指南
🔧 Postman 集合
下載並匯入我們的 Postman 集合,包含所有端點的測試案例。
快速測試流程:
-
設定環境變數
{ "baseUrl": "http://localhost:5000", "token": "{{your-jwt-token}}" } -
登入取得令牌
// 在 Tests 標籤中加入此腳本 const response = pm.response.json(); if (response.success) { pm.environment.set("token", response.data.access_token); }
-
執行完整測試套件
- 使用 Collection Runner 執行所有測試
- 檢查測試結果和覆蓋率
- 匯出測試報告
🐚 cURL 命令範例
# 登入
curl -X POST http://localhost:5000/auth/login \
-H "Content-Type: application/json" \
-d '{
"username": "admin",
"password": "password"
}'
# 取得使用者資料
curl -X GET http://localhost:5000/auth/profile \
-H "Authorization: Bearer <your-token>"# 取得攝影機狀態
curl -X GET http://localhost:5000/camera/status \
-H "Authorization: Bearer <your-token>"
# 新增攝影機
curl -X POST http://localhost:5000/camera/add \
-H "Authorization: Bearer <your-token>" \
-H "Content-Type: application/json" \
-d '{
"name": "測試攝影機",
"url": "rtsp://test.local/stream",
"description": "API 測試用攝影機",
"settings": {
"resolution": "1920x1080",
"fps": 30,
"quality": "high"
}
}'
# 更新攝影機設定
curl -X PUT http://localhost:5000/camera/1 \
-H "Authorization: Bearer <your-token>" \
-H "Content-Type: application/json" \
-d '{
"name": "更新的攝影機名稱",
"settings": {
"fps": 25,
"quality": "medium"
}
}'
# 取得攝影機快照
curl -X GET http://localhost:5000/camera/1/snapshot \
-H "Authorization: Bearer <your-token>" \
--output snapshot.jpg
# 刪除攝影機
curl -X DELETE http://localhost:5000/camera/1?force=true \
-H "Authorization: Bearer <your-token>"# 取得偵測區域
curl -X GET http://localhost:5000/camera/1/areas \
-H "Authorization: Bearer <your-token>"
# 建立偵測區域
curl -X POST http://localhost:5000/camera/1/areas \
-H "Authorization: Bearer <your-token>" \
-H "Content-Type: application/json" \
-d '{
"name": "入口監控區",
"type": "polygon",
"points": [
{"x": 100, "y": 100},
{"x": 300, "y": 100},
{"x": 300, "y": 300},
{"x": 100, "y": 300}
],
"properties": {
"active": true,
"sensitivity": 0.8,
"detection_classes": ["person", "vehicle"]
}
}'# 取得通知設定
curl -X GET http://localhost:5000/notification/settings \
-H "Authorization: Bearer <your-token>"
# 更新通知設定
curl -X PUT http://localhost:5000/notification/settings \
-H "Authorization: Bearer <your-token>" \
-H "Content-Type: application/json" \
-d '{
"channels": {
"email": {
"enabled": true,
"template": "professional"
},
"line": {
"enabled": true
}
},
"filtering": {
"min_confidence": 0.8
}
}'
# 查詢通知歷史
curl -X GET "http://localhost:5000/notification/history?start_date=2024-01-01&limit=50" \
-H "Authorization: Bearer <your-token>"# 基礎健康檢查(無需認證)
curl -X GET http://localhost:5000/health
# 詳細健康檢查
curl -X GET http://localhost:5000/health/detailed \
-H "Authorization: Bearer <your-token>"
# 系統效能指標
curl -X GET "http://localhost:5000/health/metrics?period=24h" \
-H "Authorization: Bearer <your-token>"🔍 除錯技巧
1. 認證失敗 (401)
# 檢查令牌是否有效
curl -X GET http://localhost:5000/auth/profile \
-H "Authorization: Bearer <token>" \
-v
# 常見原因:
# - 令牌已過期
# - 令牌格式錯誤
# - 權限不足2. 速率限制 (429)
# 檢查回應標頭中的重試時間
curl -X GET http://localhost:5000/camera/status \
-H "Authorization: Bearer <token>" \
-I
# X-RateLimit-Limit: 30
# X-RateLimit-Remaining: 0
# X-RateLimit-Reset: 1642694400
# Retry-After: 603. 攝影機連線問題
# 檢查攝影機網路連線
ping camera-ip-address
# 測試 RTSP 串流
ffprobe -v quiet -print_format json -show_streams rtsp://camera-url
# 檢查防火牆設定
telnet camera-ip 554啟用偵錯模式:
# 設定環境變數
export VISIONFLOW_DEBUG=true
export LOG_LEVEL=debug
# 重新啟動服務
systemctl restart visionflow查看日誌:
# 即時查看日誌
tail -f /var/log/visionflow/app.log
# 搜尋特定錯誤
grep "ERROR" /var/log/visionflow/app.log | tail -20
# 分析 API 請求
grep "api_request" /var/log/visionflow/app.log | jq '.'監控 API 回應時間:
# 使用 curl 測量回應時間
curl -w "@curl-format.txt" -o /dev/null -s http://localhost:5000/camera/status
# curl-format.txt 內容:
# time_namelookup: %{time_namelookup}\n
# time_connect: %{time_connect}\n
# time_appconnect: %{time_appconnect}\n
# time_pretransfer: %{time_pretransfer}\n
# time_redirect: %{time_redirect}\n
# time_starttransfer: %{time_starttransfer}\n
# time_total: %{time_total}\n使用 Apache Bench 進行負載測試:
# 測試 API 端點效能
ab -n 1000 -c 10 -H "Authorization: Bearer <token>" \
http://localhost:5000/camera/status
# 測試結果分析:
# - Requests per second
# - Time per request
# - Transfer rate
# - Connection TimesAPI 版本歷史與功能演進
🆕 v2.1.0 (2024-01-20) - 當前版本
- 🔒 增強安全性: 新增 API Key 認證方式,支援服務間安全通訊
- 🎯 智慧偵測區域: 支援複雜多邊形區域,提升偵測精度
- 📊 即時效能監控: 新增系統效能指標 API,提供詳細監控資料
- 🔄 WebSocket 改進: 優化連線穩定性,支援事件過濾和訂閱管理
- 📱 多平台通知: 新增 Slack、Webhook 通知支援
- 🚀 批次操作: 支援批次攝影機管理和區域設定
- ⚡ 效能優化: API 回應時間平均提升 35%
- 📈 擴展性: 支援最多 50 台攝影機同時管理
- 🔍 搜尋功能: 新增通知歷史和偵測記錄的進階搜尋
- 📊 統計資料: 提供詳細的偵測統計和分析報表
- 🎨 UI 改進: 更新 API 文檔格式,提升可讀性
- 修復攝影機斷線重連的記憶體洩漏問題
- 解決高並發情況下的資料庫鎖定問題
- 修正 WebSocket 連線在網路不穩時的異常行為
- 修復偵測區域重疊計算的準確性問題
GET /camera/status回應格式調整,新增health_metrics欄位- 統一錯誤回應格式,所有 API 都採用一致的錯誤結構
- WebSocket 事件命名調整:
detection→object_detected
📦 v2.0.0 (2024-01-01) - 主要版本更新
- 🤖 AI 模型升級: 採用 YOLOv8 物件偵測模型,準確度提升 25%
- 🏗️ 架構重構: 微服務架構設計,提升系統穩定性和擴展性
- 🔐 JWT 認證: 完整的 JWT 令牌系統,支援 Refresh Token
- 📡 即時通訊: WebSocket 支援,提供即時事件推送
- 🎯 多邊形偵測: 支援自定義多邊形偵測區域
- 全新的 RESTful API 設計
- 統一的回應格式和錯誤處理
- 新增速率限制和配額管理
- 支援 API 版本控制
- 自動從 v1.x 版本遷移資料
- 提供資料備份和還原工具
- 保持向後相容性(有限支援)
🔧 v1.2.0 (2023-12-01)
- 📧 電子郵件通知: 支援 SMTP 郵件發送
- 📱 LINE 通知整合: 完整的 LINE Notify 支援
- 🎥 錄影功能: 偵測觸發自動錄影
- 📊 基礎統計: 偵測數量和頻率統計
- 修復攝影機連線超時問題
- 解決並發偵測時的效能問題
- 修正通知重複發送的問題
🚀 v1.1.0 (2023-11-01)
- 🎯 區域偵測: 基礎的矩形偵測區域
- 🔧 設定管理: 攝影機參數調整
- 📋 日誌系統: 基本的操作日誌記錄
- 提升偵測穩定性
- 優化記憶體使用
- 改善錯誤處理
🎯 v1.0.0 (2023-10-01) - 初始版本
- 🏠 基礎功能: 攝影機管理和狀態監控
- 🤖 物件偵測: 基於 YOLO 的人員和車輛偵測
- 📧 基礎通知: 簡單的郵件通知系統
- 🌐 Web 介面: 基本的管理界面
- 最多 10 台攝影機管理
- RTSP 串流支援
- 基礎的移動偵測
- SQLite 資料庫存儲
📅 v2.2.0 (計劃中 - 2024年2月)
- 🧠 智慧分析: 行為分析和異常偵測
- 🌍 多語言支援: 支援英文、中文、日文介面
- 📱 行動應用: iOS/Android 原生應用程式
- ☁️ 雲端整合: AWS/Azure 雲端儲存支援
- 🔐 SSO 整合: Active Directory 和 LDAP 支援
🚀 v3.0.0 (遠期規劃 - 2024年6月)
- 🤖 AI 平台: 支援自定義 AI 模型訓練
- 🏢 企業功能: 多租戶架構,權限管理
- 📊 商業智慧: 詳細的分析報表和儀表板
- 🔄 自動化: 規則引擎和工作流程自動化
📞 聯絡資訊
- 📧 Email: support@visionflow.com
- 💬 技術論壇: https://forum.visionflow.com
- 📚 知識庫: https://docs.visionflow.com
- 🐛 問題回報: GitHub Issues
| 層級 | 回應時間 | 可用時間 | 聯絡方式 |
|---|---|---|---|
| 🆓 社群支援 | 1-3 工作天 | 工作時間 | 論壇、GitHub |
| 💼 標準支援 | 24 小時 | 工作時間 | Email、電話 |
| 🚀 企業支援 | 4 小時 | 24/7 | 專屬窗口 |
- 問題描述: 詳細描述遇到的問題和錯誤訊息
- 環境資訊: 提供系統版本、配置和日誌檔案
- 重現步驟: 說明如何重現問題的具體步驟
- 期望結果: 描述預期的正確行為