Add API service layer, authentication store, and composables

- Implement API service modules for sensors, rooms, analytics, health,
and auth - Add Pinia auth store for JWT token management and validation
- Create Vue composables for API integration and state management -
Update settings and AI optimization views for code style and connection
URLs - Add test-websocket.html for local WebSocket testing
This commit is contained in:
rafaeldpsilva
2025-09-18 14:29:36 +01:00
parent 32c63628b6
commit faed09d3b6
10 changed files with 1498 additions and 114 deletions

View File

@@ -0,0 +1,56 @@
/**
* Analytics API Service
* Handles analytics and reporting API calls
*/
import {
apiClient,
type AnalyticsSummary,
type EnergyTrends,
type RoomComparison,
type SystemEvent,
} from './api'
export const analyticsApi = {
/**
* Get analytics summary for the specified time period
*/
async getAnalyticsSummary(hours: number = 24): Promise<AnalyticsSummary> {
return apiClient.get<AnalyticsSummary>('/api/v1/analytics/summary', { hours })
},
/**
* Get energy consumption trends
*/
async getEnergyTrends(hours: number = 168): Promise<EnergyTrends> {
return apiClient.get<EnergyTrends>('/api/v1/analytics/trends', { hours })
},
/**
* Get room-by-room comparison analytics
*/
async getRoomComparison(hours: number = 24): Promise<RoomComparison> {
return apiClient.get<RoomComparison>('/api/v1/analytics/rooms', { hours })
},
/**
* Get recent system events and alerts
*/
async getEvents(params?: {
severity?: string
event_type?: string
hours?: number
limit?: number
}): Promise<{
events: SystemEvent[]
count: number
period_hours: number
}> {
return apiClient.get<{
events: SystemEvent[]
count: number
period_hours: number
}>('/api/v1/events', params)
},
}
export default analyticsApi

336
src/services/api.ts Normal file
View File

@@ -0,0 +1,336 @@
/**
* API Service Layer for Energy Monitoring Dashboard
* Handles all backend API communications
*/
// Base configuration
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000'
// API Response types
export interface ApiResponse<T = any> {
data: T
total_count?: number
query?: any
execution_time_ms?: number
}
export interface HealthCheck {
status: 'healthy' | 'degraded'
mongodb_connected: boolean
redis_connected: boolean
total_sensors: number
active_sensors: number
total_readings: number
uptime_seconds: number
}
export interface SystemStatus {
timestamp: number
uptime_seconds: number
active_websocket_connections: number
database_stats: {
total_sensors: number
active_sensors: number
total_readings: number
}
}
export interface DataQuery {
sensor_ids?: string[]
rooms?: string[]
sensor_types?: SensorType[]
start_time?: number
end_time?: number
limit?: number
offset?: number
sort_by?: string
sort_order?: 'asc' | 'desc'
}
export interface DataResponse {
data: SensorReading[]
total_count: number
query: DataQuery
execution_time_ms: number
}
export interface SensorReading {
_id?: string
sensor_id: string
room?: string
sensor_type: string
timestamp: number
created_at?: string
energy?: {
value: number
unit: string
}
co2?: {
value: number
unit: string
}
temperature?: {
value: number
unit: string
}
humidity?: {
value: number
unit: string
}
metadata?: Record<string, any>
}
export interface SensorInfo {
sensor_id: string
sensor_type: SensorType
room?: string
status: SensorStatus
first_seen: number
last_seen: number
total_readings: number
latest_values?: Record<string, any>
metadata?: Record<string, any>
}
export interface RoomInfo {
room: string
sensor_count: number
sensor_types: SensorType[]
latest_metrics?: {
energy?: { current: number; unit: string }
co2?: { current: number; unit: string }
temperature?: { current: number; unit: string }
humidity?: { current: number; unit: string }
}
last_updated?: number
}
export interface RoomData {
room: string
sensors: string[]
data: SensorReading[]
aggregated_metrics: {
energy?: { total: number; average: number; current: number; unit: string }
co2?: { average: number; max: number; current: number; unit: string }
temperature?: { average: number; min: number; max: number; current: number; unit: string }
}
execution_time_ms?: number
}
export interface AnalyticsSummary {
period_hours: number
total_energy_consumption: { value: number; unit: string }
average_power: { value: number; unit: string }
peak_power: { value: number; unit: string; timestamp: number }
sensor_count: number
room_count: number
co2_analysis?: {
average_ppm: number
max_ppm: number
rooms_above_threshold: string[]
}
top_consuming_sensors: Array<{
sensor_id: string
room: string
consumption: number
unit: string
}>
top_consuming_rooms: Array<{
room: string
consumption: number
unit: string
sensor_count: number
}>
}
export interface EnergyTrends {
period_hours: number
hourly_consumption: Array<{
hour: string
consumption: number
unit: string
}>
daily_averages: Array<{
date: string
average_consumption: number
unit: string
}>
trend_analysis: {
trend: 'increasing' | 'decreasing' | 'stable'
percentage_change: number
prediction_next_24h: number
}
}
export interface RoomComparison {
period_hours: number
rooms: Array<{
room: string
total_consumption: number
average_consumption: number
peak_consumption: number
sensor_count: number
efficiency_rating: 'excellent' | 'good' | 'average' | 'poor'
unit: string
}>
insights: {
most_efficient: string
least_efficient: string
total_consumption: number
average_per_room: number
}
}
export interface SystemEvent {
_id: string
timestamp: number
event_type: string
severity: 'info' | 'warning' | 'error' | 'critical'
message: string
details?: Record<string, any>
sensor_id?: string
room?: string
}
export enum SensorType {
ENERGY = 'energy',
CO2 = 'co2',
TEMPERATURE = 'temperature',
HUMIDITY = 'humidity',
HVAC = 'hvac',
LIGHTING = 'lighting',
SECURITY = 'security',
}
export enum SensorStatus {
ONLINE = 'online',
OFFLINE = 'offline',
ERROR = 'error',
}
// HTTP Client class
class ApiClient {
private baseUrl: string
constructor(baseUrl: string = API_BASE_URL) {
this.baseUrl = baseUrl
}
private getAuthHeaders(): Record<string, string> {
// Dynamically get auth headers to avoid circular imports
try {
const authStore = (window as any).__AUTH_STORE__
if (authStore && typeof authStore.getAuthHeader === 'function') {
return authStore.getAuthHeader()
}
} catch (error) {
console.warn('Could not get auth headers:', error)
}
return {}
}
private async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
const url = `${this.baseUrl}${endpoint}`
const authHeaders = this.getAuthHeaders()
const config: RequestInit = {
headers: {
'Content-Type': 'application/json',
...authHeaders,
...options.headers,
},
...options,
}
try {
const response = await fetch(url, config)
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
throw new Error(`HTTP ${response.status}: ${errorData.detail || response.statusText}`)
}
return await response.json()
} catch (error) {
if (error instanceof Error) {
throw new Error(`API request failed: ${error.message}`)
}
throw new Error('Unknown API error')
}
}
async get<T>(endpoint: string, params?: Record<string, any>): Promise<T> {
const url = new URL(`${this.baseUrl}${endpoint}`)
if (params) {
Object.keys(params).forEach((key) => {
const value = params[key]
if (value !== undefined && value !== null) {
if (Array.isArray(value)) {
value.forEach((v) => url.searchParams.append(key, String(v)))
} else {
url.searchParams.append(key, String(value))
}
}
})
}
const authHeaders = this.getAuthHeaders()
const response = await fetch(url.toString(), {
method: 'GET',
headers: {
'Content-Type': 'application/json',
...authHeaders,
},
})
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
throw new Error(`HTTP ${response.status}: ${errorData.detail || response.statusText}`)
}
return await response.json()
}
async post<T>(endpoint: string, data?: any): Promise<T> {
return this.request<T>(endpoint, {
method: 'POST',
body: data ? JSON.stringify(data) : undefined,
})
}
async put<T>(endpoint: string, data?: any): Promise<T> {
return this.request<T>(endpoint, {
method: 'PUT',
body: data ? JSON.stringify(data) : undefined,
})
}
async delete<T>(endpoint: string): Promise<T> {
return this.request<T>(endpoint, {
method: 'DELETE',
})
}
}
// Create singleton instance
export const apiClient = new ApiClient()
// Health and status endpoints
export const healthApi = {
/**
* Get system health status
*/
async getHealth(): Promise<HealthCheck> {
return apiClient.get<HealthCheck>('/health')
},
/**
* Get detailed system status
*/
async getStatus(): Promise<SystemStatus> {
return apiClient.get<SystemStatus>('/api/v1/overview')
},
}
export default apiClient

47
src/services/authApi.ts Normal file
View File

@@ -0,0 +1,47 @@
/**
* Authentication API Service
* Handles JWT token generation and validation
*/
import { apiClient } from './api'
export interface TokenRequest {
name: string
list_of_resources: string[]
}
export interface TokenResponse {
token: string
expires_at: string
resources: string[]
}
export interface TokenValidation {
valid: boolean
expires_at?: string
resources?: string[]
}
export const authApi = {
/**
* Generate a new JWT token for the dashboard
*/
async generateToken(request: TokenRequest): Promise<TokenResponse> {
return apiClient.post<TokenResponse>('/api/v1/tokens/generate', request)
},
/**
* Validate an existing token
*/
async validateToken(token: string): Promise<TokenValidation> {
return apiClient.post<TokenValidation>('/api/v1/tokens/validate', { token })
},
/**
* Revoke a token
*/
async revokeToken(token: string): Promise<{ message: string }> {
return apiClient.post<{ message: string }>('/api/v1/tokens/revoke', { token })
}
}
export default authApi

30
src/services/index.ts Normal file
View File

@@ -0,0 +1,30 @@
/**
* API Services Index
* Central export point for all API services
*/
// Export base API client and types
export * from './api'
// Export service modules
export { sensorsApi } from './sensorsApi'
export { roomsApi } from './roomsApi'
export { analyticsApi } from './analyticsApi'
export { healthApi } from './api'
export { authApi } from './authApi'
// Re-export commonly used types for convenience
export type {
SensorReading,
SensorInfo,
RoomInfo,
RoomData,
DataQuery,
DataResponse,
AnalyticsSummary,
EnergyTrends,
RoomComparison,
SystemEvent,
HealthCheck,
SystemStatus
} from './api'