| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| <title>TG Store · Add Session</title> |
| <style> |
| *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } |
| :root { |
| --bg: #0d1117; --surface: #161b22; --border: #30363d; |
| --accent: #2f81f7; --accent2: #388bfd; --text: #e6edf3; |
| --muted: #8b949e; --success: #3fb950; --error: #f85149; |
| } |
| body { |
| background: var(--bg); color: var(--text); |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; |
| min-height: 100vh; display: flex; align-items: center; |
| justify-content: center; padding: 24px; |
| } |
| .card { |
| background: var(--surface); border: 1px solid var(--border); |
| border-radius: 10px; padding: 36px 40px; |
| width: 100%; max-width: 460px; box-shadow: 0 8px 32px #0009; |
| } |
| .logo { display: flex; align-items: center; gap: 12px; margin-bottom: 28px; } |
| .logo svg { color: var(--accent); flex-shrink: 0; } |
| .logo h1 { font-size: 1.25rem; font-weight: 700; } |
| .logo span { color: var(--muted); font-weight: 400; } |
| .steps { display: flex; gap: 8px; margin-bottom: 28px; } |
| .step { height: 4px; flex: 1; background: var(--border); border-radius: 2px; transition: background .3s; } |
| .step.on { background: var(--accent); } |
| label { |
| display: block; font-size: .78rem; color: var(--muted); |
| text-transform: uppercase; letter-spacing: .06em; |
| margin-bottom: 6px; margin-top: 18px; |
| } |
| input { |
| width: 100%; background: var(--bg); border: 1px solid var(--border); |
| border-radius: 6px; color: var(--text); padding: 10px 14px; |
| font-size: .95rem; outline: none; transition: border-color .2s; |
| } |
| input:focus { border-color: var(--accent); } |
| button { |
| margin-top: 22px; width: 100%; background: var(--accent); |
| color: #fff; border: none; border-radius: 6px; padding: 11px; |
| font-size: .98rem; font-weight: 600; cursor: pointer; |
| transition: background .2s, opacity .2s; |
| } |
| button:hover { background: var(--accent2); } |
| button:disabled { opacity: .45; cursor: not-allowed; } |
| .msg { |
| margin-top: 14px; padding: 11px 14px; border-radius: 6px; |
| font-size: .88rem; display: none; |
| } |
| .msg.ok { background:#1a3a2a; border:1px solid #2d6040; color:var(--success); display:block; } |
| .msg.err { background:#3b1a1a; border:1px solid #7a2828; color:var(--error); display:block; } |
| .session-out { |
| margin-top: 12px; background: var(--bg); border: 1px solid var(--border); |
| border-radius: 6px; padding: 10px 14px; font-size: .72rem; |
| font-family: monospace; color: var(--muted); word-break: break-all; |
| } |
| .hidden { display: none !important; } |
| footer { margin-top: 28px; font-size: .76rem; color: var(--muted); text-align: center; } |
| </style> |
| </head> |
| <body> |
| <div class="card"> |
|
|
| <div class="logo"> |
| <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| <polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/> |
| </svg> |
| <h1>TG Store <span>· Add Session</span></h1> |
| </div> |
|
|
| <div class="steps"> |
| <div class="step on" id="s1"></div> |
| <div class="step" id="s2"></div> |
| <div class="step" id="s3"></div> |
| </div> |
|
|
| |
| <div id="p1"> |
| <label>API ID</label> |
| <input id="apiId" type="number" placeholder="12345678" /> |
| <label>API Hash</label> |
| <input id="apiHash" type="text" placeholder="0123456789abcdef..." /> |
| <label>Phone Number (with country code)</label> |
| <input id="phone" type="tel" placeholder="+1 555 000 0000" /> |
| <button id="btnCode">Send OTP →</button> |
| <div class="msg" id="m1"></div> |
| </div> |
|
|
| |
| <div id="p2" class="hidden"> |
| <p style="color:var(--muted);font-size:.88rem;line-height:1.5"> |
| Check your Telegram app or SMS for the one-time code. |
| </p> |
| <label>OTP Code</label> |
| <input id="otp" type="text" placeholder="12345" maxlength="12" /> |
| <div id="twoFAWrap" class="hidden"> |
| <label>Two-Factor Password</label> |
| <input id="twoFA" type="password" placeholder="Cloud password" /> |
| </div> |
| <button id="btnVerify">Verify & Save →</button> |
| <div class="msg" id="m2"></div> |
| </div> |
|
|
| |
| <div id="p3" class="hidden"> |
| <p style="color:var(--success);font-weight:700;font-size:1.05rem">✓ Session saved!</p> |
| <p style="color:var(--muted);font-size:.88rem;margin-top:8px;line-height:1.5"> |
| This account is now in the upload pool and will be used for file uploads. |
| </p> |
| <label style="margin-top:20px">String Session (store safely)</label> |
| <div class="session-out" id="sessionOut"></div> |
| <button onclick="location.reload()" |
| style="margin-top:18px;background:#21262d;border:1px solid var(--border)"> |
| + Add Another Account |
| </button> |
| </div> |
|
|
| <footer>Powered by GramJS · Node.js · MongoDB</footer> |
| </div> |
|
|
| <script> |
| const $ = id => document.getElementById(id); |
| |
| function setStep(n) { |
| ['p1','p2','p3'].forEach((id,i) => $(id).classList.toggle('hidden', i+1 !== n)); |
| ['s1','s2','s3'].forEach((id,i) => $(id).classList.toggle('on', i < n)); |
| } |
| |
| function msg(id, text, type) { |
| const el = $(id); |
| el.textContent = text; |
| el.className = 'msg ' + (type === 'ok' ? 'ok' : 'err'); |
| } |
| |
| let pendingPhone = ''; |
| |
| $('btnCode').addEventListener('click', async () => { |
| const btn = $('btnCode'); |
| btn.disabled = true; btn.textContent = 'Sending…'; |
| msg('m1', '', ''); |
| |
| const body = { |
| apiId: $('apiId').value.trim(), |
| apiHash: $('apiHash').value.trim(), |
| phone: $('phone').value.trim(), |
| }; |
| if (!body.apiId || !body.apiHash || !body.phone) { |
| msg('m1', 'All three fields are required.', 'err'); |
| btn.disabled = false; btn.textContent = 'Send OTP →'; |
| return; |
| } |
| |
| try { |
| const r = await fetch('/strings/send-code', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify(body), |
| }); |
| const d = await r.json(); |
| if (!r.ok) throw new Error(d.error); |
| pendingPhone = body.phone; |
| setStep(2); |
| } catch(e) { |
| msg('m1', e.message, 'err'); |
| btn.disabled = false; btn.textContent = 'Send OTP →'; |
| } |
| }); |
| |
| $('btnVerify').addEventListener('click', async () => { |
| const btn = $('btnVerify'); |
| btn.disabled = true; btn.textContent = 'Verifying…'; |
| msg('m2', '', ''); |
| |
| const body = { phone: pendingPhone, code: $('otp').value.trim() }; |
| const pw = $('twoFA').value.trim(); |
| if (pw) body.password = pw; |
| |
| try { |
| const r = await fetch('/strings/verify', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify(body), |
| }); |
| const d = await r.json(); |
| if (d.twoFA) { |
| $('twoFAWrap').classList.remove('hidden'); |
| msg('m2', '2FA required — enter your cloud password above and try again.', 'err'); |
| btn.disabled = false; btn.textContent = 'Verify & Save →'; |
| return; |
| } |
| if (!r.ok) throw new Error(d.error); |
| $('sessionOut').textContent = d.session || ''; |
| setStep(3); |
| } catch(e) { |
| msg('m2', e.message, 'err'); |
| btn.disabled = false; btn.textContent = 'Verify & Save →'; |
| } |
| }); |
| </script> |
| </body> |
| </html> |
|
|