Browse Source

what is this guy thinking

Matthew Trejo 3 days ago
parent
commit
4099dde899
8 changed files with 293 additions and 3 deletions
  1. BIN
      __pycache__/routes.cpython-313.pyc
  2. 5 1
      app.py
  3. 5 0
      extensions.py
  4. 4 0
      requirements.txt
  5. 111 1
      routes.py
  6. 132 0
      templates/admin.html
  7. 10 1
      templates/base.html
  8. 26 0
      templates/login.html

BIN
__pycache__/routes.cpython-313.pyc


+ 5 - 1
app.py

@@ -1,6 +1,7 @@
 import os
 from flask import Flask
 from models import db
+from extensions import socketio, login_manager
 
 def create_app():
     app = Flask(__name__)
@@ -12,6 +13,9 @@ def create_app():
     app.config['SECRET_KEY'] = 'dev-secret-key-12345'
 
     db.init_app(app)
+    socketio.init_app(app)
+    login_manager.init_app(app)
+    login_manager.login_view = 'main.login'
 
     with app.app_context():
         from routes import main
@@ -22,4 +26,4 @@ def create_app():
 
 if __name__ == '__main__':
     app = create_app()
-    app.run(host='0.0.0.0', debug=True)
+    socketio.run(app, host='0.0.0.0', debug=True, allow_unsafe_werkzeug=True)

+ 5 - 0
extensions.py

@@ -0,0 +1,5 @@
+from flask_socketio import SocketIO
+from flask_login import LoginManager
+
+socketio = SocketIO()
+login_manager = LoginManager()

+ 4 - 0
requirements.txt

@@ -1,6 +1,10 @@
 Flask
 Flask-SQLAlchemy
 psycopg2-binary
+flask-login
+flask-socketio
+paramiko
+eventlet
 
 
 

+ 111 - 1
routes.py

@@ -1,9 +1,119 @@
-from flask import Blueprint, render_template, request, redirect, url_for, flash
+from flask import Blueprint, render_template, request, redirect, url_for, flash, session
 from models import db, CatalogoPaneles, DatosSolaresCiudad, ProyectosUsuario, Casa, Configuracion
 from datetime import datetime
+from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
+from extensions import socketio, login_manager
+import paramiko
+import threading
+import time
+import os
 
 main = Blueprint('main', __name__)
 
+# --- Auth Setup ---
+class User(UserMixin):
+    def __init__(self, id):
+        self.id = id
+
+@login_manager.user_loader
+def load_user(user_id):
+    if user_id == '1':
+        return User(id='1')
+    return None
+
+@main.route('/login', methods=['GET', 'POST'])
+def login():
+    if request.method == 'POST':
+        username = request.form.get('username')
+        password = request.form.get('password')
+        if username == 'admin' and password == 'solaradmin':
+            user = User(id='1')
+            login_user(user)
+            return redirect(url_for('main.admin'))
+        else:
+            flash('Credenciales inválidas', 'danger')
+    return render_template('login.html')
+
+@main.route('/logout')
+@login_required
+def logout():
+    logout_user()
+    return redirect(url_for('main.index'))
+
+@main.route('/admin')
+@login_required
+def admin():
+    return render_template('admin.html')
+
+# --- SSH Logic ---
+ssh_sessions = {}
+
+@socketio.on('connect', namespace='/ssh')
+def connect_ssh():
+    if not current_user.is_authenticated:
+        return False
+    # Start a new SSH session
+    # WARNING: Hardcoded credentials for demo purposes. EXTREMELY INSECURE.
+    # In a real app, use keys or prompt for credentials.
+    # Using the server's own credentials or a specific user.
+    # For this demo, we'll try to connect to localhost if possible, or just echo.
+    # But the user asked for "SSH console".
+    # Let's assume we want to connect to the machine running this app.
+    # We need credentials. The user asked for "dialogo que indique credenciales".
+    # So we will expect the user to provide them via the UI, but for the initial connection
+    # we might need to wait for a 'start_ssh' event with credentials.
+    pass
+
+@socketio.on('start_ssh', namespace='/ssh')
+def start_ssh(data):
+    if not current_user.is_authenticated:
+        return
+    
+    host = data.get('host', 'localhost')
+    port = int(data.get('port', 22))
+    username = data.get('username')
+    password = data.get('password')
+    
+    sid = request.sid
+    
+    try:
+        client = paramiko.SSHClient()
+        client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
+        client.connect(host, port=port, username=username, password=password)
+        
+        channel = client.invoke_shell()
+        ssh_sessions[sid] = {'client': client, 'channel': channel}
+        
+        def listen_to_ssh():
+            while True:
+                if channel.recv_ready():
+                    data = channel.recv(1024).decode('utf-8')
+                    socketio.emit('ssh_output', {'data': data}, room=sid, namespace='/ssh')
+                socketio.sleep(0.1)
+                if not channel.active:
+                    break
+                    
+        socketio.start_background_task(listen_to_ssh)
+        
+        socketio.emit('ssh_status', {'status': 'connected'}, room=sid, namespace='/ssh')
+        
+    except Exception as e:
+        socketio.emit('ssh_status', {'status': 'error', 'message': str(e)}, room=sid, namespace='/ssh')
+
+@socketio.on('ssh_input', namespace='/ssh')
+def ssh_input(data):
+    sid = request.sid
+    if sid in ssh_sessions:
+        channel = ssh_sessions[sid]['channel']
+        channel.send(data['data'])
+
+@socketio.on('disconnect', namespace='/ssh')
+def disconnect_ssh():
+    sid = request.sid
+    if sid in ssh_sessions:
+        ssh_sessions[sid]['client'].close()
+        del ssh_sessions[sid]
+
 @main.route('/')
 def index():
     return render_template('index.html')

+ 132 - 0
templates/admin.html

@@ -0,0 +1,132 @@
+{% extends 'base.html' %}
+
+{% block head %}
+<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css" />
+<script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.js"></script>
+<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.js"></script>
+<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
+{% endblock %}
+
+{% block content %}
+<div class="container-fluid mt-4">
+    <div class="row">
+        <div class="col-md-8">
+            <div class="card">
+                <div class="card-header bg-dark text-white">
+                    SSH Console
+                </div>
+                <div class="card-body p-0">
+                    <div id="terminal" style="height: 500px;"></div>
+                </div>
+            </div>
+        </div>
+        <div class="col-md-4">
+            <div class="card mb-3">
+                <div class="card-header bg-info text-white">
+                    Conexión SSH
+                </div>
+                <div class="card-body">
+                    <form id="ssh-form">
+                        <div class="mb-2">
+                            <label>Host</label>
+                            <input type="text" class="form-control" id="host" value="localhost">
+                        </div>
+                        <div class="mb-2">
+                            <label>Port</label>
+                            <input type="number" class="form-control" id="port" value="22">
+                        </div>
+                        <div class="mb-2">
+                            <label>Username</label>
+                            <input type="text" class="form-control" id="username" placeholder="root">
+                        </div>
+                        <div class="mb-2">
+                            <label>Password</label>
+                            <input type="password" class="form-control" id="password">
+                        </div>
+                        <button type="submit" class="btn btn-primary w-100">Conectar</button>
+                    </form>
+                    <div id="status" class="mt-2"></div>
+                </div>
+            </div>
+            
+            <div class="card">
+                <div class="card-header bg-warning">
+                    Credenciales
+                </div>
+                <div class="card-body">
+                    <h2>IP: 167.99.161.63</h2>
+                    <h6>Credenciales SSH</h6>
+                    <p><strong>User:</strong> root<br>
+                    <strong>Pass:</strong>solarEn2026EsTodo</p>
+                    
+                    <hr>
+                    <h6>Credenciales PostgreSQL</h6>
+                    <p><strong>User:</strong> postgres<br>
+                    <strong>Pass:</strong>solarEn2026EsTodo</p>
+
+                    <hr>
+                    <h6>Comandos PostgreSQL</h6>
+                    <ul class="list-unstyled">
+                        <li><code>psql -U postgres -h localhost -d solarcalc</code> - Conectar a DB</li>
+                        <li><code>\dt</code> - Listar tablas (dentro de psql)</li>
+                        <li><code>select * from casas;</code> - Ver casas</li>
+                        <li><code>systemctl status postgresql</code> - Estado del servicio</li>
+                    </ul>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<script>
+    const socket = io('/ssh');
+    const term = new Terminal({
+        cursorBlink: true,
+        theme: {
+            background: '#1e1e1e'
+        }
+    });
+    const fitAddon = new FitAddon.FitAddon();
+    term.loadAddon(fitAddon);
+    term.open(document.getElementById('terminal'));
+    fitAddon.fit();
+    
+    window.addEventListener('resize', () => fitAddon.fit());
+
+    term.onData(data => {
+        socket.emit('ssh_input', {data: data});
+    });
+
+    socket.on('ssh_output', function(msg) {
+        term.write(msg.data);
+    });
+    
+    socket.on('ssh_status', function(msg) {
+        const statusDiv = document.getElementById('status');
+        if (msg.status === 'connected') {
+            statusDiv.innerHTML = '<span class="text-success">Conectado</span>';
+            term.focus();
+        } else {
+            statusDiv.innerHTML = '<span class="text-danger">Error: ' + msg.message + '</span>';
+        }
+    });
+
+    document.getElementById('ssh-form').addEventListener('submit', function(e) {
+        e.preventDefault();
+        const host = document.getElementById('host').value;
+        const port = document.getElementById('port').value;
+        const username = document.getElementById('username').value;
+        const password = document.getElementById('password').value;
+        
+        document.getElementById('status').innerHTML = '<span class="text-warning">Conectando...</span>';
+        term.clear();
+        
+        socket.emit('start_ssh', {
+            host: host,
+            port: port,
+            username: username,
+            password: password
+        });
+    });
+</script>
+{% endblock %}

+ 10 - 1
templates/base.html

@@ -5,6 +5,7 @@
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Calculadora Solar</title>
     <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
+    {% block head %}{% endblock %}
     <script>
       // Inicialización del tema
       const getStoredTheme = () => localStorage.getItem('theme');
@@ -19,7 +20,7 @@
 <body>
     <nav class="navbar navbar-expand-lg navbar-dark bg-primary mb-4">
         <div class="container">
-            <a class="navbar-brand" href="{{ url_for('main.index') }}">Calculadora de Viabilidad Solar</a>
+            <a class="navbar-brand" href="{{ url_for('main.index') }}">Calculadora de Viabilidad para Sistemas Fotovoltaicos Residenciales</a>
             <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
                 <span class="navbar-toggler-icon"></span>
             </button>
@@ -37,6 +38,14 @@
                     <li class="nav-item">
                         <a class="nav-link" href="{{ url_for('main.configuracion') }}">Configuración</a>
                     </li>
+                    {% if current_user.is_authenticated %}
+                    <li class="nav-item">
+                        <a class="nav-link" href="{{ url_for('main.admin') }}">Admin</a>
+                    </li>
+                    <li class="nav-item">
+                        <a class="nav-link" href="{{ url_for('main.logout') }}">Salir</a>
+                    </li>
+                    {% endif %}
                     <li class="nav-item">
                         <button class="btn nav-link" id="theme-toggle" title="Cambiar tema">
                             🌓

+ 26 - 0
templates/login.html

@@ -0,0 +1,26 @@
+{% extends 'base.html' %}
+
+{% block content %}
+<div class="row justify-content-center">
+    <div class="col-md-6">
+        <div class="card mt-5">
+            <div class="card-header">
+                <h4>Iniciar Sesión</h4>
+            </div>
+            <div class="card-body">
+                <form method="POST">
+                    <div class="mb-3">
+                        <label for="username" class="form-label">Usuario</label>
+                        <input type="text" class="form-control" id="username" name="username" required>
+                    </div>
+                    <div class="mb-3">
+                        <label for="password" class="form-label">Contraseña</label>
+                        <input type="password" class="form-control" id="password" name="password" required>
+                    </div>
+                    <button type="submit" class="btn btn-primary">Ingresar</button>
+                </form>
+            </div>
+        </div>
+    </div>
+</div>
+{% endblock %}