Files
bookhoard/web/src/device-management.ts
T
john-okeefe c9ebc5b11a feat: add authorization header to device token regeneration
- Add Bearer token from localStorage to regenerate-token API request
- Update code formatting for consistency (double quotes, indentation)

This ensures the device token regeneration endpoint receives proper
authentication via the Authorization header.
2026-02-16 09:18:02 -05:00

103 lines
2.8 KiB
TypeScript

// Device Management - Token copy and regeneration
// Procedural style with proper types (no OOP)
interface RegenerateTokenResponse {
message: string;
auth_token: string;
device: {
id: string;
device_name: string;
device_type: string;
auth_token: string;
sync_enabled: boolean;
auto_sync: boolean;
sync_frequency_minutes: number;
};
sync_urls?: {
sync_url?: string;
markup?: string;
bookmark?: string;
init?: string;
progress?: string;
metadata?: string;
bookmarks?: string;
};
}
// Copy sync URL or auth token to clipboard
function copyToClipboard(text: string, label: string): void {
navigator.clipboard
.writeText(text)
.then(() => {
const toast = (window as any).showToast;
if (toast) {
toast.success(`${label} copied to clipboard`);
}
})
.catch((err: unknown) => {
console.error("Failed to copy:", err);
const toast = (window as any).showToast;
if (toast) {
toast.error("Failed to copy to clipboard");
}
});
}
// Regenerate device token with confirmation
function regenerateDeviceToken(deviceId: string, event: Event): void {
const confirmation =
"⚠️ This will revoke current token and generate a new one.\n\n" +
"The old token will immediately stop working.\n\n" +
"You will need to update your device configuration with new token.\n\n" +
"Continue?";
if (!confirm(confirmation)) {
return;
}
const btn = event.target as HTMLButtonElement;
const originalText = btn.innerHTML;
btn.disabled = true;
btn.innerHTML = "🔄 Regenerating...";
const token = localStorage.getItem("token");
fetch(`/api/devices/${deviceId}/regenerate-token`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
})
.then((response: Response) => {
if (!response.ok) {
throw new Error("Failed to regenerate token");
}
return response.json() as Promise<RegenerateTokenResponse>;
})
.then((_data: RegenerateTokenResponse) => {
const toast = (window as any).showToast;
if (toast) {
toast.success(
"Token regenerated successfully - update your device config",
);
}
// Reload page to show new token
setTimeout(() => location.reload(), 1500);
})
.catch((error: unknown) => {
console.error("Error:", error);
const toast = (window as any).showToast;
if (toast) {
toast.error("Failed to regenerate token");
}
if (btn) {
btn.disabled = false;
btn.innerHTML = originalText;
}
});
}
// Export functions for global access (called from template onclick attributes)
window.copyToClipboard = copyToClipboard;
window.regenerateDeviceToken = regenerateDeviceToken;