From 0497f5767ba5c5b2b2f04d68a64794f95c6c742a Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Fri, 23 Jan 2026 13:54:20 -0500 Subject: [PATCH] Initial scaffold for Aether Native V3 Electron Launcher --- Aether App Native setup documentation.txt | 8 - ae_native_app_config.default.json | 73 - aether_app_native.code-workspace | 8 - app/css/aether_app_native_v3.css | 28 - app/files/launcher.reset | 1 - app/files/test.txt | 1 - app/img/favicon.ico | Bin 1626 -> 0 bytes app/img/oneskyit_logo.png | Bin 9760 -> 0 bytes app/img/osit_logo_100.png | Bin 7497 -> 0 bytes app/img/osit_logo_150.png | Bin 9760 -> 0 bytes app/img/osit_logo_32.png | Bin 1626 -> 0 bytes app/img/site_background.png | Bin 41785 -> 0 bytes app/img/site_background.svg | 1 - app/img/site_background.webp | Bin 16974 -> 0 bytes app/index.bootstrap.html | 47 - app/index.current.html | 362 ---- app/index.html | 279 --- app/js/aether_app_native_v3.js | 632 ------ app/js/aether_app_native_v4.js | 1191 ----------- app/script.current.js | 1391 ------------ app/script.js | 0 app/style.css | 0 app/style.current.css | 28 - app/test.html | 279 --- dist/main/api_client.js | 69 + dist/main/api_client.js.map | 1 + dist/main/config_loader.js | 62 + dist/main/config_loader.js.map | 1 + dist/main/index.js | 99 + dist/main/index.js.map | 1 + dist/preload/index.js | 10 + dist/preload/index.js.map | 1 + dist/shared/types.js | 3 + dist/shared/types.js.map | 1 + index.js | 586 ----- package-lock.json | 2354 --------------------- package.json | 25 - src/main/api_client.ts | 79 + src/main/config_loader.ts | 30 + src/main/index.ts | 76 + src/preload/index.ts | 8 + src/shared/types.ts | 20 + tests/test_device_lookup.py | 55 + tsconfig.json | 21 + 44 files changed, 537 insertions(+), 7294 deletions(-) delete mode 100644 Aether App Native setup documentation.txt delete mode 100644 ae_native_app_config.default.json delete mode 100644 aether_app_native.code-workspace delete mode 100644 app/css/aether_app_native_v3.css delete mode 100644 app/files/launcher.reset delete mode 100644 app/files/test.txt delete mode 100755 app/img/favicon.ico delete mode 100644 app/img/oneskyit_logo.png delete mode 100644 app/img/osit_logo_100.png delete mode 100644 app/img/osit_logo_150.png delete mode 100644 app/img/osit_logo_32.png delete mode 100644 app/img/site_background.png delete mode 100644 app/img/site_background.svg delete mode 100644 app/img/site_background.webp delete mode 100644 app/index.bootstrap.html delete mode 100644 app/index.current.html delete mode 100644 app/index.html delete mode 100644 app/js/aether_app_native_v3.js delete mode 100644 app/js/aether_app_native_v4.js delete mode 100644 app/script.current.js delete mode 100644 app/script.js delete mode 100644 app/style.css delete mode 100644 app/style.current.css delete mode 100644 app/test.html create mode 100644 dist/main/api_client.js create mode 100644 dist/main/api_client.js.map create mode 100644 dist/main/config_loader.js create mode 100644 dist/main/config_loader.js.map create mode 100644 dist/main/index.js create mode 100644 dist/main/index.js.map create mode 100644 dist/preload/index.js create mode 100644 dist/preload/index.js.map create mode 100644 dist/shared/types.js create mode 100644 dist/shared/types.js.map delete mode 100644 index.js delete mode 100644 package-lock.json delete mode 100644 package.json create mode 100644 src/main/api_client.ts create mode 100644 src/main/config_loader.ts create mode 100644 src/main/index.ts create mode 100644 src/preload/index.ts create mode 100644 src/shared/types.ts create mode 100644 tests/test_device_lookup.py create mode 100644 tsconfig.json diff --git a/Aether App Native setup documentation.txt b/Aether App Native setup documentation.txt deleted file mode 100644 index f4a925b..0000000 --- a/Aether App Native setup documentation.txt +++ /dev/null @@ -1,8 +0,0 @@ -git clone https://scott_idem@bitbucket.org/oneskyit/one-sky-it-app-native.git ~/OSIT_dev/aether_app_native - -cd ~/OSIT_dev/aether_app_native/ -npm update -npm start - -git branch -git checkout development diff --git a/ae_native_app_config.default.json b/ae_native_app_config.default.json deleted file mode 100644 index ee150c2..0000000 --- a/ae_native_app_config.default.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "developer_tools": false, - "native_app_which_html": "default", - "native_app_index_path": "[home]/OSIT/native_app/app/index.html", - "native_app_index_url": "https://app.oneskyit.com/native/index.html", - - "native_app_js_css_base_url": "https://demo.oneskyit.com", - "native_app_js_css_base_url_bak": "https://bak-demo.oneskyit.com", - - "account_id": "", - "event_id": "", - "event_device_id": "soon_to_be_required", - "event_location_id": "", - "event_session_id": "", - - "account_code": "", - "event_code": "", - "event_device_code": "eventually_use_code", - "event_location_code": "", - "event_session_code": "", - - "app_root_path": "[home]/OSIT/native_app", - - "api_protocol": "https", - "api_server": "api.oneskyit.com", - "api_port": 443, - "api_path": "", - "api_secret_key": "ABCD1234XYZ", - - "api_protocol_backup": "https", - "api_server_backup": "bak-api.oneskyit.com", - "api_port_backup": 443, - "api_path_backup": "", - "api_secret_key_backup": "ABCD1234XYZ", - - "access_control_allow_origin": "*", - - "idb_name": "osit", - - "local_file_cache_path": "[home]/OSIT/file_cache", - "host_file_temp_path": "[home]/OSIT/temp", - - - - "display_arrangement": "mirror_and_extend", - "display_builtin_resolution": "", - "display_builtin_refresh": "", - "display_builtin_rotation": "", - "display_external_resolution": "", - "display_external_refresh": "", - "display_external_rotation": "", - - "audio_out_volume": null, - "audio_in_volume": null, - - "recording_fps": 30, - "recording_show_cursor": true, - "recording_highlight_clicks": false, - "recording_screen_id": null, - "recording_audio_device_id": null, - "known_builtin_screen_ids": [69732032, 69733952, 69733248], - "known_builtin_audio_device_ids": [ "AppleHDAEngineInput:1B,0,1,0:1", "BuiltInMicrophoneDevice" ], - "recording_video_codec": "h264", - "recording_path": "[home]/recordings", - "recording_base_filename": "recording", - "aperture_bin_path": null, - "recording_start_datetime": "2019-10-12 01:01:01Z", - "recording_stop_datetime": "2019-10-31 23:59:59Z", - "recordings_datetime": [ - { "start": "2019-10-11T09:50:00.00", "stop": "2019-10-11T10:15:00.00" }, - { "start": "2019-10-11T10:50:00.00", "stop": "2019-10-11T11:15:00.00" } - ] -} diff --git a/aether_app_native.code-workspace b/aether_app_native.code-workspace deleted file mode 100644 index 876a149..0000000 --- a/aether_app_native.code-workspace +++ /dev/null @@ -1,8 +0,0 @@ -{ - "folders": [ - { - "path": "." - } - ], - "settings": {} -} \ No newline at end of file diff --git a/app/css/aether_app_native_v3.css b/app/css/aether_app_native_v3.css deleted file mode 100644 index cbebafa..0000000 --- a/app/css/aether_app_native_v3.css +++ /dev/null @@ -1,28 +0,0 @@ -body { - /* min-height: 100%; - height: 100%; - max-height: 100%; */ - - /* margin: .1em; - padding: .1em; */ -} - -section#Main-Body { - /* outline: solid thin red; */ - - /* min-height: 100%; - height: 100%; - max-height: 100%; */ -} - -section#Main-Nav-Menu { - /* min-height: 100%; - height: 100%; - max-height: 100%; */ -} - -section#Main-Content { - /* min-height: 100%; - height: 100%; - max-height: 100%; */ -} \ No newline at end of file diff --git a/app/files/launcher.reset b/app/files/launcher.reset deleted file mode 100644 index 3a0225e..0000000 --- a/app/files/launcher.reset +++ /dev/null @@ -1 +0,0 @@ -launcher reset diff --git a/app/files/test.txt b/app/files/test.txt deleted file mode 100644 index 5eb252b..0000000 --- a/app/files/test.txt +++ /dev/null @@ -1 +0,0 @@ -test txt diff --git a/app/img/favicon.ico b/app/img/favicon.ico deleted file mode 100755 index cf4994903e3855b16cff0b2ad17a7c3e66ae59e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1626 zcmV-g2BrClP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L00gN300gN4SoPiQ00007bV*G`2iyz; z5<4*rqpSx200r7fL_t(o!|j(_h+bC}$A4>|ea?5z+%hvv(#A_{GBI9CZ6Xw_U=vg9 zG#V`wq4Y^fnsy!{eJb`%un$&5siMU3LP^0YeMwN1Rwu@wG-xQ2ASPpiPJ-6h#K}x% zzWKg$+slV@=DUA0ee=Z)n{zmOum4`_zb<rC3&mYu_vwHsW^Vi$tU3swW#5EmN`eLF5lxp4hi!CO(b`s0zjd%|IQ+ zy9y#Kn#~XVZen`xHm_%Vr9iZv0`*>6o~RPO5$^w@^TI$t5h-KKG~;uP1uqKU2p>Fp zHE;O6bJY50!Qr!CCK?BAVM~0sW%2d*Y`$~rVW(>XT8L(KFaa3T#&>g*zcI1z z8ZK=vionWs;l;gQ=~m$I>yI-w`7*ZBm8nsBlb!jcGz-UlX?10u7DswPivohJ-H5OF z_f%?jyxaA83JX^r=SLs?Y10J%?Zv=;kxU^Vlthek_EqHOMbK;qr z8R}Xuo;i$de~zgahMNlPoH~RJwqrcSMsrLf)viWlr)*QX8@{2ra#L@;=PU}zhv;8HMtQ)n$c+$!7S^O+Zpc%>zR zlx3ulQGl$zl+IW71c`~dt#=t1Ti_#Xz!>VMA}j z4#QRqX*DlclU`FumS;J(d$zqFxcrMJnf&B|MryUvmew+c?g$YhWw!KYK~)hWRE90L z{}E#cO!uA~=70NCcSB+WOEeMS`5m4Rs4p3uQ@pp?XQ?Xwphq}ryRh;E;ziqi8%|Em z;91JNZUJ*jxXF@L9qRg9U$7;pQ<9aOaMbq1@_hKfqciOaOrL*AZKTrK>s%XhRe(}w4yAh;kpQ@@DYe-jfyudUS>)3-r zTFnWDie|PKTY@0QN6aYid*q}ch(1Cv@R@a+WDzOKv@c|FA7Bx(L@`0Jx38!OW=WP`VQ}JpL^k+? zkm5qAFHQ6EXMe@rpKH&Fxu4}6{N6(h-TBM5>emkog?RZDi?J^fuS`=J+O^?NOuYOW zwsMMFS8{&;eBNDzqEp1TUnLrQ_-$Za!FWG39J^$R;`!K-+%LT(NFXVtQ(h}W%ik6{y%{K Y0_>udzR@tf*Z=?k07*qoM6N<$f-{T{WB>pF diff --git a/app/img/oneskyit_logo.png b/app/img/oneskyit_logo.png deleted file mode 100644 index 64f24870c7c69df27c92adf46d16968ed98b323d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9760 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 TO}#iT00000NkvXXu0mjfb$@SF diff --git a/app/img/osit_logo_150.png b/app/img/osit_logo_150.png deleted file mode 100644 index 64f24870c7c69df27c92adf46d16968ed98b323d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9760 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 Y0_>udzR@tf*Z=?k07*qoM6N<$f-{T{WB>pF diff --git a/app/img/site_background.png b/app/img/site_background.png deleted file mode 100644 index fe4b84f5a96d266dd97aa624135627aa76152d26..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41785 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 \ No newline at end of file diff --git a/app/img/site_background.webp b/app/img/site_background.webp deleted file mode 100644 index e5a15b56dbea18156314c04a0c08969126f4430c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16974 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 - - - - - - One Sky IT's Aether App - External XXX - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Loaded HTML - - - diff --git a/app/index.current.html b/app/index.current.html deleted file mode 100644 index 6ddb121..0000000 --- a/app/index.current.html +++ /dev/null @@ -1,362 +0,0 @@ - - - - - - - One Sky IT's Aether App - External - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Site-Nav-Menu
- -
- - - - - -
-
System-Notifications (and Site-Notifications)
- -
- -
- -
-
-
- - - -
- -
- - - -
System-Debug
- - - - - - - - - - diff --git a/app/index.html b/app/index.html deleted file mode 100644 index a0ee9ca..0000000 --- a/app/index.html +++ /dev/null @@ -1,279 +0,0 @@ - - - - - - - One Sky IT's Aether App - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Site-Nav-Menu
- -
- - - - - -
-
System-Notifications (and Site-Notifications)
- -
- -
- -
-
- - - -
- -
- - - -
System-Debug
- - - - - - - - - - diff --git a/app/js/aether_app_native_v3.js b/app/js/aether_app_native_v3.js deleted file mode 100644 index 1297d64..0000000 --- a/app/js/aether_app_native_v3.js +++ /dev/null @@ -1,632 +0,0 @@ -'use strict'; -const os = require('os'); -const path = require('path'); -const fs = require('fs'); -const fs_promises = require('node:fs/promises'); -const child_process = require('child_process'); -const { ipcRenderer } = require('electron'); - -// import psList from 'ps-list'; -// const ps_list = require('ps-list'); - -let home_directory = require('os').homedir(); -console.log('Home: '+home_directory); - -let tmp_directory = require('os').tmpdir(); -console.log('Temporary: '+tmp_directory); - -let config = null; - -exports.load_config = function () { - console.log('*** Electron framework: load_config() ***'); - - let cwd = process.cwd(); - console.log(`CWD: ${cwd}`); - - try { - if (cwd == '/') { - cwd = home_directory; - } - console.log('Reading directory...'); - let directory_list = fs_promises.readdir(cwd).then(function (read_dir_result) { - console.log('Got contents:'); - for (let file of read_dir_result) { - console.log(file); - } - }); - } catch (err) { - console.error(err); - } - - // let home_directory = require('os').homedir(); - // console.log('Home: '+home_directory); - - // let tmp_directory = require('os').tmpdir(); - // console.log('Temporary: '+tmp_directory); - - // let config = null; - let config_directory = null; - // let default_config_path = path.join(process.cwd(),'config.json.default'); - let default_config_path = 'config.json.default'; - console.log(default_config_path); - let config_path = null; - - // Set the config path for macOS or Linux - if (os.platform == 'darwin') { - config_directory = path.join(home_directory, 'Library/Application Support/OSIT'); - console.log('macOS config directory: '+config_directory); - } else if (os.platform == 'linux') { - config_directory = path.join(home_directory, '.config/OSIT'); - console.log('Linux config directory: '+config_directory); - } - - // Look for the config file and copy the default if not found. - if (fs.existsSync(config_directory)) { - console.log('Config directory found: '+config_directory); - } else { - fs.mkdirSync(config_directory); - console.log('Config directory created: '+config_directory); - - //default_config_path = path.join(process.cwd(),'config.json.default'); - // config_path = path.join(config_directory, 'config.json'); - // fs.copyFileSync(default_config_path, config_path); - // console.log('Default config file copied: '+config_directory); - } - - config_path = path.join(config_directory, 'config.json'); - - // Attempt to open the config file. The preferred location is based on the OS's config directory. - if (fs.existsSync(config_path)) { - console.log(`Config file (config.json) found under ${config_directory}`); - } else if (!fs.existsSync(config_path) && fs.existsSync(default_config_path)) { - fs.copyFileSync(default_config_path, config_path); - console.log('Default config file copied: '+config_directory); - - // config = JSON.parse(fs.readFileSync(config_path)); - // console.log('Config file read.'); - } else if (fs.existsSync(path.join(cwd, 'config.json'))) { - //fs.copyFileSync(default_config_path, config_path); - //console.log('Default config file copied: '+config_directory); - - console.log(`Config file (config.json) not found under ${config_directory}. Using config in CWD. ${cwd}`); - config_path = path.join(cwd, 'config.json'); - - console.log(`Config file (config.json) not found under ${config_directory}. Using config in CWD. ${cwd}`); - let found_config_path = path.join(cwd, 'config.json'); - - fs.copyFileSync(found_config_path, config_path); - console.log(`Found config file copied: ${config_directory}`); - } else if (fs.existsSync(path.join(cwd, 'config.json.default'))) { - console.log(`Config file (config.json) not found under ${config_directory} or CWD. Using default config in CWD. ${cwd}`); - default_config_path = path.join(cwd, 'config.json.default'); - - fs.copyFileSync(default_config_path, config_path); - console.log(`Default config file copied: ${config_directory}`); - } else { - console.log('Can not find a config file.'); - - return false; - } - - config = JSON.parse(fs.readFileSync(config_path)); - console.log('Config file read.'); - - config.home_directory = home_directory; // From the OS platform - config.tmp_directory = tmp_directory; // From the OS platform - - config.app_root_path = config.app_root_path.replace('[home]', home_directory); - config.app_root_path = config.app_root_path.replace('[tmp]', tmp_directory); - console.log(config.app_root_path); - - config.local_file_cache_path = config.local_file_cache_path.replace('[home]', home_directory); - config.local_file_cache_path = config.local_file_cache_path.replace('[tmp]', tmp_directory); - console.log(config.local_file_cache_path); - // if (fs.existsSync(config.local_file_cache_path)) { - // } else { - // fs.mkdirSync(config.local_file_cache_path); - // console.log(`Host file cache directory created: ${config.local_file_cache_path}`); - // } - - config.host_file_temp_path = config.host_file_temp_path.replace('[home]', home_directory); - config.host_file_temp_path = config.host_file_temp_path.replace('[tmp]', tmp_directory); - console.log(config.host_file_temp_path); - // if (fs.existsSync(config.host_file_temp_path)) { - // } else { - // fs.mkdirSync(config.host_file_temp_path); - // console.log(`Host file temp directory created: ${config.host_file_temp_path}`); - // } - - let import_config_to_ipc_result = ipcRenderer.invoke('import_config', config).then((result) => { - console.log('IPC import config finished'); - console.log(result); - return true; - }) - - //console.log(config); - return config; -} - - -// Check for local file -// Updated 2022-05-06 -exports.check_local_file = async function ({local_file_path, filename}) { - console.log('*** Electron framework export: check_local_file() ***'); - // console.log('Check for local file'); - console.log(`Local File Path: ${local_file_path}; Filename: ${filename}`); - - let full_local_file_path = path.join(local_file_path, filename); - console.log(full_local_file_path); - - if (fs.existsSync(full_local_file_path)) { - console.log(`Local file exists: ${full_local_file_path}`); - return true; - } else { - return false; - } -} - - -// Check local hash file cache -// Used by Svelte Event Launcher -// NOTE: Trying to replace this with something directly in the Svelte app part. 2022-10-11 -// Updated 2022-05-06 -exports.check_hash_file_cache = async function ({local_file_cache_path, hash}) { - // console.log('*** Electron framework export: check_hash_file_cache() ***'); - // console.log('Check local hash file cache'); - console.log(`*** Electron framework export: check_hash_file_cache() *** Host File Cache Path: ${local_file_cache_path}; Hash: ${hash}`); - - let hash_filename = `${hash}.file`; - - let subdirectory = hash_filename.substring(0,2); - let subdirectory_path = path.join(local_file_cache_path, subdirectory); - if (fs.existsSync(subdirectory_path)) { - } else { - console.log(`Hashed file subdirectory not found in cache: ${subdirectory_path}`); - return false; - } - - let hash_file_cache_path = path.join(subdirectory_path, hash_filename); - // console.log(hash_file_cache_path); - - if (fs.existsSync(hash_file_cache_path)) { - // console.log(`Hashed file exists in cache: ${hash_file_cache_path}`); - return true; - } else { - console.log(`Hashed file not found in cache: ${hash_file_cache_path}`); - return false; - } -} - - -// Download hash file to cache -// Used by Svelte Event Launcher -// Updated 2022-05-06 -exports.download_hash_file_to_cache = async function ({api_base_url, local_file_cache_path, event_file_id=null, hash=null}) { - // console.log('*** Electron framework export: download_hash_file_to_cache() ***'); - // console.log('Download hash file to cache'); - console.log(`*** Electron framework export: download_hash_file_to_cache() *** Base URL: ${api_base_url}; Host File Cache Path: ${local_file_cache_path}; Event File ID: ${event_file_id}; Hash: ${hash}`); - - let endpoint = `/event/file/${event_file_id}/download`; - - let hash_filename = `${hash}.file`; - - let subdirectory = hash_filename.substring(0,2); - let subdirectory_path = path.join(local_file_cache_path, subdirectory); - if (fs.existsSync(subdirectory_path)) { - } else { - fs.mkdirSync(subdirectory_path); - console.log(`Subdirectory directory created: ${subdirectory_path}`); - } - - let hash_file_cache_path = path.join(subdirectory_path, hash_filename); - if (fs.existsSync(hash_file_cache_path)) { - if (check_hash) { - const file_buffer = fs.readFileSync(hash_file_cache_path); - const file_hash_sha256 = crypto.createHash('sha256'); - file_hash_sha256.update(file_buffer); - - const file_hash_sha256_check = file_hash_sha256.digest('hex');S - if (file_hash_sha256_check == hash) { - console.log('File hash match', file_hash_sha256_check); - return true; - } else { - // This should only happen if the file is actively being downloaded or it is corrupt. - console.log('File hash does not match', file_hash_sha256_check); - return false; - } - } - } - // console.log(`!!!ABOUT TO CALL DOWNLOAD FILE HANDLER!!! exports.download_hash_file_to_cache(); Base URL: ${api_base_url}`); - let download_file_result = await ipcRenderer.invoke('download_file', api_base_url, endpoint, hash_file_cache_path).then((result) => { - if (result) { - console.log('IPC download file process finished successfully'); - return true; - } else if (result == null) { - console.log('IPC Download Result (file not found?):', result); - return null; - } else { - console.log('IPC Download Result (file being downloaded or something went wrong):', result); - return false; - } - }); - // console.log(`!!!DONE WITH DOWNLOAD FILE HANDLER!!! exports.download_hash_file_to_cache(); Base URL: ${api_base_url}`); - - return download_file_result; -} - - -// Open cached hash file after copying to temp directory -// Used by Svelte Event Launcher -// NOTE: Trying to replace this with something directly in the Svelte app part. 2022-10-11 -// Updated 2022-05-06 -exports.open_hash_file_to_temp = async function ({local_file_cache_path, hash, host_file_temp_path, filename}) { - console.log('*** Electron framework export: open_hash_file_to_temp() ***'); - // console.log('Open cached hash file after copying to temp directory'); - console.log(`Host File Cache Path: ${local_file_cache_path}; Hash: ${hash}; Host File Temp Path: ${host_file_temp_path}; Filename: ${filename}`); - - let subdirectory = hash.substring(0,2); - let subdirectory_path = path.join(local_file_cache_path, subdirectory); - if (fs.existsSync(subdirectory_path)) { - } else { - console.log(`Hashed file subdirectory not found in cache: ${subdirectory_path}`); - return false; - } - - let open_hash_file_to_temp_result = await ipcRenderer.invoke('open_hash_file_to_temp', subdirectory_path, hash, host_file_temp_path, filename).then((result) => { - console.log('IPC open hash file to temp finished'); - console.log(result); - return true; - }) - - // let result = await ipcRenderer.send('open_local_file', local_file_cache_path, hash, host_file_temp_path, filename); - // console.log(result); - - console.log(open_hash_file_to_temp_result); - console.log('End: open_hash_file_to_temp()'); - if (open_hash_file_to_temp_result) { - console.log('File opened successfully'); - return true; - } else { - console.log('File was not opened successfully'); - return false; - } -} - - -// Open local file -// Used by Svelte Event Launcher -// NOTE: Trying to replace this with something directly in the Svelte app part. 2022-10-11 -// Updated 2022-03-10 -exports.open_local_file = async function ({local_file_path, filename}) { - console.log('*** Electron framework export: open_local_file() ***'); - // console.log('Open local file'); - console.log(`Local File Path: ${local_file_path}; Filename: ${filename}`); - - // let full_local_file_path = path.join(local_file_path, filename); - // console.log(full_local_file_path); - - // if (fs.existsSync(full_local_file_path)) { - // console.log(`Local file exists: ${full_local_file_path}`); - // // return true; - // } else { - // return false; - // } - - let open_local_file_result = await ipcRenderer.invoke('open_local_file', local_file_path, filename).then((result) => { - console.log('IPC open local file finished'); - console.log(result); - return true; - }) - - console.log(open_local_file_result); - console.log('End: open_local_file()'); - if (open_local_file_result) { - console.log('File opened successfully'); - return true; - } else { - console.log('File was not opened successfully'); - return false; - } -} - - -// // Check local file cache and download from server if needed. -// // Updated 2022-03-09 -// // exports.check_file_cache = async function ({local_file_cache_path, event_file_id, hash}) { -// exports.check_file_cache = async function ({api_base_url, local_file_cache_path, event_file_id, hash}) { -// console.log('*** Electron framework export: check_file_cache() ***'); -// // console.log('Check local file cache and download from server if needed.'); -// console.log(`Host File Cache Path: ${local_file_cache_path}; Event File ID: ${event_file_id}; Hash: ${hash}`); - -// // NOTE: event_file_id is the event_file.id_random or event_file.event_file_id_random -// let hash_filename = hash+'.file'; - -// let save_path = path.join(local_file_cache_path, hash_filename); -// console.log(save_path); - -// if (fs.existsSync(save_path)) { -// console.log('Hashed file cache already exists: '+save_path); -// return true; -// } else { -// console.log('Hashed file not found in local cache. Downloading file: '+save_path); -// let endpoint = `/event/file/${event_file_id}/download`; -// let result = await ipcRenderer.send('download_file', api_base_url, endpoint, save_path); // Must download file using main node.js thread. -// console.log(result); - -// return new Promise((resolve, reject) => { -// ipcRenderer.once('download_file_reply', function(event, response){ -// console.log(response); -// return response; -// }) -// resolve(true); -// }); - -// // await ipcRenderer.once('download_file_reply', function(event, response){ -// // console.log(response); -// // return response; -// // }); - -// // result.then(function (response) { -// // console.log('Downloaded!!!???'); -// // return true; -// // }).catch(function (error) { -// // console.log(error); -// // return false; -// // }); - -// // return result; - -// // console.log(result); -// // if (result) { -// // return true; -// // } else { -// // return false; -// // } -// } -// } - - -// Check local file cache and download from server if needed. Must use IPC to Main to download file. Set a Promise to wait for download_file_reply. -// Updated 2022-03-09 -async function check_file_cache({api_base_url, local_file_cache_path, event_file_id, hash}) { - console.log('*** Electron framework: check_file_cache() ***'); - // console.log('Check local file cache and download from server if needed.'); - console.log(`Host File Cache Path: ${local_file_cache_path}; Event File ID: ${event_file_id}; Hash: ${hash}`); - - // NOTE: event_file_id is the event_file.id_random or event_file.event_file_id_random - let hash_filename = hash+'.file'; - - let save_path = path.join(local_file_cache_path, hash_filename); - console.log(save_path); - - if (fs.existsSync(save_path)) { - console.log('Hashed file cache already exists: '+save_path); - return true; - } else { - console.log('Hashed file not found in local cache. Downloading file: '+save_path); - let endpoint = `/event/file/${event_file_id}/download`; - let result = await ipcRenderer.send('download_file', api_base_url, endpoint, save_path); // Must download file using main node.js thread. - console.log(result); - - return new Promise((resolve, reject) => { - ipcRenderer.once('download_file_reply', function(event, response){ - console.log(response); - return response; - }) - resolve(true); - }); - - // await ipcRenderer.once('download_file_reply', function(event, response){ - // console.log(response); - // return response; - // }); - - // result.then(function (response) { - // console.log('Downloaded!!!???'); - // return true; - // }).catch(function (error) { - // console.log(error); - // return false; - // }); - - // return result; - - // console.log(result); - // if (result) { - // return true; - // } else { - // return false; - // } - } -} - - -// IPC to Main: Open local file cache if available. Copy to temp directory with given filename first. -// Updated 2022-03-09 -async function open_local_file({local_file_cache_path, hash, host_file_temp_path, filename}) { - console.log('*** Electron framework: open_local_file() ***'); - // console.log('Open local file cache if available. Copy to temp directory with given filename first.'); - console.log(`Host File Cache Path: ${local_file_cache_path}; Hash: ${hash}; Host File Temp Path: ${host_file_temp_path}; Filename: ${filename}`); - - console.log(local_file_cache_path); - console.log(hash); - console.log(filename); - - let result = await ipcRenderer.send('open_local_file', local_file_cache_path, hash, host_file_temp_path, filename); - console.log(result); - - return true; -} - - -// No longer needed? Not referenced as of 2022-10-11 -exports.check_file_cache_and_open_local_file = async function ({local_file_cache_path, event_file_id, hash, host_file_temp_path, filename}) { - console.log('*** Electron framework: check_file_cache_and_open_local_file() ***'); - console.log('Checking the local file cache against the remote server and then opening the local file.'); - - let check_file_cache_result = check_file_cache({local_file_cache_path: local_file_cache_path, event_file_id: event_file_id, hash: hash}); - console.log(check_file_cache_result); - - if (check_file_cache_result) { - let open_local_file_result = open_local_file({local_file_cache_path: local_file_cache_path, hash: hash, host_file_temp_path: host_file_temp_path, filename: filename}); - console.log(open_local_file_result); - - return open_local_file_result; - } - - ipcRenderer.once('download_file_reply', function(event, response){ - console.log(response); - - let open_local_file_result = open_local_file({local_file_cache_path: local_file_cache_path, hash: hash, host_file_temp_path: host_file_temp_path, filename: filename}); - console.log(open_local_file_result); - - return open_local_file_result; - }) -} - - -// Kill processes -// Updated 2022-05-07 -exports.kill_processes = async function ({process_name = null}) { - console.log('*** Electron framework export: kill_processes() ***'); - console.log(process_name); // process_name or grep pattern - - let cmd = ''; - if (os.platform == 'darwin') { - // cmd = `osascript -e 'quit app "${process_name}" saving no'`; - cmd = `osascript -e 'quit application "${process_name}" saving no'`; - } else { - cmd = `pkill ${process_name}`; - } - - child_process.exec(cmd, (err, stdout, stdin) => { - // if (err) throw err; - if (err) console.log(err); - console.log(stdout); - }); - console.log(`Killed processes matching ${process_name}`); - - if (os.platform == 'darwin') { - if (process_name == 'Parallels:Acrobat Reader') { - // Regular expression: (Parallels).*(Acrobat Reader) - // This will find any process with Parallels and Acrobat Reader in the name - cmd = `pkill -i -f '(Parallels).*(Acrobat Reader)'`; - - child_process.exec(cmd, (err, stdout, stdin) => { - if (err) throw err; - console.log(stdout); - }); - console.log('Killed Parallels Acrobat Reader process'); - } - - if (process_name == 'Parallels:PowerPoint') { - // Regular expression: (Parallels).*(PowerPoint) - // This will find any process with Parallels and PowerPoint in the name - cmd = `pkill -i -f '(Parallels).*(PowerPoint)'`; - - child_process.exec(cmd, (err, stdout, stdin) => { - if (err) throw err; - console.log(stdout); - }); - console.log('Killed Parallels PowerPoint process'); - } - } - - // let signal = 'SIGTERM'; // 'SIGTERM', 'SIGINT', 'SIGHUP' - // process.kill(pid, signal); - // process.kill(pid, 0); // Special case test if process exists - - return true; -} - - -// Run raw osascript -// Updated 2022-05-07 -exports.run_osascript = async function ({cmd=null, interactive=false, language=null, flags='h', program_file=null}) { - console.log('*** Electron framework export: run_osascript() ***'); - console.log(cmd); - - if (os.platform == 'darwin') { - } else { - console.log('Not available for this platform. macOS (darwin) only.'); - return false; - } - - let osascript_str = ''; - - if (Array.isArray(cmd)) { - console.log('List of cmd strings'); - let cmds_str = ''; - for (let i = 0; i < cmd.length; i++) { - cmds_str += `-e '${cmd[i]}'`; - } - osascript_str = `osascript ${cmds_str}` - - } else if (typeof cmd === 'string') { - console.log('Single cmd string'); - osascript_str = `osascript -e '${cmd}'`; - } else { - return false; - } - - if (language) { - console.log(`Language: ${language}`); - osascript_str = `${osascript_str} -l ${language}`; - } - - if (flags) { - console.log(`Flags: ${flags}`); - osascript_str = `${osascript_str} -s ${flags}`; - } - - console.log(`OSA Script String: ${osascript_str}`); - child_process.exec(osascript_str, (err, stdout, stdin) => { - if (err) throw err; - console.log(stdout); - console.log(stdin); - }); - - console.log('Finished'); - return true; -} - - -// Run raw command -// Updated 2022-05-07 -exports.run_cmd = async function ({cmd=null}) { - console.log('*** Electron framework export: run_cmd() ***'); - - console.log(`Command String: ${cmd}`); - - child_process.exec(cmd, (err, stdout, stdin) => { - if (err) throw err; - console.log(stdout); - console.log(stdin); - }); - - console.log('Finished'); - return true; -} - - -// Run raw command -// Updated 2022-05-25 -exports.get_device_info = async function () { - console.log('*** Electron framework export: get_device_info() ***'); - - let data = {}; - data['arch'] = os.arch(); - data['hostname'] = os.hostname(); - data['cpus'] = os.cpus(); - data['freemem'] = os.freemem(); - data['totalmem'] = os.totalmem(); - data['loadavg'] = os.loadavg(); - data['networkInterfaces'] = os.networkInterfaces(); - data['platform'] = os.platform(); - data['release'] = os.release(); - data['uptime'] = os.uptime(); - data['version'] = os.version(); - - console.log(data); - return data; -} diff --git a/app/js/aether_app_native_v4.js b/app/js/aether_app_native_v4.js deleted file mode 100644 index ae9c392..0000000 --- a/app/js/aether_app_native_v4.js +++ /dev/null @@ -1,1191 +0,0 @@ -'use strict'; -/* This should only contain functions that can not be pulled easily into Svelte */ -/* -Exported functions in use: -* load_config() -* check_hash_file_cache() -* download_hash_file_to_cache() -* open_hash_file_to_temp() -* open_local_file() -* kill_processes() -* run_osascript() -* run_cmd() -* get_device_info - -Local functions in use: -* open_local_file() -*/ - -const child_process = require('child_process'); -const crypto = require('crypto'); -const fs = require('fs'); -const fs_promises = require('node:fs/promises'); -const os = require('os'); -const path = require('path'); -const { ipcRenderer } = require('electron'); - -// import psList from 'ps-list'; -// const ps_list = require('ps-list'); - -let home_directory = require('os').homedir(); -console.log('Home: '+home_directory); - -let tmp_directory = require('os').tmpdir(); -console.log('Temporary: '+tmp_directory); - -let config = null; - -exports.load_init_config = function () { - console.log('*** Aether App Native export: load_init_config() ***'); - - let cwd = process.cwd(); - console.log(`CWD: ${cwd}`); - - try { - if (cwd == '/') { - cwd = home_directory; - } - let directory_list = fs.readdirSync(cwd) - console.log('CWD Contents:', directory_list); - } catch (err) { - console.error(err); - } - - // let home_directory = require('os').homedir(); - // console.log('Home: '+home_directory); - - // let tmp_directory = require('os').tmpdir(); - // console.log('Temporary: '+tmp_directory); - - // let config = null; - let config_directory = null; - // let default_config_path = path.join(process.cwd(),'ae_native_app_config.current.json'); - let default_config_path = 'ae_native_app_config.current.json'; - console.log(`Default Config File (path): ${default_config_path}`); - let config_path = null; - - // Set the config path for macOS or Linux - if (os.platform == 'darwin') { - // config_directory = path.join(home_directory, 'Library/Application Support/OSIT'); - config_directory = path.join(home_directory, 'OSIT/native_app'); - console.log('macOS config directory: '+config_directory); - } else if (os.platform == 'linux') { - config_directory = path.join(home_directory, '.config/OSIT'); - console.log('Linux config directory: '+config_directory); - } - - // Look for the config file and copy the default if not found. - if (fs.existsSync(config_directory)) { - console.log('Config directory found: '+config_directory); - } else { - fs.mkdirSync(config_directory); - console.log('Config directory created: '+config_directory); - - //default_config_path = path.join(process.cwd(),'ae_native_app_config.current.json'); - // config_path = path.join(config_directory, 'ae_native_app_config.json'); - // fs.copyFileSync(default_config_path, config_path); - // console.log('Default config file copied: '+config_directory); - } - - config_path = path.join(config_directory, 'ae_native_app_config.json'); - - // Attempt to open the config file. The preferred location is based on the OS's config directory. - if (fs.existsSync(config_path)) { - console.log(`Config file (ae_native_app_config.json) found under ${config_directory}`); - } else if (!fs.existsSync(config_path) && fs.existsSync(default_config_path)) { - fs.copyFileSync(default_config_path, config_path); - console.log('Default config file copied: '+config_directory); - - // config = JSON.parse(fs.readFileSync(config_path)); - // console.log('Config file read.'); - } else if (fs.existsSync(path.join(cwd, 'ae_native_app_config.json'))) { - //fs.copyFileSync(default_config_path, config_path); - //console.log('Default config file copied: '+config_directory); - - console.log(`Config file (config.json) not found under ${config_directory}. Using config in CWD. ${cwd}`); - config_path = path.join(cwd, 'ae_native_app_config.json'); - - console.log(`Config file (config.json) not found under ${config_directory}. Using config in CWD. ${cwd}`); - let found_config_path = path.join(cwd, 'ae_native_app_config.json'); - - fs.copyFileSync(found_config_path, config_path); - console.log(`Found config file copied: ${config_directory}`); - } else if (fs.existsSync(path.join(cwd, 'ae_native_app_config.current.json'))) { - console.log(`Config file (config.json) not found under ${config_directory} or CWD. Using default config in CWD. ${cwd}`); - default_config_path = path.join(cwd, 'ae_native_app_config.current.json'); - - fs.copyFileSync(default_config_path, config_path); - console.log(`Default config file copied: ${config_directory}`); - } else { - console.log('Can not find a config file.'); - - return false; - } - - let local_config = JSON.parse(fs.readFileSync(config_path)); - console.log('Config file read.', local_config); - config = local_config; - - // Do some quick clean up. - - config.home_directory = home_directory; // From the OS platform - config.tmp_directory = tmp_directory; // From the OS platform - - config.app_root_path = config.app_root_path.replace('[home]', home_directory); - config.app_root_path = config.app_root_path.replace('[tmp]', tmp_directory); - console.log(`App Root Path: ${config.app_root_path}`); - - config.local_file_cache_path = config.local_file_cache_path.replace('[home]', home_directory); - config.local_file_cache_path = config.local_file_cache_path.replace('[tmp]', tmp_directory); - console.log(`Local File Cache Path: ${config.local_file_cache_path}`); - if (fs.existsSync(config.local_file_cache_path)) { - } else { - fs.mkdirSync(config.local_file_cache_path); - console.log(`Host file cache directory created: ${config.local_file_cache_path}`); - } - - config.host_file_temp_path = config.host_file_temp_path.replace('[home]', home_directory); - config.host_file_temp_path = config.host_file_temp_path.replace('[tmp]', tmp_directory); - console.log(`Host file temp path: ${config.host_file_temp_path}`); - if (fs.existsSync(config.host_file_temp_path)) { - } else { - fs.mkdirSync(config.host_file_temp_path); - console.log(`Host file temp directory created: ${config.host_file_temp_path}`); - } - - if (!config.account_id) { - config.account_id = '_XY7DXtc9MY'; // Not ideal...? - } - - let import_config_to_ipc_result = ipcRenderer.invoke('import_config', config).then((result) => { - console.log('IPC import config finished'); - // console.log(result); - return result; // Result should be "true" - }) - - //console.log(config); - return config; -} - - -exports.load_full_config = function (init_config) { - console.log('*** Aether App Native export: load_config() ***'); - - console.log('Init config.', init_config); - // config = init_config; - - config = get_url_cfg(init_config).then((cfg) => { - console.log('URL config file downloaded.', cfg); - - let new_config = init_config; - - // Update with remote device config settings from API. - new_config.account_id = cfg.account_id_random; - new_config.event_id = cfg.event_id_random; - new_config.event_device_id = cfg.event_device_id; - new_config.event_location_id = cfg.event_location_id; - new_config.event_session_id = cfg.event_session_id; - - new_config.check_event_device_loop_period = cfg.check_event_device_loop_period; - new_config.check_event_location_loop_period = cfg.check_event_location_loop_period; - new_config.check_event_loop_period = cfg.check_event_loop_period; - new_config.check_event_session_loop_period = cfg.check_event_session_loop_period; - - new_config.record_audio = cfg.record_audio; - new_config.record_video = cfg.record_video; - new_config.recording_path = cfg.recording_path; - new_config.recording_path = new_config.recording_path.replace('[home]', home_directory); - new_config.recording_path = new_config.recording_path.replace('[tmp]', home_directory); - - new_config.home_directory = home_directory; // From the OS platform - new_config.tmp_directory = tmp_directory; // From the OS platform - - new_config.app_root_path = new_config.app_root_path.replace('[home]', home_directory); - new_config.app_root_path = new_config.app_root_path.replace('[tmp]', tmp_directory); - console.log(`App Root Path: ${new_config.app_root_path}`); - - new_config.local_file_cache_path = new_config.local_file_cache_path.replace('[home]', home_directory); - new_config.local_file_cache_path = new_config.local_file_cache_path.replace('[tmp]', tmp_directory); - console.log(`Local File Cache Path: ${new_config.local_file_cache_path}`); - if (fs.existsSync(new_config.local_file_cache_path)) { - } else { - fs.mkdirSync(new_config.local_file_cache_path); - console.log(`Host file cache directory created: ${new_config.local_file_cache_path}`); - } - - new_config.host_file_temp_path = new_config.host_file_temp_path.replace('[home]', home_directory); - new_config.host_file_temp_path = new_config.host_file_temp_path.replace('[tmp]', tmp_directory); - console.log(`Host file temp path: ${new_config.host_file_temp_path}`); - if (fs.existsSync(new_config.host_file_temp_path)) { - } else { - fs.mkdirSync(new_config.host_file_temp_path); - console.log(`Host file temp directory created: ${new_config.host_file_temp_path}`); - } - - let import_config_to_ipc_result = ipcRenderer.invoke('import_config', new_config).then((result) => { - console.log('IPC import config finished'); - // console.log(result); - return result; // Result should be "true" - }) - - return new_config - }) - - //console.log(config); - return config; -} - - -async function get_url_cfg(cfg) { - let base_url = `${cfg.api_protocol}://${cfg.api_server}:${cfg.api_port}/${cfg.api_path}`; - - let axios_api = axios.create({ - baseURL: base_url, - timeout: 60000, // in milliseconds; 60000 = 60 seconds - /* other custom settings */ - }); - // axios_api.defaults.headers = cfg['headers']; - - // axios.defaults.baseURL = `${cfg.api_protocol}://${cfg.api_server}:${cfg.api_port}/${cfg.api_path}`; - axios_api.defaults.headers.common['Access-Control-Allow-Origin'] = cfg.access_control_allow_origin; // '*'; // app_cfg.access_control_allow_origin; - axios_api.defaults.headers.common['content-type'] = 'application/json'; - axios_api.defaults.headers.common['x-aether-api-key'] = cfg.api_secret_key; - // axios_api.defaults.headers.common['x-account-id'] = cfg.account_id; - - let event_device_id = 'dbgMWS3KEHE'; - let endpoint = `/event/device/${event_device_id}`; - - let params = {'event_device_code': 'asdf'}; - - let response_data_promise = await axios_api.get( - endpoint, - { - params: params, - onDownloadProgress: (progressEvent) => { - let percent_completed = Math.round( - (progressEvent.loaded * 100) / progressEvent.total - ); - console.log('GET Data Timestamp:', progressEvent.timeStamp, 'Total:', progressEvent.total, 'Loaded:', progressEvent.loaded, 'Percent Completed', percent_completed); - - // temp_get_object_percent_completed = percent_completed; - } - } - ) - .then(function (response) { - console.log(`Response: ${response}`); - - let return_data = response.data['data']; - if (Array.isArray(return_data)) { - console.log(`Data result is an array/list. Array length: ${return_data.length}`); - } else { - console.log(`Data result is a dictionary/object, not an array/list.`); - } - return return_data; - }) - .catch(function (error) { - console.log(`Base URL: ${base_url} | Endpoint: ${endpoint}`); - - console.log('Error Message:', error.message); // Is this needed here or below in the in the else portion??? - if (error.response) { - console.log(`Response Status: ${error.response.status}; Status Text: ${error.response.statusText}`); - } else { - console.log('Error:', error); - } - - if (error.response && error.response.status === 404) { - return null; // Returning null since there were no results - } - return false; // Returning false since something may have gone wrong. Also more in line with what the API returns. - }); - - return response_data_promise; -} - - - - - - - - -// Check for local file -// Updated 2022-05-06 -exports.check_local_file = async function ({local_file_path, filename}) { - console.log('*** Electron framework export: check_local_file() ***'); - // console.log('Check for local file'); - console.log(`Local File Path: ${local_file_path}; Filename: ${filename}`); - - let full_local_file_path = path.join(local_file_path, filename); - console.log(full_local_file_path); - - if (fs.existsSync(full_local_file_path)) { - console.log(`Local file exists: ${full_local_file_path}`); - return true; - } else { - return false; - } -} - - -// Check local hash file cache -// Used by Svelte Event Launcher -// NOTE: Trying to replace this with something directly in the Svelte app part. 2022-10-11 -// Updated 2022-05-06 -exports.check_hash_file_cache = async function ({local_file_cache_path, hash}) { - // console.log('*** Electron framework export: check_hash_file_cache() ***'); - // console.log('Check local hash file cache'); - console.log(`*** Electron framework export: check_hash_file_cache() *** Host File Cache Path: ${local_file_cache_path}; Hash: ${hash}`); - - let hash_filename = `${hash}.file`; - - let subdirectory = hash_filename.substring(0,2); - let subdirectory_path = path.join(local_file_cache_path, subdirectory); - if (fs.existsSync(subdirectory_path)) { - } else { - console.log(`Hashed file subdirectory not found in cache: ${subdirectory_path}`); - return false; - } - - let hash_file_cache_path = path.join(subdirectory_path, hash_filename); - // console.log(hash_file_cache_path); - - if (fs.existsSync(hash_file_cache_path)) { - // console.log(`Hashed file exists in cache: ${hash_file_cache_path}`); - return true; - } else { - console.log(`Hashed file not found in cache: ${hash_file_cache_path}`); - return false; - } -} - - -// Check local hash file cache -// Used by Svelte Event Launcher -// Updated 2022-10-12 -exports.check_hash_file_cache_v2 = async function ({local_file_cache_path, hash, verify_hash=false}) { - console.log('*** Aether App Native export: check_hash_file_cache_v2() ***'); - console.log(`Local File Cache Path: ${local_file_cache_path}; Hash: ${hash}`); - - if (fs.existsSync(local_file_cache_path)) { - } else { - // This should not happen. The directory needs to be created. - console.log(`Cache directory for hashed files was not found: ${local_file_cache_path}`); - return false; - } - - let hash_filename = `${hash}.file`; - - let hash_subdirectory = hash_filename.substring(0,2); - let subdirectory_path = path.join(local_file_cache_path, hash_subdirectory); - - if (fs.existsSync(subdirectory_path)) { - } else { - // This should not happen. The subdirectory needs to be created. - console.log(`Hashed file subdirectory not found in cache directory: ${subdirectory_path}`); - return false; - } - - let full_cached_hash_path = path.join(subdirectory_path, hash_filename); - - if (fs.existsSync(full_cached_hash_path)) { - // console.log(`Hashed file exists in cache: ${full_cached_hash_path}`); - - if (verify_hash) { - const file_buffer = fs.readFileSync(full_cached_hash_path); - const file_hash_sha256 = crypto.createHash('sha256'); - file_hash_sha256.update(file_buffer); - - const file_hash_sha256_check = file_hash_sha256.digest('hex'); - if (file_hash_sha256_check == hash) { - console.log('File hash match', file_hash_sha256_check); - } else { - // This should only happen if the file is actively being downloaded or it is corrupt. - console.log('File hash does not match', file_hash_sha256_check); - return false; - } - } - return true; - } else { - console.log(`Hashed file not found in cache: ${full_cached_hash_path}`); - return null; - } -} - - -// Download hash file to cache -// Used by Svelte Event Launcher -// Updated 2022-05-06 -exports.download_hash_file_to_cache = async function ({api_base_url, local_file_cache_path, event_file_id=null, hash=null}) { - // console.log('*** Electron framework export: download_hash_file_to_cache() ***'); - // console.log('Download hash file to cache'); - console.log(`*** Electron framework export: download_hash_file_to_cache() *** Base URL: ${api_base_url}; Host File Cache Path: ${local_file_cache_path}; Event File ID: ${event_file_id}; Hash: ${hash}`); - - let endpoint = `/event/file/${event_file_id}/download`; - - let hash_filename = `${hash}.file`; - - let subdirectory = hash_filename.substring(0,2); - let subdirectory_path = path.join(local_file_cache_path, subdirectory); - if (fs.existsSync(subdirectory_path)) { - } else { - fs.mkdirSync(subdirectory_path); - console.log(`Subdirectory directory created: ${subdirectory_path}`); - } - - let hash_file_cache_path = path.join(subdirectory_path, hash_filename); - if (fs.existsSync(hash_file_cache_path)) { - if (check_hash) { - const file_buffer = fs.readFileSync(hash_file_cache_path); - const file_hash_sha256 = crypto.createHash('sha256'); - file_hash_sha256.update(file_buffer); - - const file_hash_sha256_check = file_hash_sha256.digest('hex');S - if (file_hash_sha256_check == hash) { - console.log('File hash match', file_hash_sha256_check); - return true; - } else { - // This should only happen if the file is actively being downloaded or it is corrupt. - console.log('File hash does not match', file_hash_sha256_check); - return false; - } - } - } - // console.log(`!!!ABOUT TO CALL DOWNLOAD FILE HANDLER!!! exports.download_hash_file_to_cache(); Base URL: ${api_base_url}`); - let download_file_result = await ipcRenderer.invoke('download_file', api_base_url, endpoint, hash_file_cache_path).then((result) => { - if (result) { - console.log('IPC download file process finished successfully'); - return true; - } else if (result == null) { - console.log('IPC Download Result (file not found?):', result); - return null; - } else { - console.log('IPC Download Result (file being downloaded or something went wrong):', result); - return false; - } - }); - // console.log(`!!!DONE WITH DOWNLOAD FILE HANDLER!!! exports.download_hash_file_to_cache(); Base URL: ${api_base_url}`); - - return download_file_result; -} - - -// Download hash file to cache -// Used by Svelte Event Launcher -// Updated 2022-10-12 -exports.download_hash_file_to_cache_v2 = async function ({api_base_url, local_file_cache_path, event_file_id=null, hash=null, verify_hash=true, overwrite_existing=false}) { - console.log('*** Aether App Native export: download_hash_file_to_cache_v2() ***'); - console.log(`Base URL: ${api_base_url}; Host File Cache Path: ${local_file_cache_path}; Event File ID: ${event_file_id}; Hash: ${hash}`); - - if (fs.existsSync(local_file_cache_path)) { - } else { - // This should not happen. The directory needs to be created. - console.log(`Cache directory for hashed files was not found: ${local_file_cache_path}`); - return false; - } - - let endpoint = `/event/file/${event_file_id}/download`; - - let hash_filename = `${hash}.file`; - - let hash_subdirectory = hash_filename.substring(0,2); - let subdirectory_path = path.join(local_file_cache_path, hash_subdirectory); - - if (fs.existsSync(subdirectory_path)) { - } else { - fs.mkdirSync(subdirectory_path); - console.log(`Hashed file subdirectory directory created in cache directory: ${subdirectory_path}`); - } - - let full_cached_hash_path = path.join(subdirectory_path, hash_filename); - - if (fs.existsSync(full_cached_hash_path)) { - console.log(`Hashed file exists in cache: ${full_cached_hash_path}`); - - if (verify_hash) { - const file_buffer = fs.readFileSync(full_cached_hash_path); - const file_hash_sha256 = crypto.createHash('sha256'); - file_hash_sha256.update(file_buffer); - - const file_hash_sha256_check = file_hash_sha256.digest('hex'); - if (file_hash_sha256_check == hash) { - // console.log('File hash match', file_hash_sha256_check); - if (overwrite_existing) { - console.log('Going to overwrite the existing file even though the hash matches.'); - } else { - return true; - } - } else { - // This should only happen if the file is actively being copied or it is corrupt. - console.log('File hash does not match', file_hash_sha256_check); - if (overwrite_existing) { - console.log('Going to overwrite the existing file because the hash does not match.'); - } else { - return false; - } - } - } - } - - let download_file_result = await ipcRenderer.invoke('download_file', api_base_url, endpoint, full_cached_hash_path, hash, verify_hash, overwrite_existing).then((result) => { - if (result) { - console.log('IPC download file process finished successfully'); - return true; - } else if (result == null) { - console.log('IPC Download Result (file not found?):', result); - return null; - } else { - console.log('IPC Download Result (file being downloaded or something went wrong):', result); - return false; - } - }); - - return download_file_result; -} - - - -// Open cached hash file after copying to temp directory -// Used by Svelte Event Launcher -// NOTE: Trying to replace this with something directly in the Svelte app part. 2022-10-11 -// Updated 2022-05-06 -exports.open_hash_file_to_temp = async function ({local_file_cache_path, hash, host_file_temp_path, filename}) { - console.log('*** Electron framework export: open_hash_file_to_temp() ***'); - // console.log('Open cached hash file after copying to temp directory'); - console.log(`Host File Cache Path: ${local_file_cache_path}; Hash: ${hash}; Host File Temp Path: ${host_file_temp_path}; Filename: ${filename}`); - - let subdirectory = hash.substring(0,2); - let subdirectory_path = path.join(local_file_cache_path, subdirectory); - if (fs.existsSync(subdirectory_path)) { - } else { - console.log(`Hashed file subdirectory not found in cache: ${subdirectory_path}`); - return false; - } - - let open_hash_file_to_temp_result = await ipcRenderer.invoke('open_hash_file_to_temp', subdirectory_path, hash, host_file_temp_path, filename).then((result) => { - console.log('IPC open hash file to temp finished'); - console.log(result); - return true; - }) - - // let result = await ipcRenderer.send('open_local_file', local_file_cache_path, hash, host_file_temp_path, filename); - // console.log(result); - - console.log(open_hash_file_to_temp_result); - console.log('End: open_hash_file_to_temp()'); - if (open_hash_file_to_temp_result) { - console.log('File opened successfully'); - return true; - } else { - console.log('File was not opened successfully'); - return false; - } -} - - - -// Open cached hash file after copying to temp directory -// Used by Svelte Event Launcher -// Updated 2022-10-12 -exports.open_hash_file_to_temp_v2 = async function ({local_file_cache_path, hash, host_file_temp_path, filename, verify_hash=true}) { - console.log('*** Aether App Native export: open_hash_file_to_temp_v2() ***'); - console.log(`Local File Cache Path: ${local_file_cache_path}; Hash: ${hash}; Local File Temp Path: ${host_file_temp_path}; Filename: ${filename}`); - - // console.log('Process: Check local hash file cache, Download hash file to cache, and Open cached hash file after copying to temp directory'); - - if (fs.existsSync(local_file_cache_path)) { - } else { - // This should not happen. The directory needs to be created. - console.log(`Cache directory for hashed files was not found: ${local_file_cache_path}`); - return false; - } - - let hash_filename = `${hash}.file`; - - let hash_subdirectory = hash_filename.substring(0,2); - let subdirectory_path = path.join(local_file_cache_path, hash_subdirectory); - - if (fs.existsSync(subdirectory_path)) { - } else { - // This should not happen. The subdirectory needs to be created. - console.log(`Hashed file subdirectory not found in cache directory: ${subdirectory_path}`); - return false; - } - - let full_cached_hash_path = path.join(subdirectory_path, hash_filename); - - if (fs.existsSync(full_cached_hash_path)) { - console.log(`Hashed file exists in cache: ${full_cached_hash_path}`); - - if (verify_hash) { - const file_buffer = fs.readFileSync(full_cached_hash_path); - const file_hash_sha256 = crypto.createHash('sha256'); - file_hash_sha256.update(file_buffer); - - const file_hash_sha256_check = file_hash_sha256.digest('hex'); - if (file_hash_sha256_check == hash) { - // console.log('File hash match', file_hash_sha256_check); - } else { - // This should only happen if the file is actively being downloaded or it is corrupt. - console.log('File hash does not match', file_hash_sha256_check); - return false; - } - } - } else { - console.log(`Hashed file not found in cache: ${full_cached_hash_path}`); - return null; - } - - let local_file_cache_path_w_sub = subdirectory_path; // I need to go back and clean up the variable names related file directory and file paths. - // NOTE: Setting check_hash to false since by default it was checked above. - let open_hash_file_to_temp_result = await ipcRenderer.invoke('open_hash_file_to_temp', local_file_cache_path_w_sub, hash, host_file_temp_path, filename, verify_hash=false).then((result) => { - console.log('IPC open hash file to temp finished'); - if (result) { - console.log('Local hash file was opened from temp directory.'); - return result; - } else { - console.log('Local hash file was not opened from the temp directory. Something went wrong.'); - console.log(result); - return false; - } - }) - - // let result = await ipcRenderer.send('open_local_file', local_file_cache_path, hash, host_file_temp_path, filename); - // console.log(result); - - // console.log(open_hash_file_to_temp_result); - // console.log('End: open_hash_file_to_temp()'); - // if (open_hash_file_to_temp_result) { - // console.log('File opened successfully'); - // return true; - // } else { - // console.log('File was not opened successfully'); - // return false; - // } - - // let open_hash_file_to_temp_result = await ipcRenderer.invoke('open_hash_file_to_temp', subdirectory_path, hash, host_file_temp_path, filename).then((result) => { - // console.log('IPC open hash file to temp finished'); - // if (result) { - // console.log('Local hash file was opened from temp directory.'); - // return result; - // } else { - // console.log('Local hash file was not opened from the temp directory. Something went wrong.'); - // console.log(result); - // return false; - // } - // }) - - return open_hash_file_to_temp_result; -} - - - - -// Open local file -// Used by Svelte Event Launcher -// NOTE: Trying to replace this with something directly in the Svelte app part. 2022-10-11 -// Updated 2022-03-10 -exports.open_local_file = async function ({local_file_path, filename}) { - console.log('*** Electron framework export: open_local_file() ***'); - // console.log('Open local file'); - console.log(`Local File Path: ${local_file_path}; Filename: ${filename}`); - - // let full_local_file_path = path.join(local_file_path, filename); - // console.log(full_local_file_path); - - // if (fs.existsSync(full_local_file_path)) { - // console.log(`Local file exists: ${full_local_file_path}`); - // // return true; - // } else { - // return false; - // } - - let open_local_file_result = await ipcRenderer.invoke('open_local_file', local_file_path, filename).then((result) => { - console.log('IPC open local file finished'); - console.log(result); - return true; - }) - - console.log(open_local_file_result); - console.log('End: open_local_file()'); - if (open_local_file_result) { - console.log('File opened successfully'); - return true; - } else { - console.log('File was not opened successfully'); - return false; - } -} - - -// // Check local file cache and download from server if needed. -// // Updated 2022-03-09 -// // exports.check_file_cache = async function ({local_file_cache_path, event_file_id, hash}) { -// exports.check_file_cache = async function ({api_base_url, local_file_cache_path, event_file_id, hash}) { -// console.log('*** Electron framework export: check_file_cache() ***'); -// // console.log('Check local file cache and download from server if needed.'); -// console.log(`Host File Cache Path: ${local_file_cache_path}; Event File ID: ${event_file_id}; Hash: ${hash}`); - -// // NOTE: event_file_id is the event_file.id_random or event_file.event_file_id_random -// let hash_filename = hash+'.file'; - -// let save_path = path.join(local_file_cache_path, hash_filename); -// console.log(save_path); - -// if (fs.existsSync(save_path)) { -// console.log('Hashed file cache already exists: '+save_path); -// return true; -// } else { -// console.log('Hashed file not found in local cache. Downloading file: '+save_path); -// let endpoint = `/event/file/${event_file_id}/download`; -// let result = await ipcRenderer.send('download_file', api_base_url, endpoint, save_path); // Must download file using main node.js thread. -// console.log(result); - -// return new Promise((resolve, reject) => { -// ipcRenderer.once('download_file_reply', function(event, response){ -// console.log(response); -// return response; -// }) -// resolve(true); -// }); - -// // await ipcRenderer.once('download_file_reply', function(event, response){ -// // console.log(response); -// // return response; -// // }); - -// // result.then(function (response) { -// // console.log('Downloaded!!!???'); -// // return true; -// // }).catch(function (error) { -// // console.log(error); -// // return false; -// // }); - -// // return result; - -// // console.log(result); -// // if (result) { -// // return true; -// // } else { -// // return false; -// // } -// } -// } - - -// Check local file cache and download from server if needed. Must use IPC to Main to download file. Set a Promise to wait for download_file_reply. -// Updated 2022-03-09 -async function check_file_cache({api_base_url, local_file_cache_path, event_file_id, hash}) { - console.log('*** Electron framework: check_file_cache() ***'); - // console.log('Check local file cache and download from server if needed.'); - console.log(`Host File Cache Path: ${local_file_cache_path}; Event File ID: ${event_file_id}; Hash: ${hash}`); - - // NOTE: event_file_id is the event_file.id_random or event_file.event_file_id_random - let hash_filename = hash+'.file'; - - let save_path = path.join(local_file_cache_path, hash_filename); - console.log(save_path); - - if (fs.existsSync(save_path)) { - console.log('Hashed file cache already exists: '+save_path); - return true; - } else { - console.log('Hashed file not found in local cache. Downloading file: '+save_path); - let endpoint = `/event/file/${event_file_id}/download`; - let result = await ipcRenderer.send('download_file', api_base_url, endpoint, save_path); // Must download file using main node.js thread. - console.log(result); - - return new Promise((resolve, reject) => { - ipcRenderer.once('download_file_reply', function(event, response){ - console.log(response); - return response; - }) - resolve(true); - }); - - // await ipcRenderer.once('download_file_reply', function(event, response){ - // console.log(response); - // return response; - // }); - - // result.then(function (response) { - // console.log('Downloaded!!!???'); - // return true; - // }).catch(function (error) { - // console.log(error); - // return false; - // }); - - // return result; - - // console.log(result); - // if (result) { - // return true; - // } else { - // return false; - // } - } -} - - -// IPC to Main: Open local file cache if available. Copy to temp directory with given filename first. -// Updated 2022-03-09 -async function open_local_file({local_file_cache_path, hash, host_file_temp_path, filename}) { - console.log('*** Electron framework: open_local_file() ***'); - // console.log('Open local file cache if available. Copy to temp directory with given filename first.'); - console.log(`Host File Cache Path: ${local_file_cache_path}; Hash: ${hash}; Host File Temp Path: ${host_file_temp_path}; Filename: ${filename}`); - - console.log(local_file_cache_path); - console.log(hash); - console.log(filename); - - let result = await ipcRenderer.send('open_local_file', local_file_cache_path, hash, host_file_temp_path, filename); - console.log(result); - - return true; -} - - -// No longer needed? Not referenced as of 2022-10-11 -exports.check_file_cache_and_open_local_file = async function ({local_file_cache_path, event_file_id, hash, host_file_temp_path, filename}) { - console.log('*** Electron framework: check_file_cache_and_open_local_file() ***'); - console.log('Checking the local file cache against the remote server and then opening the local file.'); - - let check_file_cache_result = check_file_cache({local_file_cache_path: local_file_cache_path, event_file_id: event_file_id, hash: hash}); - console.log(check_file_cache_result); - - if (check_file_cache_result) { - let open_local_file_result = open_local_file({local_file_cache_path: local_file_cache_path, hash: hash, host_file_temp_path: host_file_temp_path, filename: filename}); - console.log(open_local_file_result); - - return open_local_file_result; - } - - ipcRenderer.once('download_file_reply', function(event, response){ - console.log(response); - - let open_local_file_result = open_local_file({local_file_cache_path: local_file_cache_path, hash: hash, host_file_temp_path: host_file_temp_path, filename: filename}); - console.log(open_local_file_result); - - return open_local_file_result; - }) -} - - -// Kill processes -// Signals: HUP (hang up), INT (interrupt), QUIT (quit), ABRT (abort), KILL (non-catchable, non-ignoraable kill), ALRMn (alarm clock), TERM (default; software termination signal) -// Updated 2022-05-07 -exports.kill_processes = async function ({process_name = null, process_id = null, signal = null}) { - console.log('*** Electron framework export: kill_processes() ***'); - console.log(process_name); // process_name or grep pattern - - let cmd = ''; - if (os.platform == 'darwin') { - if (signal == 'HUP') { - cmd = `killall -HUP '${process_name}'`; - } else if (signal == 'INT') { - cmd = `killall -INT '${process_name}'`; - } else if (signal == 'QUIT') { - cmd = `killall -QUIT '${process_name}'`; - } else if (signal == 'ABRT') { - cmd = `killall -ABRT '${process_name}'`; - } else if (signal == 'KILL') { - cmd = `killall -KILL '${process_name}'`; - } else if (signal == 'ALRM') { - cmd = `killall -ALRM '${process_name}'`; - } else if (signal == 'TERM') { - cmd = `killall -TERM '${process_name}'`; - } else if (process_id && signal == 'HUP') { - cmd = `killall -HUP ${process_id}`; - } else if (process_id && signal == 'INT') { - cmd = `killall -INT ${process_id}`; - } else if (process_id && signal == 'QUIT') { - cmd = `killall -QUIT ${process_id}`; - } else if (process_id && signal == 'ABRT') { - cmd = `killall -ABRT ${process_id}`; - } else if (process_id && signal == 'KILL') { - cmd = `killall -KILL ${process_id}`; - } else if (process_id && signal == 'ALRM') { - cmd = `killall -ALRM ${process_id}`; - } else if (process_id && signal == 'TERM') { - cmd = `killall -TERM ${process_id}`; - } else { - // cmd = `osascript -e 'quit app "${process_name}" saving no'`; - cmd = `osascript -e 'quit application "${process_name}" saving no'`; - } - - } else { - cmd = `pkill ${process_name}`; - } - - child_process.exec(cmd, (err, stdout, stdin) => { - // if (err) throw err; - if (err) console.log(err); - console.log(stdout); - }); - console.log(`Killed processes matching ${process_name}`); - - if (os.platform == 'darwin') { - if (process_name == 'Parallels:Acrobat Reader') { - // Regular expression: (Parallels).*(Acrobat Reader) - // This will find any process with Parallels and Acrobat Reader in the name - cmd = `pkill -i -f '(Parallels).*(Acrobat Reader)'`; - - child_process.exec(cmd, (err, stdout, stdin) => { - if (err) throw err; - console.log(stdout); - }); - console.log('Killed Parallels Acrobat Reader process'); - } - - if (process_name == 'Parallels:PowerPoint') { - // Regular expression: (Parallels).*(PowerPoint) - // This will find any process with Parallels and PowerPoint in the name - cmd = `pkill -i -f '(Parallels).*(PowerPoint)'`; - - child_process.exec(cmd, (err, stdout, stdin) => { - if (err) throw err; - console.log(stdout); - }); - console.log('Killed Parallels PowerPoint process'); - } - } - - // let signal = 'SIGTERM'; // 'SIGTERM', 'SIGINT', 'SIGHUP' - // process.kill(pid, signal); - // process.kill(pid, 0); // Special case test if process exists - - return true; -} - - -// Run raw osascript -// Updated 2022-05-07 -exports.run_osascript = async function ({cmd=null, interactive=false, language=null, flags='h', program_file=null}) { - console.log('*** Electron framework export: run_osascript() ***'); - console.log(cmd); - - if (os.platform == 'darwin') { - } else { - console.log('Not available for this platform. macOS (darwin) only.'); - return false; - } - - let osascript_str = ''; - - if (Array.isArray(cmd)) { - console.log('List of cmd strings'); - let cmds_str = ''; - for (let i = 0; i < cmd.length; i++) { - cmds_str += `-e '${cmd[i]}'`; - } - osascript_str = `osascript ${cmds_str}` - - } else if (typeof cmd === 'string') { - console.log('Single cmd string'); - osascript_str = `osascript -e '${cmd}'`; - } else { - return false; - } - - if (language) { - console.log(`Language: ${language}`); - osascript_str = `${osascript_str} -l ${language}`; - } - - if (flags) { - console.log(`Flags: ${flags}`); - osascript_str = `${osascript_str} -s ${flags}`; - } - - console.log(`OSA Script String: ${osascript_str}`); - child_process.exec(osascript_str, (err, stdout, stdin) => { - if (err) throw err; - console.log(stdout); - console.log(stdin); - }); - - console.log('Finished'); - return true; -} - - -// Run raw command -// Updated 2022-05-07 -exports.run_cmd = async function ({cmd=null, return_stdout=null, return_stdin=null, sync=null}) { - console.log('*** Electron framework export: run_cmd() ***'); - - console.log(`Command String: ${cmd}`); - - let result; - - if (!sync) { - result = child_process.exec(cmd, (err, stdout, stdin) => { - // if (err) throw err; - if (err) { - console.log('Error:', err); - return false; - }; - - console.log('stdout:', stdout); - // console.log('stdin:', stdin); - - if (return_stdout) { - console.log('Finished and returning stdout'); - return stdout; - } else { - console.log('Finished and returning true'); - return true; - } - }); - } else { - result = child_process.execSync(cmd, (err, stdout, stdin) => { - // if (err) throw err; - if (err) { - console.log('Error:', err); - return false; - }; - - console.log('stdout:', stdout); - // console.log('stdin:', stdin); - - if (return_stdout) { - console.log('Finished and returning stdout'); - return stdout; - } else { - console.log('Finished and returning true'); - return true; - } - }); - } - - console.log('Result:', result); - return result; -} - - -// Run raw command sync -// Updated 2022-05-07 -exports.run_cmd_sync = function ({cmd=null, return_stdout=null, return_stdin=null}) { - console.log('*** Electron framework export: run_cmd() ***'); - - console.log(`Command String: ${cmd}`); - - let stdout; - - try { - stdout = child_process.execSync(cmd, {encoding: 'utf8'}); - console.log('Std Out:', stdout); - } catch (err) { - console.error('Error:', err); - return false; - } - - if (return_stdout) { - console.log('Finished and returning stdout'); - return stdout; - } else { - console.log('Finished and returning true'); - return true; - } - - // let result; - // let stdout; - // let stderr; - // try { - // let { stdout, stderr } = child_process.execSync(cmd, (err, stdout, stdin) => { - // // if (err) throw err; - // if (err) { - // console.log('Error:', err); - // return false; - // }; - - // console.log('stdout:', stdout); - // // console.log('stdin:', stdin); - - // if (return_stdout) { - // console.log('Finished and returning stdout'); - // return stdout; - // } else { - // console.log('Finished and returning true'); - // return true; - // } - // }); - // } catch (err) { - // console.error(err); - // return false; - // } - - // console.log('Result:', result); - // return result; -} - - -// Run raw command -// Updated 2022-05-25 -exports.get_device_info = async function () { - console.log('*** Electron framework export: get_device_info() ***'); - - // https://nodejs.org/api/os.html - - let data = {}; - data['arch'] = os.arch(); - data['hostname'] = os.hostname(); - data['cpus'] = os.cpus(); - data['freemem'] = os.freemem(); - data['totalmem'] = os.totalmem(); - data['loadavg'] = os.loadavg(); - data['networkInterfaces'] = os.networkInterfaces(); - data['platform'] = os.platform(); - data['release'] = os.release(); - data['uptime'] = os.uptime(); - data['version'] = os.version(); - - console.log(data); - return data; -} - - - -// For loading JS file -function loadJS(){ - - // Gives -1 when the given input is not in the string - // i.e this file has not been added - - if(filesAdded.indexOf('script.js') !== -1) - return - - // Head tag - var head = document.getElementsByTagName('head')[0] - - // Creating script element - var script = document.createElement('script') - script.src = 'script.js' - script.type = 'text/javascript' - - // Adding script element - head.append(script) - - // Adding the name of the file to keep record - filesAdded += ' script.js' -} - -// To load CSS file -function loadCSS() { - - if(filesAdded.indexOf('styles.css') !== -1) - return - - var head = document.getElementsByTagName('head')[0] - - // Creating link element - var style = document.createElement('link') - style.href = 'styles.css' - style.type = 'text/css' - style.rel = 'stylesheet' - head.append(style); - - // Adding the name of the file to keep record - filesAdded += ' styles.css' -} diff --git a/app/script.current.js b/app/script.current.js deleted file mode 100644 index e87d49a..0000000 --- a/app/script.current.js +++ /dev/null @@ -1,1391 +0,0 @@ -'use strict'; -/* This should only contain functions that can not be pulled easily into Svelte */ -/* -Exported functions in use: -* load_config() -* check_hash_file_cache() -* download_hash_file_to_cache() -* open_hash_file_to_temp() -* open_local_file() -* kill_processes() -* run_osascript() -* run_cmd() -* get_device_info - -Local functions in use: -* open_local_file() -*/ - -const child_process = require('child_process'); -const crypto = require('crypto'); -const fs = require('fs'); -const fs_promises = require('node:fs/promises'); -const os = require('os'); -const path = require('path'); -const { ipcRenderer } = require('electron'); - -// import psList from 'ps-list'; -// const ps_list = require('ps-list'); - -let home_directory = require('os').homedir(); -console.log('Home: '+home_directory); - -let tmp_directory = require('os').tmpdir(); -console.log('Temporary: '+tmp_directory); - -let app_root_path = path.join(home_directory, 'OSIT/native_app'); // macOS default - -let config = null; - -let default_osit_sync_app_root_relative_path = 'OSIT/Speaker Ready System/Admin Share/Custom Applications'; // Mainly for macOS laptops -let default_osit_sync_app_root_path = path.join(home_directory, default_osit_sync_app_root_relative_path); // macOS default -let default_osit_sync_native_app_relative_path = 'OSIT/Speaker Ready System/Admin Share/Custom Applications/osit_binaries/osit_aether.current.app'; // Mainly for macOS laptops -let default_native_app_relative_path = 'OSIT/native_app/osit_aether.app'; // Mainly for macOS laptops -let default_current_native_app_relative_path = 'OSIT/native_app/osit_aether.current.app'; // Mainly for macOS laptops -let test_native_app_relative_path = 'OSIT/native_app/osit_aether cmsc remote.app'; // Mainly for macOS laptops - - - -// exports.check_for_native_app_update = function () { -// console.log('*** Aether App Native export: check_for_native_app_update() ***'); -// -// let config_directory = null; -// -// if (os.platform == 'darwin') { -// config_directory = path.join(home_directory, 'OSIT/native_app'); -// console.log('macOS config directory: '+config_directory); -// -// let electron_app_path = path.join(home_directory, default_native_app_relative_path); -// let test_stats = fs.statSync(electron_app_path); -// console.log(test_stats); -// -// if (test_stats.ctimeMs < 1684182140759) { -// console.log(`This may be an old version!`); -// } -// } else if (os.platform == 'linux') { -// config_directory = path.join(home_directory, '.config/OSIT'); -// console.log('Linux config directory: '+config_directory); -// } else { -// console.log('Unknown OS... Is this Windows?'); -// return false; -// } -// -// } - - -exports.load_init_config = function () { - console.log('*** Aether App Native export: load_init_config() ***'); - - let cwd = process.cwd(); - console.log(`CWD: ${cwd}`); - - try { - if (cwd == '/') { - cwd = home_directory; - } - let directory_list = fs.readdirSync(cwd) - console.log('CWD Contents:', directory_list); - } catch (err) { - console.error(err); - } - - // let home_directory = require('os').homedir(); - // console.log('Home: '+home_directory); - - // let tmp_directory = require('os').tmpdir(); - // console.log('Temporary: '+tmp_directory); - - // let config = null; - let config_directory = null; - // let default_config_path = path.join(process.cwd(),'ae_native_app_config.current.json'); - let default_config_path = 'ae_native_app_config.current.json'; - console.log(`Default Config File (path): ${default_config_path}`); - let config_path = null; - - // Set the config path for macOS or Linux - if (os.platform == 'darwin') { - // config_directory = path.join(home_directory, 'Library/Application Support/OSIT'); - config_directory = path.join(home_directory, 'OSIT/native_app'); - console.log('macOS config directory: '+config_directory); - - let electron_app_path = path.join(home_directory, default_native_app_relative_path); - let test_stats = fs.statSync(electron_app_path); - console.log(test_stats); - } else if (os.platform == 'linux') { - config_directory = path.join(home_directory, '.config/OSIT'); - console.log('Linux config directory: '+config_directory); - } - - // Look for the config file and copy the default if not found. - if (fs.existsSync(config_directory)) { - console.log('Config directory found: '+config_directory); - } else { - fs.mkdirSync(config_directory); - console.log('Config directory created: '+config_directory); - - //default_config_path = path.join(process.cwd(),'ae_native_app_config.current.json'); - // config_path = path.join(config_directory, 'ae_native_app_config.json'); - // fs.copyFileSync(default_config_path, config_path); - // console.log('Default config file copied: '+config_directory); - } - - config_path = path.join(config_directory, 'ae_native_app_config.json'); - - // Attempt to open the config file. The preferred location is based on the OS's config directory. - if (fs.existsSync(config_path)) { - console.log(`Config file (ae_native_app_config.json) found under ${config_directory}`); - } else if (!fs.existsSync(config_path) && fs.existsSync(default_config_path)) { - fs.copyFileSync(default_config_path, config_path); - console.log('Default config file copied: '+config_directory); - - // config = JSON.parse(fs.readFileSync(config_path)); - // console.log('Config file read.'); - } else if (fs.existsSync(path.join(cwd, 'ae_native_app_config.json'))) { - //fs.copyFileSync(default_config_path, config_path); - //console.log('Default config file copied: '+config_directory); - - console.log(`Config file (config.json) not found under ${config_directory}. Using config in CWD. ${cwd}`); - config_path = path.join(cwd, 'ae_native_app_config.json'); - - console.log(`Config file (config.json) not found under ${config_directory}. Using config in CWD. ${cwd}`); - let found_config_path = path.join(cwd, 'ae_native_app_config.json'); - - fs.copyFileSync(found_config_path, config_path); - console.log(`Found config file copied: ${config_directory}`); - } else if (fs.existsSync(path.join(cwd, 'ae_native_app_config.current.json'))) { - console.log(`Config file (config.json) not found under ${config_directory} or CWD. Using default config in CWD. ${cwd}`); - default_config_path = path.join(cwd, 'ae_native_app_config.current.json'); - - fs.copyFileSync(default_config_path, config_path); - console.log(`Default config file copied: ${config_directory}`); - } else { - console.log('Can not find a config file.'); - - return false; - } - - let local_config = JSON.parse(fs.readFileSync(config_path)); - console.log('Config file read.', local_config); - config = local_config; - - // Do some quick clean up. - - config.home_directory = home_directory; // From the OS platform - config.tmp_directory = tmp_directory; // From the OS platform - - config.app_root_path = config.app_root_path.replace('[home]', home_directory); - config.app_root_path = config.app_root_path.replace('[tmp]', tmp_directory); - console.log(`App Root Path: ${config.app_root_path}`); - - config.local_file_cache_path = config.local_file_cache_path.replace('[home]', home_directory); - config.local_file_cache_path = config.local_file_cache_path.replace('[tmp]', tmp_directory); - console.log(`Local File Cache Path: ${config.local_file_cache_path}`); - if (fs.existsSync(config.local_file_cache_path)) { - } else { - fs.mkdirSync(config.local_file_cache_path); - console.log(`Host file cache directory created: ${config.local_file_cache_path}`); - } - - config.host_file_temp_path = config.host_file_temp_path.replace('[home]', home_directory); - config.host_file_temp_path = config.host_file_temp_path.replace('[tmp]', tmp_directory); - console.log(`Host file temp path: ${config.host_file_temp_path}`); - if (fs.existsSync(config.host_file_temp_path)) { - } else { - fs.mkdirSync(config.host_file_temp_path); - console.log(`Host file temp directory created: ${config.host_file_temp_path}`); - } - - // NOTE: This is not ideal... - if (!config.account_id) { - if (config.event_id == 'pjrcghqwert' || config.event_id == 'nmBfuGFeR0k') { - config.account_id = '_XY7DXtc9MY'; - } else if (config.event_id == 'r8c-rr-I4-52') { // CMSC - config.account_id = '8Gfxbxr19Nw'; - } else if (config.event_id == 'bM4PL-l8j8p') { // LCI - config.account_id = 'xFP7AhU8Zlc'; - } else if (config.event_id == 'Y7v-Zg-fe-gu') { // NCSD - config.account_id = 'FrodrLD7B4w'; - } else if (config.event_id == '9jW-Db-SF-wt') { // AAPOR - config.account_id = 'j5EBhRDqPuw'; - } else if (config.event_id == 'zJP-Ld-A7-I4') { // BGH - config.account_id = 'GpLf_bnywCs'; - } else { - console.log('WARNING WARNING WARNING: The account ID could not be guessed. Using the default of 1. <<< WARNING'); - config.account_id = '_XY7DXtc9MY'; - } - } - - let import_config_to_ipc_result = ipcRenderer.invoke('import_config', config).then((result) => { - console.log('IPC import config finished'); - // console.log(result); - return result; // Result should be "true" - }) - - //console.log(config); - return config; -} - - -exports.load_full_config = function (init_config) { - console.log('*** Aether App Native export: load_config() ***'); - - console.log('Init config.', init_config); - // config = init_config; - - config = get_url_cfg(init_config).then((cfg) => { - console.log('URL config file downloaded.', cfg); - - let new_config = init_config; - - // Update with remote device config settings from API. - new_config.account_id = cfg.account_id_random; - new_config.event_id = cfg.event_id_random; - new_config.event_device_id = cfg.event_device_id; - new_config.event_location_id = cfg.event_location_id; - new_config.event_session_id = cfg.event_session_id; - - new_config.check_event_device_loop_period = cfg.check_event_device_loop_period; - new_config.check_event_location_loop_period = cfg.check_event_location_loop_period; - new_config.check_event_loop_period = cfg.check_event_loop_period; - new_config.check_event_session_loop_period = cfg.check_event_session_loop_period; - - new_config.record_audio = cfg.record_audio; - new_config.record_video = cfg.record_video; - new_config.recording_path = cfg.recording_path; - new_config.recording_path = new_config.recording_path.replace('[home]', home_directory); - new_config.recording_path = new_config.recording_path.replace('[tmp]', home_directory); - - new_config.home_directory = home_directory; // From the OS platform - new_config.tmp_directory = tmp_directory; // From the OS platform - - new_config.app_root_path = new_config.app_root_path.replace('[home]', home_directory); - new_config.app_root_path = new_config.app_root_path.replace('[tmp]', tmp_directory); - console.log(`App Root Path: ${new_config.app_root_path}`); - - new_config.local_file_cache_path = new_config.local_file_cache_path.replace('[home]', home_directory); - new_config.local_file_cache_path = new_config.local_file_cache_path.replace('[tmp]', tmp_directory); - console.log(`Local File Cache Path: ${new_config.local_file_cache_path}`); - if (fs.existsSync(new_config.local_file_cache_path)) { - } else { - fs.mkdirSync(new_config.local_file_cache_path); - console.log(`Host file cache directory created: ${new_config.local_file_cache_path}`); - } - - new_config.host_file_temp_path = new_config.host_file_temp_path.replace('[home]', home_directory); - new_config.host_file_temp_path = new_config.host_file_temp_path.replace('[tmp]', tmp_directory); - console.log(`Host file temp path: ${new_config.host_file_temp_path}`); - if (fs.existsSync(new_config.host_file_temp_path)) { - } else { - fs.mkdirSync(new_config.host_file_temp_path); - console.log(`Host file temp directory created: ${new_config.host_file_temp_path}`); - } - - let import_config_to_ipc_result = ipcRenderer.invoke('import_config', new_config).then((result) => { - console.log('IPC import config finished'); - // console.log(result); - return result; // Result should be "true" - }) - - return new_config - }) - - //console.log(config); - return config; -} - - -async function get_url_cfg(cfg) { - let base_url = `${cfg.api_protocol}://${cfg.api_server}:${cfg.api_port}/${cfg.api_path}`; - - let axios_api = axios.create({ - baseURL: base_url, - timeout: 60000, // in milliseconds; 60000 = 60 seconds - /* other custom settings */ - }); - // axios_api.defaults.headers = cfg['headers']; - - // axios.defaults.baseURL = `${cfg.api_protocol}://${cfg.api_server}:${cfg.api_port}/${cfg.api_path}`; - axios_api.defaults.headers.common['Access-Control-Allow-Origin'] = cfg.access_control_allow_origin; // '*'; // app_cfg.access_control_allow_origin; - axios_api.defaults.headers.common['content-type'] = 'application/json'; - axios_api.defaults.headers.common['x-aether-api-key'] = cfg.api_secret_key; - // axios_api.defaults.headers.common['x-account-id'] = cfg.account_id; - - let event_device_id = 'dbgMWS3KEHE'; - let endpoint = `/event/device/${event_device_id}`; - - let params = {'event_device_code': 'asdf'}; - - let response_data_promise = await axios_api.get( - endpoint, - { - params: params, - onDownloadProgress: (progressEvent) => { - let percent_completed = Math.round( - (progressEvent.loaded * 100) / progressEvent.total - ); - console.log('GET Data Timestamp:', progressEvent.timeStamp, 'Total:', progressEvent.total, 'Loaded:', progressEvent.loaded, 'Percent Completed', percent_completed); - - // temp_get_object_percent_completed = percent_completed; - } - } - ) - .then(function (response) { - console.log(`Response: ${response}`); - - let return_data = response.data['data']; - if (Array.isArray(return_data)) { - console.log(`Data result is an array/list. Array length: ${return_data.length}`); - } else { - console.log(`Data result is a dictionary/object, not an array/list.`); - } - return return_data; - }) - .catch(function (error) { - console.log(`Base URL: ${base_url} | Endpoint: ${endpoint}`); - - console.log('Error Message:', error.message); // Is this needed here or below in the in the else portion??? - if (error.response) { - console.log(`Response Status: ${error.response.status}; Status Text: ${error.response.statusText}`); - } else { - console.log('Error:', error); - } - - if (error.response && error.response.status === 404) { - return null; // Returning null since there were no results - } - return false; // Returning false since something may have gone wrong. Also more in line with what the API returns. - }); - - return response_data_promise; -} - - - - - - - - -// Check for local file -// Updated 2022-05-06 -exports.check_local_file = async function ({local_file_path, filename}) { - console.log('*** Electron framework export: check_local_file() ***'); - // console.log('Check for local file'); - console.log(`Local File Path: ${local_file_path}; Filename: ${filename}`); - - let full_local_file_path = path.join(local_file_path, filename); - console.log(full_local_file_path); - - if (fs.existsSync(full_local_file_path)) { - console.log(`Local file exists: ${full_local_file_path}`); - return true; - } else { - return false; - } -} - - -// Check local hash file cache -// Used by Svelte Event Launcher -// NOTE: Trying to replace this with something directly in the Svelte app part. 2022-10-11 -// Updated 2022-05-06 -exports.check_hash_file_cache = async function ({local_file_cache_path, hash}) { - // console.log('*** Electron framework export: check_hash_file_cache() ***'); - // console.log('Check local hash file cache'); - console.log(`*** Electron framework export: check_hash_file_cache() *** Host File Cache Path: ${local_file_cache_path}; Hash: ${hash}`); - - let hash_filename = `${hash}.file`; - - let subdirectory = hash_filename.substring(0,2); - let subdirectory_path = path.join(local_file_cache_path, subdirectory); - if (fs.existsSync(subdirectory_path)) { - } else { - console.log(`Hashed file subdirectory not found in cache: ${subdirectory_path}`); - return false; - } - - let hash_file_cache_path = path.join(subdirectory_path, hash_filename); - // console.log(hash_file_cache_path); - - if (fs.existsSync(hash_file_cache_path)) { - // console.log(`Hashed file exists in cache: ${hash_file_cache_path}`); - return true; - } else { - console.log(`Hashed file not found in cache: ${hash_file_cache_path}`); - return false; - } -} - - -// Check local hash file cache -// Used by Svelte Event Launcher -// Updated 2022-10-12 -exports.check_hash_file_cache_v2 = async function ({local_file_cache_path, hash, verify_hash=false}) { - console.log('*** Aether App Native export: check_hash_file_cache_v2() ***'); - console.log(`Local File Cache Path: ${local_file_cache_path}; Hash: ${hash}`); - - if (fs.existsSync(local_file_cache_path)) { - } else { - // This should not happen. The directory needs to be created. - console.log(`Cache directory for hashed files was not found: ${local_file_cache_path}`); - return false; - } - - let hash_filename = `${hash}.file`; - - let hash_subdirectory = hash_filename.substring(0,2); - let subdirectory_path = path.join(local_file_cache_path, hash_subdirectory); - - if (fs.existsSync(subdirectory_path)) { - } else { - // This should not happen. The subdirectory needs to be created. - console.log(`Hashed file subdirectory not found in cache directory: ${subdirectory_path}`); - return false; - } - - let full_cached_hash_path = path.join(subdirectory_path, hash_filename); - - if (fs.existsSync(full_cached_hash_path)) { - // console.log(`Hashed file exists in cache: ${full_cached_hash_path}`); - - if (verify_hash) { - const file_buffer = fs.readFileSync(full_cached_hash_path); - const file_hash_sha256 = crypto.createHash('sha256'); - file_hash_sha256.update(file_buffer); - - const file_hash_sha256_check = file_hash_sha256.digest('hex'); - if (file_hash_sha256_check == hash) { - console.log('File hash match', file_hash_sha256_check); - } else { - // This should only happen if the file is actively being downloaded or it is corrupt. - console.log('File hash does not match', file_hash_sha256_check); - return false; - } - } - return true; - } else { - console.log(`Hashed file not found in cache: ${full_cached_hash_path}`); - return null; - } -} - - -// Download hash file to cache -// Used by Svelte Event Launcher -// Updated 2022-05-06 -exports.download_hash_file_to_cache = async function ({api_base_url, local_file_cache_path, event_file_id=null, hash=null}) { - // console.log('*** Electron framework export: download_hash_file_to_cache() ***'); - // console.log('Download hash file to cache'); - console.log(`*** Electron framework export: download_hash_file_to_cache() *** Base URL: ${api_base_url}; Host File Cache Path: ${local_file_cache_path}; Event File ID: ${event_file_id}; Hash: ${hash}`); - - let endpoint = `/event/file/${event_file_id}/download`; - - let hash_filename = `${hash}.file`; - - let subdirectory = hash_filename.substring(0,2); - let subdirectory_path = path.join(local_file_cache_path, subdirectory); - if (fs.existsSync(subdirectory_path)) { - } else { - fs.mkdirSync(subdirectory_path); - console.log(`Subdirectory directory created: ${subdirectory_path}`); - } - - let hash_file_cache_path = path.join(subdirectory_path, hash_filename); - if (fs.existsSync(hash_file_cache_path)) { - if (check_hash) { - const file_buffer = fs.readFileSync(hash_file_cache_path); - const file_hash_sha256 = crypto.createHash('sha256'); - file_hash_sha256.update(file_buffer); - - const file_hash_sha256_check = file_hash_sha256.digest('hex');S - if (file_hash_sha256_check == hash) { - console.log('File hash match', file_hash_sha256_check); - return true; - } else { - // This should only happen if the file is actively being downloaded or it is corrupt. - console.log('File hash does not match', file_hash_sha256_check); - return false; - } - } - } - // console.log(`!!!ABOUT TO CALL DOWNLOAD FILE HANDLER!!! exports.download_hash_file_to_cache(); Base URL: ${api_base_url}`); - let download_file_result = await ipcRenderer.invoke('download_file', api_base_url, endpoint, hash_file_cache_path).then((result) => { - if (result) { - console.log('IPC download file process finished successfully'); - return true; - } else if (result == null) { - console.log('IPC Download Result (file not found?):', result); - return null; - } else { - console.log('IPC Download Result (file being downloaded or something went wrong):', result); - return false; - } - }); - // console.log(`!!!DONE WITH DOWNLOAD FILE HANDLER!!! exports.download_hash_file_to_cache(); Base URL: ${api_base_url}`); - - return download_file_result; -} - - -// Download hash file to cache -// Used by Svelte Event Launcher -// Updated 2023-05-26 -exports.download_hash_file_to_cache_v2 = async function ({api_base_url, api_base_url_backup, local_file_cache_path, event_file_id=null, hash=null, verify_hash=true, overwrite_existing=false}) { - console.log('*** Aether App Native export: download_hash_file_to_cache_v2() ***'); - console.log(`Base URL: ${api_base_url}; Base URL Backup: ${api_base_url_backup}; Host File Cache Path: ${local_file_cache_path}; Event File ID: ${event_file_id}; Hash: ${hash}`); - - if (fs.existsSync(local_file_cache_path)) { - } else { - // This should not happen. The directory needs to be created. - console.log(`Cache directory for hashed files was not found: ${local_file_cache_path}`); - return false; - } - - let endpoint = `/event/file/${event_file_id}/download`; - - let hash_filename = `${hash}.file`; - - let hash_subdirectory = hash_filename.substring(0,2); - let subdirectory_path = path.join(local_file_cache_path, hash_subdirectory); - - if (fs.existsSync(subdirectory_path)) { - } else { - fs.mkdirSync(subdirectory_path); - console.log(`Hashed file subdirectory directory created in cache directory: ${subdirectory_path}`); - } - - let full_cached_hash_path = path.join(subdirectory_path, hash_filename); - - if (fs.existsSync(full_cached_hash_path)) { - console.log(`Hashed file exists in cache: ${full_cached_hash_path}`); - - if (verify_hash) { - const file_buffer = fs.readFileSync(full_cached_hash_path); - const file_hash_sha256 = crypto.createHash('sha256'); - file_hash_sha256.update(file_buffer); - - const file_hash_sha256_check = file_hash_sha256.digest('hex'); - if (file_hash_sha256_check == hash) { - // console.log('File hash match', file_hash_sha256_check); - if (overwrite_existing) { - console.log('Going to overwrite the existing file even though the hash matches.'); - } else { - return true; - } - } else { - // This should only happen if the file is actively being copied or it is corrupt. - console.log('File hash does not match', file_hash_sha256_check); - if (overwrite_existing) { - console.log('Going to overwrite the existing file because the hash does not match.'); - } else { - return false; - } - } - } - } - - const tmp_full_save_path = full_cached_hash_path+'.tmp'; - - if (fs.existsSync(tmp_full_save_path)) { - console.log(`A temp download file was found! ${tmp_full_save_path}`); - - let stats = null; - try { - stats = fs.statSync(tmp_full_save_path); - - // console.log(`File Accessed Last: ${stats.atime}`); // File data last changed (actual contents) - console.log(`File Data Last Modified: ${stats.mtime}`); // File data last changed (actual contents) - console.log(`File Metadata Last Modified: ${stats.ctime}`); // File metadata last changed (filename, permissions, etc) - } catch (error) { - console.log(error); - } - - let current_datetime = new Date(); - let offset_minutes = 3; // In minutes. 5 minutes of no changes to the file content seems reasonable? Trying with 3 minutes 2022-10-12 - let offset_datetime = new Date(current_datetime.getTime() - offset_minutes*60000); - - // console.log(`Times: ${current_datetime} ${offset_datetime} | File ${stats.mtime}`); - if (stats.mtime < offset_datetime) { - // console.log(`Marking as expired temp file based on modified datetime. Expire after: ${offset_minutes} minutes`); - // overwrite_existing = true; - } else { - console.log(`Temp download file has not expired yet. Expire after: ${offset_minutes} minutes`); - // return false; - return 'tmp'; - } - } - - console.log(`Trying IPC API download from: ${api_base_url}`); - let download_file_result = await ipcRenderer.invoke('download_file', api_base_url, endpoint, full_cached_hash_path, hash, verify_hash, overwrite_existing).then(async (result) => { - - if (result) { - console.log(`IPC API download file process finished successfully: ${hash}`); - return true; - } else if (result == null) { - console.log(`IPC API download result (file not found?):`, result); - return null; - } else if (result === 'in_progress') { - console.log(`IPC API primary download IN PROGRESS: ${hash}`); - // return false; // Should return result instead??? - return result; - } else if (result === 'tmp') { - console.log(`IPC API primary download FOUND TMP: ${hash}`); - // return false; // Should return result instead??? - return result; - } else { - console.log(`IPC API download result (file being downloaded or something went wrong): ${hash}`, result); - return false; - - // if (api_base_url_backup) { - // console.log(`Download FAILED! Trying again with backup API server. API Backup: ${api_base_url_backup}`); - // let download_backup_file_result = await ipcRenderer.invoke('download_file', api_base_url_backup, endpoint, full_cached_hash_path, hash, verify_hash, overwrite_existing).then((result_backup) => { - // if (result_backup) { - // console.log('IPC download file (from backup) process finished successfully'); - // return true; - // } else if (result_backup == null) { - // console.log('IPC Download Result (from backup) (file not found?):', result_backup); - // return null; - // } else { - // console.log('IPC Download Result (from backup) (file being downloaded or something went wrong):', result_backup); - // return false; - // } - // }); - // } else { - // return false; - // } - } - return true; - // }); - }).then(async (result) => { - if (result === false) { - console.log(`IPC API primary download FAILED!`); // Trying again with backup API server. API Backup: ${api_base_url_backup}`); - if (api_base_url_backup) { - console.log(`IPC API primary download FAILED! Trying again with backup API server. API Backup: ${api_base_url_backup}`); - console.log(`Trying IPC API backup download from: ${api_base_url_backup}`); - let download_backup_file_result = await ipcRenderer.invoke('download_file', api_base_url_backup, endpoint, full_cached_hash_path, hash, verify_hash, overwrite_existing).then((result_backup) => { - if (result_backup) { - console.log(`IPC API backup download file (from backup) process finished successfully: ${hash}`); - return true; - } else if (result_backup == null) { - console.log('IPC API backup download result (file not found?):', result_backup); - return null; - } else { - console.log(`IPC API backup download result (file being downloaded or something went wrong): ${hash}`, result_backup); - return false; - } - }); - return download_backup_file_result; - } else { - console.log(`IPC Download FAILED! No backup API server passed. Returning false.`); - return false; - } - - } else if (result === 'in_progress') { - console.log(`IPC API primary download IN PROGRESS: ${hash}`); - return false; // Should return result instead??? - } else if (result === 'tmp') { - console.log(`IPC API primary download FOUND TMP: ${hash}`); - return false; // Should return result instead??? - } else { - return result; - } - }); - - // if (download_file_result === false) { - // console.log(`Download FAILED! Trying again with backup API server. API Backup: ${api_base_url_backup}`) - // download_file_result = await ipcRenderer.invoke('download_file', api_base_url_backup, endpoint, full_cached_hash_path, hash, verify_hash, overwrite_existing).then((result) => { - // if (result) { - // console.log('IPC download file (from backup) process finished successfully'); - // return true; - // } else if (result == null) { - // console.log('IPC Download Result (from backup) (file not found?):', result); - // return null; - // } else { - // console.log('IPC Download Result (from backup) (file being downloaded or something went wrong):', result); - // return false; - // } - // }); - // } - - return download_file_result; -} - - - -// Open cached hash file after copying to temp directory -// Used by Svelte Event Launcher -// NOTE: Trying to replace this with something directly in the Svelte app part. 2022-10-11 -// Updated 2022-05-06 -exports.open_hash_file_to_temp = async function ({local_file_cache_path, hash, host_file_temp_path, filename}) { - console.log('*** Electron framework export: open_hash_file_to_temp() ***'); - // console.log('Open cached hash file after copying to temp directory'); - console.log(`Host File Cache Path: ${local_file_cache_path}; Hash: ${hash}; Host File Temp Path: ${host_file_temp_path}; Filename: ${filename}`); - - let subdirectory = hash.substring(0,2); - let subdirectory_path = path.join(local_file_cache_path, subdirectory); - if (fs.existsSync(subdirectory_path)) { - } else { - console.log(`Hashed file subdirectory not found in cache: ${subdirectory_path}`); - return false; - } - - let open_hash_file_to_temp_result = await ipcRenderer.invoke('open_hash_file_to_temp', subdirectory_path, hash, host_file_temp_path, filename).then((result) => { - console.log('IPC open hash file to temp finished'); - console.log(result); - return true; - }) - - // let result = await ipcRenderer.send('open_local_file', local_file_cache_path, hash, host_file_temp_path, filename); - // console.log(result); - - console.log(open_hash_file_to_temp_result); - console.log('End: open_hash_file_to_temp()'); - if (open_hash_file_to_temp_result) { - console.log('File opened successfully'); - return true; - } else { - console.log('File was not opened successfully'); - return false; - } -} - - - -// Open cached hash file after copying to temp directory -// Used by Svelte Event Launcher -// Updated 2022-10-12 -exports.open_hash_file_to_temp_v2 = async function ({local_file_cache_path, hash, host_file_temp_path, filename, verify_hash=true}) { - console.log('*** Aether App Native export: open_hash_file_to_temp_v2() ***'); - console.log(`Local File Cache Path: ${local_file_cache_path}; Hash: ${hash}; Local File Temp Path: ${host_file_temp_path}; Filename: ${filename}`); - - // console.log('Process: Check local hash file cache, Download hash file to cache, and Open cached hash file after copying to temp directory'); - - if (fs.existsSync(local_file_cache_path)) { - } else { - // This should not happen. The directory needs to be created. - console.log(`Cache directory for hashed files was not found: ${local_file_cache_path}`); - return false; - } - - let hash_filename = `${hash}.file`; - - let hash_subdirectory = hash_filename.substring(0,2); - let subdirectory_path = path.join(local_file_cache_path, hash_subdirectory); - - if (fs.existsSync(subdirectory_path)) { - } else { - // This should not happen. The subdirectory needs to be created. - console.log(`Hashed file subdirectory not found in cache directory: ${subdirectory_path}`); - return false; - } - - let full_cached_hash_path = path.join(subdirectory_path, hash_filename); - - if (fs.existsSync(full_cached_hash_path)) { - console.log(`Hashed file exists in cache: ${full_cached_hash_path}`); - - if (verify_hash) { - const file_buffer = fs.readFileSync(full_cached_hash_path); - const file_hash_sha256 = crypto.createHash('sha256'); - file_hash_sha256.update(file_buffer); - - const file_hash_sha256_check = file_hash_sha256.digest('hex'); - if (file_hash_sha256_check == hash) { - // console.log('File hash match', file_hash_sha256_check); - } else { - // This should only happen if the file is actively being downloaded or it is corrupt. - console.log('File hash does not match', file_hash_sha256_check); - return false; - } - } - } else { - console.log(`Hashed file not found in cache: ${full_cached_hash_path}`); - return null; - } - - let local_file_cache_path_w_sub = subdirectory_path; // I need to go back and clean up the variable names related file directory and file paths. - // NOTE: Setting check_hash to false since by default it was checked above. - let open_hash_file_to_temp_result = await ipcRenderer.invoke('open_hash_file_to_temp', local_file_cache_path_w_sub, hash, host_file_temp_path, filename, verify_hash=false).then((result) => { - console.log('IPC open hash file to temp finished'); - if (result) { - console.log('Local hash file was opened from temp directory.'); - return result; - } else { - console.log('Local hash file was not opened from the temp directory. Something went wrong.'); - console.log(result); - return false; - } - }) - - // let result = await ipcRenderer.send('open_local_file', local_file_cache_path, hash, host_file_temp_path, filename); - // console.log(result); - - // console.log(open_hash_file_to_temp_result); - // console.log('End: open_hash_file_to_temp()'); - // if (open_hash_file_to_temp_result) { - // console.log('File opened successfully'); - // return true; - // } else { - // console.log('File was not opened successfully'); - // return false; - // } - - // let open_hash_file_to_temp_result = await ipcRenderer.invoke('open_hash_file_to_temp', subdirectory_path, hash, host_file_temp_path, filename).then((result) => { - // console.log('IPC open hash file to temp finished'); - // if (result) { - // console.log('Local hash file was opened from temp directory.'); - // return result; - // } else { - // console.log('Local hash file was not opened from the temp directory. Something went wrong.'); - // console.log(result); - // return false; - // } - // }) - - return open_hash_file_to_temp_result; -} - - - - -// Open local file -// Used by Svelte Event Launcher -// NOTE: Trying to replace this with something directly in the Svelte app part. 2022-10-11 -// Updated 2022-03-10 -exports.open_local_file = async function ({local_file_path, filename}) { - console.log('*** Electron framework export: open_local_file() ***'); - // console.log('Open local file'); - console.log(`Local File Path: ${local_file_path}; Filename: ${filename}`); - - // let full_local_file_path = path.join(local_file_path, filename); - // console.log(full_local_file_path); - - // if (fs.existsSync(full_local_file_path)) { - // console.log(`Local file exists: ${full_local_file_path}`); - // // return true; - // } else { - // return false; - // } - - let open_local_file_result = await ipcRenderer.invoke('open_local_file', local_file_path, filename).then((result) => { - console.log('IPC open local file finished'); - console.log(result); - return true; - }) - - console.log(open_local_file_result); - console.log('End: open_local_file()'); - if (open_local_file_result) { - console.log('File opened successfully'); - return true; - } else { - console.log('File was not opened successfully'); - return false; - } -} - - -// // Check local file cache and download from server if needed. -// // Updated 2022-03-09 -// // exports.check_file_cache = async function ({local_file_cache_path, event_file_id, hash}) { -// exports.check_file_cache = async function ({api_base_url, local_file_cache_path, event_file_id, hash}) { -// console.log('*** Electron framework export: check_file_cache() ***'); -// // console.log('Check local file cache and download from server if needed.'); -// console.log(`Host File Cache Path: ${local_file_cache_path}; Event File ID: ${event_file_id}; Hash: ${hash}`); - -// // NOTE: event_file_id is the event_file.id_random or event_file.event_file_id_random -// let hash_filename = hash+'.file'; - -// let save_path = path.join(local_file_cache_path, hash_filename); -// console.log(save_path); - -// if (fs.existsSync(save_path)) { -// console.log('Hashed file cache already exists: '+save_path); -// return true; -// } else { -// console.log('Hashed file not found in local cache. Downloading file: '+save_path); -// let endpoint = `/event/file/${event_file_id}/download`; -// let result = await ipcRenderer.send('download_file', api_base_url, endpoint, save_path); // Must download file using main node.js thread. -// console.log(result); - -// return new Promise((resolve, reject) => { -// ipcRenderer.once('download_file_reply', function(event, response){ -// console.log(response); -// return response; -// }) -// resolve(true); -// }); - -// // await ipcRenderer.once('download_file_reply', function(event, response){ -// // console.log(response); -// // return response; -// // }); - -// // result.then(function (response) { -// // console.log('Downloaded!!!???'); -// // return true; -// // }).catch(function (error) { -// // console.log(error); -// // return false; -// // }); - -// // return result; - -// // console.log(result); -// // if (result) { -// // return true; -// // } else { -// // return false; -// // } -// } -// } - - -// Check local file cache and download from server if needed. Must use IPC to Main to download file. Set a Promise to wait for download_file_reply. -// Updated 2022-03-09 -async function check_file_cache({api_base_url, local_file_cache_path, event_file_id, hash}) { - console.log('*** Electron framework: check_file_cache() ***'); - // console.log('Check local file cache and download from server if needed.'); - console.log(`Host File Cache Path: ${local_file_cache_path}; Event File ID: ${event_file_id}; Hash: ${hash}`); - - // NOTE: event_file_id is the event_file.id_random or event_file.event_file_id_random - let hash_filename = hash+'.file'; - - let save_path = path.join(local_file_cache_path, hash_filename); - console.log(save_path); - - if (fs.existsSync(save_path)) { - console.log('Hashed file cache already exists: '+save_path); - return true; - } else { - console.log('Hashed file not found in local cache. Downloading file: '+save_path); - let endpoint = `/event/file/${event_file_id}/download`; - let result = await ipcRenderer.send('download_file', api_base_url, endpoint, save_path); // Must download file using main node.js thread. - console.log(result); - - return new Promise((resolve, reject) => { - ipcRenderer.once('download_file_reply', function(event, response){ - console.log(response); - return response; - }) - resolve(true); - }); - - // await ipcRenderer.once('download_file_reply', function(event, response){ - // console.log(response); - // return response; - // }); - - // result.then(function (response) { - // console.log('Downloaded!!!???'); - // return true; - // }).catch(function (error) { - // console.log(error); - // return false; - // }); - - // return result; - - // console.log(result); - // if (result) { - // return true; - // } else { - // return false; - // } - } -} - - -// IPC to Main: Open local file cache if available. Copy to temp directory with given filename first. -// Updated 2022-03-09 -async function open_local_file({local_file_cache_path, hash, host_file_temp_path, filename}) { - console.log('*** Electron framework: open_local_file() ***'); - // console.log('Open local file cache if available. Copy to temp directory with given filename first.'); - console.log(`Host File Cache Path: ${local_file_cache_path}; Hash: ${hash}; Host File Temp Path: ${host_file_temp_path}; Filename: ${filename}`); - - console.log(local_file_cache_path); - console.log(hash); - console.log(filename); - - let result = await ipcRenderer.send('open_local_file', local_file_cache_path, hash, host_file_temp_path, filename); - console.log(result); - - return true; -} - - -// No longer needed? Not referenced as of 2022-10-11 -exports.check_file_cache_and_open_local_file = async function ({local_file_cache_path, event_file_id, hash, host_file_temp_path, filename}) { - console.log('*** Electron framework: check_file_cache_and_open_local_file() ***'); - console.log('Checking the local file cache against the remote server and then opening the local file.'); - - let check_file_cache_result = check_file_cache({local_file_cache_path: local_file_cache_path, event_file_id: event_file_id, hash: hash}); - console.log(check_file_cache_result); - - if (check_file_cache_result) { - let open_local_file_result = open_local_file({local_file_cache_path: local_file_cache_path, hash: hash, host_file_temp_path: host_file_temp_path, filename: filename}); - console.log(open_local_file_result); - - return open_local_file_result; - } - - ipcRenderer.once('download_file_reply', function(event, response){ - console.log(response); - - let open_local_file_result = open_local_file({local_file_cache_path: local_file_cache_path, hash: hash, host_file_temp_path: host_file_temp_path, filename: filename}); - console.log(open_local_file_result); - - return open_local_file_result; - }) -} - - -// Kill processes -// Signals: HUP (hang up), INT (interrupt), QUIT (quit), ABRT (abort), KILL (non-catchable, non-ignoraable kill), ALRMn (alarm clock), TERM (default; software termination signal) -// Updated 2022-05-07 -exports.kill_processes = async function ({process_name = null, process_id = null, signal = null}) { - console.log('*** Electron framework export: kill_processes() ***'); - console.log(process_name); // process_name or grep pattern - - let cmd = ''; - if (os.platform == 'darwin') { - if (signal == 'HUP') { - cmd = `killall -HUP '${process_name}'`; - } else if (signal == 'INT') { - cmd = `killall -INT '${process_name}'`; - } else if (signal == 'QUIT') { - cmd = `killall -QUIT '${process_name}'`; - } else if (signal == 'ABRT') { - cmd = `killall -ABRT '${process_name}'`; - } else if (signal == 'KILL') { - cmd = `killall -KILL '${process_name}'`; - } else if (signal == 'ALRM') { - cmd = `killall -ALRM '${process_name}'`; - } else if (signal == 'TERM') { - cmd = `killall -TERM '${process_name}'`; - } else if (process_id && signal == 'HUP') { - cmd = `killall -HUP ${process_id}`; - } else if (process_id && signal == 'INT') { - cmd = `killall -INT ${process_id}`; - } else if (process_id && signal == 'QUIT') { - cmd = `killall -QUIT ${process_id}`; - } else if (process_id && signal == 'ABRT') { - cmd = `killall -ABRT ${process_id}`; - } else if (process_id && signal == 'KILL') { - cmd = `killall -KILL ${process_id}`; - } else if (process_id && signal == 'ALRM') { - cmd = `killall -ALRM ${process_id}`; - } else if (process_id && signal == 'TERM') { - cmd = `killall -TERM ${process_id}`; - } else { - // cmd = `osascript -e 'quit app "${process_name}" saving no'`; - cmd = `osascript -e 'quit application "${process_name}" saving no'`; - } - - } else { - cmd = `pkill ${process_name}`; - } - - child_process.exec(cmd, (err, stdout, stdin) => { - // if (err) throw err; - if (err) console.log(err); - console.log(stdout); - }); - console.log(`Killed processes matching ${process_name}`); - - if (os.platform == 'darwin') { - if (process_name == 'Parallels:Acrobat Reader') { - // Regular expression: (Parallels).*(Acrobat Reader) - // This will find any process with Parallels and Acrobat Reader in the name - cmd = `pkill -i -f '(Parallels).*(Acrobat Reader)'`; - - child_process.exec(cmd, (err, stdout, stdin) => { - if (err) throw err; - console.log(stdout); - }); - console.log('Killed Parallels Acrobat Reader process'); - } - - if (process_name == 'Parallels:PowerPoint') { - // Regular expression: (Parallels).*(PowerPoint) - // This will find any process with Parallels and PowerPoint in the name - cmd = `pkill -i -f '(Parallels).*(PowerPoint)'`; - - child_process.exec(cmd, (err, stdout, stdin) => { - if (err) throw err; - console.log(stdout); - }); - console.log('Killed Parallels PowerPoint process'); - } - } - - // let signal = 'SIGTERM'; // 'SIGTERM', 'SIGINT', 'SIGHUP' - // process.kill(pid, signal); - // process.kill(pid, 0); // Special case test if process exists - - return true; -} - - -// Run raw osascript -// Updated 2022-05-07 -exports.run_osascript = async function ({cmd=null, interactive=false, language=null, flags='h', program_file=null}) { - console.log('*** Electron framework export: run_osascript() ***'); - console.log(cmd); - - if (os.platform == 'darwin') { - } else { - console.log('Not available for this platform. macOS (darwin) only.'); - return false; - } - - let osascript_str = ''; - - if (Array.isArray(cmd)) { - console.log('List of cmd strings'); - let cmds_str = ''; - for (let i = 0; i < cmd.length; i++) { - cmds_str += `-e '${cmd[i]}'`; - } - osascript_str = `osascript ${cmds_str}` - - } else if (typeof cmd === 'string') { - console.log('Single cmd string'); - osascript_str = `osascript -e '${cmd}'`; - } else { - return false; - } - - if (language) { - console.log(`Language: ${language}`); - osascript_str = `${osascript_str} -l ${language}`; - } - - if (flags) { - console.log(`Flags: ${flags}`); - osascript_str = `${osascript_str} -s ${flags}`; - } - - console.log(`OSA Script String: ${osascript_str}`); - child_process.exec(osascript_str, (err, stdout, stdin) => { - if (err) throw err; - console.log(stdout); - console.log(stdin); - }); - - console.log('Finished'); - return true; -} - - -// Run raw command -// Updated 2022-05-07 -exports.run_cmd = async function ({cmd=null, return_stdout=null, return_stdin=null, sync=null}) { - console.log('*** Electron framework export: run_cmd() ***'); - - console.log(`Command String: ${cmd}`); - - let result; - - if (!sync) { - result = child_process.exec(cmd, (err, stdout, stdin) => { - // if (err) throw err; - if (err) { - console.log('Error:', err); - return false; - }; - - console.log('stdout:', stdout); - // console.log('stdin:', stdin); - - if (return_stdout) { - console.log('Finished and returning stdout'); - return stdout; - } else { - console.log('Finished and returning true'); - return true; - } - }); - } else { - result = child_process.execSync(cmd, (err, stdout, stdin) => { - // if (err) throw err; - if (err) { - console.log('Error:', err); - return false; - }; - - console.log('stdout:', stdout); - // console.log('stdin:', stdin); - - if (return_stdout) { - console.log('Finished and returning stdout'); - return stdout; - } else { - console.log('Finished and returning true'); - return true; - } - }); - } - - console.log('Result:', result); - return result; -} - - -// Run raw command sync -// Updated 2022-05-07 -exports.run_cmd_sync = function ({cmd=null, return_stdout=null, return_stdin=null}) { - console.log('*** Electron framework export: run_cmd() ***'); - - console.log(`Command String: ${cmd}`); - - let stdout; - - try { - stdout = child_process.execSync(cmd, {encoding: 'utf8'}); - console.log('Std Out:', stdout); - } catch (err) { - console.error('Error:', err); - return false; - } - - if (return_stdout) { - console.log('Finished and returning stdout'); - return stdout; - } else { - console.log('Finished and returning true'); - return true; - } - - // let result; - // let stdout; - // let stderr; - // try { - // let { stdout, stderr } = child_process.execSync(cmd, (err, stdout, stdin) => { - // // if (err) throw err; - // if (err) { - // console.log('Error:', err); - // return false; - // }; - - // console.log('stdout:', stdout); - // // console.log('stdin:', stdin); - - // if (return_stdout) { - // console.log('Finished and returning stdout'); - // return stdout; - // } else { - // console.log('Finished and returning true'); - // return true; - // } - // }); - // } catch (err) { - // console.error(err); - // return false; - // } - - // console.log('Result:', result); - // return result; -} - - -// Run raw command -// Updated 2022-05-25 -exports.get_device_info = async function () { - console.log('*** Electron framework export: get_device_info() ***'); - - // https://nodejs.org/api/os.html - - let data = {}; - data['arch'] = os.arch(); - data['hostname'] = os.hostname(); - data['cpus'] = os.cpus(); - data['freemem'] = os.freemem(); - data['totalmem'] = os.totalmem(); - data['loadavg'] = os.loadavg(); - data['networkInterfaces'] = os.networkInterfaces(); - data['platform'] = os.platform(); - data['release'] = os.release(); - data['uptime'] = os.uptime(); - data['version'] = os.version(); - - console.log(data); - return data; -} - - - -// For loading JS file -function loadJS(){ - - // Gives -1 when the given input is not in the string - // i.e this file has not been added - - if(filesAdded.indexOf('script.js') !== -1) - return - - // Head tag - var head = document.getElementsByTagName('head')[0] - - // Creating script element - var script = document.createElement('script') - script.src = 'script.js' - script.type = 'text/javascript' - - // Adding script element - head.append(script) - - // Adding the name of the file to keep record - filesAdded += ' script.js' -} - -// To load CSS file -function loadCSS() { - - if(filesAdded.indexOf('styles.css') !== -1) - return - - var head = document.getElementsByTagName('head')[0] - - // Creating link element - var style = document.createElement('link') - style.href = 'styles.css' - style.type = 'text/css' - style.rel = 'stylesheet' - head.append(style); - - // Adding the name of the file to keep record - filesAdded += ' styles.css' -} - - - -exports.check_and_get_updated_native_app_config = function ({file_path='device_configs/ae_native_app_config.default.json', overwrite=false}) { - console.log('*** Aether App Native export: check_and_get_updated_native_app_config() ***'); - - if (os.platform == 'darwin') { - let default_osit_sync_app_config_path = path.join(default_osit_sync_app_root_path, file_path); - - let default_app_config_path = path.join(app_root_path, 'ae_native_app_config.default.json'); - console.log('macOS app root and config directory: '+default_app_config_path); - - fs.copyFileSync(default_osit_sync_app_config_path, default_app_config_path); - - if (overwrite) { - console.log('Overwriting the current active config file...'); - let active_app_config_path = path.join(app_root_path, 'ae_native_app_config.json'); - console.log('macOS app root and config directory: '+active_app_config_path); - - fs.copyFileSync(default_osit_sync_app_config_path, active_app_config_path); - } - } else if (os.platform == 'linux') { - app_root_path = path.join(home_directory, '.config/OSIT'); - console.log('Linux config directory: '+app_root_path); - return false; - } else { - console.log('Unknown OS... Is this Windows?'); - return false; - } - - return true; -} \ No newline at end of file diff --git a/app/script.js b/app/script.js deleted file mode 100644 index e69de29..0000000 diff --git a/app/style.css b/app/style.css deleted file mode 100644 index e69de29..0000000 diff --git a/app/style.current.css b/app/style.current.css deleted file mode 100644 index cbebafa..0000000 --- a/app/style.current.css +++ /dev/null @@ -1,28 +0,0 @@ -body { - /* min-height: 100%; - height: 100%; - max-height: 100%; */ - - /* margin: .1em; - padding: .1em; */ -} - -section#Main-Body { - /* outline: solid thin red; */ - - /* min-height: 100%; - height: 100%; - max-height: 100%; */ -} - -section#Main-Nav-Menu { - /* min-height: 100%; - height: 100%; - max-height: 100%; */ -} - -section#Main-Content { - /* min-height: 100%; - height: 100%; - max-height: 100%; */ -} \ No newline at end of file diff --git a/app/test.html b/app/test.html deleted file mode 100644 index a0ee9ca..0000000 --- a/app/test.html +++ /dev/null @@ -1,279 +0,0 @@ - - - - - - - One Sky IT's Aether App - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Site-Nav-Menu
- -
- - - - - -
-
System-Notifications (and Site-Notifications)
- -
- -
- -
-
- - - -
- -
- - - -
System-Debug
- - - - - - - - - - diff --git a/dist/main/api_client.js b/dist/main/api_client.js new file mode 100644 index 0000000..652a239 --- /dev/null +++ b/dist/main/api_client.js @@ -0,0 +1,69 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.fetchFullConfig = fetchFullConfig; +async function fetchFullConfig(seed) { + const apiUrls = [ + seed.onsite_api_base_url, + seed.primary_api_base_url, + seed.backup_api_base_url + ].filter(url => url !== null && url !== undefined); + let lastError = null; + for (const baseUrl of apiUrls) { + try { + console.log(`Bootstrap: Attempting connection to ${baseUrl}...`); + // --- STEP 1: Get Device Config --- + const deviceUrl = `${baseUrl}/v3/crud/event_device/${seed.event_device_id}`; + const deviceResponse = await fetch(deviceUrl, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'x-aether-api-key': seed.aether_api_key, + 'x-no-account-id': 'Nothing to See Here' + }, + }); + if (!deviceResponse.ok) { + throw new Error(`Device lookup failed (${deviceResponse.status})`); + } + const deviceResult = await deviceResponse.json(); + const deviceData = deviceResult.data || deviceResult; + // Use 'app_base_url' as the FQDN for the site lookup + const fqdn = deviceData.app_base_url || 'native-demo.oneskyit.com'; + console.log(`Bootstrap Step 1 Success: Device identified. FQDN to use: ${fqdn}`); + // --- STEP 2: Get Site Context --- + const searchUrl = `${baseUrl}/v3/crud/site_domain/search`; + const siteResponse = await fetch(searchUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-aether-api-key': seed.aether_api_key, + 'x-no-account-id': 'Nothing to See Here', + 'x-account-id': deviceData.account_id_random || deviceData.account_id || '' + }, + body: JSON.stringify({ + search_query: { + and: [{ field: 'fqdn', op: 'eq', value: fqdn }] + }, + limit: 1 + }) + }); + if (!siteResponse.ok) { + throw new Error(`Site context lookup failed (${siteResponse.status})`); + } + const siteResult = await siteResponse.json(); + const siteDomain = (siteResult.data && siteResult.data.length > 0) ? siteResult.data[0] : null; + console.log(`Bootstrap Success using ${baseUrl}`); + return { + ...siteDomain, + native_device: deviceData + }; + } + catch (error) { + console.warn(`Bootstrap failed for ${baseUrl}: `, error); + lastError = error; + continue; // Try next URL + } + } + console.error('Bootstrap Critical Failure: All API endpoints exhausted.', lastError); + return null; +} +//# sourceMappingURL=api_client.js.map \ No newline at end of file diff --git a/dist/main/api_client.js.map b/dist/main/api_client.js.map new file mode 100644 index 0000000..250e999 --- /dev/null +++ b/dist/main/api_client.js.map @@ -0,0 +1 @@ +{"version":3,"file":"api_client.js","sourceRoot":"","sources":["../../src/main/api_client.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/main/config_loader.js b/dist/main/config_loader.js new file mode 100644 index 0000000..2ac4594 --- /dev/null +++ b/dist/main/config_loader.js @@ -0,0 +1,62 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.loadSeedConfig = loadSeedConfig; +const fs = __importStar(require("fs")); +const path = __importStar(require("path")); +const os = __importStar(require("os")); +async function loadSeedConfig() { + // For development, we look in the home directory + const configPath = path.join(os.homedir(), 'seed.json'); + try { + if (!fs.existsSync(configPath)) { + console.log(`Seed config not found at: ${configPath}`); + return null; + } + const data = fs.readFileSync(configPath, 'utf-8'); + const config = JSON.parse(data); + // Basic validation + if (!config.event_device_id) { + console.error('Invalid seed config: missing event_device_id'); + return null; + } + return config; + } + catch (error) { + console.error('Error loading seed config:', error); + return null; + } +} +//# sourceMappingURL=config_loader.js.map \ No newline at end of file diff --git a/dist/main/config_loader.js.map b/dist/main/config_loader.js.map new file mode 100644 index 0000000..1d3e3c3 --- /dev/null +++ b/dist/main/config_loader.js.map @@ -0,0 +1 @@ +{"version":3,"file":"config_loader.js","sourceRoot":"","sources":["../../src/main/config_loader.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAKA,wCAwBC;AA7BD,uCAAyB;AACzB,2CAA6B;AAC7B,uCAAyB;AAGlB,KAAK,UAAU,cAAc;IAClC,iDAAiD;IACjD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;IAExD,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC/B,OAAO,CAAC,GAAG,CAAC,6BAA6B,UAAU,EAAE,CAAC,CAAC;YACvD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAe,CAAC;QAE9C,mBAAmB;QACnB,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;YAC5B,OAAO,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;YAC9D,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"} \ No newline at end of file diff --git a/dist/main/index.js b/dist/main/index.js new file mode 100644 index 0000000..5596a18 --- /dev/null +++ b/dist/main/index.js @@ -0,0 +1,99 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +const electron_1 = require("electron"); +const path = __importStar(require("path")); +const config_loader_1 = require("./config_loader"); +const api_client_1 = require("./api_client"); +let mainWindow = null; +let cachedSeed = null; +let cachedFullConfig = null; +async function createWindow() { + // 1. Initial Load of Configs + cachedSeed = await (0, config_loader_1.loadSeedConfig)(); + if (cachedSeed) { + cachedFullConfig = await (0, api_client_1.fetchFullConfig)(cachedSeed); + } + mainWindow = new electron_1.BrowserWindow({ + width: 1400, + height: 900, + title: 'OSIT Aether Launcher (Native)', + webPreferences: { + preload: path.join(__dirname, '../preload/index.js'), + contextIsolation: true, + nodeIntegration: false, + }, + }); + // Prioritize demo.localhost for local development + const devUrl = 'http://demo.localhost:5173'; + // Fallback URL if local is offline + const fallbackUrl = 'https://dev-demo.oneskyit.com/'; + console.log(`Loading UI from: ${devUrl}`); + mainWindow.loadURL(devUrl).catch(() => { + console.warn(`Failed to load ${devUrl}. Falling back to ${fallbackUrl}`); + mainWindow?.loadURL(fallbackUrl); + }); + mainWindow.on('closed', () => { + mainWindow = null; + }); +} +electron_1.app.on('ready', createWindow); +electron_1.app.on('window-all-closed', () => { + if (process.platform !== 'darwin') { + electron_1.app.quit(); + } +}); +electron_1.app.on('activate', () => { + if (mainWindow === null) { + createWindow(); + } +}); +// IPC Handlers +electron_1.ipcMain.handle('get-seed-config', async () => { + return cachedSeed || await (0, config_loader_1.loadSeedConfig)(); +}); +electron_1.ipcMain.handle('get-device-config', async () => { + if (cachedFullConfig) + return cachedFullConfig; + if (cachedSeed) { + cachedFullConfig = await (0, api_client_1.fetchFullConfig)(cachedSeed); + return cachedFullConfig; + } + return null; +}); +electron_1.ipcMain.handle('get-jwt', async () => { + return null; +}); +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/main/index.js.map b/dist/main/index.js.map new file mode 100644 index 0000000..70256a4 --- /dev/null +++ b/dist/main/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/main/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAAuD;AACvD,2CAA6B;AAC7B,mDAAiD;AACjD,6CAA+C;AAG/C,IAAI,UAAU,GAAyB,IAAI,CAAC;AAC5C,IAAI,UAAU,GAAsB,IAAI,CAAC;AACzC,IAAI,gBAAgB,GAAQ,IAAI,CAAC;AAEjC,KAAK,UAAU,YAAY;IACzB,6BAA6B;IAC7B,UAAU,GAAG,MAAM,IAAA,8BAAc,GAAE,CAAC;IACpC,IAAI,UAAU,EAAE,CAAC;QACf,gBAAgB,GAAG,MAAM,IAAA,4BAAe,EAAC,UAAU,CAAC,CAAC;IACvD,CAAC;IAED,UAAU,GAAG,IAAI,wBAAa,CAAC;QAC7B,KAAK,EAAE,IAAI;QACX,MAAM,EAAE,GAAG;QACX,KAAK,EAAE,+BAA+B;QACtC,cAAc,EAAE;YACd,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,qBAAqB,CAAC;YACpD,gBAAgB,EAAE,IAAI;YACtB,eAAe,EAAE,KAAK;SACvB;KACF,CAAC,CAAC;IAEH,kDAAkD;IAClD,MAAM,MAAM,GAAG,4BAA4B,CAAC;IAC5C,mCAAmC;IACnC,MAAM,WAAW,GAAG,gCAAgC,CAAC;IAErD,OAAO,CAAC,GAAG,CAAC,oBAAoB,MAAM,EAAE,CAAC,CAAC;IAE1C,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;QACpC,OAAO,CAAC,IAAI,CAAC,kBAAkB,MAAM,qBAAqB,WAAW,EAAE,CAAC,CAAC;QACzE,UAAU,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;QAC3B,UAAU,GAAG,IAAI,CAAC;IACpB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,cAAG,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;AAE9B,cAAG,CAAC,EAAE,CAAC,mBAAmB,EAAE,GAAG,EAAE;IAC/B,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAClC,cAAG,CAAC,IAAI,EAAE,CAAC;IACb,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,cAAG,CAAC,EAAE,CAAC,UAAU,EAAE,GAAG,EAAE;IACtB,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QACxB,YAAY,EAAE,CAAC;IACjB,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,eAAe;AACf,kBAAO,CAAC,MAAM,CAAC,iBAAiB,EAAE,KAAK,IAAI,EAAE;IAC3C,OAAO,UAAU,IAAI,MAAM,IAAA,8BAAc,GAAE,CAAC;AAC9C,CAAC,CAAC,CAAC;AAEH,kBAAO,CAAC,MAAM,CAAC,mBAAmB,EAAE,KAAK,IAAI,EAAE;IAC7C,IAAI,gBAAgB;QAAE,OAAO,gBAAgB,CAAC;IAC9C,IAAI,UAAU,EAAE,CAAC;QACf,gBAAgB,GAAG,MAAM,IAAA,4BAAe,EAAC,UAAU,CAAC,CAAC;QACrD,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC,CAAC;AAEH,kBAAO,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;IACnC,OAAO,IAAI,CAAC;AACd,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/preload/index.js b/dist/preload/index.js new file mode 100644 index 0000000..38d7869 --- /dev/null +++ b/dist/preload/index.js @@ -0,0 +1,10 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const electron_1 = require("electron"); +electron_1.contextBridge.exposeInMainWorld('aetherNative', { + getSeedConfig: () => electron_1.ipcRenderer.invoke('get-seed-config'), + getDeviceConfig: () => electron_1.ipcRenderer.invoke('get-device-config'), + getJWT: () => electron_1.ipcRenderer.invoke('get-jwt'), + log: (message) => console.log('[Native Log]', message), +}); +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/preload/index.js.map b/dist/preload/index.js.map new file mode 100644 index 0000000..be7eba0 --- /dev/null +++ b/dist/preload/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/preload/index.ts"],"names":[],"mappings":";;AAAA,uCAAsD;AAEtD,wBAAa,CAAC,iBAAiB,CAAC,cAAc,EAAE;IAC9C,aAAa,EAAE,GAAG,EAAE,CAAC,sBAAW,CAAC,MAAM,CAAC,iBAAiB,CAAC;IAC1D,eAAe,EAAE,GAAG,EAAE,CAAC,sBAAW,CAAC,MAAM,CAAC,mBAAmB,CAAC;IAC9D,MAAM,EAAE,GAAG,EAAE,CAAC,sBAAW,CAAC,MAAM,CAAC,SAAS,CAAC;IAC3C,GAAG,EAAE,CAAC,OAAe,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,OAAO,CAAC;CAC/D,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/shared/types.js b/dist/shared/types.js new file mode 100644 index 0000000..11e638d --- /dev/null +++ b/dist/shared/types.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/dist/shared/types.js.map b/dist/shared/types.js.map new file mode 100644 index 0000000..aff6fd6 --- /dev/null +++ b/dist/shared/types.js.map @@ -0,0 +1 @@ +{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/index.js b/index.js deleted file mode 100644 index f7f7279..0000000 --- a/index.js +++ /dev/null @@ -1,586 +0,0 @@ -const { app, BrowserWindow, ipcMain, shell, systemPreferences } = require('electron'); - - -const axios = require('axios'); -const crypto = require('crypto'); -const fs = require('fs'); -const os = require('os'); -const path = require('path'); -const process = require('process'); -const { strict } = require('assert'); - -//const http = require('http'); -//const request = require('request'); -//const url = require('url'); -// const usb = require('usb') // Compiled with an old version of Node.js - -console.log(`OS: ${os.type()} ${process.getSystemVersion()}`); -// console.log(process.getSystemVersion()); - -let home_directory = require('os').homedir(); -console.log('Home: '+home_directory); - -let tmp_directory = require('os').tmpdir(); -console.log('Temporary: '+tmp_directory); - -// Set the config path for macOS or Linux -let config_directory = 'OSIT/native_app'; -let config_filename = 'ae_native_app_sk_config.json'; -let config_path = ''; - -let local_file_cache_path = null; -let host_file_temp_path = null; - -let endpoints_in_progress = []; - -/* Look for and load a JSON formatted config file. */ -if (os.platform == 'darwin') { - let config_path_default = path.join(home_directory, config_directory, config_filename); - let config_path_macos = path.join(home_directory, 'Library/Application Support/OSIT', config_filename); - // let config_path_opt2 = path.join(home_directory, 'OSIT', config_filename); - - if (fs.existsSync(config_path_default)) { - console.log('Default config file path exists: '+config_path_default); - config_path = config_path_default; - } else if (fs.existsSync(config_path_macos)) { - console.log('macOS config file path exists: '+config_path_macos); - config_path = config_path_macos; - } else { - console.log(`No config file found: ${config_path_default} or ${config_path_macos}`); - config_path = ''; - // fs.mkdirSync(config_file_directory_path, true); - // console.log('Config directory path created: '+config_file_directory_path); - } - - console.log(`Using config found on macOS: ${config_path}`); -} else if (os.platform == 'linux') { - let config_path_default = path.join(home_directory, config_directory, config_filename); - let config_path_linux_os = path.join(home_directory, '.config/OSIT', config_filename); - let config_path_temp = path.join(home_directory, 'tmp/OSIT', config_filename); - - if (fs.existsSync(config_path_default)) { - console.log('Default config file path exists: '+config_path_default); - config_path = config_path_default; - } else if (fs.existsSync(config_path_linux_os)) { - console.log('Linux config file path exists: '+config_path_linux_os); - config_path = config_path_linux_os; - } else if (fs.existsSync(config_path_temp)) { - console.log('Temp config file path exists: '+config_path_temp); - config_path = config_path_temp; - } else { - console.log(`No config file found: ${config_path_default} or ${config_path_linux_os} or ${config_path_temp}`); - config_path = ''; - } - - console.log(`Using config found on Linux: ${config_path}`); -} - -let config = JSON.parse(fs.readFileSync(config_path)); -console.log('Config file read.', config); -/* -Minimal configuration contains: -* conf_file_check_path = '~/OSIT/sync/admin_share/internal/ae_osit_app.default.conf' -* conf_file_check_path_backup = 'ae_osit_app.conf' - -* api_pref_use = 'local' or 'remote' or 'backup' -* api_base_url_local = https://local-api.oneskyit.com -* api_base_url_remote = https://api.oneskyit.com -* api_base_url_backup = https://bak-api.oneskyit.com - -* app_pref_use = 'local' or 'remote' or 'backup' -* app_base_url_local = https://local-demo.oneskyit.com -* app_base_url_remote = https://demo.oneskyit.com -* app_base_url_backup = https://bak-demo.oneskyit.com - -* device_id = 'abcd1234' -*/ - - -/* -Ask for permissions from macOS to use the microphone, screen, camera. The OS may delay actually asking for permission until the permission is actually attempted to be used. It may be worth doing a test attempt early on if access has not already been granted. -STI 2023-06-03 -*/ -if (os.type == 'Darwin') { - if (systemPreferences.getMediaAccessStatus('microphone') != 'granted') { - systemPreferences.askForMediaAccess('microphone'); - } else { - console.log('Microphone access:', systemPreferences.getMediaAccessStatus('microphone')); - } - - if (systemPreferences.getMediaAccessStatus('screen') != 'granted') { - systemPreferences.askForMediaAccess('screen'); - } else { - console.log('Screen access:', systemPreferences.getMediaAccessStatus('screen')); - } - - if (systemPreferences.getMediaAccessStatus('camera') != 'granted') { - systemPreferences.askForMediaAccess('camera'); - } else { - console.log('Camera access:', systemPreferences.getMediaAccessStatus('camera')); - } -} - - - -async function get_url_cfg() { - let base_url = `${config.api_protocol}://${config.api_server}:${config.api_port}/${config.api_path}`; - - let axios_api = axios.create({ - baseURL: base_url, - timeout: 60000, // in milliseconds; 60000 = 60 seconds - /* other custom settings */ - }); - // axios_api.defaults.headers = config['headers']; - - // axios.defaults.baseURL = `${config.api_protocol}://${config.api_server}:${config.api_port}/${config.api_path}`; - axios_api.defaults.headers.common['Access-Control-Allow-Origin'] = config.access_control_allow_origin; // '*'; // app_config.access_control_allow_origin; - axios_api.defaults.headers.common['content-type'] = 'application/json'; - axios_api.defaults.headers.common['x-aether-api-key'] = config.api_secret_key; - // axios_api.defaults.headers.common['x-account-id'] = config.account_id ?? 'xFP7AhU8Zlc'; - axios_api.defaults.headers.common['x-no-account-id'] = 'nothing to see here'; - - let event_device_id = config.event_device_id ?? 'dbgMWS3KEHE'; - // let endpoint = `/event/device/${event_device_id}`; - let endpoint = `/v2/crud/event/device/${event_device_id}`; - - let params = { - inc_event_cfg: false, - inc_event_location: false, - enabled: 'enabled', - }; - - let response_data_promise = await axios_api.get( - endpoint, - { - params: params, - onDownloadProgress: (progressEvent) => { - let percent_completed = Math.round( - (progressEvent.loaded * 100) / progressEvent.total - ); - console.log('GET Data Timestamp:', progressEvent.timeStamp, 'Total:', progressEvent.total, 'Loaded:', progressEvent.loaded, 'Percent Completed', percent_completed); - - // temp_get_object_percent_completed = percent_completed; - } - } - ) - .then(function (response) { - console.log(`Response: ${response}`); - - let return_data = response.data['data']; - if (Array.isArray(return_data)) { - console.log(`Data result is an array/list. Array length: ${return_data.length}`); - } else { - console.log(`Data result is a dictionary/object, not an array/list.`); - } - return return_data; - }) - .catch(function (error) { - console.log(`Base URL: ${base_url} | Endpoint: ${endpoint}`); - - console.log('Error Message:', error.message); // Is this needed here or below in the in the else portion??? - if (error.response) { - console.log(`Response Status: ${error.response.status}; Status Text: ${error.response.statusText}`); - } else { - console.log('Error:', error); - } - - console.log('Full Error:', error); - - if (error.response && error.response.status === 404) { - return null; // Returning null since there were no results - } - return false; // Returning false since something may have gone wrong. Also more in line with what the API returns. - }); - - return response_data_promise; -} - -let new_config = get_url_cfg(); -console.log(new_config); - - - - -function createWindow () { - // Create the browser window. - win = new BrowserWindow({ - height: config.height ?? 720, - width: config.width ?? 1280, - backgroundColor: '#aaa', - icon: './app/img/favicon.ico', - webPreferences: { - contextIsolation: false, - nodeIntegration: true, - nodeIntegrationInWorker: true, - enableRemoteModule: true - } - }) - - // win.setMinimumSize(1024, 768); - // win.setMinimumSize(1280, 768); - win.setMinimumSize(1400, 768); - - //win.setFullScreenable(false) - win.FullScreenable = false; - - // win.webContents.session.clearStorageData(['filesystem']); // Does this do anything??? - - // native_app_which_html = 'default', 'path', 'url' - // 'default' (url) is within the bundled native app - // 'path' (external to app) is a file path on the host, probably under the home directory - // 'url' is over HTTPS, maybe onsite or offsite - // unknown points to an index.html file in the app directory - - // Load the index.html of the app - if (config.native_app_which_html == '' || config.native_app_which_html == 'default') { - // win.loadFile('app/index.html'); - let index_url = config.native_app_index_url ?? 'https://oneskyit.com/'; - win.loadURL(index_url); - } else if (config.native_app_which_html == 'path') { - let index_path = ''; - - if (config.native_app_index_path) { - index_path = config.native_app_index_path.replace('[home]', home_directory); - } else { - index_path = path.join(home_directory, 'OSIT/native_app/app/index.html'); - } - win.loadFile(index_path); - } else if (config.native_app_which_html == 'url') { - let index_url = config.native_app_index_url ?? 'https://oneskyit.com/'; - win.loadURL(index_url); - } else { - win.loadFile('app/index.html'); - } - - // Open the DevTools. - if (config.developer_tools) { - win.webContents.openDevTools(); // Comment out for production - } - - // Emitted when the window is closed. - win.on('closed', () => { - // Dereference the window object, usually you would store windows - // in an array if your app supports multi windows, this is the time - // when you should delete the corresponding element. - win = null; - }) - - win.on('minimize', () => { - //win.restore(); - }) -} - - -// This method will be called when Electron has finished -// initialization and is ready to create browser windows. -// Some APIs can only be used after this event occurs. -app.on('ready', createWindow); - - -// Quit when all windows are closed. -app.on('window-all-closed', () => { - // On macOS it is common for applications and their menu bar - // to stay active until the user quits explicitly with Cmd + Q - if (process.platform !== 'darwin') { - if (win) { - win.close(); - } - app.quit(); - } -}) - - -app.on('activate', () => { - // On macOS it's common to re-create a window in the app when the - // dock icon is clicked and there are no other windows open.location_files - if (win === null) { - createWindow(); - } -}) - - -// Import config data -// Updated 2022-04-16 -ipcMain.handle('import_config', async (event, config_data) => { - console.log('*** Electron IPC Main: import_config() ***'); - // console.log('ipcMain on download_file: api_base_url='+api_base_url+' | api_temporary_token='+api_temporary_token); - console.log('ipcMain on import_config:'); - console.log(config_data); - - config = config_data; - - local_file_cache_path = config.local_file_cache_path; - host_file_temp_path = config.host_file_temp_path; - - if (fs.existsSync(local_file_cache_path)) { - console.log('Host file cache path exists: '+local_file_cache_path); - } else { - fs.mkdirSync(local_file_cache_path, true); - console.log('Host file cache path created: '+local_file_cache_path); - } - - if (fs.existsSync(host_file_temp_path)) { - console.log('Host file temp path exists: '+host_file_temp_path); - } else { - fs.mkdirSync(host_file_temp_path, true); - console.log('Host file temp path created: '+host_file_temp_path); - } - - return true; -}); - - -// Download file to path -// full_save_path should be the full path that includes the filename -// Updated 2023-05-14 -ipcMain.handle('download_file', async (event, api_base_url, api_endpoint, full_save_path, hash=null, verify_hash=false, overwrite_existing=false, offset_minutes=3) => { - console.log('*** Electron IPC Main: download_file() ***'); - // console.log('ipcMain on download_file: api_base_url='+api_base_url+' | api_temporary_token='+api_temporary_token); - // console.log('ipcMain on download_file: api_base_url='+api_base_url); - console.log(`ipcMain download and save file: HTTP ${api_endpoint} -> FILE ${full_save_path}`); - if (!api_base_url) { - console.log('API Base URL is required. Returning false'); - return false; - } - - axios.defaults.baseURL = api_base_url; - axios.defaults.headers.common['Access-Control-Allow-Origin'] = config.access_control_allow_origin; // '*'; // app_config.access_control_allow_origin; - axios.defaults.headers.common['content-type'] = 'application/json'; - axios.defaults.headers.common['x-aether-api-key'] = config.api_secret_key; - axios.defaults.headers.common['x-account-id'] = config.account_id; - - const url = api_endpoint; - - const tmp_full_save_path = full_save_path+'.tmp'; - - if (fs.existsSync(tmp_full_save_path)) { - console.log(`A temp download file was found! ${tmp_full_save_path}`); - - let stats = null; - try { - stats = fs.statSync(tmp_full_save_path); - - // console.log(`File Accessed Last: ${stats.atime}`); // File data last changed (actual contents) - console.log(`File Data Last Modified: ${stats.mtime}`); // File data last changed (actual contents) - console.log(`File Metadata Last Modified: ${stats.ctime}`); // File metadata last changed (filename, permissions, etc) - } catch (error) { - console.log(error); - } - - let current_datetime = new Date(); - // In minutes. After 5ish minutes of no changes to the file content seems reasonable? Tested with 3 minutes for multiple meetings and no noticable problem. - let offset_datetime = new Date(current_datetime.getTime() - offset_minutes*60000); - - // console.log(`Times: ${current_datetime} ${offset_datetime} | File ${stats.mtime}`); - if (stats.mtime < offset_datetime) { - console.log(`Marking as expired temp file based on modified datetime. Expire after: ${offset_minutes} minutes`); - overwrite_existing = true; - } else { - console.log(`Temp download file has not expired yet. Expire after: ${offset_minutes} minutes`); - // return false; - return 'tmp'; - } - } - if (fs.existsSync(full_save_path)) { - console.log(`A cached file was found! ${full_save_path}`); - if (verify_hash) { - const file_buffer = fs.readFileSync(full_save_path); - const file_hash_sha256 = crypto.createHash('sha256'); - file_hash_sha256.update(file_buffer); - - const file_hash_sha256_check = file_hash_sha256.digest('hex'); - if (file_hash_sha256_check == hash) { - // console.log('File hash match', file_hash_sha256_check); - } else { - console.log('File hash does not match', file_hash_sha256_check); - if (overwrite_existing) { - console.log('Going to overwrite the existing file because the hash does not match.'); - } else { - return false; - } - } - } - // return false; - } - - console.log('Endpoints in Progress:', endpoints_in_progress); - if (endpoints_in_progress.includes(api_endpoint)) { - console.log(`Endpoint already being downloaded: ${api_endpoint}`); - // return false; - return 'in_progress'; - } - // console.log(`Done with checks. Time to download! Endpoint: ${api_endpoint}`); - endpoints_in_progress.push(api_endpoint); - - let download_result = await axios({ - method: 'get', - url: url, - responseType: 'stream' /* responseType must be stream */ - }).then(function (response) { - console.log(`Creating write stream for downloading endpoint: ${api_endpoint}`); - const writer = fs.createWriteStream(tmp_full_save_path); - - return new Promise((resolve, reject) => { - response.data.pipe(writer); - let error = null; - writer.on('error', err => { - console.log('Writer error!'); - error = err; - console.log(error); - writer.close(); - reject(err); - }); - writer.on('close', () => { - - if (!error) { - // console.log(`Download complete! Writer closed.`); - resolve(true); - } else { - console.log('Writer closed unexpectedly!', error); - } - //no need to call the reject here, as it will have been called in the - //'error' stream; - }); - }); - }) - .then(function (response) { - console.log(`Download complete. Temporary file moved/renamed: ${full_save_path}`); - fs.renameSync(tmp_full_save_path, full_save_path); - - for( let i = 0; i < endpoints_in_progress.length; i++){ - if ( endpoints_in_progress[i] === api_endpoint) { - endpoints_in_progress.splice(i, 1); - // NOTE: Decrement the index variable so it does not skip the next item in the array. - i--; - } - } - return true; - }) - .catch(function (error) { - console.log(`Error downloading! Endpoint: ${api_endpoint}`); - - for( let i = 0; i < endpoints_in_progress.length; i++){ - if ( endpoints_in_progress[i] === api_endpoint) { - endpoints_in_progress.splice(i, 1); - // NOTE: Decrement the index variable so it does not skip the next item in the array. - i--; - } - } - - if (error.response) { - console.log(`Response Status: ${error.response.status}; Status Text: ${error.response.statusText}`); - } else { - console.log('Error:', error); - } - - if (error.response && error.response.status === 404) { - return null; // Returning null since there were no results - } - return false; // Returning false since something may have gone wrong. Also more in line with what the API returns. - }); - // console.log(`Done with download function! Endpoint: ${api_endpoint}`); - - return download_result; -}); - - -ipcMain.handle('open_hash_file_to_temp', async (event, local_file_cache_path, hash, host_file_temp_path, filename, verify_hash=true) => { - console.log('*** Electron IPC Main: open_hash_file_to_temp() ***'); - console.log('ipcMain on open_hash_file_to_temp'); - console.log(`ipcMain open hash file from temp directory: ${local_file_cache_path} -> ${host_file_temp_path}/${filename}`); - - // NOTE: This may be needed later? Uncomment if paths are relative to working directory. - // let cache_file_path = path.join(process.cwd(), local_file_cache_path); - let cache_file_path = local_file_cache_path; - console.log(cache_file_path); - - let hash_filename = hash+'.file'; - let full_cache_file_path = path.join(cache_file_path, hash_filename); - console.log(full_cache_file_path); - - // NOTE: This may be needed later? Uncomment if paths are relative to working directory. - // open_temp_file_path = path.join(process.cwd(), host_file_temp_path, filename); // 'temp/' - open_temp_file_path = path.join(host_file_temp_path, filename); // 'temp/' - console.log(open_temp_file_path); - - if (fs.existsSync(open_temp_file_path)) { - console.log('A file with the same name already exists in the local temp directory: '+open_temp_file_path); - // NOTE: Should the file be checked to see if it has changed from the hashed cache version??? - // NOTE: What if they made changes to the file locally in temp? The changed file would be used since a new copy is not being made. - // NOTE: It might make sense for this to be a configurable option depending on the group. Some do not allow changes. This helps enforce that. - } - - if (fs.existsSync(full_cache_file_path)) { - // console.log(`Hashed file exists in cache: ${full_cache_file_path}`); - console.log(`Copying file to temp: ${open_temp_file_path}`); - try { - fs.copyFileSync(full_cache_file_path, open_temp_file_path); - } catch (error) { - console.error(error); - return false; - } - - if (verify_hash) { - const file_buffer = fs.readFileSync(full_cache_file_path); - const file_hash_sha256 = crypto.createHash('sha256'); - file_hash_sha256.update(file_buffer); - - const file_hash_sha256_check = file_hash_sha256.digest('hex'); - if (file_hash_sha256_check == hash) { - // console.log('File hash match', file_hash_sha256_check); - } else { - console.log('File hash does not match', file_hash_sha256_check); - return false; - } - } - - // console.log('Creating file link: '+open_temp_file_path); - // fs.linkSync(full_cache_file_path, open_temp_file_path); - } else { - console.log(`Hashed file not found in cache: ${full_cache_file_path}`); - return false; - } - - try { - await shell.openPath(open_temp_file_path); - } catch (error) { - console.error(error); - return false; - } - - console.log('End: Electron IPC Main: open_hash_file_to_temp()'); - return true; -}); - - -ipcMain.handle('open_local_file', async (event, local_file_path, filename, use_cwd=true) => { - console.log('*** Electron IPC Main: open_local_file() ***'); - console.log('ipcMain on open_local_file'); - console.log(`ipcMain open local file from directory: ${local_file_path}/${filename}`); - - let full_local_file_path = null; - - if (use_cwd) { - full_local_file_path = path.join(process.cwd(), local_file_path, filename); - console.log(full_local_file_path); - } else { - full_local_file_path = path.join(local_file_path, filename); - console.log(full_local_file_path); - } - - if (fs.existsSync(full_local_file_path)) { - console.log(`Local file exists: ${full_local_file_path}`); - } else { - console.log(`Local file not found: ${full_local_file_path}`); - return false; - } - - try { - await shell.openPath(full_local_file_path); - } catch (error) { - console.error(error); - return false; - } - - console.log('End: Electron IPC Main: open_local_file()'); - return true; -}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index ab0433f..0000000 --- a/package-lock.json +++ /dev/null @@ -1,2354 +0,0 @@ -{ - "name": "aether_app_native", - "version": "2023.5.14.2", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "aether_app_native", - "version": "2023.5.14.2", - "license": "ISC", - "dependencies": { - "axios": "^1.1.2", - "child_process": "^1.0.2", - "fs": "^0.0.1-security", - "os": "^0.1.2", - "path": "^0.12.7" - }, - "devDependencies": { - "electron": "^31.0.0", - "electron-packager": "^17.1.2" - } - }, - "node_modules/@electron/asar": { - "version": "3.2.10", - "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.10.tgz", - "integrity": "sha512-mvBSwIBUeiRscrCeJE1LwctAriBj65eUDm0Pc11iE5gRwzkmsdbS7FnZ1XUWjpSeQWL1L5g12Fc/SchPM9DUOw==", - "dev": true, - "license": "MIT", - "dependencies": { - "commander": "^5.0.0", - "glob": "^7.1.6", - "minimatch": "^3.0.4" - }, - "bin": { - "asar": "bin/asar.js" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/@electron/get": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", - "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "env-paths": "^2.2.0", - "fs-extra": "^8.1.0", - "got": "^11.8.5", - "progress": "^2.0.3", - "semver": "^6.2.0", - "sumchecker": "^3.0.1" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "global-agent": "^3.0.0" - } - }, - "node_modules/@electron/notarize": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-1.2.4.tgz", - "integrity": "sha512-W5GQhJEosFNafewnS28d3bpQ37/s91CDWqxVchHfmv2dQSTWpOzNlUVQwYzC1ay5bChRV/A9BTL68yj0Pa+TSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "fs-extra": "^9.0.1" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron/notarize/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@electron/notarize/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/notarize/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron/osx-sign": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.1.tgz", - "integrity": "sha512-BAfviURMHpmb1Yb50YbCxnOY0wfwaLXH5KJ4+80zS0gUkzDX3ec23naTlEqKsN+PwYn+a1cCzM7BJ4Wcd3sGzw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "compare-version": "^0.1.2", - "debug": "^4.3.4", - "fs-extra": "^10.0.0", - "isbinaryfile": "^4.0.8", - "minimist": "^1.2.6", - "plist": "^3.0.5" - }, - "bin": { - "electron-osx-flat": "bin/electron-osx-flat.js", - "electron-osx-sign": "bin/electron-osx-sign.js" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@electron/osx-sign/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@electron/osx-sign/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/osx-sign/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron/universal": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.5.1.tgz", - "integrity": "sha512-kbgXxyEauPJiQQUNG2VgUeyfQNFk6hBF11ISN2PNI6agUgPl55pv4eQmaqHzTAzchBvqZ2tQuRVaPStGf0mxGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@electron/asar": "^3.2.1", - "@malept/cross-spawn-promise": "^1.1.0", - "debug": "^4.3.1", - "dir-compare": "^3.0.0", - "fs-extra": "^9.0.1", - "minimatch": "^3.0.4", - "plist": "^3.0.4" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/@electron/universal/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@electron/universal/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/universal/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@malept/cross-spawn-promise": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", - "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" - } - ], - "license": "Apache-2.0", - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", - "dev": true, - "license": "MIT", - "dependencies": { - "defer-to-connect": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@types/cacheable-request": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "^3.1.4", - "@types/node": "*", - "@types/responselike": "^1.0.0" - } - }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", - "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/node": { - "version": "20.14.15", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.15.tgz", - "integrity": "sha512-Fz1xDMCF/B00/tYSVMlmK7hVeLh7jE5f3B7X1/hmV0MJBwE27KlS7EvD/Yp+z1lm8mVhwV5w+n8jOZG8AfTlKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/responselike": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", - "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@xmldom/xmldom": { - "version": "0.8.10", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", - "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/author-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/author-regex/-/author-regex-1.0.0.tgz", - "integrity": "sha512-KbWgR8wOYRAPekEmMXrYYdc7BRyhn2Ftk7KWfMUnQ43hFdojWEFRxhhRUm3/OFEdPa1r0KAvTTg9YQK57xTe0g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/axios": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", - "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true, - "license": "MIT" - }, - "node_modules/boolean": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", - "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/buffer-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", - "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.6.0" - } - }, - "node_modules/cacheable-request": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", - "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/child_process": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz", - "integrity": "sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g==", - "license": "ISC" - }, - "node_modules/clone-response": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-response": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/compare-version": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", - "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cross-spawn-windows-exe": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/cross-spawn-windows-exe/-/cross-spawn-windows-exe-1.2.0.tgz", - "integrity": "sha512-mkLtJJcYbDCxEG7Js6eUnUNndWjyUZwJ3H7bErmmtOYU/Zb99DyUkpamuIZE0b3bhmJyZ7D90uS6f+CGxRRjOw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-cross-spawn-windows-exe?utm_medium=referral&utm_source=npm_fund" - } - ], - "license": "Apache-2.0", - "dependencies": { - "@malept/cross-spawn-promise": "^1.1.0", - "is-wsl": "^2.2.0", - "which": "^2.0.2" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/debug": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/dir-compare": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz", - "integrity": "sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-equal": "^1.0.0", - "minimatch": "^3.0.4" - } - }, - "node_modules/electron": { - "version": "31.3.1", - "resolved": "https://registry.npmjs.org/electron/-/electron-31.3.1.tgz", - "integrity": "sha512-9fiuWlRhBfygtcT+auRd/WdBK/f8LZZcrpx0RjpXhH2DPTP/PfnkC4JB1PW55qCbGbh4wAgkYbf4ExIag8oGCA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@electron/get": "^2.0.0", - "@types/node": "^20.9.0", - "extract-zip": "^2.0.1" - }, - "bin": { - "electron": "cli.js" - }, - "engines": { - "node": ">= 12.20.55" - } - }, - "node_modules/electron-packager": { - "version": "17.1.2", - "resolved": "https://registry.npmjs.org/electron-packager/-/electron-packager-17.1.2.tgz", - "integrity": "sha512-XofXdikjYI7MVBcnXeoOvRR+yFFFHOLs3J7PF5KYQweigtgLshcH4W660PsvHr4lYZ03JBpLyEcUB8DzHZ+BNw==", - "deprecated": "Please use @electron/packager moving forward. There is no API change, just a package name change", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@electron/asar": "^3.2.1", - "@electron/get": "^2.0.0", - "@electron/notarize": "^1.2.3", - "@electron/osx-sign": "^1.0.5", - "@electron/universal": "^1.3.2", - "cross-spawn-windows-exe": "^1.2.0", - "debug": "^4.0.1", - "extract-zip": "^2.0.0", - "filenamify": "^4.1.0", - "fs-extra": "^11.1.0", - "galactus": "^1.0.0", - "get-package-info": "^1.0.0", - "junk": "^3.1.0", - "parse-author": "^2.0.0", - "plist": "^3.0.0", - "rcedit": "^3.0.1", - "resolve": "^1.1.6", - "semver": "^7.1.3", - "yargs-parser": "^21.1.1" - }, - "bin": { - "electron-packager": "bin/electron-packager.js" - }, - "engines": { - "node": ">= 14.17.5" - }, - "funding": { - "url": "https://github.com/electron/electron-packager?sponsor=1" - } - }, - "node_modules/electron-packager/node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/electron-packager/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/electron-packager/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/electron-packager/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "pend": "~1.2.0" - } - }, - "node_modules/filename-reserved-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", - "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/filenamify": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", - "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "filename-reserved-regex": "^2.0.0", - "strip-outer": "^1.0.1", - "trim-repeated": "^1.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/flora-colossus": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/flora-colossus/-/flora-colossus-2.0.0.tgz", - "integrity": "sha512-dz4HxH6pOvbUzZpZ/yXhafjbR2I8cenK5xL0KtBFb7U2ADsR+OwXifnxZjij/pZWF775uSCMzWVd+jDik2H2IA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4", - "fs-extra": "^10.1.0" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/flora-colossus/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/flora-colossus/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/flora-colossus/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fs": { - "version": "0.0.1-security", - "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", - "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==", - "license": "ISC" - }, - "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/galactus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/galactus/-/galactus-1.0.0.tgz", - "integrity": "sha512-R1fam6D4CyKQGNlvJne4dkNF+PvUUl7TAJInvTGa9fti9qAv95quQz29GXapA4d8Ec266mJJxFVh82M4GIIGDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4", - "flora-colossus": "^2.0.0", - "fs-extra": "^10.1.0" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/galactus/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/galactus/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/galactus/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-package-info": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-package-info/-/get-package-info-1.0.0.tgz", - "integrity": "sha512-SCbprXGAPdIhKAXiG+Mk6yeoFH61JlYunqdFQFHDtLjJlDjFf6x07dsS8acO+xWt52jpdVo49AlVDnUVK1sDNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "bluebird": "^3.1.1", - "debug": "^2.2.0", - "lodash.get": "^4.0.0", - "read-pkg-up": "^2.0.0" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/get-package-info/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/get-package-info/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, - "node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/global-agent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", - "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "boolean": "^3.0.1", - "es6-error": "^4.1.1", - "matcher": "^3.0.0", - "roarr": "^2.15.3", - "semver": "^7.3.2", - "serialize-error": "^7.0.1" - }, - "engines": { - "node": ">=10.0" - } - }, - "node_modules/global-agent/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "optional": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/got": { - "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=10.19.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true, - "license": "ISC" - }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-core-module": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", - "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isbinaryfile": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", - "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/gjtorikian/" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true, - "license": "ISC", - "optional": true - }, - "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "license": "MIT", - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/junk": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz", - "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha512-3p6ZOGNbiX4CdvEd1VcE6yi78UrGNpjHO33noGwHCnT/o2fyllJDepsm8+mFFv/DvtwFHht5HIHSyOy5a+ChVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/matcher": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", - "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "escape-string-regexp": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/os": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/os/-/os-0.1.2.tgz", - "integrity": "sha512-ZoXJkvAnljwvc56MbvhtKVWmSkzV712k42Is2mA0+0KTSRakq5XXuXpjZjgAt9ctzl51ojhQWakQQpmOvXWfjQ==", - "license": "MIT" - }, - "node_modules/p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/parse-author": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/parse-author/-/parse-author-2.0.0.tgz", - "integrity": "sha512-yx5DfvkN8JsHL2xk2Os9oTia467qnvRgey4ahSm2X8epehBLx/gWLcy5KI+Y36ful5DzGbCS6RazqZGgy1gHNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "author-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "error-ex": "^1.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path": { - "version": "0.12.7", - "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", - "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", - "license": "MIT", - "dependencies": { - "process": "^0.11.1", - "util": "^0.10.3" - } - }, - "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true, - "license": "MIT" - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/plist": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", - "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@xmldom/xmldom": "^0.8.8", - "base64-js": "^1.5.1", - "xmlbuilder": "^15.1.1" - }, - "engines": { - "node": ">=10.4.0" - } - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/rcedit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/rcedit/-/rcedit-3.1.0.tgz", - "integrity": "sha512-WRlRdY1qZbu1L11DklT07KuHfRk42l0NFFJdaExELEu4fEQ982bP5Z6OWGPj/wLLIuKRQDCxZJGAwoFsxhZhNA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn-windows-exe": "^1.1.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", - "dev": true, - "license": "MIT", - "dependencies": { - "lowercase-keys": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/roarr": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", - "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "boolean": "^3.0.1", - "detect-node": "^2.0.4", - "globalthis": "^1.0.1", - "json-stringify-safe": "^5.0.1", - "semver-compare": "^1.0.0", - "sprintf-js": "^1.1.2" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/serialize-error": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "type-fest": "^0.13.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true, - "license": "CC-BY-3.0" - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz", - "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "dev": true, - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-outer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", - "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-outer/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/sumchecker": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", - "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "debug": "^4.1.0" - }, - "engines": { - "node": ">= 8.0" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/trim-repeated": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", - "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/trim-repeated/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "optional": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true, - "license": "MIT" - }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/util": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", - "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", - "license": "MIT", - "dependencies": { - "inherits": "2.0.3" - } - }, - "node_modules/util/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "license": "ISC" - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/xmlbuilder": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", - "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 10c5bd6..0000000 --- a/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "aether_app_native", - "productName": "Aether App: Native", - "version": "2024.8.14.1", - "description": "One Sky IT's Native Aether App", - "main": "index.js", - "scripts": { - "start": "electron .", - "start_nogpu": "electron . --disable-gpu", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "Scott Idem", - "license": "ISC", - "dependencies": { - "axios": "^1.1.2", - "child_process": "^1.0.2", - "fs": "^0.0.1-security", - "os": "^0.1.2", - "path": "^0.12.7" - }, - "devDependencies": { - "electron": "^31.0.0", - "electron-packager": "^17.1.2" - } -} diff --git a/src/main/api_client.ts b/src/main/api_client.ts new file mode 100644 index 0000000..6e77b1f --- /dev/null +++ b/src/main/api_client.ts @@ -0,0 +1,79 @@ +import { SeedConfig } from '../shared/types'; + +export async function fetchFullConfig(seed: SeedConfig): Promise { + const apiUrls = [ + seed.onsite_api_base_url, + seed.primary_api_base_url, + seed.backup_api_base_url + ].filter(url => url !== null && url !== undefined) as string[]; + + let lastError: any = null; + + for (const baseUrl of apiUrls) { + try { + console.log(`Bootstrap: Attempting connection to ${baseUrl}...`); + + // --- STEP 1: Get Device Config --- + const deviceUrl = `${baseUrl}/v3/crud/event_device/${seed.event_device_id}`; + const deviceResponse = await fetch(deviceUrl, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'x-aether-api-key': seed.aether_api_key, + 'x-no-account-id': 'Nothing to See Here' + }, + }); + + if (!deviceResponse.ok) { + throw new Error(`Device lookup failed (${deviceResponse.status})`); + } + + const deviceResult = await deviceResponse.json(); + const deviceData = deviceResult.data || deviceResult; + + // Use 'app_base_url' as the FQDN for the site lookup + const fqdn = deviceData.app_base_url || 'native-demo.oneskyit.com'; + console.log(`Bootstrap Step 1 Success: Device identified. FQDN to use: ${fqdn}`); + + // --- STEP 2: Get Site Context --- + const searchUrl = `${baseUrl}/v3/crud/site_domain/search`; + const siteResponse = await fetch(searchUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-aether-api-key': seed.aether_api_key, + 'x-no-account-id': 'Nothing to See Here', + 'x-account-id': deviceData.account_id_random || deviceData.account_id || '' + }, + body: JSON.stringify({ + search_query: { + and: [{ field: 'fqdn', op: 'eq', value: fqdn }] + }, + limit: 1 + }) + }); + + if (!siteResponse.ok) { + throw new Error(`Site context lookup failed (${siteResponse.status})`); + } + + const siteResult = await siteResponse.json(); + const siteDomain = (siteResult.data && siteResult.data.length > 0) ? siteResult.data[0] : null; + + console.log(`Bootstrap Success using ${baseUrl}`); + + return { + ...siteDomain, + native_device: deviceData + }; + + } catch (error) { + console.warn(`Bootstrap failed for ${baseUrl}: `, error); + lastError = error; + continue; // Try next URL + } + } + + console.error('Bootstrap Critical Failure: All API endpoints exhausted.', lastError); + return null; +} diff --git a/src/main/config_loader.ts b/src/main/config_loader.ts new file mode 100644 index 0000000..ebe9dd0 --- /dev/null +++ b/src/main/config_loader.ts @@ -0,0 +1,30 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import * as os from 'os'; +import { SeedConfig } from '../shared/types'; + +export async function loadSeedConfig(): Promise { + // For development, we look in the home directory + const configPath = path.join(os.homedir(), 'seed.json'); + + try { + if (!fs.existsSync(configPath)) { + console.log(`Seed config not found at: ${configPath}`); + return null; + } + + const data = fs.readFileSync(configPath, 'utf-8'); + const config = JSON.parse(data) as SeedConfig; + + // Basic validation + if (!config.event_device_id) { + console.error('Invalid seed config: missing event_device_id'); + return null; + } + + return config; + } catch (error) { + console.error('Error loading seed config:', error); + return null; + } +} diff --git a/src/main/index.ts b/src/main/index.ts new file mode 100644 index 0000000..1109a4f --- /dev/null +++ b/src/main/index.ts @@ -0,0 +1,76 @@ +import { app, BrowserWindow, ipcMain } from 'electron'; +import * as path from 'path'; +import { loadSeedConfig } from './config_loader'; +import { fetchFullConfig } from './api_client'; +import { SeedConfig } from '../shared/types'; + +let mainWindow: BrowserWindow | null = null; +let cachedSeed: SeedConfig | null = null; +let cachedFullConfig: any = null; + +async function createWindow() { + // 1. Initial Load of Configs + cachedSeed = await loadSeedConfig(); + if (cachedSeed) { + cachedFullConfig = await fetchFullConfig(cachedSeed); + } + + mainWindow = new BrowserWindow({ + width: 1400, + height: 900, + title: 'OSIT Aether Launcher (Native)', + webPreferences: { + preload: path.join(__dirname, '../preload/index.js'), + contextIsolation: true, + nodeIntegration: false, + }, + }); + + // Prioritize demo.localhost for local development + const devUrl = 'http://demo.localhost:5173'; + // Fallback URL if local is offline + const fallbackUrl = 'https://dev-demo.oneskyit.com/'; + + console.log(`Loading UI from: ${devUrl}`); + + mainWindow.loadURL(devUrl).catch(() => { + console.warn(`Failed to load ${devUrl}. Falling back to ${fallbackUrl}`); + mainWindow?.loadURL(fallbackUrl); + }); + + mainWindow.on('closed', () => { + mainWindow = null; + }); +} + +app.on('ready', createWindow); + +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') { + app.quit(); + } +}); + +app.on('activate', () => { + if (mainWindow === null) { + createWindow(); + } +}); + +// IPC Handlers +ipcMain.handle('get-seed-config', async () => { + return cachedSeed || await loadSeedConfig(); +}); + +ipcMain.handle('get-device-config', async () => { + if (cachedFullConfig) return cachedFullConfig; + if (cachedSeed) { + cachedFullConfig = await fetchFullConfig(cachedSeed); + return cachedFullConfig; + } + return null; +}); + +ipcMain.handle('get-jwt', async () => { + return null; +}); diff --git a/src/preload/index.ts b/src/preload/index.ts new file mode 100644 index 0000000..31be28c --- /dev/null +++ b/src/preload/index.ts @@ -0,0 +1,8 @@ +import { contextBridge, ipcRenderer } from 'electron'; + +contextBridge.exposeInMainWorld('aetherNative', { + getSeedConfig: () => ipcRenderer.invoke('get-seed-config'), + getDeviceConfig: () => ipcRenderer.invoke('get-device-config'), + getJWT: () => ipcRenderer.invoke('get-jwt'), + log: (message: string) => console.log('[Native Log]', message), +}); diff --git a/src/shared/types.ts b/src/shared/types.ts new file mode 100644 index 0000000..4d6096a --- /dev/null +++ b/src/shared/types.ts @@ -0,0 +1,20 @@ +export interface SeedConfig { + event_device_id: string; + primary_api_base_url: string; + backup_api_base_url: string | null; + onsite_api_base_url: string | null; + aether_api_key: string; +} + +export interface AetherNativeBridge { + getSeedConfig: () => Promise; + getDeviceConfig: () => Promise; + getJWT: () => Promise; + log: (message: string) => void; +} + +declare global { + interface Window { + aetherNative: AetherNativeBridge; + } +} \ No newline at end of file diff --git a/tests/test_device_lookup.py b/tests/test_device_lookup.py new file mode 100644 index 0000000..a3888af --- /dev/null +++ b/tests/test_device_lookup.py @@ -0,0 +1,55 @@ +import requests +import json + +def test_device_lookup(): + device_id = 'dbgMWS3KEHE' + api_key = 'INSdG85ANwsEIru3nUttMw' + base_url = 'https://dev-api.oneskyit.com' + endpoint = f'{base_url}/v3/crud/event_device/{device_id}' + + headers = { + 'x-aether-api-key': api_key, + 'x-no-account-id': 'Nothing to See Here', + 'Content-Type': 'application/json' + } + + params = { + 'view': 'enriched' + } + + print(f'Testing lookup for device: {device_id}') + print(f'Endpoint: {endpoint}') + + try: + response = requests.get(endpoint, headers=headers, params=params) + print(f'Status Code: {response.status_code}') + + if response.status_code == 200: + data = response.json() + device_data = data.get('data', {}) + + print('Returned Fields (Key Values):') + important_fields = [ + 'account_id_random', + 'app_base_url', + 'code', + 'name', + 'event_id_random', + 'event_location_id_random', + 'local_file_cache_path', + 'host_file_temp_path', + 'recording_path', + 'cfg_json' + ] + + for field in important_fields: + val = device_data.get(field, 'MISSING') + print(f' {field}: {val}') + else: + print(f'Error Response: {response.text}') + + except Exception as e: + print(f'Request failed: {e}') + +if __name__ == '__main__': + test_device_lookup() diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..ee7283d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "CommonJS", + "lib": ["ESNext", "DOM"], + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + "outDir": "dist", + "rootDir": "src", + "sourceMap": true, + "baseUrl": ".", + "paths": { + "@shared/*": ["src/shared/*"] + } + }, + "include": ["src/**/*"], + "exclude": ["node_modules"] +} \ No newline at end of file