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>
91 lines
3.1 KiB
Python
91 lines
3.1 KiB
Python
import requests
|
|
import json
|
|
|
|
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': 'bypass',
|
|
}
|
|
|
|
print(f'\n-- Step 1: GET {device_url}')
|
|
try:
|
|
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}')
|
|
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__':
|
|
# Dev device (dev-api)
|
|
test_bootstrap(
|
|
device_id='dbgMWS3KEHE',
|
|
api_key='INSdG85ANwsEIru3nUttMw',
|
|
base_url='https://dev-api.oneskyit.com',
|
|
)
|