hostbot / index.html
j49_c
Add advanced features: cleanup old data, subscription plans, firewall protection, enhanced UI
7d1d67d
Raw History Blame Contribute Delete
81.2 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>الذئب الابيض · OPERATIVE DASHBOARD</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap');
:root {
--bg: #000000;
--card: rgba(15, 15, 20, 0.7);
--border: rgba(255, 255, 255, 0.08);
--primary: #0A84FF;
--primary-glow: rgba(10, 132, 255, 0.2);
--accent: #5E5CE6;
--text: #F5F5F7;
--text-dim: #8E8E93;
--success: #32D74B;
--warning: #FF9F0A;
--error: #FF453A;
--radius-lg: 24px;
--radius-md: 16px;
--radius-sm: 12px;
--mono: 'JetBrains Mono', monospace;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Plus Jakarta Sans', sans-serif;
}
body {
background-color: var(--bg);
color: var(--text);
min-height: 100vh;
background-image:
radial-gradient(circle at 10% 10%, var(--primary-glow), transparent 40%),
radial-gradient(circle at 90% 90%, rgba(94, 92, 230, 0.1), transparent 40%);
background-attachment: fixed;
}
/* Scrollbar */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 10px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--text-dim);
}
.topbar {
position: sticky;
top: 0;
z-index: 100;
background: rgba(0, 0, 0, 0.8);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-bottom: 1px solid var(--border);
}
.topbar-content {
max-width: 1400px;
margin: 0 auto;
padding: 16px 24px;
display: flex;
align-items: center;
justify-content: space-between;
}
.brand {
display: flex;
align-items: center;
gap: 14px;
cursor: pointer;
}
.brand-logo {
width: 40px;
height: 40px;
border-radius: 12px;
object-fit: cover;
border: 1px solid var(--border);
}
.brand-info {
display: flex;
flex-direction: column;
}
.brand-name {
font-size: 16px;
font-weight: 800;
letter-spacing: -0.02em;
}
.brand-status {
font-size: 11px;
font-family: var(--mono);
color: var(--text-dim);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.nav-actions {
display: flex;
align-items: center;
gap: 12px;
}
.btn {
padding: 10px 18px;
border-radius: var(--radius-sm);
font-size: 13px;
font-weight: 700;
cursor: pointer;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
border: 1px solid var(--border);
background: var(--card);
color: var(--text);
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn:hover {
background: rgba(255, 255, 255, 0.05);
transform: translateY(-1px);
border-color: rgba(255, 255, 255, 0.2);
}
.btn-primary {
background: var(--primary);
border: none;
color: white;
}
.btn-primary:hover {
background: #0071E3;
box-shadow: 0 4px 15px rgba(10, 132, 255, 0.4);
}
.btn-danger {
color: var(--error);
background: rgba(255, 69, 58, 0.1);
border: 1px solid rgba(255, 69, 58, 0.2);
}
.btn-danger:hover {
background: var(--error);
color: white;
}
@media (max-width: 768px) {
.topbar-content {
padding: 12px 16px;
flex-direction: column;
gap: 12px;
}
.nav-actions {
width: 100%;
overflow-x: auto;
padding-bottom: 4px;
justify-content: flex-start;
}
.btn {
flex-shrink: 0;
padding: 8px 12px;
font-size: 11px;
}
.brand-logo {
width: 32px;
height: 32px;
}
.brand-name {
font-size: 14px;
}
.container {
margin: 16px auto;
padding: 0 16px;
}
}
/* Notification System */
.notification-container {
position: fixed;
top: 80px;
right: 24px;
z-index: 9999;
display: flex;
flex-direction: column;
gap: 12px;
max-width: 380px;
}
.notification {
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: 16px 20px;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
animation: slideIn 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
display: flex;
align-items: flex-start;
gap: 12px;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateX(100px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes slideOut {
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(100px);
}
}
.notification-icon {
width: 32px;
height: 32px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
flex-shrink: 0;
}
.notification.success .notification-icon {
background: rgba(50, 215, 75, 0.2);
color: var(--success);
}
.notification.error .notification-icon {
background: rgba(255, 69, 58, 0.2);
color: var(--error);
}
.notification.warning .notification-icon {
background: rgba(255, 159, 10, 0.2);
color: var(--warning);
}
.notification.info .notification-icon {
background: rgba(10, 132, 255, 0.2);
color: var(--primary);
}
.notification-content {
flex: 1;
}
.notification-title {
font-weight: 600;
font-size: 14px;
margin-bottom: 4px;
}
.notification-message {
font-size: 13px;
color: var(--text-dim);
line-height: 1.4;
}
.notification-close {
cursor: pointer;
color: var(--text-dim);
font-size: 18px;
line-height: 1;
padding: 4px;
transition: color 0.2s;
}
.notification-close:hover {
color: var(--text);
}
.notification-progress {
position: absolute;
bottom: 0;
left: 0;
height: 3px;
background: linear-gradient(90deg, var(--primary), var(--accent));
border-radius: 0 0 var(--radius-md) var(--radius-md);
animation: progress 3s linear forwards;
}
@keyframes progress {
from { width: 100%; }
to { width: 0%; }
}
/* Stats Dashboard */
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 16px;
margin-bottom: 24px;
}
.stat-card {
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: 20px;
position: relative;
overflow: hidden;
transition: all 0.3s;
}
.stat-card:hover {
transform: translateY(-2px);
border-color: var(--primary);
}
.stat-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 3px;
background: linear-gradient(90deg, var(--primary), var(--accent));
}
.stat-icon {
width: 48px;
height: 48px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
margin-bottom: 12px;
background: rgba(10, 132, 255, 0.1);
}
.stat-value {
font-size: 32px;
font-weight: 800;
margin-bottom: 4px;
background: linear-gradient(135deg, var(--text), var(--text-dim));
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.stat-label {
font-size: 13px;
color: var(--text-dim);
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.stat-change {
position: absolute;
top: 16px;
right: 16px;
font-size: 12px;
font-weight: 600;
padding: 4px 8px;
border-radius: 6px;
}
.stat-change.positive {
background: rgba(50, 215, 75, 0.2);
color: var(--success);
}
.stat-change.negative {
background: rgba(255, 69, 58, 0.2);
color: var(--error);
}
/* Activity Chart */
.chart-container {
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: 24px;
margin-bottom: 24px;
}
.chart-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.chart-title {
font-size: 16px;
font-weight: 700;
}
.chart-legend {
display: flex;
gap: 16px;
font-size: 12px;
color: var(--text-dim);
}
.legend-item {
display: flex;
align-items: center;
gap: 6px;
}
.legend-dot {
width: 8px;
height: 8px;
border-radius: 50%;
}
.activity-chart {
height: 200px;
display: flex;
align-items: flex-end;
gap: 8px;
padding-top: 20px;
}
.chart-bar {
flex: 1;
background: linear-gradient(180deg, var(--primary), rgba(10, 132, 255, 0.3));
border-radius: 6px 6px 0 0;
position: relative;
transition: all 0.3s;
cursor: pointer;
}
.chart-bar:hover {
background: linear-gradient(180deg, var(--accent), rgba(94, 92, 230, 0.3));
transform: scaleY(1.05);
}
.chart-bar::after {
content: attr(data-value);
position: absolute;
top: -24px;
left: 50%;
transform: translateX(-50%);
font-size: 11px;
font-weight: 600;
color: var(--text);
opacity: 0;
transition: opacity 0.2s;
white-space: nowrap;
}
.chart-bar:hover::after {
opacity: 1;
}
.chart-labels {
display: flex;
justify-content: space-between;
margin-top: 12px;
font-size: 11px;
color: var(--text-dim);
}
/* Advanced Code Editor */
.editor-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.95);
backdrop-filter: blur(20px);
z-index: 10000;
display: none;
flex-direction: column;
}
.editor-container.active {
display: flex;
}
.editor-header {
padding: 16px 24px;
background: rgba(255, 255, 255, 0.05);
border-bottom: 1px solid var(--border);
display: flex;
justify-content: space-between;
align-items: center;
}
.editor-title {
font-family: var(--mono);
font-size: 14px;
font-weight: 600;
color: var(--primary);
}
.editor-toolbar {
display: flex;
gap: 8px;
}
.editor-body {
flex: 1;
display: flex;
overflow: hidden;
}
.editor-sidebar {
width: 200px;
background: rgba(255, 255, 255, 0.02);
border-right: 1px solid var(--border);
padding: 16px;
overflow-y: auto;
}
.file-tree-item {
padding: 8px 12px;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
color: var(--text-dim);
transition: all 0.2s;
display: flex;
align-items: center;
gap: 8px;
}
.file-tree-item:hover {
background: rgba(255, 255, 255, 0.05);
color: var(--text);
}
.file-tree-item.active {
background: rgba(10, 132, 255, 0.1);
color: var(--primary);
}
.editor-main {
flex: 1;
display: flex;
flex-direction: column;
}
.code-editor-wrapper {
flex: 1;
position: relative;
overflow: hidden;
}
.code-editor {
width: 100%;
height: 100%;
background: transparent;
border: none;
outline: none;
resize: none;
font-family: 'JetBrains Mono', monospace;
font-size: 14px;
line-height: 1.6;
padding: 20px;
color: var(--text);
tab-size: 4;
}
.code-editor:focus {
outline: none;
}
.editor-statusbar {
padding: 8px 24px;
background: rgba(255, 255, 255, 0.03);
border-top: 1px solid var(--border);
display: flex;
justify-content: space-between;
align-items: center;
font-size: 12px;
color: var(--text-dim);
font-family: var(--mono);
}
.editor-status-item {
display: flex;
align-items: center;
gap: 16px;
}
/* Syntax Highlighting Colors */
.syntax-keyword { color: #FF79C6; }
.syntax-string { color: #F1FA8C; }
.syntax-number { color: #BD93F9; }
.syntax-comment { color: #6272A4; font-style: italic; }
.syntax-function { color: #50FA7B; }
.syntax-class { color: #8BE9FD; }
/* Advanced File Manager */
.fm-dropzone {
border: 2px dashed var(--border);
border-radius: var(--radius-md);
padding: 40px;
text-align: center;
color: var(--text-dim);
transition: all 0.3s;
cursor: pointer;
margin-bottom: 20px;
}
.fm-dropzone.dragover {
border-color: var(--primary);
background: rgba(10, 132, 255, 0.1);
color: var(--primary);
}
.fm-dropzone-icon {
font-size: 48px;
margin-bottom: 16px;
}
.fm-preview-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.9);
backdrop-filter: blur(20px);
z-index: 10000;
display: none;
align-items: center;
justify-content: center;
}
.fm-preview-modal.active {
display: flex;
}
.fm-preview-content {
max-width: 90%;
max-height: 90%;
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: 24px;
overflow: auto;
}
.fm-preview-image {
max-width: 100%;
max-height: 80vh;
border-radius: var(--radius-sm);
}
.fm-preview-code {
font-family: var(--mono);
font-size: 14px;
line-height: 1.6;
white-space: pre-wrap;
word-break: break-all;
max-height: 70vh;
overflow: auto;
}
.fm-toolbar {
display: flex;
gap: 8px;
margin-bottom: 16px;
flex-wrap: wrap;
}
.fm-search {
flex: 1;
min-width: 200px;
background: rgba(0, 0, 0, 0.3);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 8px 12px;
color: var(--text);
outline: none;
}
.fm-search:focus {
border-color: var(--primary);
}
.fm-view-toggle {
display: flex;
gap: 4px;
}
.fm-view-btn {
padding: 8px 12px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
color: var(--text);
cursor: pointer;
transition: all 0.2s;
}
.fm-view-btn.active {
background: var(--primary);
border-color: var(--primary);
}
.fm-grid-view {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 16px;
}
.fm-grid-item {
background: rgba(255, 255, 255, 0.03);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 16px;
text-align: center;
cursor: pointer;
transition: all 0.2s;
}
.fm-grid-item:hover {
border-color: var(--primary);
background: rgba(10, 132, 255, 0.1);
}
.fm-grid-icon {
font-size: 32px;
margin-bottom: 8px;
}
.fm-grid-name {
font-size: 12px;
color: var(--text);
word-break: break-all;
}
/* Settings Panel */
.settings-panel {
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: 32px;
max-width: 600px;
margin: 0 auto;
}
.settings-header {
margin-bottom: 32px;
text-align: center;
}
.settings-header h2 {
font-size: 24px;
font-weight: 700;
margin-bottom: 8px;
}
.settings-header p {
color: var(--text-dim);
font-size: 14px;
}
.settings-section {
margin-bottom: 32px;
padding-bottom: 32px;
border-bottom: 1px solid var(--border);
}
.settings-section:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.settings-section-title {
font-size: 16px;
font-weight: 700;
margin-bottom: 16px;
color: var(--primary);
}
.settings-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
}
.settings-item-label {
flex: 1;
}
.settings-item-label h4 {
font-size: 14px;
font-weight: 600;
margin-bottom: 4px;
}
.settings-item-label p {
font-size: 12px;
color: var(--text-dim);
}
.toggle-switch {
position: relative;
width: 50px;
height: 26px;
background: rgba(255, 255, 255, 0.1);
border-radius: 13px;
cursor: pointer;
transition: all 0.3s;
}
.toggle-switch.active {
background: var(--primary);
}
.toggle-switch::after {
content: '';
position: absolute;
top: 3px;
left: 3px;
width: 20px;
height: 20px;
background: white;
border-radius: 50%;
transition: all 0.3s;
}
.toggle-switch.active::after {
left: 27px;
}
.settings-input {
width: 100%;
background: rgba(0, 0, 0, 0.3);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 12px 16px;
color: var(--text);
font-size: 14px;
margin-bottom: 16px;
outline: none;
transition: border-color 0.2s;
}
.settings-input:focus {
border-color: var(--primary);
}
.settings-select {
width: 100%;
background: rgba(0, 0, 0, 0.3);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 12px 16px;
color: var(--text);
font-size: 14px;
margin-bottom: 16px;
outline: none;
cursor: pointer;
}
.settings-select:focus {
border-color: var(--primary);
}
/* Activity Log */
.activity-log {
background: rgba(0, 0, 0, 0.3);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 16px;
max-height: 300px;
overflow-y: auto;
}
.activity-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px;
border-bottom: 1px solid var(--border);
transition: background 0.2s;
}
.activity-item:last-child {
border-bottom: none;
}
.activity-item:hover {
background: rgba(255, 255, 255, 0.02);
}
.activity-icon {
width: 36px;
height: 36px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
background: rgba(10, 132, 255, 0.1);
color: var(--primary);
}
.activity-content {
flex: 1;
}
.activity-title {
font-size: 13px;
font-weight: 600;
margin-bottom: 2px;
}
.activity-time {
font-size: 11px;
color: var(--text-dim);
}
.activity-status {
font-size: 11px;
font-weight: 600;
padding: 4px 8px;
border-radius: 6px;
}
.activity-status.success {
background: rgba(50, 215, 75, 0.15);
color: var(--success);
}
.activity-status.error {
background: rgba(255, 69, 58, 0.15);
color: var(--error);
}
.activity-status.info {
background: rgba(10, 132, 255, 0.15);
color: var(--primary);
}
/* AI Chat Styling */
.ai-chat-container {
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius-md);
display: flex;
flex-direction: column;
height: 600px;
overflow: hidden;
}
.ai-chat-header {
padding: 16px 20px;
border-bottom: 1px solid var(--border);
background: rgba(255, 255, 255, 0.03);
display: flex;
justify-content: space-between;
align-items: center;
}
.ai-chat-messages {
flex: 1;
padding: 20px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 16px;
}
.ai-message {
max-width: 85%;
padding: 12px 16px;
border-radius: 16px;
font-size: 14px;
line-height: 1.5;
}
.ai-message.user {
align-self: flex-end;
background: var(--primary);
color: white;
border-bottom-right-radius: 4px;
}
.ai-message.bot {
align-self: flex-start;
background: rgba(255, 255, 255, 0.08);
border: 1px solid var(--border);
border-bottom-left-radius: 4px;
}
.ai-chat-input-area {
padding: 16px;
border-top: 1px solid var(--border);
display: flex;
gap: 10px;
}
.ai-chat-input {
flex: 1;
background: rgba(0, 0, 0, 0.3);
border: 1px solid var(--border);
border-radius: 12px;
padding: 12px 16px;
color: white;
outline: none;
}
.ai-chat-input:focus {
border-color: var(--primary);
}
.container {
max-width: 1400px;
margin: 32px auto;
padding: 0 24px;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 20px;
animation: fadeIn 0.5s ease;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.card {
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: 24px;
cursor: pointer;
transition: all 0.3s;
display: flex;
flex-direction: column;
justify-content: space-between;
min-height: 160px;
position: relative;
overflow: hidden;
}
.card::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 4px;
background: var(--primary);
opacity: 0;
transition: opacity 0.3s;
}
.card:hover {
transform: translateY(-4px);
border-color: var(--primary);
background: rgba(25, 25, 30, 0.8);
}
.card:hover::before {
opacity: 1;
}
.srv-name {
font-size: 18px;
font-weight: 700;
margin-bottom: 4px;
letter-spacing: -0.02em;
}
.srv-meta {
font-size: 13px;
color: var(--text-dim);
font-weight: 500;
}
.status-pill {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 14px;
border-radius: 99px;
background: rgba(0, 0, 0, 0.3);
border: 1px solid var(--border);
font-size: 10px;
font-weight: 800;
font-family: var(--mono);
text-transform: uppercase;
letter-spacing: 0.05em;
margin-top: 20px;
}
.dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--text-dim);
}
.status-pill.running {
border-color: var(--success);
color: var(--success);
}
.status-pill.running .dot {
background: var(--success);
box-shadow: 0 0 8px var(--success);
}
.status-pill.installing {
border-color: var(--warning);
color: var(--warning);
}
.status-pill.installing .dot {
background: var(--warning);
animation: blink 1s infinite;
}
.status-pill.error {
border-color: var(--error);
color: var(--error);
}
.status-pill.error .dot {
background: var(--error);
}
@keyframes blink {
0%,
100% {
opacity: 0.3;
}
50% {
opacity: 1;
}
}
/* Server Manager View */
#serverView {
display: none;
}
.view-header {
display: flex;
align-items: flex-end;
justify-content: space-between;
margin-bottom: 24px;
gap: 16px;
flex-wrap: wrap;
}
.view-title h1 {
font-size: 32px;
font-weight: 800;
letter-spacing: -0.04em;
}
.tab-group {
display: flex;
background: var(--card);
padding: 6px;
border-radius: var(--radius-sm);
border: 1px solid var(--border);
}
.tab {
padding: 8px 18px;
border-radius: calc(var(--radius-sm) - 4px);
font-size: 13px;
font-weight: 700;
color: var(--text-dim);
cursor: pointer;
transition: all 0.2s;
}
.tab:hover {
color: var(--text);
}
.tab.active {
background: rgba(255, 255, 255, 0.1);
color: var(--text);
}
.manager-layout {
display: grid;
grid-template-columns: 1fr 320px;
gap: 20px;
}
@media (max-width: 1000px) {
.manager-layout {
grid-template-columns: 1fr;
}
}
.console-box {
background: #000;
border: 1px solid var(--border);
border-radius: var(--radius-md);
display: flex;
flex-direction: column;
overflow: hidden;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4);
}
.console-nav {
background: #111;
padding: 10px 16px;
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: space-between;
font-size: 11px;
font-family: var(--mono);
color: var(--text-dim);
}
.dots {
display: flex;
gap: 6px;
}
.dots span {
width: 10px;
height: 10px;
border-radius: 50%;
}
.red {
background: #ff5f56;
}
.yellow {
background: #ffbd2e;
}
.green {
background: #27c93f;
}
.console-out {
padding: 20px;
height: 560px;
overflow-y: auto;
font-family: var(--mono);
font-size: 13px;
line-height: 1.6;
color: #e0e0e0;
white-space: pre-wrap;
position: relative;
}
.console-out::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.2) 50%),
linear-gradient(90deg, rgba(255, 0, 0, 0.05), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.05));
background-size: 100% 4px, 3px 100%;
pointer-events: none;
z-index: 10;
opacity: 0.3;
}
.side-panel {
display: flex;
flex-direction: column;
gap: 16px;
}
.side-card {
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: 20px;
}
.stat-row {
display: flex;
align-items: center;
gap: 14px;
margin-bottom: 12px;
}
.stat-row:last-child {
margin-bottom: 0;
}
.ico-box {
width: 40px;
height: 40px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.05);
display: flex;
align-items: center;
justify-content: center;
color: var(--primary);
}
.stat-val {
display: flex;
flex-direction: column;
}
.stat-label {
font-size: 10px;
font-weight: 800;
color: var(--text-dim);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.stat-num {
font-size: 14px;
font-weight: 600;
font-family: var(--mono);
}
/* File Manager */
.file-manager {
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius-md);
overflow: hidden;
}
.fm-header {
padding: 16px 24px;
border-bottom: 1px solid var(--border);
display: flex;
justify-content: space-between;
align-items: center;
}
.fm-path {
font-family: var(--mono);
font-size: 13px;
color: var(--text-dim);
}
.fm-path a {
color: var(--primary);
text-decoration: none;
border-bottom: 1px dotted transparent;
}
.fm-path a:hover {
border-bottom-color: var(--primary);
}
.fm-table {
width: 100%;
border-collapse: collapse;
}
.fm-table th,
.fm-table td {
text-align: left;
padding: 14px 24px;
border-bottom: 1px solid var(--border);
}
.fm-table th {
font-size: 11px;
font-weight: 800;
color: var(--text-dim);
text-transform: uppercase;
letter-spacing: 0.05em;
background: rgba(0, 0, 0, 0.2);
}
.fm-table tr:hover td {
background: rgba(255, 255, 255, 0.02);
}
.fm-name {
cursor: pointer;
display: flex;
align-items: center;
gap: 10px;
font-weight: 600;
font-size: 14px;
}
.fm-name:hover {
color: var(--primary);
}
/* Modal Styling */
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.8);
backdrop-filter: blur(8px);
z-index: 1000;
display: none;
align-items: center;
justify-content: center;
padding: 20px;
}
.modal-box {
background: #111;
border: 1px solid var(--border);
border-radius: var(--radius-lg);
width: 100%;
max-width: 440px;
padding: 32px;
box-shadow: 0 30px 60px rgba(0, 0, 0, 0.5);
}
.modal-box h3 {
font-size: 20px;
font-weight: 800;
margin-bottom: 16px;
letter-spacing: -0.02em;
}
.modal-box input {
width: 100%;
background: #000;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 14px 16px;
color: white;
margin-bottom: 24px;
outline: none;
}
.modal-box input:focus {
border-color: var(--primary);
}
/* Editor */
#editor-view {
position: fixed;
inset: 0;
background: var(--bg);
z-index: 2000;
display: none;
flex-direction: column;
}
.editor-top {
padding: 16px 24px;
border-bottom: 1px solid var(--border);
display: flex;
justify-content: space-between;
align-items: center;
background: #0a0a0a;
}
.editor-area {
flex: 1;
background: #000;
color: #d4d4d4;
font-family: var(--mono);
font-size: 14px;
padding: 30px;
border: none;
outline: none;
resize: none;
line-height: 1.6;
}
.toast-container {
position: fixed;
top: 80px;
right: 24px;
z-index: 9999;
display: flex;
flex-direction: column;
gap: 10px;
}
.toast {
background: rgba(20, 20, 25, 0.95);
backdrop-filter: blur(20px);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 14px 20px;
font-size: 13px;
font-weight: 600;
color: var(--text);
animation: toastIn 0.3s ease, toastOut 0.3s ease 2.7s;
max-width: 360px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
}
.toast.success {
border-left: 3px solid var(--success);
}
.toast.error {
border-left: 3px solid var(--error);
}
.toast.info {
border-left: 3px solid var(--primary);
}
@keyframes toastIn {
from {
opacity: 0;
transform: translateX(30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes toastOut {
from {
opacity: 1;
}
to {
opacity: 0;
transform: translateY(-10px);
}
}
</style>
</head>
<body>
<div class="notification-container" id="notificationContainer"></div>
<div class="toast-container" id="toastBox" style="display:none"></div>
<div class="topbar">
<div class="topbar-content">
<div class="brand" onclick="goHome()">
<img src="https://f.top4top.io/p_37210bgwm1.jpg" alt="Logo" class="brand-logo">
<div class="brand-info">
<span class="brand-name">الذئب الابيض</span>
<span class="brand-status">v0.1 · SECURE</span>
</div>
</div>
<div class="nav-actions">
<button class="btn" onclick="showHelp()">❓ HELP / مساعدة</button>
<button class="btn" onclick="switchView('settings')">⚙️ SETTINGS / إعدادات</button>
<button id="adminBtn" class="btn" style="display:none" onclick="location.href='/admin'">OPERATIONS HQ /
الإدارة</button>
<button id="homeBtn" class="btn" style="display:none" onclick="goHome()">DASHBOARD / لوحة التحكم</button>
<button id="newBtn" class="btn btn-primary">+ NEW ENGINE / سيرفر جديد</button>
<button id="delBtn" class="btn btn-danger" style="display:none" onclick="deleteServer()">PURGE / حذف</button>
<button class="btn btn-danger" onclick="location.href='/logout'">TERMINATE / خروج</button>
</div>
</div>
</div>
<div class="container" id="homeView">
<div class="stats-grid" id="statsGrid" style="display:none">
<div class="stat-card">
<div class="stat-icon">🚀</div>
<div class="stat-value" id="statServers">0</div>
<div class="stat-label">Active Servers</div>
<div class="stat-change positive">+0%</div>
</div>
<div class="stat-card">
<div class="stat-icon">⚡</div>
<div class="stat-value" id="statRunning">0</div>
<div class="stat-label">Running</div>
<div class="stat-change positive">+0%</div>
</div>
<div class="stat-card">
<div class="stat-icon">💾</div>
<div class="stat-value" id="statMemory">0 MB</div>
<div class="stat-label">Memory Used</div>
<div class="stat-change positive">+0%</div>
</div>
<div class="stat-card">
<div class="stat-icon">📊</div>
<div class="stat-value" id="statUptime">0h</div>
<div class="stat-label">Uptime</div>
<div class="stat-change positive">+0%</div>
</div>
</div>
<div class="chart-container" id="chartContainer" style="display:none">
<div class="chart-header">
<div class="chart-title">📈 Server Activity</div>
<div class="chart-legend">
<div class="legend-item">
<div class="legend-dot" style="background: var(--primary)"></div>
<span>Requests</span>
</div>
<div class="legend-item">
<div class="legend-dot" style="background: var(--accent)"></div>
<span>Errors</span>
</div>
</div>
</div>
<div class="activity-chart" id="activityChart"></div>
<div class="chart-labels">
<span>Mon</span><span>Tue</span><span>Wed</span><span>Thu</span><span>Fri</span><span>Sat</span><span>Sun</span>
</div>
</div>
<div class="billing-panel" id="billingPanel"
style="display:none; padding: 24px; border-radius: 20px; border: 1px solid var(--border); background: var(--card); margin-bottom: 24px; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 16px;">
<div>
<h3 style="margin-bottom: 8px;">مرحباً <span id="pName" style="color:var(--text)"></span></h3>
<p style="color:var(--text-muted); font-size:14px; font-family: var(--mono);">
PLAN: <strong id="pPlan"></strong> |
LIMIT: <span id="pBots"></span> BOTS / <span id="pMem"></span> RAM<br>
<span id="aiUsageTxt" style="font-size:11px; color:var(--primary)">AI REQUESTS TODAY: --</span>
</p>
</div>
<div style="display: flex; gap: 10px;">
<button class="btn btn-primary" onclick="switchView('ai')">🤖 AI ASSISTANT / مساعد ذكي</button>
</div>
</div>
<div id="adPanel"
style="display:none; margin-bottom: 24px; padding: 20px; border-radius: 16px; background: rgba(255,255,255,0.03); border: 1px dashed var(--border); text-align: center;">
</div>
<div class="grid" id="cardsContainer"></div>
</div>
<div class="container" id="settingsView" style="display:none">
<div class="settings-panel">
<div class="settings-header">
<h2>⚙️ Settings / الإعدادات</h2>
<p>Customize your experience / تخصيص تجربتك</p>
</div>
<div class="settings-section">
<div class="settings-section-title">🔔 Notifications / الإشعارات</div>
<div class="settings-item">
<div class="settings-item-label">
<h4>Push Notifications</h4>
<p>Receive real-time alerts</p>
</div>
<div class="toggle-switch active" onclick="this.classList.toggle('active')"></div>
</div>
<div class="settings-item">
<div class="settings-item-label">
<h4>Email Notifications</h4>
<p>Get updates via email</p>
</div>
<div class="toggle-switch" onclick="this.classList.toggle('active')"></div>
</div>
</div>
<div class="settings-section">
<div class="settings-section-title">🎨 Appearance / المظهر</div>
<div class="settings-item">
<div class="settings-item-label">
<h4>Theme</h4>
<p>Choose your preferred theme</p>
</div>
<select class="settings-select" style="width:150px; margin-bottom:0">
<option>Dark</option>
<option>Light</option>
<option>Auto</option>
</select>
</div>
<div class="settings-item">
<div class="settings-item-label">
<h4>Language</h4>
<p>Select interface language</p>
</div>
<select class="settings-select" style="width:150px; margin-bottom:0">
<option>العربية</option>
<option>English</option>
</select>
</div>
</div>
<div class="settings-section">
<div class="settings-section-title">� Subscription Plan / خطة الاشتراك</div>
<div id="currentPlanInfo" style="margin-bottom: 20px;">
<div style="display: flex; justify-content: space-between; align-items: center; padding: 16px; background: rgba(10, 132, 255, 0.1); border-radius: 12px; border: 1px solid rgba(10, 132, 255, 0.3);">
<div>
<h4 style="margin-bottom: 4px;">Current Plan: <span id="currentPlanName">Free</span></h4>
<p style="color: var(--text-dim); font-size: 12px;">Bots: <span id="botsUsed">0</span>/<span id="botsMax">3</span></p>
</div>
<button class="btn btn-primary" onclick="showUpgradeModal()">Upgrade Plan</button>
</div>
</div>
<div id="plansGrid" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px;"></div>
</div>
<div class="settings-section">
<div class="settings-section-title">� Security / الأمان</div>
<input type="password" class="settings-input" placeholder="Current Password">
<input type="password" class="settings-input" placeholder="New Password">
<input type="password" class="settings-input" placeholder="Confirm New Password">
<button class="btn btn-primary" style="width:100%" onclick="showNotification('Success', 'Password updated', 'success')">Update Password</button>
</div>
<div class="settings-section">
<div class="settings-section-title">💾 Data / البيانات</div>
<div class="settings-item">
<div class="settings-item-label">
<h4>Export Data</h4>
<p>Download your account data</p>
</div>
<button class="btn" onclick="showNotification('Info', 'Export started', 'info')">Export</button>
</div>
<div class="settings-item">
<div class="settings-item-label">
<h4>Delete Account</h4>
<p>Permanently remove your account</p>
</div>
<button class="btn btn-danger" onclick="showNotification('Warning', 'This action is irreversible', 'warning')">Delete</button>
</div>
</div>
<div class="settings-section">
<div class="settings-section-title">📋 Activity Log / سجل النشاطات</div>
<div class="activity-log" id="userActivityLog">
<div class="activity-item">
<div class="activity-icon">🚀</div>
<div class="activity-content">
<div class="activity-title">Created new server</div>
<div class="activity-time">2 minutes ago</div>
</div>
<div class="activity-status success">Success</div>
</div>
<div class="activity-item">
<div class="activity-icon">📝</div>
<div class="activity-content">
<div class="activity-title">Edited file: main.py</div>
<div class="activity-time">15 minutes ago</div>
</div>
<div class="activity-status info">Info</div>
</div>
<div class="activity-item">
<div class="activity-icon">🔄</div>
<div class="activity-content">
<div class="activity-title">Restarted server</div>
<div class="activity-time">1 hour ago</div>
</div>
<div class="activity-status success">Success</div>
</div>
<div class="activity-item">
<div class="activity-icon">🔐</div>
<div class="activity-content">
<div class="activity-title">Changed password</div>
<div class="activity-time">3 hours ago</div>
</div>
<div class="activity-status info">Info</div>
</div>
<div class="activity-item">
<div class="activity-icon">📤</div>
<div class="activity-content">
<div class="activity-title">Uploaded files</div>
<div class="activity-time">5 hours ago</div>
</div>
<div class="activity-status success">Success</div>
</div>
</div>
</div>
</div>
</div>
<div class="container" id="aiView" style="display:none">
<div class="view-header">
<div class="view-title">
<h1>WHITE WOLF AI</h1>
<div style="color:var(--text-dim); font-size:13px; margin-top:4px;">POWERED BY OLLAMA FREE API</div>
</div>
<button class="btn" onclick="goHome()">BACK / رجوع</button>
</div>
<div class="ai-chat-container">
<div class="ai-chat-header">
<span style="font-weight: 700;">AI CHAT TERMINAL</span>
<select id="aiModelSelect" class="btn" style="padding: 4px 8px; font-size: 11px;">
<option value="deepseek-r1:latest">DeepSeek R1 (Strong)</option>
<option value="llama3.2:3b">Llama 3.2 3B (Fast)</option>
<option value="mistral:latest">Mistral</option>
<option value="gpt-oss:20b">Gemma 20B</option>
</select>
</div>
<div class="ai-chat-messages" id="aiMessages">
<div class="ai-message bot">مرحباً! أنا المساعد الذكي لمنصة الذئب الأبيض. كيف يمكنني مساعدتك اليوم؟</div>
</div>
<div class="ai-chat-input-area">
<input type="text" id="aiInput" class="ai-chat-input" placeholder="Type your message / اكتب رسالتك هنا..."
onkeypress="if(event.key==='Enter') sendAiMessage()">
<button class="btn btn-primary" onclick="sendAiMessage()">SEND</button>
</div>
</div>
</div>
<div class="container" id="serverView">
<div class="view-header">
<div class="view-title">
<h1 id="srvHeader">OPERATIVE</h1>
<div style="color:var(--text-dim); font-size:13px; margin-top:4px;" id="srvSub">ACTIVE SESSION / جلسة نشطة</div>
</div>
<div class="tab-group">
<div class="tab active" id="tabConsole" onclick="switchTab('console')">CONSOLE / موجه الأوامر</div>
<div class="tab" id="tabFiles" onclick="switchTab('files')">FILE SYSTEM / الملفات</div>
</div>
</div>
<div id="viewConsole">
<div class="manager-layout">
<div class="console-box">
<div class="console-nav">
<div class="dots"><span class="red"></span><span class="yellow"></span><span class="green"></span></div>
<span>STDOUT STREAM</span>
<span>UTF-8</span>
</div>
<div class="console-out" id="logOut">INITIALIZING VIRTUAL ENVIRONMENT...</div>
</div>
<div class="side-panel">
<button id="btnStart" class="btn btn-primary" style="width:100%; height:50px; font-size:14px;"
onclick="srvAction('start')">EXECUTE / تشغيل</button>
<button class="btn" style="width:100%; padding:14px;" onclick="srvAction('restart')">REBOOT / إعادة
تشغيل</button>
<button class="btn btn-danger" style="width:100%; padding:14px;" onclick="srvAction('stop')">HALT /
إيقاف</button>
<div class="side-card">
<div class="stat-row">
<div class="ico-box">⚡</div>
<div class="stat-val"><span class="stat-label">STATUS</span><span class="stat-num"
id="statusTxt">OFFLINE</span></div>
</div>
<div class="stat-row">
<div class="ico-box">💾</div>
<div class="stat-val"><span class="stat-label">MEMORY</span><span class="stat-num" id="memTxt">0.0
MB</span></div>
</div>
<div class="stat-row">
<div class="ico-box">⚙️</div>
<div class="stat-val"><span class="stat-label">CPU</span><span class="stat-num" id="cpuTxt">0%</span>
</div>
</div>
<div class="stat-row">
<div class="ico-box">🌐</div>
<div class="stat-val"><span class="stat-label">IP ADDRESS</span><span class="stat-num"
id="ipTxt">0.0.0.0</span></div>
</div>
</div>
<div class="side-card">
<div class="stat-label" style="margin-bottom:8px;">PROTOCOL ENTRYPOINT</div>
<div
style="font-family:var(--mono); font-size:13px; color:var(--primary); cursor:pointer; text-decoration:underline"
onclick="changeStartup()" id="mainFileTxt">NOT SET</div>
</div>
</div>
</div>
</div>
<div id="viewFiles" style="display:none" class="file-manager">
<div class="fm-header">
<div class="fm-path" id="pathBar"></div>
<div class="fm-toolbar">
<input type="text" class="fm-search" placeholder="Search files... / بحث..." id="fmSearch" onkeyup="filterFiles()">
<div class="fm-view-toggle">
<button class="fm-view-btn active" onclick="setFileView('list')">📋</button>
<button class="fm-view-btn" onclick="setFileView('grid')">⊞</button>
</div>
</div>
<div class="nav-actions">
<button id="btnBack" class="btn" style="display:none" onclick="goUp()">BACK / رجوع</button>
<button class="btn" onclick="newFolder()">+ DIR / مجلد جديد</button>
<button class="btn btn-primary" onclick="document.getElementById('uplFiles').click()">UPLOAD / رفع
ملف</button>
<button class="btn" onclick="document.getElementById('uplFolder').click()">BUNDLE / رفع مجلد</button>
</div>
</div>
<div class="fm-dropzone" id="fmDropzone" ondrop="handleDrop(event)" ondragover="handleDragOver(event)" ondragleave="handleDragLeave(event)">
<div class="fm-dropzone-icon">📁</div>
<div>Drag & drop files here / اسحب الملفات هنا</div>
</div>
<input type="file" id="uplFiles" multiple hidden onchange="doUploadFiles(this)">
<input type="file" id="uplFolder" webkitdirectory directory multiple hidden onchange="doUploadFolder(this)">
<table class="fm-table">
<thead>
<tr>
<th>ENTITY NAME / اسم الملف</th>
<th style="width:120px">SIZE / الحجم</th>
<th style="width:280px">PROTOCOLS / العمليات</th>
</tr>
</thead>
<tbody id="fileList"></tbody>
</table>
</div>
</div>
<div class="editor-container" id="advancedEditor">
<div class="editor-header">
<div class="editor-title" id="advancedEditorTitle">main.py</div>
<div class="editor-toolbar">
<button class="btn" onclick="formatCode()">🎨 Format</button>
<button class="btn" onclick="toggleMinimap()">🗺️ Minimap</button>
<button class="btn btn-primary" onclick="saveAdvancedFile()">💾 Save</button>
<button class="btn btn-danger" onclick="closeAdvancedEditor()">✕ Close</button>
</div>
</div>
<div class="editor-body">
<div class="editor-sidebar" id="fileTree">
<div class="file-tree-item active">📄 main.py</div>
</div>
<div class="editor-main">
<div class="code-editor-wrapper">
<textarea class="code-editor" id="advancedCodeEditor" spellcheck="false" placeholder="Start coding..."></textarea>
</div>
<div class="editor-statusbar">
<div class="editor-status-item">
<span id="cursorPosition">Ln 1, Col 1</span>
<span id="fileSize">0 bytes</span>
</div>
<div class="editor-status-item">
<span id="encoding">UTF-8</span>
<span id="language">Python</span>
</div>
</div>
</div>
</div>
</div>
<div id="editor-view">
<div class="editor-top">
<div style="font-family:var(--mono); font-weight:600" id="ed-title">ENTITY</div>
<div class="nav-actions">
<button class="btn" onclick="openAdvancedEditor()">🔧 Advanced Editor</button>
<button class="btn btn-primary" onclick="saveFile()">WRITE TO DISK / حفظ</button>
<button class="btn" onclick="closeEditor()">CLOSE / إغلاق</button>
</div>
</div>
<textarea class="editor-area" id="ed-area" spellcheck="false"></textarea>
</div>
<div id="modalOverlay" class="modal-overlay">
<div class="modal-box">
<h3 id="mTitle">ACTION</h3>
<input id="mIn" autocomplete="off" spellcheck="false">
<div class="nav-actions" style="justify-content:flex-end">
<button class="btn" onclick="closeM()">ABORT / إلغاء</button>
<button id="mConfirm" class="btn btn-primary">PROCEED / تأكيد</button>
</div>
</div>
</div>
<div class="fm-preview-modal" id="fmPreviewModal">
<div class="fm-preview-content" id="fmPreviewContent"></div>
<button class="btn btn-danger" style="position:fixed; top:20px; right:20px;" onclick="closePreview()">✕ Close</button>
</div>
<script>
let currentSrv = null, poll = null, currentFile = null, currentPath = '';
function escapeHtml(s) { return (s || '').replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;"); }
function escapeAttr(s) { return escapeHtml(s).replace(/\\/g, "\\\\"); }
async function loadServers() {
const r = await fetch('/servers');
const d = await r.json();
const cont = document.getElementById('cardsContainer');
cont.innerHTML = '';
fetch('/admin').then(res => { if (!res.redirected && res.status === 200) document.getElementById('adminBtn').style.display = 'inline-flex'; });
fetch('/api/user/profile').then(res => res.json()).then(data => {
if (data.success) {
document.getElementById('billingPanel').style.display = 'flex';
document.getElementById('statsGrid').style.display = 'grid';
document.getElementById('chartContainer').style.display = 'block';
document.getElementById('pName').textContent = data.username.toUpperCase();
document.getElementById('pPlan').textContent = data.plan_label.toUpperCase();
document.getElementById('pBots').textContent = data.max_bots;
document.getElementById('pMem').textContent = data.max_mem_mb + 'MB';
if (data.ai_usage) {
const usage = data.ai_usage;
document.getElementById('aiUsageTxt').textContent = `AI REQUESTS TODAY: ${usage.count || 0} / 20`;
}
}
});
loadAds();
updateStats(d.servers || []);
generateActivityChart();
(d.servers || []).forEach(s => {
const div = document.createElement('div');
div.className = 'card';
const st = (s.status || 'Offline').toLowerCase();
let sub = s.subtitle || 'NO PROTOCOL';
let expHtml = '';
if (s.last_renewed) {
const days = (Date.now() / 1000 - s.last_renewed) / (24 * 3600);
if (days > 4) {
sub = `<span style="color:var(--error)">EXPIRED / انتهت الصلاحية</span>`;
expHtml = `<button class="btn btn-primary" style="margin-top:10px; width:100%" onclick="event.stopPropagation(); renewBot('${s.key}')">RENEW / تجديد</button>`;
} else {
const rem = (4 - days).toFixed(1);
sub += ` <br><span style="font-size:10px; color:var(--text-dim)">RENEW IN: ${rem} DAYS</span>`;
}
}
const cls = st === 'running' ? 'running' : (st === 'installing' || st === 'starting' ? 'installing' : (s.banned ? 'error' : ''));
div.innerHTML = `
<div style="flex-grow:1">
<div class="srv-name">${escapeHtml(s.title)}</div>
<div class="srv-meta">${sub}</div>
${expHtml}
</div>
<div class="status-pill ${cls}"><span class="dot"></span>${st.toUpperCase()}</div>
`;
div.onclick = () => openServer(s);
cont.appendChild(div);
});
}
async function loadAds() {
const res = await fetch('/api/ads');
const data = await res.json();
const adBox = document.getElementById('adPanel');
if (data.current_ad) {
adBox.style.display = 'block';
adBox.innerHTML = data.current_ad;
} else {
adBox.style.display = 'block';
adBox.innerHTML = `<div style="color:var(--text-dim); font-size:14px">Your ad here? / أعلن هنا؟ <a href="${data.contact_link}" target="_blank" style="color:var(--primary); text-decoration:none">CONTACT DEVELOPER</a></div>`;
}
}
async function renewBot(key) {
const r = await fetch(`/api/server/renew/${encodeURIComponent(key)}`, { method: 'POST' });
const d = await r.json();
if (d.success) { toast('Bot Renewed!', 'success'); loadServers(); }
}
function showHelp() {
const helpText = `
<div id="helpTxtInner" style="text-align:right; direction:rtl; line-height:1.6; margin-bottom:20px; font-size:14px; max-height: 300px; overflow-y: auto;">
<h2 style="color:var(--primary); margin-bottom:10px">📖 دليل استخدام المنصة</h2>
<p>1. <b>البادئ:</b> اضغط "سيرفر جديد" وحدد اسماً لمشروعك.</p>
<p>2. <b>الملفات:</b> داخل لوحة تحكم السيرفر، ارفع ملفاتك (بايثون أو PHP).</p>
<p>3. <b>التشغيل:</b> اضغط على اسم ملف التشغيل واضبطه كـ Entrypoint.</p>
<p>4. <b>الموارد:</b> الموارد حالياً محدودة، لذا احرص على كتابة كود فعال.</p>
<p>5. <b>التجديد:</b> ⚠️ <b>هام جداً:</b> يجب الضغط على زر التنشيط كل 4 أيام وإلا سيتوقف البوت.</p>
<p>6. <b>PHP:</b> ندعم الآن لغة PHP بشكل كامل.</p>
<p>7. <b>الذكاء الاصطناعي:</b> لك 20 طلباً يومياً من المساعد الذكي.</p>
</div>
`;
openM("Platform Guide / دليل المنصة", "", () => closeM());
document.getElementById('mIn').style.display = 'none';
document.getElementById('mTitle').insertAdjacentHTML('afterend', helpText);
}
function openServer(s) {
currentSrv = s;
switchView('server');
document.getElementById('homeBtn').style.display = 'inline-flex';
document.getElementById('newBtn').style.display = 'none';
document.getElementById('delBtn').style.display = 'inline-flex';
document.getElementById('srvHeader').textContent = s.title.toUpperCase();
document.getElementById('mainFileTxt').textContent = s.startup_file || "NOT SET";
switchTab('console');
}
function goHome() {
switchView('home');
document.getElementById('homeBtn').style.display = 'none';
document.getElementById('delBtn').style.display = 'none';
document.getElementById('newBtn').style.display = 'inline-flex';
clearInterval(poll);
}
function switchView(v) {
document.getElementById('homeView').style.display = v === 'home' ? 'block' : 'none';
document.getElementById('serverView').style.display = v === 'server' ? 'block' : 'none';
document.getElementById('aiView').style.display = v === 'ai' ? 'block' : 'none';
document.getElementById('settingsView').style.display = v === 'settings' ? 'block' : 'none';
if (v === 'home') loadServers();
if (v === 'ai' || v === 'settings') {
document.getElementById('homeBtn').style.display = 'inline-flex';
document.getElementById('newBtn').style.display = 'none';
}
if (v === 'settings') {
loadSubscriptionPlans();
loadCurrentSubscription();
}
}
async function sendAiMessage() {
const input = document.getElementById('aiInput');
const msg = input.value.trim();
if (!msg) return;
const model = document.getElementById('aiModelSelect').value;
const msgsCont = document.getElementById('aiMessages');
const uDiv = document.createElement('div');
uDiv.className = 'ai-message user';
uDiv.textContent = msg;
msgsCont.appendChild(uDiv);
input.value = '';
msgsCont.scrollTop = msgsCont.scrollHeight;
const bDiv = document.createElement('div');
bDiv.className = 'ai-message bot';
bDiv.textContent = 'Thinking... / جاري التفكير...';
msgsCont.appendChild(bDiv);
msgsCont.scrollTop = msgsCont.scrollHeight;
try {
const r = await fetch('/api/ai/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: msg, model })
});
const d = await r.json();
bDiv.textContent = d.success ? d.response : (d.message || 'Error');
if (!d.success) bDiv.style.color = 'var(--error)';
} catch (e) {
bDiv.textContent = 'Connection error.';
bDiv.style.color = 'var(--error)';
}
msgsCont.scrollTop = msgsCont.scrollHeight;
}
function switchTab(t) {
document.getElementById('tabConsole').classList.toggle('active', t === 'console');
document.getElementById('tabFiles').classList.toggle('active', t === 'files');
document.getElementById('viewConsole').style.display = t === 'console' ? 'block' : 'none';
document.getElementById('viewFiles').style.display = t === 'files' ? 'block' : 'none';
if (t === 'console') startPolling(); else { clearInterval(poll); loadFiles(); }
}
async function startPolling() {
if (poll) clearInterval(poll);
const fn = async () => {
try {
const r = await fetch(`/server/stats/${encodeURIComponent(currentSrv.key || currentSrv.folder)}`);
const d = await r.json();
document.getElementById('logOut').textContent = d.logs || "ESTABLISHING VIRTUAL LINK...";
document.getElementById('statusTxt').textContent = (d.status || 'OFFLINE').toUpperCase();
document.getElementById('memTxt').textContent = d.mem || '0.0 MB';
document.getElementById('cpuTxt').textContent = d.cpu || '0%';
document.getElementById('ipTxt').textContent = d.ip || '0.0.0.0';
const running = ['running', 'installing', 'starting'].includes(d.status.toLowerCase());
document.getElementById('btnStart').disabled = running;
document.getElementById('btnStart').style.opacity = running ? 0.5 : 1;
const lo = document.getElementById('logOut'); lo.scrollTop = lo.scrollHeight;
} catch (e) { }
};
fn(); poll = setInterval(fn, 2000);
}
async function srvAction(act) { await fetch(`/server/action/${encodeURIComponent(currentSrv.key || currentSrv.folder)}/${act}`, { method: 'POST' }); }
async function loadFiles() {
const r = await fetch(`/files/list/${encodeURIComponent(currentSrv.key || currentSrv.folder)}?path=${encodeURIComponent(currentPath)}`);
const data = await r.json();
document.getElementById("btnBack").style.display = currentPath ? "inline-flex" : "none";
let crumbs = [`<span class="crumb"><a href="#" onclick="jumpPath('')">ROOT</a></span>`];
let acc = '';
(data.path || '').split('/').filter(Boolean).forEach(p => {
acc = acc ? (acc + '/' + p) : p;
crumbs.push('<span style="opacity:0.3"> / </span>' + `<span class="crumb"><a href="#" onclick="jumpPath('${escapeAttr(acc)}')">${escapeHtml(p)}</a></span>`);
});
document.getElementById('pathBar').innerHTML = crumbs.join('');
let html = '';
(data.dirs || []).forEach(d => {
html += `<tr><td><div class="fm-name" onclick="openFolder('${escapeAttr(d.name)}')">📁 ${escapeHtml(d.name)}</div></td><td style="color:var(--text-dim)">DIR</td><td>
<div class="nav-actions"><button class="btn" style="padding:4px 8px" onclick="renameDir('${escapeAttr(d.name)}')">RENAME</button>
<button class="btn btn-danger" style="padding:4px 8px" onclick="deleteDir('${escapeAttr(d.name)}')">DEL</button></div></td></tr>`;
});
(data.files || []).forEach(f => {
const rel = currentPath ? `${currentPath}/${f.name}` : f.name;
html += `<tr><td><div class="fm-name" onclick="openEditor('${escapeAttr(rel)}')">📄 ${escapeHtml(f.name)}</div></td><td style="font-family:var(--mono);font-size:12px">${f.size}</td><td>
<div class="nav-actions"><button class="btn" style="padding:4px 8px" onclick="renameFilePrompt('${escapeAttr(f.name)}')">RENAME</button>
<button class="btn btn-danger" style="padding:4px 8px" onclick="deleteFilePrompt('${escapeAttr(f.name)}')">DEL</button></div></td></tr>`;
});
document.getElementById('fileList').innerHTML = html;
}
function openFolder(name) { currentPath = currentPath ? (currentPath + '/' + name) : name; loadFiles(); }
function jumpPath(p) { currentPath = p; loadFiles(); }
function goUp() { let p = currentPath.split('/'); p.pop(); currentPath = p.join('/'); loadFiles(); }
async function openEditor(name) {
currentFile = name; document.getElementById('ed-title').textContent = name.toUpperCase();
const res = await fetch(`/files/content/${encodeURIComponent(currentSrv.key || currentSrv.folder)}?file=${encodeURIComponent(name)}`);
const d = await res.json();
document.getElementById('ed-area').value = d.content || '';
document.getElementById('editor-view').style.display = 'flex';
}
function closeEditor() { document.getElementById('editor-view').style.display = 'none'; }
function showNotification(title, message, type = 'info', duration = 4000) {
const container = document.getElementById('notificationContainer');
const notification = document.createElement('div');
notification.className = `notification ${type}`;
const icons = {
success: '✓',
error: '✕',
warning: '⚠',
info: 'ℹ'
};
notification.innerHTML = `
<div class="notification-icon">${icons[type] || icons.info}</div>
<div class="notification-content">
<div class="notification-title">${title}</div>
<div class="notification-message">${message}</div>
</div>
<div class="notification-close" onclick="this.parentElement.remove()">×</div>
<div class="notification-progress"></div>
`;
container.appendChild(notification);
setTimeout(() => {
notification.style.animation = 'slideOut 0.3s cubic-bezier(0.4, 0, 0.2, 1)';
setTimeout(() => notification.remove(), 300);
}, duration);
}
function toast(msg, type = 'info') {
const titles = {
success: 'Success',
error: 'Error',
warning: 'Warning',
info: 'Info'
};
showNotification(titles[type] || 'Info', msg, type);
}
function updateStats(servers) {
const total = servers.length;
const running = servers.filter(s => s.status === 'Running').length;
const memory = servers.reduce((sum, s) => {
if (s.mem) {
const match = s.mem.match(/([\d.]+)\s*MB/i);
if (match) return sum + parseFloat(match[1]);
}
return sum;
}, 0);
document.getElementById('statServers').textContent = total;
document.getElementById('statRunning').textContent = running;
document.getElementById('statMemory').textContent = memory.toFixed(1) + ' MB';
document.getElementById('statUptime').textContent = Math.floor(Math.random() * 24 + 1) + 'h';
}
function generateActivityChart() {
const chart = document.getElementById('activityChart');
chart.innerHTML = '';
const data = [45, 72, 38, 91, 65, 82, 58];
const max = Math.max(...data);
data.forEach((value, index) => {
const bar = document.createElement('div');
bar.className = 'chart-bar';
const height = (value / max) * 100;
bar.style.height = height + '%';
bar.setAttribute('data-value', value);
chart.appendChild(bar);
});
}
// Advanced Editor Functions
function openAdvancedEditor() {
const editor = document.getElementById('advancedEditor');
const codeArea = document.getElementById('ed-area');
const advancedEditor = document.getElementById('advancedCodeEditor');
document.getElementById('advancedEditorTitle').textContent = currentFile || 'Untitled';
advancedEditor.value = codeArea.value;
editor.classList.add('active');
updateEditorStats();
advancedEditor.addEventListener('input', updateEditorStats);
advancedEditor.addEventListener('click', updateCursorPosition);
advancedEditor.addEventListener('keyup', updateCursorPosition);
}
function closeAdvancedEditor() {
const editor = document.getElementById('advancedEditor');
const codeArea = document.getElementById('ed-area');
const advancedEditor = document.getElementById('advancedCodeEditor');
codeArea.value = advancedEditor.value;
editor.classList.remove('active');
}
function saveAdvancedFile() {
const advancedEditor = document.getElementById('advancedCodeEditor');
document.getElementById('ed-area').value = advancedEditor.value;
saveFile();
showNotification('Success', 'File saved successfully', 'success');
}
function formatCode() {
const editor = document.getElementById('advancedCodeEditor');
// Simple indentation formatting
let code = editor.value;
code = code.replace(/\n\s*\n/g, '\n'); // Remove extra empty lines
editor.value = code;
showNotification('Formatted', 'Code formatted', 'info');
}
function toggleMinimap() {
showNotification('Minimap', 'Minimap feature coming soon', 'info');
}
function updateEditorStats() {
const editor = document.getElementById('advancedCodeEditor');
const content = editor.value;
const lines = content.split('\n').length;
const chars = content.length;
document.getElementById('fileSize').textContent = chars + ' bytes';
}
function updateCursorPosition() {
const editor = document.getElementById('advancedCodeEditor');
const pos = editor.selectionStart;
const text = editor.value.substring(0, pos);
const lines = text.split('\n');
const line = lines.length;
const col = lines[lines.length - 1].length + 1;
document.getElementById('cursorPosition').textContent = `Ln ${line}, Col ${col}`;
}
// Advanced File Manager Functions
let currentFileView = 'list';
function setFileView(view) {
currentFileView = view;
const buttons = document.querySelectorAll('.fm-view-btn');
buttons.forEach(btn => btn.classList.remove('active'));
event.target.classList.add('active');
loadFiles();
}
function filterFiles() {
const search = document.getElementById('fmSearch').value.toLowerCase();
const items = document.querySelectorAll('.fm-name');
items.forEach(item => {
const text = item.textContent.toLowerCase();
item.closest('tr').style.display = text.includes(search) ? '' : 'none';
});
}
function handleDragOver(e) {
e.preventDefault();
e.stopPropagation();
document.getElementById('fmDropzone').classList.add('dragover');
}
function handleDragLeave(e) {
e.preventDefault();
e.stopPropagation();
document.getElementById('fmDropzone').classList.remove('dragover');
}
function handleDrop(e) {
e.preventDefault();
e.stopPropagation();
document.getElementById('fmDropzone').classList.remove('dragover');
const files = e.dataTransfer.files;
if (files.length > 0) {
uploadDroppedFiles(files);
}
}
async function uploadDroppedFiles(files) {
let fd = new FormData();
for (let f of files) {
fd.append("files", f);
}
await fetch(`/files/upload/${encodeURIComponent(currentSrv.key || currentSrv.folder)}?path=${encodeURIComponent(currentPath)}`, { method: "POST", body: fd });
loadFiles();
showNotification('Success', `${files.length} file(s) uploaded`, 'success');
}
function previewFile(name) {
const ext = name.split('.').pop().toLowerCase();
const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'];
if (imageExts.includes(ext)) {
showImagePreview(name);
} else {
showCodePreview(name);
}
}
async function showImagePreview(name) {
const modal = document.getElementById('fmPreviewModal');
const content = document.getElementById('fmPreviewContent');
const rel = currentPath ? `${currentPath}/${name}` : name;
const res = await fetch(`/files/content/${encodeURIComponent(currentSrv.key || currentSrv.folder)}?file=${encodeURIComponent(rel)}`);
const d = await res.json();
if (d.success) {
content.innerHTML = `<img src="data:image/png;base64,${d.content}" class="fm-preview-image" alt="${name}">`;
modal.classList.add('active');
}
}
async function showCodePreview(name) {
const modal = document.getElementById('fmPreviewModal');
const content = document.getElementById('fmPreviewContent');
const rel = currentPath ? `${currentPath}/${name}` : name;
const res = await fetch(`/files/content/${encodeURIComponent(currentSrv.key || currentSrv.folder)}?file=${encodeURIComponent(rel)}`);
const d = await res.json();
if (d.success) {
content.innerHTML = `<pre class="fm-preview-code">${escapeHtml(d.content || '')}</pre>`;
modal.classList.add('active');
}
}
function closePreview() {
document.getElementById('fmPreviewModal').classList.remove('active');
}
// Subscription Plans Functions
async function loadSubscriptionPlans() {
try {
const r = await fetch('/api/subscription/plans');
const d = await r.json();
if (d.success) {
displayPlans(d.plans);
}
} catch (e) { console.error(e); }
}
async function loadCurrentSubscription() {
try {
const r = await fetch('/api/subscription/current');
const d = await r.json();
if (d.success) {
document.getElementById('currentPlanName').textContent = d.limits.name;
document.getElementById('botsUsed').textContent = d.usage.bots_used;
document.getElementById('botsMax').textContent = d.limits.max_bots;
}
} catch (e) { console.error(e); }
}
function displayPlans(plans) {
const grid = document.getElementById('plansGrid');
grid.innerHTML = '';
for (const [key, plan] of Object.entries(plans)) {
const card = document.createElement('div');
card.style.cssText = `
background: rgba(255,255,255,0.03);
border: 1px solid var(--border);
border-radius: 12px;
padding: 20px;
text-align: center;
transition: all 0.3s;
`;
card.onmouseover = () => card.style.borderColor = 'var(--primary)';
card.onmouseout = () => card.style.borderColor = 'var(--border)';
card.innerHTML = `
<h3 style="font-size: 18px; font-weight: 700; margin-bottom: 8px;">${plan.name}</h3>
<div style="font-size: 32px; font-weight: 800; color: var(--primary); margin-bottom: 16px;">
$${plan.price}<span style="font-size: 14px; color: var(--text-dim); font-weight: 400;">/mo</span>
</div>
<ul style="list-style: none; padding: 0; margin-bottom: 16px; text-align: left;">
${plan.features.map(f => `<li style="padding: 4px 0; font-size: 13px; color: var(--text-dim);">✓ ${f}</li>`).join('')}
</ul>
<button class="btn ${key === 'free' ? '' : 'btn-primary'}" style="width: 100%;" onclick="upgradePlan('${key}')">
${key === 'free' ? 'Current' : 'Upgrade'}
</button>
`;
grid.appendChild(card);
}
}
async function upgradePlan(plan) {
try {
const r = await fetch('/api/subscription/upgrade', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ plan })
});
const d = await r.json();
if (d.success) {
showNotification('Success', d.message, 'success');
loadCurrentSubscription();
} else {
showNotification('Error', d.message, 'error');
}
} catch (e) { showNotification('Error', 'Failed to upgrade', 'error'); }
}
function showUpgradeModal() {
showNotification('Info', 'Contact admin to upgrade your plan', 'info');
}
async function saveFile() {
try {
const r = await fetch(`/files/save/${encodeURIComponent(currentSrv.key || currentSrv.folder)}`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ file: currentFile, content: document.getElementById('ed-area').value })
});
const d = await r.json();
if (d.success) toast('FILE SAVED', 'success');
else toast(d.message || 'Save failed', 'error');
} catch (e) { toast('Network error', 'error'); }
}
function openM(t, v, cb) {
document.getElementById('modalOverlay').style.display = 'flex';
document.getElementById('mTitle').textContent = t.toUpperCase();
const mIn = document.getElementById('mIn');
mIn.value = v || '';
mIn.style.display = 'block'; // Ensure it's normally visible
const oldHelp = document.getElementById('helpTxtInner');
if (oldHelp) oldHelp.remove();
document.getElementById('mConfirm').onclick = () => cb(mIn.value);
}
function closeM() {
document.getElementById('modalOverlay').style.display = 'none';
const oldHelp = document.getElementById('helpTxtInner');
if (oldHelp) oldHelp.remove();
}
async function renameDir(n) { openM("Rename Directory", n, async v => { await fetch(`/files/rename/${encodeURIComponent(currentSrv.key || currentSrv.folder)}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ path: currentPath, old: n, new: v }) }); loadFiles(); closeM(); toast('Renamed', 'success'); }); }
async function deleteDir(n) { if (confirm(`PURGE DIRECTORY '${n}'?`)) { await fetch(`/files/delete/${encodeURIComponent(currentSrv.key || currentSrv.folder)}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ path: currentPath, name: n, kind: 'dir' }) }); loadFiles(); toast('Directory deleted', 'success'); } }
async function renameFilePrompt(n) { openM("Rename File", n, async v => { await fetch(`/files/rename/${encodeURIComponent(currentSrv.key || currentSrv.folder)}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ path: currentPath, old: n, new: v }) }); loadFiles(); closeM(); toast('Renamed', 'success'); }); }
async function deleteFilePrompt(n) { if (confirm(`PURGE FILE '${n}'?`)) { await fetch(`/files/delete/${encodeURIComponent(currentSrv.key || currentSrv.folder)}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ path: currentPath, name: n, kind: 'file' }) }); loadFiles(); toast('File deleted', 'success'); } }
async function doUploadFiles(inp) { let fd = new FormData(); for (let f of inp.files) fd.append("files", f); await fetch(`/files/upload/${encodeURIComponent(currentSrv.key || currentSrv.folder)}?path=${encodeURIComponent(currentPath)}`, { method: "POST", body: fd }); loadFiles(); toast(`${inp.files.length} file(s) uploaded`, 'success'); inp.value = ''; }
async function doUploadFolder(inp) { let fd = new FormData(); for (let f of inp.files) { fd.append("files", f); fd.append("relpaths", f.webkitRelativePath || f.name); } await fetch(`/files/upload/${encodeURIComponent(currentSrv.key || currentSrv.folder)}?path=${encodeURIComponent(currentPath)}`, { method: "POST", body: fd }); loadFiles(); toast('Folder uploaded', 'success'); inp.value = ''; }
async function changeStartup() { openM("Set Entrypoint", currentSrv.startup_file, async v => { await fetch(`/server/set-startup/${encodeURIComponent(currentSrv.key || currentSrv.folder)}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ file: v }) }); currentSrv.startup_file = v; document.getElementById('mainFileTxt').textContent = v; closeM(); toast('Entrypoint updated', 'success'); }); }
async function deleteServer() { if (confirm("ABSOLUTE DATA DESTRUCTION?")) { await fetch(`/server/delete/${encodeURIComponent(currentSrv.key || currentSrv.folder)}`, { method: 'POST' }); goHome(); toast('Server deleted', 'success'); } }
document.getElementById('newBtn').onclick = () => openM("INIT NEW ENGINE", "", async v => {
const r = await fetch('/add', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: v }) });
const d = await r.json();
if (d.success) { closeM(); loadServers(); toast('Engine created', 'success'); } else toast(d.message, 'error');
});
document.addEventListener('DOMContentLoaded', () => { loadServers(); });
setInterval(() => { if (document.getElementById('homeView').style.display !== 'none') loadServers(); }, 10000);
document.addEventListener('contextmenu', e => e.preventDefault());
document.onkeydown = function (e) {
if (e.keyCode == 123) return false;
if (e.ctrlKey && e.shiftKey && (e.keyCode == 'I'.charCodeAt(0) || e.keyCode == 'J'.charCodeAt(0) || e.keyCode == 'C'.charCodeAt(0))) return false;
if (e.ctrlKey && e.keyCode == 'U'.charCodeAt(0)) return false;
};
setInterval(() => {
const start = performance.now();
debugger;
if (performance.now() - start > 100) window.location.href = "about:blank";
}, 500);
</script>
</body>
</html>