TeaSpeak-Client/modules/core/ui-loader/local_ui_cache.ts
2020-04-02 11:13:44 +02:00

137 lines
3.7 KiB
TypeScript

import * as path from "path";
import * as util from "util";
import * as fs from "fs-extra";
import * as electron from "electron";
export namespace v1 {
/* main entry */
interface LocalUICache {
fetch_history?: FetchStatus;
versions?: LocalUICacheEntry[];
remote_index?: UIVersion[] | UIVersion;
remote_index_channel?: string; /* only set if the last status was a channel only*/
local_index?: UIVersion;
}
interface FetchStatus {
timestamp: number;
/**
* 0 = success
* 1 = connect fail
* 2 = internal fail
*/
status: number;
}
interface LocalUICacheEntry {
version: UIVersion;
download_timestamp: number;
tar_file: string;
checksum: string; /* SHA512 */
}
export interface UIVersion {
channel: string;
version: string;
git_hash: string;
timestamp: number;
required_client?: string;
filename?: string;
client_shipped?: boolean;
}
}
export interface CacheFile {
version: number; /* currently 2 */
cached_ui_packs: CachedUIPack[];
}
export interface UIPackInfo {
timestamp: number; /* build timestamp */
version: string; /* not really used anymore */
versions_hash: string; /* used, identifies the version. Its the git hash. */
channel: string;
min_client_version: string; /* minimum version from the client required for the pack */
}
export interface CachedUIPack {
download_timestamp: number;
local_file_path: string;
local_checksum: string | "none"; /* sha512 of the locally downloaded file. */
//TODO: Get the remote checksum and compare them instead of the local one
pack_info: UIPackInfo;
status: "valid" | "invalid";
invalid_reason?: string;
}
let cached_loading_promise_: Promise<CacheFile>;
let ui_cache_: CacheFile = {
version: 2,
cached_ui_packs: []
};
async function load_() : Promise<CacheFile> {
const file = path.join(cache_path(), "data.json");
try {
if(!(await fs.pathExists(file)))
return ui_cache_;
const data = await fs.readJSON(file) as CacheFile;
if(!data)
throw "invalid data object";
else if(typeof data["version"] !== "number")
throw "invalid versions tag";
else if(data["version"] !== 2) {
console.warn("UI cache file contains an old version. Ignoring file and may override with newer version.");
return ui_cache_;
}
/* validating data */
if(!Array.isArray(data.cached_ui_packs))
throw "Invalid 'cached_ui_packs' entry within the UI cache file";
return (ui_cache_ = data as CacheFile);
} catch(error) {
console.warn("Failed to load UI cache file: %o. This will cause loss of the file content.", error);
return ui_cache_;
}
}
/**
* Will not throw or return undefined!
*/
export function load() : Promise<CacheFile> {
if(cached_loading_promise_) return cached_loading_promise_;
return (cached_loading_promise_ = load_());
}
export function unload() {
ui_cache_ = undefined;
cached_loading_promise_ = undefined;
}
/**
* Will not throw anything
*/
export async function save() {
const file = path.join(cache_path(), "data.json");
try {
if(!(await fs.pathExists(path.dirname(file))))
await fs.mkdirs(path.dirname(file));
await fs.writeJson(file, ui_cache_);
} catch (error) {
console.error("Failed to save UI cache file. This will may cause some data loss: %o", error);
}
}
export function cache_path() {
return path.join(electron.app.getPath('userData'), "cache", "ui");
}