Managed to load server configuration from WebUI
This commit is contained in:
		
							
								
								
									
										174
									
								
								remote_frontend/src/api/ApiClient.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								remote_frontend/src/api/ApiClient.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,174 @@
 | 
			
		||||
interface RequestParams {
 | 
			
		||||
  uri: string;
 | 
			
		||||
  method: "GET" | "POST" | "DELETE" | "PATCH" | "PUT";
 | 
			
		||||
  allowFail?: boolean;
 | 
			
		||||
  jsonData?: any;
 | 
			
		||||
  formData?: FormData;
 | 
			
		||||
  upProgress?: (progress: number) => void;
 | 
			
		||||
  downProgress?: (e: { progress: number; total: number }) => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface APIResponse {
 | 
			
		||||
  data: any;
 | 
			
		||||
  status: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class ApiError extends Error {
 | 
			
		||||
  constructor(message: string, public code: number, public data: any) {
 | 
			
		||||
    super(`HTTP status: ${code}\nMessage: ${message}\nData=${data}`);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class APIClient {
 | 
			
		||||
  /**
 | 
			
		||||
   * Get backend URL
 | 
			
		||||
   */
 | 
			
		||||
  static backendURL(): string {
 | 
			
		||||
    const URL = import.meta.env.VITE_APP_BACKEND ?? "";
 | 
			
		||||
    if (URL.length === 0) throw new Error("Backend URL undefined!");
 | 
			
		||||
    return URL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Check out whether the backend is accessed through
 | 
			
		||||
   * HTTPS or not
 | 
			
		||||
   */
 | 
			
		||||
  static IsBackendSecure(): boolean {
 | 
			
		||||
    return this.backendURL().startsWith("https");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Perform a request on the backend
 | 
			
		||||
   */
 | 
			
		||||
  static async exec(args: RequestParams): Promise<APIResponse> {
 | 
			
		||||
    let body: string | undefined | FormData = undefined;
 | 
			
		||||
    let headers: any = {};
 | 
			
		||||
 | 
			
		||||
    // JSON request
 | 
			
		||||
    if (args.jsonData) {
 | 
			
		||||
      headers["Content-Type"] = "application/json";
 | 
			
		||||
      body = JSON.stringify(args.jsonData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Form data request
 | 
			
		||||
    else if (args.formData) {
 | 
			
		||||
      body = args.formData;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const url = this.backendURL() + args.uri;
 | 
			
		||||
 | 
			
		||||
    let data;
 | 
			
		||||
    let status: number;
 | 
			
		||||
 | 
			
		||||
    // Make the request with XMLHttpRequest
 | 
			
		||||
    if (args.upProgress) {
 | 
			
		||||
      const res: XMLHttpRequest = await new Promise((resolve, reject) => {
 | 
			
		||||
        const xhr = new XMLHttpRequest();
 | 
			
		||||
        xhr.upload.addEventListener("progress", (e) =>
 | 
			
		||||
          args.upProgress!(e.loaded / e.total)
 | 
			
		||||
        );
 | 
			
		||||
        xhr.addEventListener("load", () => resolve(xhr));
 | 
			
		||||
        xhr.addEventListener("error", () =>
 | 
			
		||||
          reject(new Error("File upload failed"))
 | 
			
		||||
        );
 | 
			
		||||
        xhr.addEventListener("abort", () =>
 | 
			
		||||
          reject(new Error("File upload aborted"))
 | 
			
		||||
        );
 | 
			
		||||
        xhr.addEventListener("timeout", () =>
 | 
			
		||||
          reject(new Error("File upload timeout"))
 | 
			
		||||
        );
 | 
			
		||||
        xhr.open(args.method, url, true);
 | 
			
		||||
        xhr.withCredentials = true;
 | 
			
		||||
        for (const key in headers) {
 | 
			
		||||
          if (headers.hasOwnProperty(key))
 | 
			
		||||
            xhr.setRequestHeader(key, headers[key]);
 | 
			
		||||
        }
 | 
			
		||||
        xhr.send(body);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      status = res.status;
 | 
			
		||||
      if (res.responseType === "json") data = JSON.parse(res.responseText);
 | 
			
		||||
      else data = res.response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Make the request with fetch
 | 
			
		||||
    else {
 | 
			
		||||
      const res = await fetch(url, {
 | 
			
		||||
        method: args.method,
 | 
			
		||||
        body: body,
 | 
			
		||||
        headers: headers,
 | 
			
		||||
        credentials: "include",
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      // Process response
 | 
			
		||||
      // JSON response
 | 
			
		||||
      if (res.headers.get("content-type") === "application/json")
 | 
			
		||||
        data = await res.json();
 | 
			
		||||
      // Text / XML response
 | 
			
		||||
      else if (
 | 
			
		||||
        ["application/xml", "text/plain"].includes(
 | 
			
		||||
          res.headers.get("content-type") ?? ""
 | 
			
		||||
        )
 | 
			
		||||
      )
 | 
			
		||||
        data = await res.text();
 | 
			
		||||
      // Binary file, tracking download progress
 | 
			
		||||
      else if (res.body !== null && args.downProgress) {
 | 
			
		||||
        // Track download progress
 | 
			
		||||
        const contentEncoding = res.headers.get("content-encoding");
 | 
			
		||||
        const contentLength = contentEncoding
 | 
			
		||||
          ? null
 | 
			
		||||
          : res.headers.get("content-length");
 | 
			
		||||
 | 
			
		||||
        const total = parseInt(contentLength ?? "0", 10);
 | 
			
		||||
        let loaded = 0;
 | 
			
		||||
 | 
			
		||||
        const resInt = new Response(
 | 
			
		||||
          new ReadableStream({
 | 
			
		||||
            start(controller) {
 | 
			
		||||
              const reader = res.body!.getReader();
 | 
			
		||||
 | 
			
		||||
              const read = async () => {
 | 
			
		||||
                try {
 | 
			
		||||
                  const ret = await reader.read();
 | 
			
		||||
                  if (ret.done) {
 | 
			
		||||
                    controller.close();
 | 
			
		||||
                    return;
 | 
			
		||||
                  }
 | 
			
		||||
                  loaded += ret.value.byteLength;
 | 
			
		||||
                  args.downProgress!({ progress: loaded, total });
 | 
			
		||||
                  controller.enqueue(ret.value);
 | 
			
		||||
                  read();
 | 
			
		||||
                } catch (e) {
 | 
			
		||||
                  console.error(e);
 | 
			
		||||
                  controller.error(e);
 | 
			
		||||
                }
 | 
			
		||||
              };
 | 
			
		||||
 | 
			
		||||
              read();
 | 
			
		||||
            },
 | 
			
		||||
          })
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        data = await resInt.blob();
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Do not track progress (binary file)
 | 
			
		||||
      else data = await res.blob();
 | 
			
		||||
 | 
			
		||||
      status = res.status;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Handle expired tokens
 | 
			
		||||
    if (status === 412) {
 | 
			
		||||
      window.location.href = "/";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!args.allowFail && (status < 200 || status > 299))
 | 
			
		||||
      throw new ApiError("Request failed!", status, data);
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      data: data,
 | 
			
		||||
      status: status,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								remote_frontend/src/api/ServerApi.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								remote_frontend/src/api/ServerApi.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
import { APIClient } from "./ApiClient";
 | 
			
		||||
 | 
			
		||||
export interface ServerConfig {
 | 
			
		||||
  authenticated: boolean;
 | 
			
		||||
  disable_auth: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let config: ServerConfig | null = null;
 | 
			
		||||
 | 
			
		||||
export class ServerApi {
 | 
			
		||||
  /**
 | 
			
		||||
   * Get server configuration
 | 
			
		||||
   */
 | 
			
		||||
  static async LoadConfig(): Promise<void> {
 | 
			
		||||
    config = (
 | 
			
		||||
      await APIClient.exec({
 | 
			
		||||
        uri: "/server/config",
 | 
			
		||||
        method: "GET",
 | 
			
		||||
      })
 | 
			
		||||
    ).data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get cached configuration
 | 
			
		||||
   */
 | 
			
		||||
  static get Config(): ServerConfig {
 | 
			
		||||
    if (config === null) throw new Error("Missing configuration!");
 | 
			
		||||
    return config;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user