from flask import Flask, render_template, request, redirect, url_for, session, flash, jsonify, send_from_directory from markupsafe import Markup import os, json, time, re import markdown from datetime import datetime from ql_interpreter import run_ql_code # Upewnij się że to jest app = Flask(__name__, static_folder='static') app.secret_key = "super_secret_key_12345" USERS_FILE = "users.json" BASE_DIR = "projects" os.makedirs(BASE_DIR, exist_ok=True) # ------------------------------ # Funkcje pomocnicze dla templatki # ------------------------------ @app.context_processor def utility_processor(): def file_size(filename): """Zwraca rozmiar pliku w czytelnej formie""" try: username = session.get('username', '') project = 'Projekt1' # Domyślny projekt if 'project' in request.view_args: project = request.view_args['project'] file_path = os.path.join(BASE_DIR, username, project, filename) if os.path.exists(file_path): size = os.path.getsize(file_path) if size < 1024: return f"{size} B" elif size < 1024 * 1024: return f"{size / 1024:.1f} KB" else: return f"{size / (1024 * 1024):.1f} MB" except: pass return "0 B" def get_file_icon(filename): """Zwraca ikonę dla typu pliku""" if filename.endswith('.ql'): return 'bi-file-earmark-code' elif filename.endswith('.md'): return 'bi-file-earmark-text' elif filename.endswith('.py'): return 'bi-file-earmark-code' elif filename.endswith('.js'): return 'bi-file-earmark-code' elif filename.endswith('.html'): return 'bi-file-earmark-code' elif filename.endswith('.css'): return 'bi-file-earmark-code' elif filename.endswith('.json'): return 'bi-file-earmark-data' else: return 'bi-file-earmark' def format_date(timestamp): """Formatuje datę""" return datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M') return dict( file_size=file_size, get_file_icon=get_file_icon, format_date=format_date ) # ------------------------------ # Pomocnicze funkcje # ------------------------------ def load_users(): if not os.path.exists(USERS_FILE): return {} try: with open(USERS_FILE, 'r', encoding='utf-8') as f: return json.load(f) except (json.JSONDecodeError, FileNotFoundError): return {} def save_users(users): with open(USERS_FILE, "w", encoding='utf-8') as f: json.dump(users, f, indent=4, ensure_ascii=False) def create_user_dir(username): user_path = os.path.join(BASE_DIR, username) os.makedirs(user_path, exist_ok=True) return user_path # ------------------------------ # Routes # ------------------------------ @app.route("/") def index(): return redirect(url_for('login')) @app.route("/login", methods=["GET", "POST"]) def login(): if 'username' in session: return redirect(url_for('dashboard')) if request.method == "POST": username = request.form.get('username', '').strip() password = request.form.get('password', '').strip() if not username or not password: flash("Wypełnij wszystkie pola!") return render_template("login.html") users = load_users() if username in users and users[username]['password'] == password: session['username'] = username session['cwd'] = os.path.join(BASE_DIR, username) return redirect(url_for('dashboard')) flash("Nieprawidłowy login lub hasło") return render_template("login.html") @app.route("/register", methods=["GET", "POST"]) def register(): if request.method == "POST": username = request.form.get('username', '').strip() password = request.form.get('password', '').strip() if not username or not password: flash("Wypełnij wszystkie pola!") return render_template("register.html") if len(username) < 3: flash("Nazwa użytkownika musi mieć co najmniej 3 znaki") return render_template("register.html") if len(password) < 4: flash("Hasło musi mieć co najmniej 4 znaki") return render_template("register.html") users = load_users() if username in users: flash("Użytkownik już istnieje!") else: users[username] = {"password": password} save_users(users) user_path = create_user_dir(username) default_project = os.path.join(user_path, "Projekt1") os.makedirs(default_project, exist_ok=True) with open(os.path.join(default_project, "main.ql"), "w", encoding='utf-8') as f: f.write("# Mój pierwszy program QL\nprint('Witaj w QL!');") with open(os.path.join(default_project, "README.md"), "w", encoding='utf-8') as f: f.write( "# Projekt1\n\nTo jest domyślny projekt.\n\n## Funkcje:\n- Edycja kodu QL\n- Konsola systemowa\n- Zarządzanie plikami") flash("Rejestracja udana! Możesz się teraz zalogować.") return redirect(url_for('login')) return render_template("register.html") @app.route("/logout") def logout(): session.clear() return redirect(url_for("login")) @app.route("/dashboard") def dashboard(): if 'username' not in session: return redirect(url_for('login')) username = session['username'] user_dir = os.path.join(BASE_DIR, username) if not os.path.exists(user_dir): os.makedirs(user_dir, exist_ok=True) projects = [] if os.path.exists(user_dir): for item in os.listdir(user_dir): item_path = os.path.join(user_dir, item) if os.path.isdir(item_path): projects.append(item) if not projects: default_project = os.path.join(user_dir, "Projekt1") os.makedirs(default_project, exist_ok=True) with open(os.path.join(default_project, "main.ql"), "w", encoding='utf-8') as f: f.write("# Mój pierwszy program QL\nprint('Witaj w QL!');") projects.append("Projekt1") last_project = projects[-1] if projects else "Projekt1" # Zaktualizowane kafelki z kolorami i ikonami tiles = [ { "title": "🔄 Modele", "desc": "Wybierz model i funkcję", "link": url_for('models_page'), "icon": "bi-diagram-3", "bg_color": "#007bff", "bg_color2": "#6610f2" }, { "title": "📁 Projekty", "desc": "Zarządzaj swoimi projektami", "link": url_for('user_projects', username=username), "icon": "bi-folder", "bg_color": "#28a745", "bg_color2": "#20c997" }, { "title": "✏️ Edytor", "desc": "Otwórz edytor kodu", "link": url_for('ql_editor', username=username, project=last_project), "icon": "bi-code-slash", "bg_color": "#343a40", "bg_color2": "#6c757d" }, { "title": "🛒 Marketplace", "desc": "Przeglądaj moduły", "link": "#", "icon": "bi-shop", "bg_color": "#ffc107", "bg_color2": "#fd7e14" }, { "title": "💬 Chat", "desc": "Czat z użytkownikami", "link": "#", "icon": "bi-chat", "bg_color": "#dc3545", "bg_color2": "#e83e8c" }, { "title": "⚙️ Ustawienia", "desc": "Konfiguracja platformy", "link": "#", "icon": "bi-gear", "bg_color": "#17a2b8", "bg_color2": "#20c997" }, { "title": "📚 Dokumentacja", "desc": "Przeczytaj dokumentację", "link": "#", "icon": "bi-book", "bg_color": "#6f42c1", "bg_color2": "#e83e8c" }, { "title": "🚪 Wyloguj", "desc": "Zakończ sesję", "link": url_for('logout'), "icon": "bi-box-arrow-right", "bg_color": "#6c757d", "bg_color2": "#495057" }, ] return render_template("dashboard.html", username=username, tiles=tiles, projects=projects) @app.route("/models") def models_page(): if 'username' not in session: return redirect(url_for('login')) return render_template("models.html", username=session['username']) # ------------------------------ # Edytor QL # ------------------------------ @app.route("/editor//", methods=["GET"]) def ql_editor(username, project): if 'username' not in session or session['username'] != username: return redirect(url_for('login')) project_path = os.path.join(BASE_DIR, username, project) if not os.path.exists(project_path): os.makedirs(project_path, exist_ok=True) with open(os.path.join(project_path, "main.ql"), "w", encoding='utf-8') as f: f.write("# Mój program QL\nprint('Witaj!');") # Pobierz wszystkie pliki (nie tylko .ql) files = [] if os.path.exists(project_path): for f in os.listdir(project_path): if os.path.isfile(os.path.join(project_path, f)): files.append(f) selected_file = request.args.get("file") if not selected_file: if files: selected_file = files[0] else: selected_file = "main.ql" with open(os.path.join(project_path, selected_file), "w", encoding='utf-8') as f: f.write("# Nowy program QL\nprint('Hello World!');") files.append(selected_file) content = "" file_path = os.path.join(project_path, selected_file) if os.path.exists(file_path): try: with open(file_path, 'r', encoding='utf-8') as f: content = f.read() except: content = f"# Błąd odczytu pliku: {selected_file}" return render_template("editor.html", username=username, project=project, files=files, selected_file=selected_file, content=content) @app.route("/editor///save", methods=["POST"]) def save_editor_file(username, project): if 'username' not in session or session['username'] != username: return jsonify({"error": "Nie zalogowano"}), 403 data = request.get_json() if not data: return jsonify({"error": "Brak danych"}), 400 filename = data.get("filename", "").strip() content = data.get("content", "") if not filename: return jsonify({"error": "Brak nazwy pliku"}), 400 project_path = os.path.join(BASE_DIR, username, project) os.makedirs(project_path, exist_ok=True) file_path = os.path.join(project_path, filename) with open(file_path, "w", encoding='utf-8') as f: f.write(content) return jsonify({"status": "ok", "filename": filename}) # ------------------------------ # Tworzenie pliku # ------------------------------ @app.route("/editor///create", methods=["POST"]) def create_file(username, project): if 'username' not in session or session['username'] != username: return jsonify({"error": "Nie zalogowano"}), 403 data = request.get_json() if not data: return jsonify({"error": "Brak danych"}), 400 filename = data.get("filename", "").strip() content = data.get("content", "") if not filename: return jsonify({"error": "Brak nazwy pliku"}), 400 project_path = os.path.join(BASE_DIR, username, project) os.makedirs(project_path, exist_ok=True) file_path = os.path.join(project_path, filename) # Sprawdź czy plik już istnieje if os.path.exists(file_path): return jsonify({"error": "Plik już istnieje"}), 400 # Utwórz plik with open(file_path, "w", encoding='utf-8') as f: f.write(content if content else "") return jsonify({"status": "ok", "filename": filename}) # ------------------------------ # Usuwanie pliku # ------------------------------ @app.route("/editor///delete", methods=["POST"]) def delete_file(username, project): if 'username' not in session or session['username'] != username: return jsonify({"error": "Nie zalogowano"}), 403 data = request.get_json() filename = data.get("filename", "").strip() if not filename: return jsonify({"error": "Brak nazwy pliku"}), 400 file_path = os.path.join(BASE_DIR, username, project, filename) if not os.path.exists(file_path): return jsonify({"error": "Plik nie istnieje"}), 404 try: os.remove(file_path) return jsonify({"status": "ok"}) except Exception as e: return jsonify({"error": f"Błąd usuwania: {str(e)}"}), 500 @app.route("/editor///execute", methods=["POST"]) def execute_ql_code(username, project): if 'username' not in session or session['username'] != username: return jsonify({"error": "Nie zalogowano"}), 403 data = request.get_json() if not data: return jsonify({"error": "Brak kodu"}), 400 code = data.get("code", "") try: # Użyj NOWEGO interpretera QL (bez verbose) result = run_ql_code(code) # Zwróć TYLKO output - bez dodatkowych komunikatów return jsonify({ "status": "success", "output": result['output'] # TYLKO to co w print() }) except Exception as e: return jsonify({ "status": "error", "output": [f"❌ {str(e)}"] # TYLKO błąd }) # ------------------------------ # Konsola systemowa # ------------------------------ @app.route("/editor///console", methods=["POST"]) def console_command(username, project): if 'username' not in session or session['username'] != username: return jsonify({"output": "Nie zalogowano"}), 403 data = request.get_json() if not data: return jsonify({"output": "Brak komendy"}), 400 cmd = data.get("command", "").strip() if 'cwd' not in session: session['cwd'] = os.path.join(BASE_DIR, username, project) cwd = session.get('cwd', os.path.join(BASE_DIR, username, project)) user_root = os.path.join(BASE_DIR, username) if not os.path.exists(cwd): cwd = user_root session['cwd'] = cwd output = execute_console_command(cmd, cwd, user_root) if 'cwd' in locals(): session['cwd'] = cwd return jsonify({"output": output}) def execute_console_command(cmd, cwd, user_root): parts = cmd.strip().split() if not parts: return "" command = parts[0].lower() try: if command == "help": return """Dostępne komendy: help - wyświetla pomoc ls - lista plików cd - zmień katalog pwd - pokaż aktualny katalog mkdir - utwórz katalog touch - utwórz plik cat - pokaż zawartość pliku rm - usuń plik clear - wyczyść konsolę""" elif command == "ls": items = os.listdir(cwd) if not items: return "Katalog jest pusty" result = [] for item in items: item_path = os.path.join(cwd, item) if os.path.isdir(item_path): result.append(f"[DIR] {item}/") else: size = os.path.getsize(item_path) result.append(f"[FILE] {item} ({size} bytes)") return "\n".join(result) elif command == "pwd": rel_path = os.path.relpath(cwd, user_root) return f"{rel_path}\nAbsolutna ścieżka: {cwd}" elif command == "cd": if len(parts) < 2: return "Użycie: cd " target = os.path.normpath(os.path.join(cwd, parts[1])) if not os.path.exists(target): return f"Katalog nie istnieje: {parts[1]}" if not target.startswith(user_root): return "Błąd: Próba dostępu poza dozwolony obszar" if not os.path.isdir(target): return f"To nie jest katalog: {parts[1]}" # Aktualizacja cwd cwd = target return f"Zmieniono katalog na: {os.path.relpath(target, user_root)}" elif command == "mkdir": if len(parts) < 2: return "Użycie: mkdir " new_dir = os.path.join(cwd, parts[1]) os.makedirs(new_dir, exist_ok=True) return f"Utworzono katalog: {parts[1]}" elif command == "touch": if len(parts) < 2: return "Użycie: touch " file_path = os.path.join(cwd, parts[1]) with open(file_path, 'a', encoding='utf-8'): pass return f"Utworzono plik: {parts[1]}" elif command == "cat": if len(parts) < 2: return "Użycie: cat " file_path = os.path.join(cwd, parts[1]) if not os.path.exists(file_path): return f"Plik nie istnieje: {parts[1]}" with open(file_path, 'r', encoding='utf-8') as f: content = f.read() return f"Zawartość pliku {parts[1]}:\n{'-' * 40}\n{content}" elif command == "rm": if len(parts) < 2: return "Użycie: rm " file_path = os.path.join(cwd, parts[1]) if not os.path.exists(file_path): return f"Plik nie istnieje: {parts[1]}" if os.path.isdir(file_path): return "Użyj 'rmdir' do usuwania katalogów" os.remove(file_path) return f"Usunięto plik: {parts[1]}" elif command == "clear": return "CLEAR_CONSOLE" else: return f"Nieznana komenda: {command}\nWpisz 'help' aby zobaczyć dostępne komendy." except PermissionError: return "Błąd: Brak uprawnień" except Exception as e: return f"Błąd wykonania: {str(e)}" # ------------------------------ # Projekty użytkowników # ------------------------------ @app.route("/user_projects/") def user_projects(username): if 'username' not in session or session['username'] != username: return redirect(url_for('login')) user_dir = os.path.join(BASE_DIR, username) if not os.path.exists(user_dir): projects = [] else: projects = [d for d in os.listdir(user_dir) if os.path.isdir(os.path.join(user_dir, d))] project_info = [] for project in projects: info = { 'name': project, 'path': os.path.join(user_dir, project), 'created': os.path.getctime(os.path.join(user_dir, project)), 'files': [] } # Liczba plików project_path = os.path.join(user_dir, project) for root, dirs, files in os.walk(project_path): for file in files: if file.endswith('.ql'): info['files'].append(file) # README md_path = os.path.join(user_dir, project, "README.md") if os.path.exists(md_path): try: with open(md_path, 'r', encoding='utf-8') as f: html_content = markdown.markdown(f.read()) info['readme'] = Markup(html_content) except: info['readme'] = Markup("

Błąd odczytu README

") else: info['readme'] = None project_info.append(info) return render_template("user_projects.html", username=username, projects=project_info) # ------------------------------ # Static files # ------------------------------ @app.route('/static/') def serve_static(filename): return send_from_directory(app.static_folder, filename) # ------------------------------ # Uruchomienie # ------------------------------ if __name__ == "__main__": # Utwórz niezbędne katalogi os.makedirs('static/css', exist_ok=True) os.makedirs('static/js', exist_ok=True) os.makedirs('templates', exist_ok=True) os.makedirs('libs', exist_ok=True) # Dodaj katalog libs os.makedirs(BASE_DIR, exist_ok=True) print("Serwer uruchamia się na http://localhost:5000") print("Katalog libs:", os.path.abspath('libs')) app.run(debug=True, host='0.0.0.0', port=5000)