fix(packaging): workaround yauzl/Node 26 hang + fix API bootstrap contract
Packaging was silently hanging forever because yauzl 2.10.0 read streams
emit no data events under Node 26, causing extract-zip to block indefinitely
inside @electron/packager 20. Fix: postinstall script patches
@electron/packager/dist/unzip.js to use bsdtar (libarchive) instead.
bsdtar was chosen over 7z because 7z refuses chained symlinks in macOS
.app framework bundles. Both package:linux and package:mac now produce
correct output.
Also corrects the V3 API bootstrap contract in api_client.ts:
- SearchQuery body was wrapped in an extra {search_query: ...} layer — removed
- x-no-account-id header standardised to 'bypass'
- Redundant x-no-account-id removed from file download headers
- Smoke test rewritten to validate the real two-step bootstrap path
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,55 +1,90 @@
|
||||
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 = {
|
||||
def test_bootstrap(device_id, api_key, base_url='https://dev-api.oneskyit.com'):
|
||||
"""
|
||||
Replicates the two-step bootstrap sequence in api_client.ts:
|
||||
Step 1: GET event_device → extract account_id + app_base_url (fqdn)
|
||||
Step 2: POST site_domain/search → returns the correct site context
|
||||
"""
|
||||
print(f'\n=== Bootstrap test ===')
|
||||
print(f'Device: {device_id}')
|
||||
print(f'Base URL: {base_url}')
|
||||
|
||||
# --- Step 1: Get Device Config ---
|
||||
device_url = f'{base_url}/v3/crud/event_device/{device_id}'
|
||||
headers_step1 = {
|
||||
'Content-Type': 'application/json',
|
||||
'x-aether-api-key': api_key,
|
||||
'x-no-account-id': 'Nothing to See Here',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
|
||||
params = {
|
||||
'view': 'enriched'
|
||||
'x-no-account-id': 'bypass',
|
||||
}
|
||||
|
||||
print(f'Testing lookup for device: {device_id}')
|
||||
print(f'Endpoint: {endpoint}')
|
||||
|
||||
print(f'\n-- Step 1: GET {device_url}')
|
||||
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}')
|
||||
|
||||
r1 = requests.get(device_url, headers=headers_step1)
|
||||
print(f' Status: {r1.status_code}')
|
||||
if r1.status_code != 200:
|
||||
print(f' Error: {r1.text}')
|
||||
return
|
||||
|
||||
device_data = r1.json().get('data', {})
|
||||
important_fields = [
|
||||
'account_id', 'app_base_url', 'code', 'name',
|
||||
'event_id', 'event_location_id',
|
||||
'local_file_cache_path', 'host_file_temp_path', 'recording_path',
|
||||
]
|
||||
for field in important_fields:
|
||||
print(f' {field}: {device_data.get(field, "MISSING")}')
|
||||
|
||||
account_id = device_data.get('account_id') or device_data.get('account_id_random')
|
||||
fqdn = device_data.get('app_base_url', 'native-demo.oneskyit.com')
|
||||
|
||||
except Exception as e:
|
||||
print(f'Request failed: {e}')
|
||||
print(f' Request failed: {e}')
|
||||
return
|
||||
|
||||
# --- Step 2: Get Site Context ---
|
||||
search_url = f'{base_url}/v3/crud/site_domain/search?limit=1'
|
||||
headers_step2 = {
|
||||
'Content-Type': 'application/json',
|
||||
'x-aether-api-key': api_key,
|
||||
'x-account-id': account_id or '',
|
||||
}
|
||||
body = {
|
||||
'and': [{'field': 'fqdn', 'op': 'eq', 'value': fqdn}]
|
||||
}
|
||||
|
||||
print(f'\n-- Step 2: POST {search_url}')
|
||||
print(f' Searching for fqdn: {fqdn}')
|
||||
try:
|
||||
r2 = requests.post(search_url, headers=headers_step2, json=body)
|
||||
print(f' Status: {r2.status_code}')
|
||||
if r2.status_code != 200:
|
||||
print(f' Error: {r2.text}')
|
||||
return
|
||||
|
||||
results = r2.json().get('data', [])
|
||||
print(f' Results returned: {len(results)}')
|
||||
if results:
|
||||
sd = results[0]
|
||||
print(f' fqdn: {sd.get("fqdn")}')
|
||||
print(f' account_id: {sd.get("account_id")}')
|
||||
print(f' site_id: {sd.get("site_id")}')
|
||||
if sd.get('account_id') != account_id:
|
||||
print(f' WARNING: site_domain account_id does not match device account_id!')
|
||||
else:
|
||||
print(f' OK: account_id matches device.')
|
||||
else:
|
||||
print(f' WARNING: No site_domain found for fqdn "{fqdn}"')
|
||||
|
||||
except Exception as e:
|
||||
print(f' Request failed: {e}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_device_lookup()
|
||||
# Dev device (dev-api)
|
||||
test_bootstrap(
|
||||
device_id='dbgMWS3KEHE',
|
||||
api_key='INSdG85ANwsEIru3nUttMw',
|
||||
base_url='https://dev-api.oneskyit.com',
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user