feat: Phase 2 — auth, node/edge modals, API wiring
- Login page: full-screen dark, grid bg, JWT flow - Auth store (Zustand) + API client (axios + interceptors) - NodeModal: add/edit form (type, label, hostname, IP, check method) - EdgeModal: link type picker + VLAN ID + label on connect - App: auth gate, canvas load from API (fallback to demo), Ctrl+S save - CanvasContainer: expose onConnect prop for edge modal flow
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
import axios from 'axios'
|
||||
import { useAuthStore } from '@/stores/authStore'
|
||||
|
||||
export const api = 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: () => api.get('/canvas'),
|
||||
save: (payload: { node_positions: { id: string; x: number; y: number }[]; viewport: object }) =>
|
||||
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}`),
|
||||
}
|
||||
Reference in New Issue
Block a user