V3 Hardening & Fixes: Structured Errors, JWT Fallbacks, and Module Stability
- Implemented Structured Error Handling across GET/POST/PATCH helpers to extract rich V3 error metadata. - Added direct localStorage fallback for JWT detection to resolve race conditions during initial page load. - Fixed async race condition in Archives leading to 'archive_content_li is undefined' crash. - Hardened generic object processor to handle non-array API responses gracefully. - Resolved zero-result bug in Event Search by using raw 'account_id_random' to bypass backend mapping conflicts. - Isolated bootstrap headers in +layout.ts and removed invalid response headers from request config. - Enhanced /testing dashboard with live header inspection and V3 hardening audits.
This commit is contained in:
@@ -50,12 +50,26 @@ export const patch_object = async function patch_object({
|
||||
const headers_cleaned: key_val = {};
|
||||
const merged_headers = { ...api_cfg['headers'], ...headers };
|
||||
|
||||
// Auto-promote account_id from api_cfg to header if missing
|
||||
if (!merged_headers['x-account-id'] && api_cfg['account_id']) {
|
||||
merged_headers['x-account-id'] = api_cfg['account_id'];
|
||||
}
|
||||
|
||||
// Handle "Bootstrap Paradox" for unauthenticated requests
|
||||
if (merged_headers.hasOwnProperty('x-no-account-id')) {
|
||||
const bypass_val = merged_headers['x-no-account-id'] || merged_headers['x_no_account_id'];
|
||||
const is_valid_bypass = bypass_val === 'bypass' ||
|
||||
bypass_val === 'Nothing to See Here' ||
|
||||
bypass_val === 'direct-download';
|
||||
|
||||
if (is_valid_bypass) {
|
||||
if (log_lvl > 1) console.log('api_patch_object: Valid bypass detected. Stripping account ID context.');
|
||||
delete merged_headers['x-account-id'];
|
||||
if (merged_headers['x-no-account-id'] === null) {
|
||||
merged_headers['x-no-account-id'] = 'Nothing to See Here';
|
||||
}
|
||||
delete merged_headers['x_account_id'];
|
||||
} else {
|
||||
// If it's a placeholder (like "No_Account_ID_Here"), just remove the bypass header
|
||||
// but DO NOT strip the valid Account ID.
|
||||
delete merged_headers['x-no-account-id'];
|
||||
delete merged_headers['x_no_account_id'];
|
||||
}
|
||||
|
||||
for (const prop in merged_headers) {
|
||||
@@ -70,7 +84,28 @@ export const patch_object = async function patch_object({
|
||||
}
|
||||
|
||||
// Auto-inject Authorization header if JWT is present but header is missing
|
||||
const jwt = headers_cleaned['jwt'] || headers_cleaned['JWT'] || api_cfg['jwt'];
|
||||
let jwt = headers_cleaned['jwt'] ||
|
||||
headers_cleaned['JWT'] ||
|
||||
headers_cleaned['x-aether-api-token'] ||
|
||||
api_cfg['jwt'] ||
|
||||
api_cfg['headers']?.['jwt'] ||
|
||||
api_cfg['headers']?.['JWT'] ||
|
||||
api_cfg['headers']?.['x-aether-api-token'];
|
||||
|
||||
// Final Fallback: Check localStorage directly to avoid store sync race conditions
|
||||
if (!jwt && typeof localStorage !== 'undefined') {
|
||||
try {
|
||||
// Check primary key first
|
||||
const ae_loc_raw = localStorage.getItem('ae_loc');
|
||||
if (ae_loc_raw) {
|
||||
const ae_loc_json = JSON.parse(ae_loc_raw);
|
||||
jwt = ae_loc_json.jwt || ae_loc_json.token;
|
||||
}
|
||||
} catch (e) {
|
||||
// Silently fail on storage read
|
||||
}
|
||||
}
|
||||
|
||||
if (jwt && !headers_cleaned['Authorization'] && !headers_cleaned['authorization']) {
|
||||
headers_cleaned['Authorization'] = `Bearer ${jwt}`;
|
||||
}
|
||||
@@ -126,18 +161,58 @@ export const patch_object = async function patch_object({
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 404) {
|
||||
console.warn('404 Not Found. Returning null.');
|
||||
if (log_lvl) {
|
||||
console.log('The response was a 404 not found "error". Returning null.');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const errorBody = await response.text();
|
||||
console.error(`HTTP error! status: ${response.status}`, errorBody);
|
||||
|
||||
if (response.status >= 400 && response.status < 404) {
|
||||
// FAIL FAST (Section 2D): Do not retry on Auth or Client errors (400, 401, 403, 422)
|
||||
if (response.status === 400 || response.status === 401 || response.status === 403 || response.status === 422) {
|
||||
if (log_lvl) console.error(`API Client Failure (${response.status}). Failing fast.`);
|
||||
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
console.warn(`AUTH DIAGNOSTICS (PATCH): Headers sent for ${endpoint}:`, {
|
||||
has_auth: !!headers_cleaned['Authorization'],
|
||||
has_api_key: !!headers_cleaned['x-aether-api-key'],
|
||||
has_account_id: !!headers_cleaned['x-account-id'],
|
||||
jwt_preview: jwt ? `${jwt.slice(0, 8)}...` : 'MISSING'
|
||||
});
|
||||
}
|
||||
|
||||
// Structured Error Handling (V3): Attempt to get rich error metadata
|
||||
let error_json: any = null;
|
||||
try {
|
||||
error_json = await response.json();
|
||||
} catch (e) {
|
||||
// Not JSON
|
||||
}
|
||||
|
||||
if (log_lvl) console.log('The response was not ok. Structured Error Check:', error_json);
|
||||
|
||||
if (error_json?.meta?.details) {
|
||||
return error_json;
|
||||
}
|
||||
|
||||
// Fallback for standard FastAPI "detail" errors
|
||||
if (error_json?.detail) {
|
||||
return {
|
||||
meta: {
|
||||
success: false,
|
||||
status_code: response.status,
|
||||
details: {
|
||||
category: 'validation',
|
||||
message: typeof error_json.detail === 'string' ? error_json.detail : JSON.stringify(error_json.detail),
|
||||
raw: error_json.detail
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new Error(`HTTP error! status: ${response.status} - ${errorBody}`);
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const json = await response.json();
|
||||
|
||||
Reference in New Issue
Block a user