2025-09-09 11:30:30 +10:00

108 lines
3.1 KiB
TypeScript

// lib/api.ts - API helpers with Zod validation for your Laravel endpoints
import { z } from "zod";
export type DeviceCommandType =
| "wifi"
| "sleep"
| "telemetry_sec"
| "poll_sec"
| "ota"
| "ring_fence"
| "lights"
| "camera";
export interface DeviceCommand {
id: number;
type: DeviceCommandType;
payload: Record<string, unknown>;
}
export interface TelemetryPost {
recorded_at?: string;
lat: number;
lng: number;
altitude_m?: number;
speed_kmh?: number;
heading_deg?: number;
accuracy_m?: number;
battery_percent?: number;
is_car_on?: boolean;
raw?: Record<string, unknown>;
}
export interface CommandReceiptPost {
command_id: number;
acked_at?: string;
executed_at?: string;
result?: "ok" | "error";
result_detail?: string;
}
// Zod schemas for validation
const telemetrySchema = z.object({
recorded_at: z.string().optional(),
lat: z.number(),
lng: z.number(),
altitude_m: z.number().optional(),
speed_kmh: z.number().optional(),
heading_deg: z.number().optional(),
accuracy_m: z.number().optional(),
battery_percent: z.number().optional(),
is_car_on: z.boolean().optional(),
raw: z.record(z.unknown()).optional(),
});
const receiptSchema = z.object({
command_id: z.number(),
acked_at: z.string().optional(),
executed_at: z.string().optional(),
result: z.enum(["ok", "error"]).optional(),
result_detail: z.string().optional(),
});
// API base from हुए env (add to .env: NEXT_PUBLIC_API_BASE=https://laravel-server.lab.audasmedia.com.au)
const API_BASE = process.env.NEXT_PUBLIC_API_BASE ?? "";
async function http<T>(path: string, init?: RequestInit): Promise<T> {
const res = await fetch(`${API_BASE}${path}`, {
method: "GET",
headers: { "Accept": "application/json", ...(init?.headers ?? {}) },
cache: "no-store",
...init,
});
if (!res.ok) {
const text = await res.text().catch(() => "");
throw new Error(`HTTP ${res.status} ${res.statusText}: ${text}`);
}
const text = await res.text();
return (text ? JSON.parse(text) : null) as T;
}
export async function postTelemetry(imei: string, body: TelemetryPost) {
const validatedBody = telemetrySchema.parse(body);
return http<{ ok: true }>(`/api/device/${imei}/telemetry`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(validatedBody),
});
}
export async function getCommands(imei: string, since?: string) {
const q = since ? `?since=${encodeURIComponent(since)}` : "";
return http<DeviceCommand[]>(`/api/device/${imei}/commands${q}`);
}
export async function postReceipt(imei: string, body: CommandReceiptPost) {
const validatedBody = receiptSchema.parse(body);
return http<{ ok: true }>(`/api/device/${imei}/command-receipts`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(validatedBody),
});
}
// Optional: Get latest GPS (adjust if your endpoint is /api/gps/latest-any)
export async function getLatestGPS(imei: string) {
return http<{ lat: number; lng: number; /* other fields */ }>(`/api/gps/latest-any?imei=${imei}`);
}