Initial scaffold: full-stack RackMapper application
Complete project scaffold with working auth, REST API, Prisma/SQLite schema, Docker config, and React frontend for both Rack Planner and Service Mapper modules. Both server and client pass TypeScript strict mode with zero errors. Initial migration applied. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
196
client/src/api/client.ts
Normal file
196
client/src/api/client.ts
Normal file
@@ -0,0 +1,196 @@
|
||||
import type {
|
||||
Rack,
|
||||
Module,
|
||||
Port,
|
||||
Vlan,
|
||||
ServiceMap,
|
||||
ServiceMapSummary,
|
||||
ServiceNode,
|
||||
ServiceEdge,
|
||||
ModuleType,
|
||||
PortType,
|
||||
VlanMode,
|
||||
NodeType,
|
||||
} from '../types';
|
||||
|
||||
// ---- Core fetch wrapper ----
|
||||
|
||||
async function request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
|
||||
const res = await fetch(`/api${endpoint}`, {
|
||||
...options,
|
||||
headers: { 'Content-Type': 'application/json', ...options.headers },
|
||||
credentials: 'include',
|
||||
});
|
||||
|
||||
const body = await res.json().catch(() => ({ data: null, error: `HTTP ${res.status}` }));
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error((body as { error?: string }).error ?? `HTTP ${res.status}`);
|
||||
}
|
||||
|
||||
return (body as { data: T }).data;
|
||||
}
|
||||
|
||||
function get<T>(path: string) {
|
||||
return request<T>(path);
|
||||
}
|
||||
|
||||
function post<T>(path: string, data?: unknown) {
|
||||
return request<T>(path, { method: 'POST', body: JSON.stringify(data) });
|
||||
}
|
||||
|
||||
function put<T>(path: string, data?: unknown) {
|
||||
return request<T>(path, { method: 'PUT', body: JSON.stringify(data) });
|
||||
}
|
||||
|
||||
function del<T>(path: string) {
|
||||
return request<T>(path, { method: 'DELETE' });
|
||||
}
|
||||
|
||||
// ---- Auth ----
|
||||
|
||||
const auth = {
|
||||
me: () => get<{ authenticated: boolean }>('/auth/me'),
|
||||
login: (username: string, password: string) =>
|
||||
post<{ success: boolean }>('/auth/login', { username, password }),
|
||||
logout: () => post<{ success: boolean }>('/auth/logout'),
|
||||
};
|
||||
|
||||
// ---- Racks ----
|
||||
|
||||
const racks = {
|
||||
list: () => get<Rack[]>('/racks'),
|
||||
get: (id: string) => get<Rack>(`/racks/${id}`),
|
||||
create: (data: { name: string; totalU?: number; location?: string; displayOrder?: number }) =>
|
||||
post<Rack>('/racks', data),
|
||||
update: (
|
||||
id: string,
|
||||
data: Partial<{ name: string; totalU: number; location: string; displayOrder: number }>
|
||||
) => put<Rack>(`/racks/${id}`, data),
|
||||
delete: (id: string) => del<null>(`/racks/${id}`),
|
||||
addModule: (
|
||||
rackId: string,
|
||||
data: {
|
||||
name: string;
|
||||
type: ModuleType;
|
||||
uPosition: number;
|
||||
uSize?: number;
|
||||
manufacturer?: string;
|
||||
model?: string;
|
||||
ipAddress?: string;
|
||||
notes?: string;
|
||||
portCount?: number;
|
||||
portType?: PortType;
|
||||
}
|
||||
) => post<Module>(`/racks/${rackId}/modules`, data),
|
||||
};
|
||||
|
||||
// ---- Modules ----
|
||||
|
||||
const modules = {
|
||||
update: (
|
||||
id: string,
|
||||
data: Partial<{
|
||||
name: string;
|
||||
uPosition: number;
|
||||
uSize: number;
|
||||
manufacturer: string;
|
||||
model: string;
|
||||
ipAddress: string;
|
||||
notes: string;
|
||||
}>
|
||||
) => put<Module>(`/modules/${id}`, data),
|
||||
delete: (id: string) => del<null>(`/modules/${id}`),
|
||||
getPorts: (id: string) => get<Port[]>(`/modules/${id}/ports`),
|
||||
};
|
||||
|
||||
// ---- Ports ----
|
||||
|
||||
const ports = {
|
||||
update: (
|
||||
id: string,
|
||||
data: {
|
||||
label?: string;
|
||||
mode?: VlanMode;
|
||||
nativeVlan?: number | null;
|
||||
notes?: string;
|
||||
vlans?: Array<{ vlanId: string; tagged: boolean }>;
|
||||
}
|
||||
) => put<Port>(`/ports/${id}`, data),
|
||||
};
|
||||
|
||||
// ---- VLANs ----
|
||||
|
||||
const vlans = {
|
||||
list: () => get<Vlan[]>('/vlans'),
|
||||
create: (data: { vlanId: number; name: string; description?: string; color?: string }) =>
|
||||
post<Vlan>('/vlans', data),
|
||||
update: (id: string, data: Partial<{ name: string; description: string; color: string }>) =>
|
||||
put<Vlan>(`/vlans/${id}`, data),
|
||||
delete: (id: string) => del<null>(`/vlans/${id}`),
|
||||
};
|
||||
|
||||
// ---- Service Maps ----
|
||||
|
||||
const maps = {
|
||||
list: () => get<ServiceMapSummary[]>('/maps'),
|
||||
get: (id: string) => get<ServiceMap>(`/maps/${id}`),
|
||||
create: (data: { name: string; description?: string }) => post<ServiceMap>('/maps', data),
|
||||
update: (id: string, data: Partial<{ name: string; description: string }>) =>
|
||||
put<ServiceMap>(`/maps/${id}`, data),
|
||||
delete: (id: string) => del<null>(`/maps/${id}`),
|
||||
addNode: (
|
||||
mapId: string,
|
||||
data: {
|
||||
label: string;
|
||||
nodeType: NodeType;
|
||||
positionX: number;
|
||||
positionY: number;
|
||||
metadata?: string;
|
||||
color?: string;
|
||||
icon?: string;
|
||||
moduleId?: string;
|
||||
}
|
||||
) => post<ServiceNode>(`/maps/${mapId}/nodes`, data),
|
||||
populate: (mapId: string) => post<ServiceMap>(`/maps/${mapId}/populate`),
|
||||
addEdge: (
|
||||
mapId: string,
|
||||
data: {
|
||||
sourceId: string;
|
||||
targetId: string;
|
||||
label?: string;
|
||||
edgeType?: string;
|
||||
animated?: boolean;
|
||||
}
|
||||
) => post<ServiceEdge>(`/maps/${mapId}/edges`, data),
|
||||
};
|
||||
|
||||
// ---- Nodes ----
|
||||
|
||||
const nodes = {
|
||||
update: (
|
||||
id: string,
|
||||
data: Partial<{
|
||||
label: string;
|
||||
positionX: number;
|
||||
positionY: number;
|
||||
metadata: string;
|
||||
color: string;
|
||||
icon: string;
|
||||
moduleId: string | null;
|
||||
}>
|
||||
) => put<ServiceNode>(`/nodes/${id}`, data),
|
||||
delete: (id: string) => del<null>(`/nodes/${id}`),
|
||||
};
|
||||
|
||||
// ---- Edges ----
|
||||
|
||||
const edges = {
|
||||
update: (
|
||||
id: string,
|
||||
data: Partial<{ label: string; edgeType: string; animated: boolean; metadata: string }>
|
||||
) => put<ServiceEdge>(`/edges/${id}`, data),
|
||||
delete: (id: string) => del<null>(`/edges/${id}`),
|
||||
};
|
||||
|
||||
export const apiClient = { auth, racks, modules, ports, vlans, maps, nodes, edges };
|
||||
Reference in New Issue
Block a user