13420bead8
Approve (single + bulk) created the canvas Node under the first design instead of the design the user is viewing, so approved devices were invisible on the active canvas and got wiped on the next save — and a re-approve returned "0 approved" because the rows were already approved. - bulk-approve now accepts design_id; the UI sends the active design. - single approve already honoured design_id; the UI now sends it too. - generalize the wireless branch (status=online, mesh props, no ICMP check) to Z-Wave as well as Zigbee, using build_zwave_properties. ha-relevant: yes
216 lines
6.4 KiB
TypeScript
216 lines
6.4 KiB
TypeScript
import axios from 'axios'
|
|
import { useAuthStore } from '@/stores/authStore'
|
|
|
|
export const api = axios.create({
|
|
baseURL: '/api/v1',
|
|
})
|
|
|
|
// Unauthenticated axios instance — no JWT, no 401 redirect (used for public endpoints)
|
|
const publicApi = axios.create({ baseURL: '/api/v1' })
|
|
|
|
api.interceptors.request.use((config) => {
|
|
const token = useAuthStore.getState().token
|
|
if (token) config.headers.Authorization = `Bearer ${token}`
|
|
return config
|
|
})
|
|
|
|
api.interceptors.response.use(
|
|
(r) => r,
|
|
(err) => {
|
|
if (err.response?.status === 401) useAuthStore.getState().logout()
|
|
return Promise.reject(err)
|
|
}
|
|
)
|
|
|
|
export const authApi = {
|
|
login: (username: string, password: string) =>
|
|
api.post<{ access_token: string }>('/auth/login', { username, password }),
|
|
}
|
|
|
|
export const canvasApi = {
|
|
load: (design_id?: string) => {
|
|
const params = design_id ? { design_id } : {}
|
|
return api.get('/canvas', { params })
|
|
},
|
|
save: (payload: {
|
|
nodes: object[]
|
|
edges: object[]
|
|
viewport: object
|
|
custom_style?: object | null
|
|
design_id?: string | null
|
|
}) => api.post('/canvas/save', payload),
|
|
}
|
|
|
|
export const nodesApi = {
|
|
create: (data: object) => api.post('/nodes', data),
|
|
update: (id: string, data: object) => api.patch(`/nodes/${id}`, data),
|
|
delete: (id: string) => api.delete(`/nodes/${id}`),
|
|
}
|
|
|
|
export const edgesApi = {
|
|
create: (data: object) => api.post('/edges', data),
|
|
delete: (id: string) => api.delete(`/edges/${id}`),
|
|
}
|
|
|
|
export const liveviewApi = {
|
|
load: (key: string, design?: string) =>
|
|
publicApi.get('/liveview', { params: { key, ...(design ? { design_id: design } : {}) } }),
|
|
getConfig: () => api.get<{ enabled: boolean; key: string | null }>('/liveview/config'),
|
|
}
|
|
|
|
export interface DeepScanConfig {
|
|
http_ranges: string[]
|
|
http_probe_enabled: boolean
|
|
verify_tls: boolean
|
|
}
|
|
|
|
export type ScanConfigData = { ranges: string[] } & DeepScanConfig
|
|
|
|
export const scanApi = {
|
|
trigger: (deepScan?: Partial<DeepScanConfig>) => api.post('/scan/trigger', deepScan ?? {}),
|
|
pending: () => api.get('/scan/pending'),
|
|
hidden: () => api.get('/scan/hidden'),
|
|
runs: () => api.get('/scan/runs'),
|
|
clearPending: () => api.delete('/scan/pending'),
|
|
approve: (id: string, nodeData: object) =>
|
|
api.post<{
|
|
approved: boolean
|
|
node_id: string
|
|
edges_created: number
|
|
edges: { id: string; source: string; target: string }[]
|
|
}>(`/scan/pending/${id}/approve`, nodeData),
|
|
hide: (id: string) => api.post(`/scan/pending/${id}/hide`),
|
|
ignore: (id: string) => api.post(`/scan/pending/${id}/ignore`),
|
|
bulkApprove: (ids: string[], designId?: string | null) =>
|
|
api.post<{
|
|
approved: number
|
|
node_ids: string[]
|
|
device_ids: string[]
|
|
edges_created: number
|
|
edges: { id: string; source: string; target: string }[]
|
|
skipped: number
|
|
}>('/scan/pending/bulk-approve', { device_ids: ids, design_id: designId ?? undefined }),
|
|
bulkHide: (ids: string[]) => api.post<{ hidden: number; skipped: number }>('/scan/pending/bulk-hide', { device_ids: ids }),
|
|
restore: (id: string) => api.post<{ restored: boolean; device_id: string }>(`/scan/pending/${id}/restore`),
|
|
bulkRestore: (ids: string[]) => api.post<{ restored: number; skipped: number }>('/scan/pending/bulk-restore', { device_ids: ids }),
|
|
stop: (runId: string) => api.post(`/scan/${runId}/stop`),
|
|
getConfig: () => api.get<ScanConfigData>('/scan/config'),
|
|
saveConfig: (data: ScanConfigData) => api.post('/scan/config', data),
|
|
}
|
|
|
|
export interface AppSettings {
|
|
interval_seconds: number
|
|
service_check_enabled: boolean
|
|
service_check_interval: number
|
|
}
|
|
|
|
export const settingsApi = {
|
|
get: () => api.get<AppSettings>('/settings'),
|
|
save: (data: AppSettings) => api.post<AppSettings>('/settings', data),
|
|
}
|
|
|
|
export const designsApi = {
|
|
list: () => api.get<import('@/types').Design[]>('/designs'),
|
|
create: (data: { name: string; icon?: string; design_type?: string }) =>
|
|
api.post<import('@/types').Design>('/designs', data),
|
|
update: (id: string, data: { name?: string; icon?: string }) =>
|
|
api.put<import('@/types').Design>(`/designs/${id}`, data),
|
|
delete: (id: string) => api.delete(`/designs/${id}`),
|
|
}
|
|
|
|
export const zigbeeApi = {
|
|
testConnection: (data: {
|
|
mqtt_host: string
|
|
mqtt_port: number
|
|
mqtt_username?: string
|
|
mqtt_password?: string
|
|
mqtt_tls?: boolean
|
|
mqtt_tls_insecure?: boolean
|
|
}) =>
|
|
api.post<{ connected: boolean; message: string }>('/zigbee/test-connection', data),
|
|
|
|
importNetwork: (data: {
|
|
mqtt_host: string
|
|
mqtt_port: number
|
|
mqtt_username?: string
|
|
mqtt_password?: string
|
|
base_topic?: string
|
|
mqtt_tls?: boolean
|
|
mqtt_tls_insecure?: boolean
|
|
}) =>
|
|
api.post<{
|
|
nodes: import('@/components/zigbee/types').ZigbeeNode[]
|
|
edges: import('@/components/zigbee/types').ZigbeeEdge[]
|
|
device_count: number
|
|
}>('/zigbee/import', data),
|
|
|
|
importToPending: (data: {
|
|
mqtt_host: string
|
|
mqtt_port: number
|
|
mqtt_username?: string
|
|
mqtt_password?: string
|
|
base_topic?: string
|
|
mqtt_tls?: boolean
|
|
mqtt_tls_insecure?: boolean
|
|
}) =>
|
|
api.post<{
|
|
id: string
|
|
status: string
|
|
kind: string
|
|
ranges: string[]
|
|
devices_found: number
|
|
started_at: string
|
|
finished_at: string | null
|
|
error: string | null
|
|
}>('/zigbee/import-pending', data),
|
|
}
|
|
|
|
export const zwaveApi = {
|
|
testConnection: (data: {
|
|
mqtt_host: string
|
|
mqtt_port: number
|
|
mqtt_username?: string
|
|
mqtt_password?: string
|
|
mqtt_tls?: boolean
|
|
mqtt_tls_insecure?: boolean
|
|
}) =>
|
|
api.post<{ connected: boolean; message: string }>('/zwave/test-connection', data),
|
|
|
|
importNetwork: (data: {
|
|
mqtt_host: string
|
|
mqtt_port: number
|
|
mqtt_username?: string
|
|
mqtt_password?: string
|
|
prefix?: string
|
|
gateway_name?: string
|
|
mqtt_tls?: boolean
|
|
mqtt_tls_insecure?: boolean
|
|
}) =>
|
|
api.post<{
|
|
nodes: import('@/components/zwave/types').ZwaveNode[]
|
|
edges: import('@/components/zwave/types').ZwaveEdge[]
|
|
device_count: number
|
|
}>('/zwave/import', data),
|
|
|
|
importToPending: (data: {
|
|
mqtt_host: string
|
|
mqtt_port: number
|
|
mqtt_username?: string
|
|
mqtt_password?: string
|
|
prefix?: string
|
|
gateway_name?: string
|
|
mqtt_tls?: boolean
|
|
mqtt_tls_insecure?: boolean
|
|
}) =>
|
|
api.post<{
|
|
id: string
|
|
status: string
|
|
kind: string
|
|
ranges: string[]
|
|
devices_found: number
|
|
started_at: string
|
|
finished_at: string | null
|
|
error: string | null
|
|
}>('/zwave/import-pending', data),
|
|
}
|