nopoh22 commited on
Commit
1308519
·
verified ·
1 Parent(s): 0472279

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +32 -102
index.html CHANGED
@@ -13,29 +13,27 @@
13
  .container { max-width: 960px; margin: 30px auto; padding: 0 20px; }
14
  .card { background: #1e293b; border: 1px solid #334155; border-radius: 12px; padding: 24px; margin-bottom: 24px; }
15
  .card h2 { color: #38bdf8; margin-bottom: 18px; font-size: 1.05rem; display:flex; align-items:center; gap:8px; }
16
- .form-row { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
17
- input[type=text], input[type=password] {
 
 
 
 
18
  flex: 1; min-width: 200px; padding: 10px 14px;
19
  background: #0f172a; border: 1px solid #475569; border-radius: 8px;
20
  color: #e2e8f0; font-size: 0.95rem;
21
  }
22
  input:focus { outline: none; border-color: #38bdf8; }
23
  input::placeholder { color: #475569; }
24
- .hint { font-size: 0.8rem; color: #64748b; margin-bottom: 10px; }
25
- .hint span { color: #fbbf24; }
26
- .divider { display: flex; align-items: center; gap: 10px; margin: 10px 0; color: #475569; font-size: 0.82rem; }
27
- .divider::before, .divider::after { content: ''; flex: 1; height: 1px; background: #334155; }
28
  button {
29
  padding: 10px 22px; border: none; border-radius: 8px; cursor: pointer;
30
  font-size: 0.9rem; font-weight: 600; transition: all .15s;
31
  }
32
  button:hover { transform: translateY(-1px); opacity: .9; }
33
- button:active { transform: translateY(0); }
34
  .btn-primary { background: #2563eb; color: #fff; }
35
- .btn-danger { background: #dc2626; color: #fff; }
36
- .btn-success { background: #16a34a; color: #fff; }
37
  .btn-warning { background: #d97706; color: #fff; }
38
- .btn-info { background: #0891b2; color: #fff; }
39
  .btn-gray { background: #475569; color: #fff; }
40
  table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
41
  th { background: #0f172a; color: #94a3b8; padding: 10px 14px; text-align: right; font-weight: 600; }
@@ -44,14 +42,12 @@
44
  .badge { display:inline-block; padding:3px 10px; border-radius:20px; font-size:0.78rem; font-weight:600; }
45
  .badge-ok { background:#14532d; color:#86efac; }
46
  .badge-na { background:#334155; color:#94a3b8; }
47
- .badge-method { background:#1e3a5f; color:#7dd3fc; font-size:0.72rem; padding:2px 8px; border-radius:10px; }
48
  #log {
49
  background: #020617; border: 1px solid #1e3a5f; border-radius: 8px;
50
  padding: 14px; height: 260px; overflow-y: auto;
51
  font-family: 'Courier New', monospace; font-size: 0.8rem; color: #7dd3fc;
52
  direction: ltr; text-align: left;
53
  }
54
- #log .line { margin: 2px 0; }
55
  #log .line.err { color: #f87171; }
56
  #log .line.ok { color: #86efac; }
57
  .sch-badge { display:inline-flex; align-items:center; gap:6px; padding:6px 16px; border-radius:20px; font-size:0.85rem; font-weight:600; margin-left:12px; }
@@ -59,14 +55,10 @@
59
  .sch-off { background:#3f1818; color:#fca5a5; }
60
  .toast { position:fixed; bottom:24px; left:50%; transform:translateX(-50%);
61
  padding:12px 28px; border-radius:10px; font-size:0.92rem; font-weight:600;
62
- z-index:9999; display:none; animation: fadeup .3s ease; }
63
  .toast-ok { background:#14532d; color:#bbf7d0; border:1px solid #16a34a; }
64
  .toast-err { background:#7f1d1d; color:#fca5a5; border:1px solid #dc2626; }
65
- @keyframes fadeup { from { opacity:0; transform:translateX(-50%) translateY(10px); } to { opacity:1; transform:translateX(-50%) translateY(0); } }
66
- .spinner { display:inline-block; width:14px; height:14px; border:2px solid #fff; border-top-color:transparent; border-radius:50%; animation:spin .6s linear infinite; vertical-align:middle; margin-left:6px; }
67
- @keyframes spin { to { transform:rotate(360deg); } }
68
  .ctrl-row { display:flex; align-items:center; flex-wrap:wrap; gap:10px; margin-bottom:14px; }
69
- .actions-row { display:flex; gap:10px; flex-wrap:wrap; }
70
  .empty { color:#475569; padding:16px 0; text-align:center; }
71
  </style>
72
  </head>
@@ -74,51 +66,28 @@
74
 
75
  <header>
76
  <h1>🐍 PythonAnywhere Auto-Renewer</h1>
77
- <p>إدارة وتجديد حسابات PythonAnywhere تلقائياً كل 10 أيام</p>
78
  </header>
79
 
80
  <div class="container">
81
 
82
- <!-- الحسابات -->
83
  <div class="card">
84
- <h2>📋 الحسابات المضافة</h2>
85
- <div id="accounts-wrap"><div class="empty">جاري التحميل...</div></div>
86
- <button class="btn-gray" style="margin-top:14px" onclick="loadAccounts()">🔄 تحديث القائمة</button>
87
- </div>
88
-
89
- <!-- إضافة حساب -->
90
- <div class="card">
91
- <h2>➕ إضافة حساب جديد</h2>
92
-
93
- <div class="hint">
94
- يمكنك استخدام <span>كلم�� المرور</span> (طريقة التمديد المباشر /extend) أو <span>API Token</span> (إعادة تحميل الويب آب).
95
- إذا أدخلت الاثنين معاً، ستُستخدم كلمة المرور أولاً.
96
- </div>
97
-
98
- <div class="form-row">
99
- <input type="text" id="new-user" placeholder="اسم المستخدم — مثال: mahmoud123">
100
- </div>
101
-
102
- <div class="divider">كلمة المرور (موصى بها)</div>
103
- <div class="form-row">
104
- <input type="password" id="new-pass" placeholder="كلمة مرور الحساب">
105
  </div>
106
-
107
- <div class="divider">أو API Token</div>
108
- <div class="form-row">
109
- <input type="password" id="new-token" placeholder="API Token — من /account/#api_token">
110
- </div>
111
-
112
- <button class="btn-primary" id="add-btn" onclick="addAccount()">➕ إضافة الحساب</button>
113
  </div>
114
 
115
- <!-- حذف حساب -->
116
  <div class="card">
117
- <h2>🗑️ حذف حساب</h2>
118
- <div class="form-row">
119
- <input type="text" id="del-user" placeholder="اسم المستخدم المراد حذفه">
120
- </div>
121
- <button class="btn-danger" onclick="deleteAccount()">🗑️ حذف</button>
122
  </div>
123
 
124
  <!-- التحكم -->
@@ -128,7 +97,7 @@
128
  <span id="sch-badge" class="sch-badge sch-off">⏹ المجدول متوقف</span>
129
  <button class="btn-success" id="sch-btn" onclick="toggleScheduler()">▶️ تشغيل المجدول التلقائي</button>
130
  </div>
131
- <div class="actions-row" style="margin-bottom:14px">
132
  <button class="btn-primary" onclick="renewAll()">🚀 تجديد كل الحسابات الآن</button>
133
  </div>
134
  <div class="form-row">
@@ -149,8 +118,6 @@
149
  <div id="toast" class="toast"></div>
150
 
151
  <script>
152
- let schedulerOn = false;
153
-
154
  function toast(msg, ok=true) {
155
  const el = document.getElementById('toast');
156
  el.textContent = msg;
@@ -175,11 +142,11 @@ async function loadAccounts() {
175
  const data = await api('/api/accounts');
176
  const el = document.getElementById('accounts-wrap');
177
  if (!Array.isArray(data) || !data.length) {
178
- el.innerHTML = '<div class="empty">لا توجد حسابات مضافة بعد.</div>';
179
  return;
180
  }
181
  let html = `<table>
182
- <tr><th>المستخدم</th><th>الخادم</th><th>طريقة الدخول</th><th>آخر تجديد</th><th>التجديد القادم</th><th>الحالة</th></tr>`;
183
  for (const a of data) {
184
  const last = a.last_renewed ? a.last_renewed.slice(0,16).replace('T',' ') : '—';
185
  const nxt = a.next_renewal ? a.next_renewal.slice(0,16).replace('T',' ') : '—';
@@ -187,15 +154,9 @@ async function loadAccounts() {
187
  const badge = a.last_renewed
188
  ? '<span class="badge badge-ok">✅ مجدَّد</span>'
189
  : '<span class="badge badge-na">⏳ لم يُجدَّد</span>';
190
- const method = a.auth_method === 'password'
191
- ? '<span class="badge-method">🔑 Password</span>'
192
- : a.auth_method === 'token'
193
- ? '<span class="badge-method">🔐 Token</span>'
194
- : '—';
195
  html += `<tr>
196
  <td><b>${a.username}</b></td>
197
  <td style="font-size:.8rem;color:#94a3b8">${a.host||'—'}</td>
198
- <td>${method}</td>
199
  <td style="font-size:.82rem">${last}</td>
200
  <td style="font-size:.82rem">${nxt}</td>
201
  <td>${badge}<br><span style="font-size:.78rem;color:#64748b">${res}</span></td>
@@ -204,36 +165,6 @@ async function loadAccounts() {
204
  el.innerHTML = html + '</table>';
205
  }
206
 
207
- async function addAccount() {
208
- const user = document.getElementById('new-user').value.trim();
209
- const pass = document.getElementById('new-pass').value.trim();
210
- const token = document.getElementById('new-token').value.trim();
211
- if (!user) { toast('❌ أدخل اسم المستخدم', false); return; }
212
- if (!pass && !token) { toast('❌ أدخل كلمة المرور أو API Token', false); return; }
213
- const btn = document.getElementById('add-btn');
214
- btn.innerHTML = 'جاري التحقق... <span class="spinner"></span>';
215
- btn.disabled = true;
216
- const r = await api('/api/accounts', 'POST', {username: user, token, password: pass});
217
- btn.innerHTML = '➕ إضافة الحساب';
218
- btn.disabled = false;
219
- toast(r.message, r.ok);
220
- if (r.ok) {
221
- document.getElementById('new-user').value = '';
222
- document.getElementById('new-pass').value = '';
223
- document.getElementById('new-token').value = '';
224
- loadAccounts();
225
- }
226
- }
227
-
228
- async function deleteAccount() {
229
- const user = document.getElementById('del-user').value.trim();
230
- if (!user) { toast('❌ أدخل اسم المستخدم', false); return; }
231
- if (!confirm(`هل أنت متأكد من حذف ${user}؟`)) return;
232
- const r = await api('/api/accounts/'+user,'DELETE');
233
- toast(r.message, r.ok);
234
- if (r.ok) { document.getElementById('del-user').value=''; loadAccounts(); }
235
- }
236
-
237
  async function renewAll() {
238
  toast('🚀 جاري إرسال طلب التجديد...');
239
  await api('/api/renew','POST');
@@ -250,17 +181,16 @@ async function renewOne() {
250
 
251
  async function toggleScheduler() {
252
  const r = await api('/api/scheduler','POST');
253
- schedulerOn = r.running;
254
- document.getElementById('sch-badge').className = 'sch-badge ' + (schedulerOn?'sch-on':'sch-off');
255
- document.getElementById('sch-badge').textContent = schedulerOn ? '🟢 المجدول شغّال' : '⏹ المجدول متوقف';
256
- document.getElementById('sch-btn').textContent = schedulerOn ? '⏹ إيقاف المجدول' : '▶️ تشغيل المجدول التلقائي';
257
- toast(schedulerOn ? '✅ المجدول شغّال الآن' : '⏹ تم إيقاف المجدول');
258
- loadLog();
259
  }
260
 
261
  function colorLine(l) {
262
- if (l.includes('❌') || l.includes('فشل') || l.includes('Error')) return 'err';
263
- if (l.includes('✅') || l.includes('تمت') || l.includes('تم تجديد')) return 'ok';
264
  return '';
265
  }
266
 
 
13
  .container { max-width: 960px; margin: 30px auto; padding: 0 20px; }
14
  .card { background: #1e293b; border: 1px solid #334155; border-radius: 12px; padding: 24px; margin-bottom: 24px; }
15
  .card h2 { color: #38bdf8; margin-bottom: 18px; font-size: 1.05rem; display:flex; align-items:center; gap:8px; }
16
+ .info-box {
17
+ background: #0f172a; border: 1px solid #1e3a5f; border-radius: 8px;
18
+ padding: 14px 18px; font-size: 0.88rem; color: #7dd3fc; line-height: 1.8;
19
+ }
20
+ .info-box code { background: #1e293b; padding: 2px 8px; border-radius: 4px; color: #fbbf24; font-size: 0.85rem; }
21
+ input[type=text] {
22
  flex: 1; min-width: 200px; padding: 10px 14px;
23
  background: #0f172a; border: 1px solid #475569; border-radius: 8px;
24
  color: #e2e8f0; font-size: 0.95rem;
25
  }
26
  input:focus { outline: none; border-color: #38bdf8; }
27
  input::placeholder { color: #475569; }
28
+ .form-row { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
 
 
 
29
  button {
30
  padding: 10px 22px; border: none; border-radius: 8px; cursor: pointer;
31
  font-size: 0.9rem; font-weight: 600; transition: all .15s;
32
  }
33
  button:hover { transform: translateY(-1px); opacity: .9; }
 
34
  .btn-primary { background: #2563eb; color: #fff; }
 
 
35
  .btn-warning { background: #d97706; color: #fff; }
36
+ .btn-success { background: #16a34a; color: #fff; }
37
  .btn-gray { background: #475569; color: #fff; }
38
  table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
39
  th { background: #0f172a; color: #94a3b8; padding: 10px 14px; text-align: right; font-weight: 600; }
 
42
  .badge { display:inline-block; padding:3px 10px; border-radius:20px; font-size:0.78rem; font-weight:600; }
43
  .badge-ok { background:#14532d; color:#86efac; }
44
  .badge-na { background:#334155; color:#94a3b8; }
 
45
  #log {
46
  background: #020617; border: 1px solid #1e3a5f; border-radius: 8px;
47
  padding: 14px; height: 260px; overflow-y: auto;
48
  font-family: 'Courier New', monospace; font-size: 0.8rem; color: #7dd3fc;
49
  direction: ltr; text-align: left;
50
  }
 
51
  #log .line.err { color: #f87171; }
52
  #log .line.ok { color: #86efac; }
53
  .sch-badge { display:inline-flex; align-items:center; gap:6px; padding:6px 16px; border-radius:20px; font-size:0.85rem; font-weight:600; margin-left:12px; }
 
55
  .sch-off { background:#3f1818; color:#fca5a5; }
56
  .toast { position:fixed; bottom:24px; left:50%; transform:translateX(-50%);
57
  padding:12px 28px; border-radius:10px; font-size:0.92rem; font-weight:600;
58
+ z-index:9999; display:none; }
59
  .toast-ok { background:#14532d; color:#bbf7d0; border:1px solid #16a34a; }
60
  .toast-err { background:#7f1d1d; color:#fca5a5; border:1px solid #dc2626; }
 
 
 
61
  .ctrl-row { display:flex; align-items:center; flex-wrap:wrap; gap:10px; margin-bottom:14px; }
 
62
  .empty { color:#475569; padding:16px 0; text-align:center; }
63
  </style>
64
  </head>
 
66
 
67
  <header>
68
  <h1>🐍 PythonAnywhere Auto-Renewer</h1>
69
+ <p>تجديد حسابات PythonAnywhere تلقائياً كل 10 أيام</p>
70
  </header>
71
 
72
  <div class="container">
73
 
74
+ <!-- تعليمات الإعداد -->
75
  <div class="card">
76
+ <h2>⚙️ طريقة إضافة الحسابات</h2>
77
+ <div class="info-box">
78
+ الحسابات تُضاف من <b>Hugging Face → Settings → Variables and secrets</b><br><br>
79
+ أضف secret باسم: <code>ACCOUNTS</code><br>
80
+ القيمة: <code>username1:password1,username2:password2</code><br><br>
81
+ مثال: <code>nopoh55678:mypass123,mahmoud:pass456</code><br><br>
82
+ أو حسابات منفردة: <code>ACCOUNT_1</code> = <code>user1:pass1</code> &nbsp;|&nbsp; <code>ACCOUNT_2</code> = <code>user2:pass2</code>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  </div>
 
 
 
 
 
 
 
84
  </div>
85
 
86
+ <!-- الحسابات -->
87
  <div class="card">
88
+ <h2>📋 الحسابات المحملة من Secrets</h2>
89
+ <div id="accounts-wrap"><div class="empty">جاري التحميل...</div></div>
90
+ <button class="btn-gray" style="margin-top:14px" onclick="loadAccounts()">🔄 تحديث</button>
 
 
91
  </div>
92
 
93
  <!-- التحكم -->
 
97
  <span id="sch-badge" class="sch-badge sch-off">⏹ المجدول متوقف</span>
98
  <button class="btn-success" id="sch-btn" onclick="toggleScheduler()">▶️ تشغيل المجدول التلقائي</button>
99
  </div>
100
+ <div style="margin-bottom:14px">
101
  <button class="btn-primary" onclick="renewAll()">🚀 تجديد كل الحسابات الآن</button>
102
  </div>
103
  <div class="form-row">
 
118
  <div id="toast" class="toast"></div>
119
 
120
  <script>
 
 
121
  function toast(msg, ok=true) {
122
  const el = document.getElementById('toast');
123
  el.textContent = msg;
 
142
  const data = await api('/api/accounts');
143
  const el = document.getElementById('accounts-wrap');
144
  if (!Array.isArray(data) || !data.length) {
145
+ el.innerHTML = '<div class="empty">⚠️ لا توجد حسابات — أضف ACCOUNTS في Secrets وأعد تشغيل الـ Space.</div>';
146
  return;
147
  }
148
  let html = `<table>
149
+ <tr><th>المستخدم</th><th>الخادم</th><th>آخر تجديد</th><th>التجديد القادم</th><th>الحالة</th></tr>`;
150
  for (const a of data) {
151
  const last = a.last_renewed ? a.last_renewed.slice(0,16).replace('T',' ') : '—';
152
  const nxt = a.next_renewal ? a.next_renewal.slice(0,16).replace('T',' ') : '—';
 
154
  const badge = a.last_renewed
155
  ? '<span class="badge badge-ok">✅ مجدَّد</span>'
156
  : '<span class="badge badge-na">⏳ لم يُجدَّد</span>';
 
 
 
 
 
157
  html += `<tr>
158
  <td><b>${a.username}</b></td>
159
  <td style="font-size:.8rem;color:#94a3b8">${a.host||'—'}</td>
 
160
  <td style="font-size:.82rem">${last}</td>
161
  <td style="font-size:.82rem">${nxt}</td>
162
  <td>${badge}<br><span style="font-size:.78rem;color:#64748b">${res}</span></td>
 
165
  el.innerHTML = html + '</table>';
166
  }
167
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  async function renewAll() {
169
  toast('🚀 جاري إرسال طلب التجديد...');
170
  await api('/api/renew','POST');
 
181
 
182
  async function toggleScheduler() {
183
  const r = await api('/api/scheduler','POST');
184
+ const on = r.running;
185
+ document.getElementById('sch-badge').className = 'sch-badge ' + (on?'sch-on':'sch-off');
186
+ document.getElementById('sch-badge').textContent = on ? '🟢 المجدول شغّال' : '⏹ المجدول متوقف';
187
+ document.getElementById('sch-btn').textContent = on ? '⏹ إيقاف المجدول' : '▶️ تشغيل المجدول التلقائي';
188
+ toast(on ? '✅ المجدول شغّال' : '⏹ تم إيقاف المجدول');
 
189
  }
190
 
191
  function colorLine(l) {
192
+ if (l.includes('❌') || l.includes('فشل')) return 'err';
193
+ if (l.includes('✅') || l.includes('تم')) return 'ok';
194
  return '';
195
  }
196