Fix frontend API calls missing auth tokens

Several frontend components were using raw fetch() instead of the API
client, causing 401 Unauthorized errors when authentication is enabled.

Changes:
- SpoolmanSettings: Use api.getSpoolmanSettings/updateSpoolmanSettings
- ProjectsPage: Use api.importProjectFile for ZIP imports
- ProjectDetailPage: Use api.exportProjectZip for exports
- CameraPage/EmbeddedCameraViewer: Use api.getCameraStatus

Added to API client:
- getSpoolmanSettings()
- updateSpoolmanSettings()
- importProjectFile()
- exportProjectZip()
- getCameraStatus()

Added tests for auth token handling in client.test.ts
Fixed VirtualPrinterSettings test expectations to match i18n strings

Closes #231
This commit is contained in:
maziggy
2026-02-03 10:19:28 +01:00
parent 9e44692765
commit 158fa88b7a
12 changed files with 330 additions and 8843 deletions
@@ -184,17 +184,14 @@ export function EmbeddedCameraViewer({ printerId, printerName, viewerIndex = 0,
stallCheckIntervalRef.current = setInterval(async () => {
try {
const response = await fetch(`/api/v1/printers/${printerId}/camera/status`);
if (response.ok) {
const status = await response.json();
if (status.stalled || (!status.active && !streamError)) {
if (stallCheckIntervalRef.current) {
clearInterval(stallCheckIntervalRef.current);
stallCheckIntervalRef.current = null;
}
setStreamLoading(false);
attemptReconnect();
const status = await api.getCameraStatus(printerId);
if (status.stalled || (!status.active && !streamError)) {
if (stallCheckIntervalRef.current) {
clearInterval(stallCheckIntervalRef.current);
stallCheckIntervalRef.current = null;
}
setStreamLoading(false);
attemptReconnect();
}
} catch {
// Ignore errors
+2 -28
View File
@@ -6,32 +6,6 @@ import type { SpoolmanSyncResult, Printer } from '../api/client';
import { Card, CardContent, CardHeader } from './Card';
import { Button } from './Button';
interface SpoolmanSettingsData {
spoolman_enabled: string;
spoolman_url: string;
spoolman_sync_mode: string;
}
async function getSpoolmanSettings(): Promise<SpoolmanSettingsData> {
const response = await fetch('/api/v1/settings/spoolman');
if (!response.ok) {
throw new Error('Failed to load Spoolman settings');
}
return response.json();
}
async function updateSpoolmanSettings(data: Partial<SpoolmanSettingsData>): Promise<SpoolmanSettingsData> {
const response = await fetch('/api/v1/settings/spoolman', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error('Failed to save Spoolman settings');
}
return response.json();
}
export function SpoolmanSettings() {
const queryClient = useQueryClient();
const [localEnabled, setLocalEnabled] = useState(false);
@@ -45,7 +19,7 @@ export function SpoolmanSettings() {
// Fetch Spoolman settings
const { data: settings, isLoading: settingsLoading } = useQuery({
queryKey: ['spoolman-settings'],
queryFn: getSpoolmanSettings,
queryFn: api.getSpoolmanSettings,
});
// Fetch Spoolman status
@@ -93,7 +67,7 @@ export function SpoolmanSettings() {
// Save mutation
const saveMutation = useMutation({
mutationFn: () =>
updateSpoolmanSettings({
api.updateSpoolmanSettings({
spoolman_enabled: localEnabled ? 'true' : 'false',
spoolman_url: localUrl,
spoolman_sync_mode: localSyncMode,