diff --git a/.env b/.env new file mode 100644 index 0000000..4782840 --- /dev/null +++ b/.env @@ -0,0 +1,5 @@ +# .env +FLASK_ENV=production +DOMAIN=http.pathl.pl +PROD_PORT=8000 +SECRET_KEY=THE_PAsswd+key \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4e0bbdf --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +venv/ +.idea/ diff --git a/__pycache__/http.cpython-313.pyc b/__pycache__/http.cpython-313.pyc new file mode 100644 index 0000000..0c19d72 Binary files /dev/null and b/__pycache__/http.cpython-313.pyc differ diff --git a/__pycache__/utils.cpython-313.pyc b/__pycache__/utils.cpython-313.pyc new file mode 100644 index 0000000..d74bd10 Binary files /dev/null and b/__pycache__/utils.cpython-313.pyc differ diff --git a/app.py b/app.py new file mode 100644 index 0000000..cacfc8f --- /dev/null +++ b/app.py @@ -0,0 +1,181 @@ +from flask import Flask, render_template, request, jsonify, redirect, url_for, session +from utils import ( + user_add, is_person, verify_password, + get_user_data, get_all_users, delete_user +) +import json + +app = Flask(__name__) +app.secret_key = 'twoj_sekretny_klucz_zmien_to_w_produkcji' # Potrzebne dla sesji + + +@app.route('/') +def root(): + return render_template('index.html') + + +@app.route('/register', methods=['GET', 'POST']) +def register(): + if request.method == 'POST': + name = request.form.get('username') + password = request.form.get('password') + confirm_password = request.form.get('confirm_password') + + # Walidacja + if not name or not password: + return "Wszystkie pola są wymagane!", 400 + + if password != confirm_password: + return "Hasła nie są identyczne!", 400 + + if len(password) < 4: + return "Hasło musi mieć co najmniej 4 znaki!", 400 + + if is_person(name): + return "Użytkownik już istnieje!", 400 + + # Dodaj użytkownika + user_add(name, password) + return redirect(url_for('login')) + + return render_template('register.html') + + +@app.route('/login', methods=['GET', 'POST']) +def login(): + if request.method == 'POST': + name = request.form.get('username') + password = request.form.get('password') + + if not name or not password: + return "Wszystkie pola są wymagane!", 400 + + if not is_person(name): + return "Użytkownik nie istnieje!", 404 + + if verify_password(name, password): + # Zapisz w sesji że użytkownik jest zalogowany + session['username'] = name + return redirect(url_for('dashboard')) + else: + return "Złe hasło!", 401 + + return render_template('login.html') + + +@app.route('/dashboard') +def dashboard(): + # Sprawdź czy użytkownik jest zalogowany + if 'username' not in session: + return redirect(url_for('login')) + + user_data = get_user_data(session['username']) + return render_template('dashboard.html', user=user_data) + + +@app.route('/logout') +def logout(): + session.pop('username', None) + return redirect(url_for('root')) + + +@app.route('/api/passwd/', methods=['GET']) +def api_get_password(name): + """API endpoint do pobierania hasła (tylko JSON)""" + if not is_person(name): + return jsonify({"error": "Brak osoby"}), 404 + + user_data = get_user_data(name) + if user_data: + return jsonify({ + "user": user_data["user"], + "password": user_data["password"], + "status": "success" + }) + else: + return jsonify({"error": "Brak hasła"}), 404 + + +@app.route('/api/users', methods=['GET']) +def api_get_all_users(): + """API endpoint do pobierania wszystkich użytkowników""" + users = get_all_users() + return jsonify({ + "count": len(users), + "users": users + }) + + +@app.route('/api/check/', methods=['GET']) +def api_check_user(name): + """API endpoint do sprawdzania czy użytkownik istnieje""" + exists = is_person(name) + return jsonify({ + "user": name, + "exists": bool(exists), + "code": exists + }) + + +@app.route('/api/register', methods=['POST']) +def api_register(): + """API endpoint do rejestracji (JSON)""" + data = request.get_json() + + if not data: + return jsonify({"error": "Brak danych JSON"}), 400 + + name = data.get('username') + password = data.get('password') + + if not name or not password: + return jsonify({"error": "Username i password są wymagane"}), 400 + + if is_person(name): + return jsonify({"error": "Użytkownik już istnieje"}), 409 + + user_add(name, password) + return jsonify({"message": f"Użytkownik {name} zarejestrowany"}), 201 + + +@app.route('/api/login', methods=['POST']) +def api_login(): + """API endpoint do logowania (JSON)""" + data = request.get_json() + + if not data: + return jsonify({"error": "Brak danych JSON"}), 400 + + name = data.get('username') + password = data.get('password') + + if not name or not password: + return jsonify({"error": "Username i password są wymagane"}), 400 + + if not is_person(name): + return jsonify({"error": "Użytkownik nie istnieje"}), 404 + + if verify_password(name, password): + return jsonify({ + "message": "Zalogowano pomyślnie", + "user": name, + "status": "success" + }) + else: + return jsonify({"error": "Złe hasło"}), 401 + + +@app.route('/debug/users') +def debug_users(): + """Debug endpoint - pokazuje wszystkich użytkowników""" + if 'username' not in session: + return redirect(url_for('login')) + + users = get_all_users() + return jsonify(users) + +@app.route("/css/") +def css(): + return render_template("index_css.html") +if __name__ == '__main__': + app.run(port=2500) \ No newline at end of file diff --git a/file.json b/file.json new file mode 100644 index 0000000..3224b48 --- /dev/null +++ b/file.json @@ -0,0 +1,8 @@ +{ + "users": [ + { + "user": "neon", + "password": "1234" + } + ] +} \ No newline at end of file diff --git a/makefile b/makefile new file mode 100644 index 0000000..09e39f7 --- /dev/null +++ b/makefile @@ -0,0 +1,284 @@ +# Zmienne +PYTHON = venv/bin/python +PIP = venv/bin/pip +FLASK = venv/bin/flask +APP = app.py +PORT = 2500 +PROD_PORT = 8000 +DOMAIN = http.pathl.pl +PROJECT_DIR = /home/neon/PycharmProjects/http +VENV_DIR = $(PROJECT_DIR)/venv + +# Kolory do outputu +GREEN = \033[0;32m +RED = \033[0;31m +YELLOW = \033[1;33m +BLUE = \033[0;34m +NC = \033[0m # No Color + +.PHONY: help setup venv install run debug clean test api status kill reset release prod-start prod-stop prod-restart prod-status init-db backup + +help: + @echo "$(GREEN)Dostępne komendy:$(NC)" + @echo "" + @echo "$(BLUE)=== Rozwój (Development) ===$(NC)" + @echo " make setup - Przygotowanie środowiska (venv + zależności)" + @echo " make venv - Tworzenie środowiska wirtualnego" + @echo " make install - Instalacja zależności" + @echo " make debug - Uruchomienie serwera w trybie debug (port $(PORT))" + @echo " make run - Uruchomienie serwera (standardowo)" + @echo "" + @echo "$(BLUE)=== Produkcja (Production) ===$(NC)" + @echo " make release - Pełne wdrożenie produkcyjne (budowa + start)" + @echo " make prod-start - Uruchomienie serwera produkcyjnego (Gunicorn)" + @echo " make prod-stop - Zatrzymanie serwera produkcyjnego" + @echo " make prod-restart - Restart serwera produkcyjnego" + @echo " make prod-status - Status serwera produkcyjnego" + @echo "" + @echo "$(BLUE)=== Narzędzia ===$(NC)" + @echo " make clean - Usunięcie plików tymczasowych" + @echo " make test - Testowanie API" + @echo " make api - Pokazuje endpointy API" + @echo " make status - Sprawdza status serwera deweloperskiego" + @echo " make kill - Zabija proces Flask na porcie $(PORT)" + @echo " make reset - Resetuje bazę danych (usuwa file.json)" + @echo " make backup - Backup bazy danych" + @echo " make logs - Podgląd logów produkcyjnych" + +# ==================== DEVELOPMENT ==================== + +setup: venv install + @echo "$(GREEN)✓ Środowisko gotowe!$(NC)" + @echo "Uruchom: make debug" + +venv: + @echo "$(GREEN)Tworzenie środowiska wirtualnego...$(NC)" + @python3 -m venv venv || python -m venv venv + @echo "$(GREEN)✓ Środowisko utworzone$(NC)" + +install: + @echo "$(GREEN)Instalacja zależności...$(NC)" + @$(PIP) install --upgrade pip + @$(PIP) install flask + @$(PIP) install flask-cors + @$(PIP) install gunicorn + @$(PIP) install python-dotenv + @echo "$(GREEN)✓ Zależności zainstalowane$(NC)" + @# Tworzenie requirements.txt + @$(PIP) freeze > requirements.txt 2>/dev/null || true + +run: + @echo "$(GREEN)Uruchamianie serwera na porcie $(PORT)...$(NC)" + @$(PYTHON) $(APP) + +debug: + @echo "$(GREEN)Uruchamianie serwera w trybie DEBUG na porcie $(PORT)...$(NC)" + @export FLASK_ENV=development FLASK_DEBUG=1 && $(PYTHON) $(APP) + +clean: + @echo "$(GREEN)Czyszczenie plików tymczasowych...$(NC)" + @find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true + @find . -type f -name "*.pyc" -delete 2>/dev/null || true + @find . -type f -name "*.pyo" -delete 2>/dev/null || true + @rm -rf .pytest_cache 2>/dev/null || true + @echo "$(GREEN)✓ Pliki tymczasowe usunięte$(NC)" + +# ==================== PRODUCTION ==================== + +release: clean install prod-stop prod-start prod-status + @echo "$(GREEN)========================================$(NC)" + @echo "$(GREEN)✓ Wdrożenie produkcyjne zakończone!$(NC)" + @echo "$(GREEN)========================================$(NC)" + @echo "Aplikacja dostępna pod adresem: http://$(DOMAIN)" + @echo "Port: $(PROD_PORT)" + @echo "" + @echo "Aby sprawdzić status: make prod-status" + @echo "Aby zatrzymać: make prod-stop" + @echo "Aby zobaczyć logi: make logs" + +prod-start: + @echo "$(GREEN)Uruchamianie serwera produkcyjnego...$(NC)" + @echo "Domena: http://$(DOMAIN)" + @echo "Port: $(PROD_PORT)" + @# Zabij stary proces jeśli istnieje + @-lsof -ti :$(PROD_PORT) | xargs kill -9 2>/dev/null || true + @# Uruchom Gunicorn w tle + @cd $(PROJECT_DIR) && nohup $(VENV_DIR)/bin/gunicorn -w 4 -b 0.0.0.0:$(PROD_PORT) app:app > logs/production.log 2>&1 & + @sleep 2 + @echo "$(GREEN)✓ Serwer produkcyjny uruchomiony!$(NC)" + @echo "PID: $$(lsof -ti :$(PROD_PORT) 2>/dev/null || echo 'Nie można odczytać')" + @echo "Logi: $(PROJECT_DIR)/logs/production.log" + +prod-stop: + @echo "$(RED)Zatrzymywanie serwera produkcyjnego...$(NC)" + @-lsof -ti :$(PROD_PORT) | xargs kill -9 2>/dev/null && echo "$(GREEN)✓ Serwer zatrzymany$(NC)" || echo "Serwer nie był uruchomiony" + @-pkill -f "gunicorn.*app:app" 2>/dev/null || true + +prod-restart: prod-stop prod-start + @echo "$(GREEN)✓ Serwer produkcyjny zrestartowany$(NC)" + +prod-status: + @echo "$(BLUE)Status serwera produkcyjnego:$(NC)" + @if lsof -i :$(PROD_PORT) > /dev/null 2>&1; then \ + echo "$(GREEN)✓ Serwer DZIAŁA na porcie $(PROD_PORT)$(NC)"; \ + echo "Domena: http://$(DOMAIN)"; \ + echo "PID: $$(lsof -ti :$(PROD_PORT))"; \ + echo ""; \ + echo "Test połączenia:"; \ + curl -s -o /dev/null -w "Status HTTP: %{http_code}\n" http://localhost:$(PROD_PORT)/; \ + else \ + echo "$(RED)✗ Serwer NIE działa na porcie $(PROD_PORT)$(NC)"; \ + fi + +# ==================== UTILS ==================== + +test: + @echo "$(GREEN)Testowanie API (dev:$(PORT), prod:$(PROD_PORT))...$(NC)" + @echo "$(YELLOW)1. Test root endpoint (dev):$(NC)" + @curl -s http://localhost:$(PORT)/ 2>/dev/null | head -c 200 || echo "Serwer dev nie uruchomiony" + @echo "\n$(YELLOW)2. Test API check user (prod):$(NC)" + @curl -s http://localhost:$(PROD_PORT)/api/check/admin 2>/dev/null || echo "Serwer prod nie uruchomiony" + @echo "\n$(YELLOW)3. Test API get users (prod):$(NC)" + @curl -s http://localhost:$(PROD_PORT)/api/users 2>/dev/null || echo "Serwer prod nie uruchomiony" + @echo "" + +api: + @echo "$(GREEN)Dostępne endpointy API:$(NC)" + @echo "" + @echo "$(BLUE)Deweloperskie (http://localhost:$(PORT)):$(NC)" + @echo " GET / - Strona główna" + @echo " GET /register - Formularz rejestracji" + @echo " POST /register - Rejestracja użytkownika" + @echo " GET /login - Formularz logowania" + @echo " POST /login - Logowanie" + @echo " GET /dashboard - Panel użytkownika" + @echo " GET /logout - Wylogowanie" + @echo "" + @echo "$(BLUE)Produkcyjne (http://$(DOMAIN)):$(NC)" + @echo " GET /api/users - Lista wszystkich użytkowników" + @echo " GET /api/passwd/ - Pobierz hasło użytkownika" + @echo " GET /api/check/ - Sprawdź czy użytkownik istnieje" + @echo " POST /api/register - Rejestracja przez API (JSON)" + @echo " POST /api/login - Logowanie przez API (JSON)" + @echo "" + @echo "$(GREEN)Przykłady użycia API (produkcja):$(NC)" + @echo " curl -X POST http://$(DOMAIN)/api/register \\" + @echo " -H \"Content-Type: application/json\" \\" + @echo " -d '{\"username\":\"test\",\"password\":\"123\"}'" + @echo "" + @echo " curl http://$(DOMAIN)/api/check/test" + @echo " curl http://$(DOMAIN)/api/users" + +status: + @echo "$(GREEN)Sprawdzanie statusu serwerów:$(NC)" + @echo "" + @echo "$(BLUE)Development (port $(PORT)):$(NC)" + @if lsof -i :$(PORT) > /dev/null 2>&1; then \ + echo " $(GREEN)✓ Działa$(NC) - PID: $$(lsof -ti :$(PORT))"; \ + else \ + echo " $(RED)✗ Nie działa$(NC)"; \ + fi + @echo "" + @echo "$(BLUE)Production (port $(PROD_PORT)):$(NC)" + @if lsof -i :$(PROD_PORT) > /dev/null 2>&1; then \ + echo " $(GREEN)✓ Działa$(NC) - PID: $$(lsof -ti :$(PROD_PORT))"; \ + echo " URL: http://$(DOMAIN)"; \ + else \ + echo " $(RED)✗ Nie działa$(NC)"; \ + fi + +kill: + @echo "$(RED)Zabijanie procesu deweloperskiego na porcie $(PORT)...$(NC)" + @lsof -ti :$(PORT) | xargs kill -9 2>/dev/null || echo "Brak procesu na porcie $(PORT)" + @echo "$(GREEN)✓ Proces deweloperski zabity$(NC)" + +reset: + @echo "$(RED)Resetowanie bazy danych...$(NC)" + @rm -f file.json + @echo "{}" > file.json + @echo "$(GREEN)✓ Baza danych zresetowana (file.json)$(NC)" + +backup: + @echo "$(GREEN)Tworzenie backupu bazy danych...$(NC)" + @if [ -f file.json ]; then \ + cp file.json file.json.backup.$$(date +%Y%m%d_%H%M%S); \ + echo "$(GREEN)✓ Backup utworzony$(NC)"; \ + else \ + echo "$(YELLOW)Brak bazy danych do backupu$(NC)"; \ + fi + +logs: + @echo "$(GREEN)Logi produkcyjne (Ctrl+C aby zakończyć):$(NC)" + @mkdir -p logs + @tail -f logs/production.log 2>/dev/null || echo "Brak logów - uruchom najpierw make prod-start" + +init-db: + @echo "$(GREEN)Inicjalizacja bazy danych...$(NC)" + @if [ ! -f file.json ]; then \ + echo '{"users": []}' > file.json; \ + echo "$(GREEN)✓ Utworzono nową bazę danych$(NC)"; \ + else \ + echo "$(YELLOW)Baza danych już istnieje$(NC)"; \ + fi + +# ==================== PRODUCTION SETUP ==================== + +prod-setup: clean install init-db + @echo "$(GREEN)✓ Środowisko produkcyjne gotowe$(NC)" + @mkdir -p logs + @echo "Uruchom: make release" + +# ==================== DLA SYSTEMD (autostart) ==================== + +install-service: + @echo "$(GREEN)Instalacja usługi systemd...$(NC)" + @sudo bash -c 'cat > /etc/systemd/system/http-api.service << EOF +[Unit] +Description=HTTP API Flask App +After=network.target + +[Service] +User=$(shell whoami) +WorkingDirectory=$(PROJECT_DIR) +ExecStart=$(VENV_DIR)/bin/gunicorn -w 4 -b 0.0.0.0:$(PROD_PORT) app:app +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target +EOF' + @sudo systemctl daemon-reload + @echo "$(GREEN)✓ Usługa zainstalowana$(NC)" + @echo "Uruchom: sudo systemctl start http-api" + @echo "Włącz autostart: sudo systemctl enable http-api" + +remove-service: + @echo "$(RED)Usuwanie usługi systemd...$(NC)" + @sudo systemctl stop http-api 2>/dev/null || true + @sudo systemctl disable http-api 2>/dev/null || true + @sudo rm -f /etc/systemd/system/http-api.service + @sudo systemctl daemon-reload + @echo "$(GREEN)✓ Usługa usunięta$(NC)" + +# ==================== DLA NGINX (proxy) ==================== + +setup-nginx: + @echo "$(GREEN)Konfiguracja Nginx jako reverse proxy...$(NC)" + @sudo bash -c 'cat > /etc/nginx/sites-available/http-api << EOF +server { + listen 80; + server_name $(DOMAIN); + + location / { + proxy_pass http://127.0.0.1:$(PROD_PORT); + proxy_set_header Host $$host; + proxy_set_header X-Real-IP $$remote_addr; + proxy_set_header X-Forwarded-For $$proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $$scheme; + } +} +EOF' + @sudo ln -sf /etc/nginx/sites-available/http-api /etc/nginx/sites-enabled/ + @sudo nginx -t && sudo systemctl reload nginx + @echo "$(GREEN)✓ Nginx skonfigurowany$(NC)" + @echo "Domena: http://$(DOMAIN)" \ No newline at end of file diff --git a/main.py b/static/css/style.css similarity index 100% rename from main.py rename to static/css/style.css diff --git a/templates/dashboard.html b/templates/dashboard.html new file mode 100644 index 0000000..4a93725 --- /dev/null +++ b/templates/dashboard.html @@ -0,0 +1,29 @@ + + + + + + Panel użytkownika + + + +
+

Witaj, {{ user.user }}!

+
+

Zalogowany jako: {{ user.user }}

+

Twoje hasło: {{ user.password }}

+
+ + Wyloguj się + +
+

Informacje API:

+

GET /api/users - lista wszystkich użytkowników

+

GET /api/passwd/<nazwa> - pobierz hasło użytkownika

+

GET /api/check/<nazwa> - sprawdź czy użytkownik istnieje

+

Zobacz stronę z CSS:

+ Kliknij tutaj, aby przejść +
+
+ + \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..ffb05de --- /dev/null +++ b/templates/index.html @@ -0,0 +1,19 @@ + + + + + + Strona Główna + + + +
+

Witaj na naszej stronie!

+

System rejestracji i logowania użytkowników

+ +
+ + \ No newline at end of file diff --git a/templates/index_css.html b/templates/index_css.html new file mode 100644 index 0000000..13bb013 --- /dev/null +++ b/templates/index_css.html @@ -0,0 +1,45 @@ + + + + + Prosta strona z CSS + + + +

Witaj na mojej prostej stronie!

+

To jest przykładowa strona HTML z prostym CSS.

+ Powrót do strony głównej + + \ No newline at end of file diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000..90a345c --- /dev/null +++ b/templates/login.html @@ -0,0 +1,22 @@ + + + + + + Logowanie + + + +
+

Logowanie

+
+ + + +
+ +
+ + \ No newline at end of file diff --git a/templates/register.html b/templates/register.html new file mode 100644 index 0000000..d7def55 --- /dev/null +++ b/templates/register.html @@ -0,0 +1,23 @@ + + + + + + Rejestracja + + + +
+

Rejestracja

+
+ + + + +
+ +
+ + \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000..e69de29 diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..2b02df5 --- /dev/null +++ b/utils.py @@ -0,0 +1,118 @@ +import json +import os + + +def user_add(new_user, new_password): + """Dodaje nowego użytkownika lub aktualizuje hasło""" + file_path = "file.json" + + # Wczytanie pliku JSON + if os.path.exists(file_path): + with open(file_path, "r") as f: + try: + data = json.load(f) + except json.JSONDecodeError: + data = {"users": []} + else: + data = {"users": []} + + # Sprawdź czy użytkownik już istnieje + for entry in data["users"]: + if entry.get("user") == new_user: + # Aktualizuj hasło + entry["password"] = new_password + with open(file_path, "w") as f: + json.dump(data, f, indent=4) + return False # Zaktualizowano + + # Dodaj nowego użytkownika + new_entry = {"user": new_user, "password": new_password} + data["users"].append(new_entry) + + # Zapis do pliku JSON + with open(file_path, "w") as f: + json.dump(data, f, indent=4) + return True # Dodano nowego + + +def is_person(user_to_find): + """Sprawdza czy użytkownik istnieje - zwraca 1 lub 0""" + file_path = "file.json" + + try: + with open(file_path, "r") as f: + data = json.load(f) + except (FileNotFoundError, json.JSONDecodeError): + return 0 + + for entry in data.get("users", []): + if entry.get("user") == user_to_find: + return 1 + return 0 + + +def verify_password(user_to_check, password_to_check): + """Sprawdza hasło bez usuwania użytkownika""" + file_path = "file.json" + + try: + with open(file_path, "r") as f: + data = json.load(f) + except (FileNotFoundError, json.JSONDecodeError): + return False + + for entry in data.get("users", []): + if entry.get("user") == user_to_check: + return entry.get("password") == password_to_check + + return False + + +def get_user_data(user_to_find): + """Pobiera dane użytkownika bez usuwania""" + file_path = "file.json" + + try: + with open(file_path, "r") as f: + data = json.load(f) + except (FileNotFoundError, json.JSONDecodeError): + return None + + for entry in data.get("users", []): + if entry.get("user") == user_to_find: + return {"user": entry["user"], "password": entry["password"]} + + return None + + +def get_all_users(): + """Zwraca listę wszystkich użytkowników""" + file_path = "file.json" + + try: + with open(file_path, "r") as f: + data = json.load(f) + except (FileNotFoundError, json.JSONDecodeError): + return [] + + return data.get("users", []) + + +def delete_user(user_to_delete): + """Usuwa użytkownika z bazy""" + file_path = "file.json" + + try: + with open(file_path, "r") as f: + data = json.load(f) + except (FileNotFoundError, json.JSONDecodeError): + return False + + for i, entry in enumerate(data.get("users", [])): + if entry.get("user") == user_to_delete: + data["users"].pop(i) + with open(file_path, "w") as f: + json.dump(data, f, indent=4) + return True + + return False \ No newline at end of file