## Summary - move the Laravel application into `apps/platform` and keep the repository root for orchestration, docs, and tooling - update the local command model, Sail/Docker wiring, runtime paths, and ignore rules around the new platform location - add relocation quickstart/contracts plus focused smoke coverage for bootstrap, command model, routes, and runtime behavior ## Validation - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/PlatformRelocation` - integrated browser smoke validated `/up`, `/`, `/admin`, `/admin/choose-workspace`, and tenant route semantics for `200`, `403`, and `404` ## Remaining Rollout Checks - validate Dokploy build context and working-directory assumptions against the new `apps/platform` layout - confirm web, queue, and scheduler processes all start from the expected working directory in staging/production - verify no legacy volume mounts or asset-publish paths still point at the old root-level `public/` or `storage/` locations Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #213
118 lines
3.0 KiB
JavaScript
118 lines
3.0 KiB
JavaScript
(() => {
|
|
if (typeof window === 'undefined') {
|
|
return;
|
|
}
|
|
|
|
if (window.__tenantpilotUnhandledRejectionLoggerApplied) {
|
|
return;
|
|
}
|
|
|
|
window.__tenantpilotUnhandledRejectionLoggerApplied = true;
|
|
|
|
const recentKeys = new Map();
|
|
|
|
const cleanupRecentKeys = (nowMs) => {
|
|
for (const [key, timestampMs] of recentKeys.entries()) {
|
|
if (nowMs - timestampMs > 5_000) {
|
|
recentKeys.delete(key);
|
|
}
|
|
}
|
|
};
|
|
|
|
const normalizeReason = (value, depth = 0) => {
|
|
if (depth > 3) {
|
|
return '[max-depth-reached]';
|
|
}
|
|
|
|
if (value instanceof Error) {
|
|
return {
|
|
type: 'Error',
|
|
name: value.name,
|
|
message: value.message,
|
|
stack: value.stack,
|
|
};
|
|
}
|
|
|
|
if (value === null || value === undefined) {
|
|
return value;
|
|
}
|
|
|
|
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
return value;
|
|
}
|
|
|
|
if (Array.isArray(value)) {
|
|
return value.slice(0, 10).map((item) => normalizeReason(item, depth + 1));
|
|
}
|
|
|
|
if (typeof value === 'object') {
|
|
const result = {};
|
|
const allowedKeys = [
|
|
'message',
|
|
'stack',
|
|
'name',
|
|
'type',
|
|
'status',
|
|
'body',
|
|
'json',
|
|
'errors',
|
|
'reason',
|
|
'code',
|
|
];
|
|
|
|
for (const key of allowedKeys) {
|
|
if (Object.prototype.hasOwnProperty.call(value, key)) {
|
|
result[key] = normalizeReason(value[key], depth + 1);
|
|
}
|
|
}
|
|
|
|
if (Object.keys(result).length > 0) {
|
|
return result;
|
|
}
|
|
|
|
const stringTag = Object.prototype.toString.call(value);
|
|
|
|
return {
|
|
type: stringTag,
|
|
value: String(value),
|
|
};
|
|
}
|
|
|
|
return String(value);
|
|
};
|
|
|
|
const toStableJson = (payload) => {
|
|
try {
|
|
return JSON.stringify(payload);
|
|
} catch {
|
|
return JSON.stringify({
|
|
source: payload.source,
|
|
href: payload.href,
|
|
timestamp: payload.timestamp,
|
|
reason: '[unserializable]',
|
|
});
|
|
}
|
|
};
|
|
|
|
window.addEventListener('unhandledrejection', (event) => {
|
|
const payload = {
|
|
source: 'window.unhandledrejection',
|
|
href: window.location.href,
|
|
timestamp: new Date().toISOString(),
|
|
reason: normalizeReason(event.reason),
|
|
};
|
|
|
|
const payloadJson = toStableJson(payload);
|
|
const nowMs = Date.now();
|
|
|
|
cleanupRecentKeys(nowMs);
|
|
|
|
if (recentKeys.has(payloadJson)) {
|
|
return;
|
|
}
|
|
|
|
recentKeys.set(payloadJson, nowMs);
|
|
|
|
console.error(`TenantPilot unhandled promise rejection ${payloadJson}`);
|
|
});
|
|
})(); |