Switch PHP views to Twig and add 2FA/UI enhancements
Migrate many view templates from raw PHP to Twig and modernize UI/UX for 2FA and settings. Controllers updated to provide avatar data and two-factor info (ProfileController, UserController) and SettingsController now includes timezone lists, notification preset selection, cron path, cached update state and rollback availability. ErrorHandler now attempts to render error pages via a new Core\TwigService with a safe fallback to raw PHP views. TwoFactorService generation silences deprecated warnings during QR code creation. Numerous .php view files were removed and replaced with .twig equivalents (2fa setup/verify/backup-codes and many auth, dashboard, domains, errors, layout, users, tags, tld-registry, etc.), and core/TwigService was added. These changes move the app toward a Twig-based templating system, improve 2FA flows, surface avatar images in lists/profiles, and make error rendering more robust.
This commit is contained in:
530
app/Views/errors/debug.twig
Normal file
530
app/Views/errors/debug.twig
Normal file
@@ -0,0 +1,530 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Debug Error - {{ error_type|default('Application Error') }}</title>
|
||||
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
DEFAULT: '#4A90E2',
|
||||
dark: '#357ABD',
|
||||
light: '#6BA3E8',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
body { background-color: #f8f9fa; }
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(20px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
.animate-fade-in { animation: fadeIn 0.4s ease-out; }
|
||||
.code-block { background-color: #1e1e1e; color: #d4d4d4; }
|
||||
.line-number { color: #858585; user-select: none; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="min-h-screen p-6">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="max-w-7xl mx-auto mb-6">
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-5">
|
||||
<div class="flex items-center justify-between flex-wrap gap-4">
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="w-12 h-12 bg-red-100 rounded-lg flex items-center justify-center">
|
||||
<i class="fas fa-bug text-red-600 text-xl"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold text-gray-900">Debug Mode</h1>
|
||||
<p class="text-sm text-gray-600 mt-0.5">
|
||||
<i class="fas fa-circle text-orange-500 mr-1 text-xs animate-pulse"></i>
|
||||
Development Environment - Detailed Error Information
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<button onclick="copyErrorReport()"
|
||||
class="inline-flex items-center px-4 py-2.5 bg-primary hover:bg-primary-dark text-white rounded-lg transition-colors font-medium text-sm">
|
||||
<i class="fas fa-clipboard mr-2"></i>
|
||||
Copy Error Report
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="max-w-7xl mx-auto">
|
||||
|
||||
<!-- Primary Error Card -->
|
||||
<div class="bg-white rounded-lg shadow-sm border-l-4 border-red-500 mb-6 animate-fade-in">
|
||||
<div class="p-6">
|
||||
<div class="flex items-start mb-6">
|
||||
<div class="flex-shrink-0 w-14 h-14 bg-red-100 rounded-lg flex items-center justify-center mr-4">
|
||||
<i class="fas fa-exclamation-triangle text-red-600 text-2xl"></i>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-2">
|
||||
{{ error_type|default('Error') }}
|
||||
</h2>
|
||||
<p class="text-lg text-gray-700 mb-4">{{ error_message|default('An error occurred') }}</p>
|
||||
|
||||
<div class="bg-red-50 border border-red-200 rounded-lg p-4">
|
||||
<h3 class="text-sm font-semibold text-gray-900 mb-3 flex items-center">
|
||||
<i class="fas fa-map-marker-alt text-red-500 mr-2 text-xs"></i>
|
||||
Error Location
|
||||
</h3>
|
||||
<div class="space-y-2">
|
||||
<div>
|
||||
<span class="text-xs font-medium text-gray-600">File:</span>
|
||||
<code class="block mt-1 bg-white px-3 py-2 rounded text-sm text-gray-800 border border-gray-200 font-mono break-all">
|
||||
{{ error_file|default('Unknown') }}
|
||||
</code>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<span class="text-xs font-medium text-gray-600 mr-2">Line:</span>
|
||||
<span class="font-mono text-red-600 font-bold text-lg">{{ error_line|default('?') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Info Grid -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<div class="bg-blue-50 rounded-lg border border-blue-200 p-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h4 class="text-xs font-semibold text-gray-700 uppercase tracking-wide">Error ID</h4>
|
||||
<button onclick="copyToClipboard('{{ error_id|default('N/A') }}')"
|
||||
class="text-primary hover:text-primary-dark" title="Copy Error ID">
|
||||
<i class="fas fa-copy text-xs"></i>
|
||||
</button>
|
||||
</div>
|
||||
<code class="text-sm font-mono font-bold text-primary">{{ error_id|default('N/A') }}</code>
|
||||
<p class="text-xs text-gray-600 mt-2">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
Use for bug reports
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg border border-gray-200 p-4">
|
||||
<h4 class="text-xs font-semibold text-gray-700 uppercase tracking-wide mb-2">Request</h4>
|
||||
<div class="space-y-1">
|
||||
<p class="text-sm">
|
||||
<span class="font-mono font-bold text-gray-900">{{ request_method|default('GET') }}</span>
|
||||
</p>
|
||||
<code class="text-xs text-gray-600 font-mono block truncate" title="{{ request_uri|default('/') }}">
|
||||
{{ request_uri|default('/') }}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg border border-gray-200 p-4">
|
||||
<h4 class="text-xs font-semibold text-gray-700 uppercase tracking-wide mb-2">User</h4>
|
||||
{% if user_info %}
|
||||
<p class="text-sm font-semibold text-gray-900">{{ user_info.username }}</p>
|
||||
<p class="text-xs text-gray-600">
|
||||
<i class="fas fa-user mr-1"></i>
|
||||
{{ user_info.role }}
|
||||
</p>
|
||||
{% else %}
|
||||
<p class="text-sm text-gray-500">
|
||||
<i class="fas fa-user-slash mr-1"></i>
|
||||
Guest (Not logged in)
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg border border-gray-200 p-4">
|
||||
<h4 class="text-xs font-semibold text-gray-700 uppercase tracking-wide mb-2">System</h4>
|
||||
<div class="space-y-1">
|
||||
<p class="text-xs text-gray-600">
|
||||
<i class="fas fa-code mr-1"></i>
|
||||
PHP {{ php_version|default('unknown') }}
|
||||
</p>
|
||||
<p class="text-xs text-gray-600">
|
||||
<i class="fas fa-memory mr-1"></i>
|
||||
{{ memory_usage_mb|default(0) }}MB
|
||||
</p>
|
||||
<p class="text-xs text-gray-600">
|
||||
<i class="fas fa-clock mr-1"></i>
|
||||
{{ occurred_at|default('now')|date("H:i:s") }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Two Column Layout -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
|
||||
<!-- Left Column -->
|
||||
<div class="space-y-6">
|
||||
|
||||
{% if stack_trace is defined and stack_trace %}
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden animate-fade-in">
|
||||
<div class="px-6 py-4 border-b border-gray-200 bg-gray-50">
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-lg font-semibold text-gray-900 flex items-center">
|
||||
<i class="fas fa-layer-group text-primary mr-2 text-sm"></i>
|
||||
Stack Trace
|
||||
</h3>
|
||||
<button onclick="copyStackTrace()"
|
||||
class="text-sm text-primary hover:text-primary-dark font-medium">
|
||||
<i class="fas fa-copy mr-1"></i>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<div class="code-block rounded-lg p-4 overflow-x-auto max-h-96 overflow-y-auto border border-gray-700" id="stack-trace">
|
||||
{% for line in stack_trace|split("\n") %}
|
||||
{% if line|trim %}
|
||||
<div class="flex font-mono text-sm">
|
||||
<span class="line-number mr-4 text-right" style="min-width: 2rem">{{ '%02d'|format(loop.index0) }}</span>
|
||||
<span class="flex-1 text-green-400">{{ line }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if request_data is defined and request_data %}
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden animate-fade-in">
|
||||
<button onclick="toggleSection('request-data')"
|
||||
class="w-full px-6 py-4 border-b border-gray-200 bg-gray-50 text-left hover:bg-gray-100 transition-colors">
|
||||
<h3 class="text-lg font-semibold text-gray-900 flex items-center justify-between">
|
||||
<span class="flex items-center">
|
||||
<i class="fas fa-paper-plane text-blue-500 mr-2 text-sm"></i>
|
||||
Request Data
|
||||
<span class="ml-2 text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded font-medium">
|
||||
{{ request_data|length }}
|
||||
</span>
|
||||
</span>
|
||||
<i class="fas fa-chevron-down text-gray-400 text-xs transition-transform" id="request-data-chevron"></i>
|
||||
</h3>
|
||||
</button>
|
||||
<div id="request-data" class="hidden p-6">
|
||||
<div class="space-y-3">
|
||||
{% for key, value in request_data %}
|
||||
<div class="bg-gray-50 rounded-lg p-3 border border-gray-200">
|
||||
<span class="text-xs font-semibold text-gray-700 uppercase tracking-wide block mb-1">
|
||||
{{ key }}
|
||||
</span>
|
||||
<code class="text-sm text-gray-800 font-mono block break-all">
|
||||
{% if value is iterable %}
|
||||
{{ value|json_encode(constant('JSON_PRETTY_PRINT')) }}
|
||||
{% else %}
|
||||
{{ value }}
|
||||
{% endif %}
|
||||
</code>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Right Column -->
|
||||
<div class="space-y-6">
|
||||
|
||||
<!-- Request Details -->
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden animate-fade-in">
|
||||
<div class="px-6 py-4 border-b border-gray-200 bg-gray-50">
|
||||
<h3 class="text-lg font-semibold text-gray-900 flex items-center">
|
||||
<i class="fas fa-globe text-green-500 mr-2 text-sm"></i>
|
||||
Request Details
|
||||
</h3>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<div class="space-y-3 text-sm">
|
||||
<div class="flex items-center justify-between py-2 border-b border-gray-100">
|
||||
<span class="font-medium text-gray-600">Method</span>
|
||||
<span class="font-mono font-bold text-gray-900">{{ request_method|default('GET') }}</span>
|
||||
</div>
|
||||
<div class="py-2 border-b border-gray-100">
|
||||
<span class="font-medium text-gray-600 block mb-1">URI</span>
|
||||
<code class="text-xs text-gray-800 font-mono block break-all bg-gray-50 px-2 py-1 rounded">
|
||||
{{ request_uri|default('/') }}
|
||||
</code>
|
||||
</div>
|
||||
<div class="py-2 border-b border-gray-100">
|
||||
<span class="font-medium text-gray-600 block mb-1">IP Address</span>
|
||||
<code class="text-xs text-gray-800 font-mono block bg-gray-50 px-2 py-1 rounded">
|
||||
{{ ip_address|default('Unknown') }}
|
||||
</code>
|
||||
</div>
|
||||
<div class="py-2">
|
||||
<span class="font-medium text-gray-600 block mb-1">User Agent</span>
|
||||
<code class="text-xs text-gray-600 font-mono block break-all bg-gray-50 px-2 py-1 rounded">
|
||||
{{ user_agent|default('Unknown') }}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- System Information -->
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden animate-fade-in">
|
||||
<div class="px-6 py-4 border-b border-gray-200 bg-gray-50">
|
||||
<h3 class="text-lg font-semibold text-gray-900 flex items-center">
|
||||
<i class="fas fa-server text-indigo-500 mr-2 text-sm"></i>
|
||||
System Information
|
||||
</h3>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<div class="space-y-3 text-sm">
|
||||
<div class="flex items-center justify-between py-2 border-b border-gray-100">
|
||||
<span class="font-medium text-gray-600">PHP Version</span>
|
||||
<span class="font-mono text-gray-900">{{ php_version|default('unknown') }}</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between py-2 border-b border-gray-100">
|
||||
<span class="font-medium text-gray-600">Memory Usage</span>
|
||||
<span class="font-mono text-gray-900">{{ memory_usage_mb|default(0) }}MB</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between py-2 border-b border-gray-100">
|
||||
<span class="font-medium text-gray-600">Peak Memory</span>
|
||||
<span class="font-mono text-gray-900">{{ peak_memory_mb|default(0) }}MB</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between py-2">
|
||||
<span class="font-medium text-gray-600">Timestamp</span>
|
||||
<span class="font-mono text-gray-900 text-xs">{{ occurred_at|default('now')|date("Y-m-d H:i:s T") }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if session_data is defined and session_data %}
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden animate-fade-in">
|
||||
<button onclick="toggleSection('session-data')"
|
||||
class="w-full px-6 py-4 border-b border-gray-200 bg-gray-50 text-left hover:bg-gray-100 transition-colors">
|
||||
<h3 class="text-lg font-semibold text-gray-900 flex items-center justify-between">
|
||||
<span class="flex items-center">
|
||||
<i class="fas fa-user-lock text-orange-500 mr-2 text-sm"></i>
|
||||
Session Data
|
||||
<span class="ml-2 text-xs bg-orange-100 text-orange-800 px-2 py-1 rounded font-medium">
|
||||
{{ session_data|length }}
|
||||
</span>
|
||||
</span>
|
||||
<i class="fas fa-chevron-down text-gray-400 text-xs transition-transform" id="session-data-chevron"></i>
|
||||
</h3>
|
||||
</button>
|
||||
<div id="session-data" class="hidden p-6">
|
||||
<div class="max-h-80 overflow-y-auto">
|
||||
<table class="min-w-full text-sm">
|
||||
<thead class="bg-gray-50 sticky top-0">
|
||||
<tr>
|
||||
<th class="px-3 py-2 text-left text-xs font-semibold text-gray-600 uppercase">Key</th>
|
||||
<th class="px-3 py-2 text-left text-xs font-semibold text-gray-600 uppercase">Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100">
|
||||
{% for key, value in session_data %}
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="px-3 py-2 font-mono text-gray-700 align-top">{{ key }}</td>
|
||||
<td class="px-3 py-2 font-mono text-gray-600 break-all">
|
||||
{% if value is iterable %}
|
||||
{{ value|json_encode }}
|
||||
{% else %}
|
||||
{{ value }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Help Card -->
|
||||
<div class="bg-blue-50 border border-blue-200 rounded-lg p-5 mt-6 animate-fade-in">
|
||||
<div class="flex items-start">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="w-10 h-10 bg-blue-500 rounded-lg flex items-center justify-center">
|
||||
<i class="fas fa-lightbulb text-white"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<h3 class="text-sm font-semibold text-gray-900 mb-2">Debug Mode Active</h3>
|
||||
<p class="text-sm text-gray-700 mb-3">
|
||||
This detailed error page is only shown in development mode. In production, users will see a clean error page with just the error ID.
|
||||
</p>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<a href="/" class="inline-flex items-center px-3 py-2 bg-white border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors text-sm font-medium">
|
||||
<i class="fas fa-home mr-2"></i>
|
||||
Go to Dashboard
|
||||
</a>
|
||||
<button onclick="location.reload()" class="inline-flex items-center px-3 py-2 bg-white border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors text-sm font-medium">
|
||||
<i class="fas fa-redo mr-2"></i>
|
||||
Reload Page
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="max-w-7xl mx-auto mt-8">
|
||||
<div class="text-center text-sm text-gray-500">
|
||||
<p>
|
||||
<i class="fas fa-globe text-primary mr-1"></i>
|
||||
Domain Monitor © {{ "now"|date("Y") }} • Development Mode
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function toggleSection(sectionId) {
|
||||
const section = document.getElementById(sectionId);
|
||||
const chevron = document.getElementById(sectionId + '-chevron');
|
||||
|
||||
if (section.classList.contains('hidden')) {
|
||||
section.classList.remove('hidden');
|
||||
if (chevron) chevron.style.transform = 'rotate(180deg)';
|
||||
} else {
|
||||
section.classList.add('hidden');
|
||||
if (chevron) chevron.style.transform = 'rotate(0deg)';
|
||||
}
|
||||
}
|
||||
|
||||
function copyToClipboard(text) {
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
showCopySuccess();
|
||||
}).catch(err => {
|
||||
fallbackCopy(text);
|
||||
});
|
||||
} else {
|
||||
fallbackCopy(text);
|
||||
}
|
||||
}
|
||||
|
||||
function fallbackCopy(text) {
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.value = text;
|
||||
textArea.style.position = 'fixed';
|
||||
textArea.style.left = '-999999px';
|
||||
document.body.appendChild(textArea);
|
||||
textArea.select();
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
showCopySuccess();
|
||||
} catch (err) {
|
||||
console.error('Copy failed:', err);
|
||||
}
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
|
||||
function copyStackTrace() {
|
||||
const stackTraceElement = document.getElementById('stack-trace');
|
||||
const lines = stackTraceElement.querySelectorAll('div');
|
||||
let stackText = '';
|
||||
lines.forEach(line => {
|
||||
const textSpan = line.querySelector('span:last-child');
|
||||
if (textSpan) stackText += textSpan.textContent + '\n';
|
||||
});
|
||||
copyToClipboard(stackText.trim());
|
||||
}
|
||||
|
||||
function copyErrorReport() {
|
||||
const errorType = {{ (error_type|default('Error'))|json_encode|raw }};
|
||||
const errorMessage = {{ (error_message|default('Unknown error'))|json_encode|raw }};
|
||||
const errorFile = {{ (error_file|default('Unknown'))|json_encode|raw }};
|
||||
const errorLine = {{ (error_line|default('?'))|json_encode|raw }};
|
||||
const errorId = {{ (error_id|default('N/A'))|json_encode|raw }};
|
||||
const phpVersion = {{ (php_version|default('unknown'))|json_encode|raw }};
|
||||
const requestMethod = {{ (request_method|default('GET'))|json_encode|raw }};
|
||||
const requestUri = {{ (request_uri|default('/'))|json_encode|raw }};
|
||||
const userAgent = {{ (user_agent|default('Unknown'))|json_encode|raw }};
|
||||
const ipAddress = {{ (ip_address|default('Unknown'))|json_encode|raw }};
|
||||
const timestamp = {{ (occurred_at|default('now'))|json_encode|raw }};
|
||||
|
||||
const userInfo = {{ (user_info|default(null))|json_encode|raw }};
|
||||
const userText = userInfo ? `${userInfo.username} (${userInfo.role}, ID: ${userInfo.id})` : 'Guest (Not logged in)';
|
||||
|
||||
const stackTraceElement = document.getElementById('stack-trace');
|
||||
let stackTrace = 'Not available';
|
||||
if (stackTraceElement) {
|
||||
const lines = stackTraceElement.querySelectorAll('div');
|
||||
let stackText = '';
|
||||
lines.forEach(line => {
|
||||
const textSpan = line.querySelector('span:last-child');
|
||||
if (textSpan) stackText += textSpan.textContent + '\n';
|
||||
});
|
||||
stackTrace = stackText.trim();
|
||||
}
|
||||
|
||||
const errorReport = `=== DOMAIN MONITOR ERROR REPORT ===
|
||||
|
||||
ERROR INFORMATION:
|
||||
- Error ID: ${errorId}
|
||||
- Type: ${errorType}
|
||||
- Message: ${errorMessage}
|
||||
|
||||
LOCATION:
|
||||
- File: ${errorFile}
|
||||
- Line: ${errorLine}
|
||||
|
||||
REQUEST DETAILS:
|
||||
- Method: ${requestMethod}
|
||||
- URI: ${requestUri}
|
||||
- Timestamp: ${timestamp}
|
||||
|
||||
USER CONTEXT:
|
||||
- User: ${userText}
|
||||
- IP Address: ${ipAddress}
|
||||
- User Agent: ${userAgent}
|
||||
|
||||
SYSTEM INFORMATION:
|
||||
- PHP Version: ${phpVersion}
|
||||
- Memory Usage: {{ memory_usage_mb|default(0) }}MB
|
||||
- Peak Memory: {{ peak_memory_mb|default(0) }}MB
|
||||
|
||||
STACK TRACE:
|
||||
${stackTrace}
|
||||
|
||||
=== END OF ERROR REPORT ===
|
||||
|
||||
Reference ID: ${errorId}
|
||||
Please include this report when reporting bugs.`;
|
||||
|
||||
copyToClipboard(errorReport);
|
||||
}
|
||||
|
||||
function showCopySuccess() {
|
||||
const message = document.createElement('div');
|
||||
message.className = 'fixed top-4 right-4 bg-green-500 text-white px-4 py-3 rounded-lg shadow-lg z-50 flex items-center animate-fade-in';
|
||||
message.innerHTML = '<i class="fas fa-check mr-2"></i>Copied to clipboard!';
|
||||
document.body.appendChild(message);
|
||||
|
||||
setTimeout(() => {
|
||||
message.style.opacity = '0';
|
||||
message.style.transform = 'translateY(-20px)';
|
||||
message.style.transition = 'all 0.3s ease-out';
|
||||
setTimeout(() => message.remove(), 300);
|
||||
}, 2000);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user