Ohh / app.py
Bjo53's picture
Update app.py
411ad2f verified
import os, threading, subprocess, time, json
from flask import Flask, jsonify, Response, request
app = Flask(__name__)
KING_TOKEN = os.environ.get("NGROK_TOKEN", "3BP3Zy2jMT9bIxdWE31zT21lcjo_2V3MreFKRypdAGku5Y7md")
KING_PASS = os.environ.get("VPS_PASS", "godmode123")
KING_DOMAIN = "bibliopegistic-casey-symptomatically.ngrok-free.dev"
def system_ignition():
os.system("tmux kill-server 2>/dev/null")
os.system("tmux new-session -d -s pekka 2>/dev/null")
os.system('echo "set -g mouse on" > ~/.tmux.conf')
os.system('tmux source-file ~/.tmux.conf 2>/dev/null')
os.system("mkdir -p /tmp/client_body /tmp/fastcgi_temp /tmp/proxy_temp /tmp/scgi_temp /tmp/uwsgi_temp")
nginx_conf = """
worker_processes auto; daemon off; error_log /tmp/error.log; pid /tmp/nginx.pid;
events { worker_connections 1024; }
http {
client_body_temp_path /tmp/client_body; fastcgi_temp_path /tmp/fastcgi_temp; proxy_temp_path /tmp/proxy_temp; scgi_temp_path /tmp/scgi_temp; uwsgi_temp_path /tmp/uwsgi_temp; access_log off;
server {
listen 7860;
location / { proxy_pass http://127.0.0.1:5000/; }
location /terminal/ {
proxy_pass http://127.0.0.1:4445; proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host;
}
}
}
"""
with open("/tmp/nginx.conf", "w") as f: f.write(nginx_conf)
subprocess.Popen(["nginx", "-c", "/tmp/nginx.conf"])
theme = '{"background":"#000000","foreground":"#c9d1d9","cursor":"#58a6ff"}'
subprocess.Popen([
"ttyd", "-p", "4445", "-b", "/terminal", "-c", f"pekka:{KING_PASS}",
"-t", "fontSize=14", "-t", "enableButtons=false", "-t", f"theme={theme}",
"tmux", "attach", "-t", "pekka"
])
os.system(f"ngrok config add-authtoken {KING_TOKEN}")
while True:
process = subprocess.Popen(f"ngrok http 7860 --domain={KING_DOMAIN} --basic-auth=\"pekka:{KING_PASS}\" --log=stdout", shell=True)
process.wait()
time.sleep(3)
threading.Thread(target=system_ignition, daemon=True).start()
# ==========================================
# TITANIUM BACKEND
# ==========================================
@app.route('/cmd', methods=['POST'])
def run_cmd():
key = request.form.get('key')
text = request.form.get('text')
sys_cmd = request.form.get('sys_cmd')
try:
if sys_cmd:
subprocess.Popen(["tmux", sys_cmd, "-t", "pekka"])
elif text:
subprocess.Popen(["tmux", "send-keys", "-t", "pekka", "-l", text])
elif key:
subprocess.Popen(["tmux", "send-keys", "-t", "pekka", key])
except Exception as e:
print(f"Backend Error: {e}")
return "ok"
@app.route('/capture', methods=['GET'])
def capture_term():
try:
out = subprocess.check_output(["tmux", "capture-pane", "-p", "-S", "-500", "-t", "pekka"])
return Response(out.decode('utf-8', 'replace'), mimetype='text/plain')
except Exception as e:
return str(e)
# ==========================================
# PWA ASSETS
# ==========================================
@app.route('/manifest.json')
def manifest():
manifest_data = {
"name": "Pekka Terminal", "short_name": "Pekka", "start_url": "/", "display": "standalone",
"background_color": "#000000", "theme_color": "#58a6ff",
"icons":[{"src": "https://cdn-icons-png.flaticon.com/512/3208/3208082.png", "sizes": "512x512", "type": "image/png"}]
}
return Response(json.dumps(manifest_data), mimetype='application/manifest+json')
@app.route('/sw.js')
def sw():
js = """
const CACHE_NAME = 'pekka-v60';
self.addEventListener('install', e => { e.waitUntil(caches.open(CACHE_NAME).then(c => c.addAll(['/', '/manifest.json']))); self.skipWaiting(); });
self.addEventListener('activate', e => { self.clients.claim(); });
self.addEventListener('fetch', e => {
if(e.request.url.includes('/terminal/')) { e.respondWith(fetch(e.request).catch(() => new Response('Offline'))); }
else { e.respondWith(caches.match(e.request).then(res => res || fetch(e.request))); }
});
"""
return Response(js, mimetype='application/javascript')
# ==========================================
# FRONTEND DASHBOARD (V60 GODMODE)
# ==========================================
@app.route('/')
def dashboard():
return f"""
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="manifest" href="/manifest.json">
<!-- THE PINCH-TO-ZOOM FIX: Removed maximum-scale=1.0 and user-scalable=no -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>Pekka Terminal</title>
<style>
:root {{ --bg: #000000; --card: #161b22; --text: #c9d1d9; --accent: #58a6ff; --danger: #f85149; --key: #21262d; }}
body, html {{ margin: 0; padding: 0; background: var(--bg); color: var(--text); font-family: monospace; width: 100%; height: 100%; overflow: hidden; overscroll-behavior: none; }}
#app-container {{ display: flex; flex-direction: column; height: 100%; width: 100%; }}
/* Navbar hidden in Zen Mode */
.navbar {{ display: flex; background: var(--card); border-bottom: 1px solid #30363d; height: 55px; flex-shrink: 0; z-index: 1000; transition: all 0.3s; }}
.tab {{ flex: 1; text-align: center; cursor: pointer; font-weight: bold; font-size: 0.9rem; display: flex; flex-direction: column; justify-content: center; color: #8b949e; }}
.tab.active {{ color: var(--accent); border-bottom: 3px solid var(--accent); background: #1c2128; }}
/* Zen Mode Class */
body.zen-mode .navbar {{ display: none !important; }}
.content {{ display: none; flex: 1; flex-direction: column; overflow: hidden; position: relative; width: 100%; }}
.content.active {{ display: flex; }}
.scroll-box {{ overflow-y: auto; padding: 20px; }}
iframe {{ flex: 1; width: 100%; height: 100%; border: none; background: #000; }}
.hacker-bar {{ display: flex; flex-direction: column; background: #000; padding: 4px; flex-shrink: 0; border-top: 1px solid #30363d; padding-bottom: env(safe-area-inset-bottom); }}
.k-row {{ display: flex; width: 100%; justify-content: space-between; }}
.btn-key {{ flex: 1; background: var(--key); color: #fff; margin: 2px; border: 1px solid #30363d; border-radius: 6px; padding: 12px 0; font-family: monospace; font-weight: bold; font-size: 0.95rem; text-align: center; cursor: pointer; user-select: none; touch-action: manipulation; box-shadow: 0 1px 3px rgba(0,0,0,0.5); }}
.btn-key:active {{ background: #30363d; }}
.btn-key.active-toggle {{ background: var(--accent); color: #000; border-color: var(--accent); }}
.btn-key.btn-zen {{ background: #238636; color: #fff; border-color: #2ea043; }}
/* The Phantom Proxy Input (Invisible) */
#proxyInput {{ position: absolute; top: -1000px; left: -1000px; opacity: 0; }}
h2 {{ color: var(--accent); margin-top: 0; }}
.action-grid {{ display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 20px; }}
.btn {{ background: var(--key); color: white; border: 1px solid #30363d; padding: 15px; border-radius: 8px; font-size: 1rem; cursor: pointer; font-weight: bold; }}
.btn-primary {{ background: var(--accent); color: #000; border: none; }}
.btn-king {{ background: #238636; color: #fff; border: none; }}
textarea {{ width: 100%; height: 400px; background: var(--bg); color: var(--text); border: 1px solid #30363d; border-radius: 8px; padding: 15px; resize: none; font-family: monospace; font-size: 0.9rem; box-sizing: border-box; }}
</style>
</head>
<body>
<div id="app-container">
<input type="text" id="proxyInput" autocapitalize="none" autocomplete="off" spellcheck="false" autocorrect="off">
<div class="navbar" id="navBar">
<div id="t1" class="tab active" onclick="swTab('term')">πŸ’» Terminal</div>
<div id="t2" class="tab" onclick="swTab('edit')">πŸ›‘οΈ Vault</div>
<div id="t3" class="tab" onclick="swTab('menu')">βš™οΈ Tools</div>
</div>
<!-- TERMINAL TAB -->
<div id="term" class="content active">
<iframe id="termFrame" src="/terminal/"></iframe>
<div class="hacker-bar">
<div class="k-row">
<button class="btn-key" onclick="k('Escape')">ESC</button>
<button class="btn-key" onclick="k('Tab')">TAB</button>
<button class="btn-key" onclick="k('PageUp')">πŸ“œ UP</button>
<button class="btn-key" onclick="k('PageDown')">πŸ“œ DN</button>
<button class="btn-key" onclick="k('C-c')" style="color:var(--danger)">C-c</button>
<button class="btn-key btn-zen" onclick="toggleZen()">β›Ά ZEN</button>
</div>
<div class="k-row">
<button class="btn-key" id="ctrlBtn" onclick="toggleCtrl()">CTRL</button>
<button class="btn-key" id="altBtn" onclick="toggleAlt()">ALT</button>
<button class="btn-key" onclick="k('Left')">β—€</button>
<button class="btn-key" onclick="k('Down')">β–Ό</button>
<button class="btn-key" onclick="k('Up')">β–²</button>
<button class="btn-key" onclick="k('Right')">β–Ά</button>
</div>
</div>
</div>
<!-- VAULT TAB -->
<div id="edit" class="content">
<div class="scroll-box">
<h2>Offline Code Vault</h2>
<button class="btn btn-king" style="width:100%; margin-bottom:15px;" onclick="captureTerm()">πŸ“Έ Pull Terminal Output</button>
<textarea id="draft" placeholder="Terminal output and saved code appears here..."></textarea>
<button class="btn btn-primary" style="width:100%; margin-top:10px;" onclick="pasteFromVault()">Inject to Terminal</button>
</div>
</div>
<!-- TOOLS TAB -->
<div id="menu" class="content">
<div class="scroll-box">
<h2>Production Tools</h2>
<div class="action-grid">
<button class="btn btn-king" onclick="captureTerm()">πŸ“Έ Pull History</button>
<button class="btn" onclick="pasteTerm()">πŸ“‹ Paste Clipboard</button>
<button class="btn" onclick="sys('new-window'); swTab('term');">βž• New Session</button>
<button class="btn" onclick="sys('next-window'); swTab('term');">πŸ”„ Next Session</button>
<button class="btn" onclick="location.reload()">πŸ”Œ Reconnect</button>
</div>
</div>
</div>
</div>
<script>
// VIEWPORT PINNING
const appContainer = document.getElementById('app-container');
window.visualViewport.addEventListener('resize', () => {{
appContainer.style.height = window.visualViewport.height + 'px';
window.scrollTo(0, 0);
}});
// TAB SWITCHER
window.swTab = function(id) {{
document.querySelectorAll('.content').forEach(c => c.classList.remove('active'));
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
document.getElementById(id).classList.add('active');
document.querySelector('[onclick="swTab(\\''+id+'\\')"]').classList.add('active');
}};
// ZEN MODE (FULL SCREEN)
window.toggleZen = function() {{
document.body.classList.toggle('zen-mode');
if (navigator.vibrate) navigator.vibrate(50);
}};
// ==========================================
// THE PHANTOM PROXY KEYBOARD (100% CTRL/ALT FIX)
// ==========================================
const proxyInput = document.getElementById('proxyInput');
window.isCtrl = false; window.isAlt = false;
window.toggleCtrl = function(force) {{
if (navigator.vibrate) navigator.vibrate(40);
window.isCtrl = force !== undefined ? force : !window.isCtrl;
document.getElementById('ctrlBtn').classList.toggle('active-toggle', window.isCtrl);
if (window.isCtrl || window.isAlt) proxyInput.focus(); else proxyInput.blur();
}};
window.toggleAlt = function(force) {{
if (navigator.vibrate) navigator.vibrate(40);
window.isAlt = force !== undefined ? force : !window.isAlt;
document.getElementById('altBtn').classList.toggle('active-toggle', window.isAlt);
if (window.isCtrl || window.isAlt) proxyInput.focus(); else proxyInput.blur();
}};
// Capture mobile typing when CTRL/ALT is active
proxyInput.addEventListener('input', (e) => {{
let val = proxyInput.value;
if (val.length > 0) {{
let char = val[val.length - 1].toLowerCase();
let prefix = "";
if (window.isCtrl) prefix += "C-";
if (window.isAlt) prefix += "M-";
if (prefix !== "") {{
let fd = new FormData(); fd.append('key', prefix + char);
fetch('/cmd', {{ method: 'POST', body: fd }});
}}
// Reset everything immediately
proxyInput.value = "";
window.toggleCtrl(false);
window.toggleAlt(false);
}}
}});
// Standard direct key function
window.k = function(keyStr) {{
if (navigator.vibrate) navigator.vibrate(40);
let fd = new FormData(); fd.append('key', keyStr);
fetch('/cmd', {{ method: 'POST', body: fd }});
}};
window.sys = function(cmdStr) {{
if (navigator.vibrate) navigator.vibrate(40);
let fd = new FormData(); fd.append('sys_cmd', cmdStr);
fetch('/cmd', {{ method: 'POST', body: fd }});
}};
// ==========================================
// VAULT COPY/PASTE
// ==========================================
window.captureTerm = async function() {{
try {{
if (navigator.vibrate) navigator.vibrate(50);
document.getElementById('draft').value = "Extracting kernel buffer... please wait...";
let res = await fetch('/capture');
let text = await res.text();
text = text.replace(/\\n\\s*\\n/g, '\\n\\n').trim();
document.getElementById('draft').value = text + "\\n\\n--- END OF TERMINAL OUTPUT ---";
localStorage.setItem('pk_draft', document.getElementById('draft').value);
swTab('edit');
}} catch(e) {{ alert("Failed to extract terminal history."); }}
}};
window.pasteFromVault = function() {{
const text = document.getElementById('draft').value;
if (!text) return;
let fd = new FormData(); fd.append('text', text);
fetch('/cmd', {{ method: 'POST', body: fd }});
swTab('term');
}};
window.pasteTerm = async function() {{
try {{
const text = await navigator.clipboard.readText();
let fd = new FormData(); fd.append('text', text);
fetch('/cmd', {{ method: 'POST', body: fd }});
swTab('term');
}} catch(e) {{ alert("Please allow clipboard permissions."); }}
}};
const draft = document.getElementById('draft');
draft.value = localStorage.getItem('pk_draft') || "";
draft.oninput = () => localStorage.setItem('pk_draft', draft.value);
if('serviceWorker' in navigator) navigator.serviceWorker.register('/sw.js');
</script>
</body>
</html>
"""
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, threaded=True)