From 23b54c7b13f6303560ec4402c9f52bf550e3e3c7 Mon Sep 17 00:00:00 2001 From: E44 <129310925+programmer-44@users.noreply.github.com> Date: Wed, 19 Nov 2025 17:05:23 +0100 Subject: [PATCH] chore(control): follow api docs, improve api code structure --- control/frontend/src/ts/api_handler.ts | 103 +++++++++++-------- control/frontend/src/ts/stores/thumbnails.ts | 6 +- control/frontend/src/ts/types.ts | 13 +++ 3 files changed, 75 insertions(+), 47 deletions(-) diff --git a/control/frontend/src/ts/api_handler.ts b/control/frontend/src/ts/api_handler.ts index 3b865bd..629e4d7 100644 --- a/control/frontend/src/ts/api_handler.ts +++ b/control/frontend/src/ts/api_handler.ts @@ -1,10 +1,12 @@ import { notifications } from "./stores/notification"; -import { to_display_status, type DisplayStatus, type FolderElement, type TreeElement } from "./types"; +import { to_display_status, type DisplayStatus, type FolderElement, type RequestResponse, type ShellCommandResponse, type TreeElement } from "./types"; import { get_uuid } from "./utils"; -export async function get_screenshot(ip: string): Promise { +export async function get_screenshot(ip: string): Promise { const options = { method: 'PATCH' }; - return await request_display(ip, '/takeScreenshot', options); + const response = await request_display(ip, '/takeScreenshot', options); + if (!response.ok || !response.blob) return null; + return response.blob; } export async function open_file(ip: string, path_to_file: string): Promise { @@ -47,11 +49,11 @@ done ` }) }; const raw_response = await request_display(ip, '/shellCommand', options); - if (is_cd_directory_error(ip, raw_response)) { - return []; - } + if (!raw_response.ok || !raw_response.json) return []; + const json_response = raw_response.json as ShellCommandResponse; + if (is_cd_directory_error(ip, json_response)) return []; - const response: FileInfo[] = raw_response.stdout.trim() + const response: FileInfo[] = json_response.stdout.trim() .split("\n") .filter(Boolean) .map((line: string) => JSON.parse(line) as FileInfo); @@ -84,15 +86,16 @@ export async function get_file_tree_data(ip: string, path: string): Promise { const options = { method: 'PATCH', headers: { 'content-type': 'application/json' }, @@ -106,70 +109,82 @@ export async function show_blackscreen(ip: string) { export async function ping_ip(ip: string): Promise { const raw_response = await request_control(`/ping?ip=${ip}`, { method: 'GET' }); - if (!raw_response) return null; - return raw_response.status ? to_display_status(raw_response.status) : null; + if (!raw_response.ok || !raw_response.json) return null; + return raw_response.json.status ? to_display_status(raw_response.json.status) : null; } -export async function get_thumbnail_blob(ip: string, path_to_file: string): Promise { - const raw_response = await request_display(ip, `/file/preview${path_to_file}`, { method: 'GET' }); - return raw_response; +export async function get_thumbnail_blob(ip: string, path_to_file: string): Promise { + const raw_response = await request_display(ip, `/file/preview${path_to_file}`, { method: 'GET' }, [415]); + if (!raw_response.ok || !raw_response.blob) return null + return raw_response.blob; } -async function request_display(ip: string, api_route: string, options: { method: string, headers?: Record, body?: any }): Promise { +async function request_display(ip: string, api_route: string, options: { method: string, headers?: Record, body?: any }, supress_error_handling_http_codes: number[] = []): Promise { const url = `http://${ip}:1323/api${api_route}`; - return await raw_request(url, options); + return await request(url, options, supress_error_handling_http_codes); } -async function request_control(api_route: string, options: { method: string, headers?: Record, body?: any }): Promise { +async function request_control(api_route: string, options: { method: string, headers?: Record, body?: any }): Promise { const url = `${window.location.origin}/api${api_route}`; - return await raw_request(url, options); + return await request(url, options); } -async function raw_request(url: string, options: { method: string, headers?: Record, body?: any }): Promise { +async function request(url: string, options: { method: string, headers?: Record, body?: any }, supress_error_handling_http_codes: number[] = []): Promise { try { const cache_buster = `${url.includes('?') ? '&' : '?'}=${Date.now()}`; console.log(url + cache_buster) const response = await fetch(url + cache_buster, options); - if (!response.ok) { - console.error(`HTTP error! Status: ${response.status}`); - notifications.push("error", `HTTP-Fehler bei API-Anfrage`, `${url}\nHTTP-Status: ${response.status}`); - } - const contentType = response.headers.get("content-type") || ""; - if (!contentType.includes("application/json")) { - return await response.blob(); - } else { - const json = await response.json(); - if (json.error && json.error !== '') { - notifications.push("error", `Interner Fehler bei API-Anfrage`, `${url}\nJSON: ${JSON.stringify(json)}`); + if (response.ok || supress_error_handling_http_codes.includes(response.status)) { + const contentType = response.headers.get("content-type") || ""; + let request_response: RequestResponse; + if (!contentType.includes("application/json")) { + const blob: Blob = await response.blob(); + request_response = { ok: response.ok, http_code: response.status, blob: blob }; + } else { + const json: Object = await response.json(); + request_response = { ok: response.ok, http_code: response.status, json: json }; } - return json; + console.log(request_response); + return request_response; } + + let error_description: string; + try { + const json: { description: string } = await response.json(); + error_description = json.description; + } catch (error_on_json_parsing: any) { + error_description = `unknown error: ${error_on_json_parsing}`; + } + console.error(url, error_description); + notifications.push("error", `Fehler ${response.status} bei API-Anfrage`, `${url}\nFehler: ${error_description}`); } catch (error: any) { if (error instanceof TypeError && /fetch|NetworkError/i.test(error.message)) { console.log("Request failed - Is the targeted device online?") } else { - console.error(error); - notifications.push("error", `Fehler bei API-Anfrage`, `${url}\nFehler: ${error}`); + console.error(url, error); + notifications.push("error", `Fataler Fehler bei API-Anfrage`, `${url}\nFehler: ${error}`); } - return null; } + return { ok: false }; } -function is_cd_directory_error(ip: string, raw_response: any): boolean { - if (!raw_response) return true; - if (raw_response.exitCode !== 0) { - if (raw_response.stderr && /bash: line \d+: cd: .+: No such file or directory/.test(raw_response.stderr)) { + + + +function is_cd_directory_error(ip: string, shell_response: ShellCommandResponse): boolean { + if (shell_response.exitCode !== 0) { + if (shell_response.stderr && /bash: line \d+: cd: .+: No such file or directory/.test(shell_response.stderr)) { console.log("current file_path does not exist on display:", ip); return true; } - console.error(raw_response); - notifications.push("error", "Fehler in ShellCommand", `Fehlercode: ${raw_response.exitCode}\nFehler: ${raw_response.stderr ?? ''}`) + console.error(shell_response); + notifications.push("error", "Fehler in ShellCommand", `Fehlercode: ${shell_response.exitCode}\nFehler: ${shell_response.stderr ?? ''}`) return true; } - if (raw_response.stdout.trim() === '') return true; + if (shell_response.stdout.trim() === '') return true; return false; } \ No newline at end of file diff --git a/control/frontend/src/ts/stores/thumbnails.ts b/control/frontend/src/ts/stores/thumbnails.ts index 04c6a15..7a9a075 100644 --- a/control/frontend/src/ts/stores/thumbnails.ts +++ b/control/frontend/src/ts/stores/thumbnails.ts @@ -1,7 +1,7 @@ import { get, writable, type Writable } from "svelte/store"; import { get_thumbnail_blob } from "../api_handler"; import { type FolderElement } from "../types"; -import { db } from "../indexdb/file_thumbnails.db"; +import { db, type ThumbnailBlobDBEntry } from "../indexdb/file_thumbnails.db"; import { get_file_type } from "../utils"; export const active_thumbnail_urls: Writable = writable([]); @@ -17,9 +17,9 @@ export async function generate_thumbnail(display_ip: string, path: string, folde await db.thumbnail_blobs.add({ hash: hash, blob: thumbnail_blob }); } -export async function get_thumbnail_url(hash: string): Promise { +export async function get_thumbnail_url(hash: string | null): Promise { if (hash === null) return null; - const thumbnail_blob = await db.thumbnail_blobs.get(hash); + const thumbnail_blob = await db.thumbnail_blobs.get(hash) as ThumbnailBlobDBEntry; if (!thumbnail_blob) return null; const new_url = URL.createObjectURL(thumbnail_blob.blob); active_thumbnail_urls.update((current: string[]) => { diff --git a/control/frontend/src/ts/types.ts b/control/frontend/src/ts/types.ts index d57f325..8613eca 100644 --- a/control/frontend/src/ts/types.ts +++ b/control/frontend/src/ts/types.ts @@ -1,6 +1,19 @@ import { FileBox, FileImage, FileText, FileVideoCamera, ImagePlay, type X } from "lucide-svelte"; import type { Snippet } from "svelte"; +export type RequestResponse = { + ok: boolean, + http_code?: number + blob?: Blob, + json?: any, +} + +export type ShellCommandResponse = { + stdout: string, + stderr: string, + exitCode: number, +} + export type SupportedFileType = { display_name: string; mime_type: string;