UI polish: global modern styles; Admin button pinned top-left across app and generated pages; index redirects to login for guests

This commit is contained in:
2025-09-03 14:37:36 +03:00
commit 85eaf6a99a
7 changed files with 468 additions and 0 deletions

182
app.py Normal file
View File

@@ -0,0 +1,182 @@
import os
import sqlite3
import uuid as uuid_lib
from datetime import datetime
from flask import (
Flask,
g,
render_template,
request,
redirect,
url_for,
session,
abort,
flash,
Response,
)
def create_app():
app = Flask(__name__)
# Basic config
app.config["SECRET_KEY"] = os.environ.get(
"SECRET_KEY",
# For production, override via env var. This default is for local/dev only.
"dev-secret-change-me",
)
app.config["DATABASE"] = os.path.join(os.path.dirname(__file__), "app.db")
# Admin credentials (can be overridden via env)
app.config["ADMIN_USERNAME"] = os.environ.get("ADMIN_USERNAME", "ruslan")
app.config["ADMIN_PASSWORD"] = os.environ.get("ADMIN_PASSWORD", "utOgbZ09ruslan")
# --------------------
# Database helpers
# --------------------
def get_db():
if "db" not in g:
g.db = sqlite3.connect(app.config["DATABASE"]) # type: ignore[attr-defined]
g.db.row_factory = sqlite3.Row
return g.db
def close_db(e=None):
db = g.pop("db", None)
if db is not None:
db.close()
def init_db():
db = get_db()
db.execute(
"""
CREATE TABLE IF NOT EXISTS pages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
uuid TEXT NOT NULL UNIQUE,
html TEXT NOT NULL,
created_at TEXT NOT NULL
);
"""
)
db.commit()
@app.before_request
def _before_request():
# Ensure DB exists
init_db()
@app.teardown_appcontext
def _teardown_appcontext(_=None):
close_db()
# --------------------
# Auth helpers
# --------------------
def is_logged_in() -> bool:
return bool(session.get("logged_in"))
def login_required():
if not is_logged_in():
# For non-auth users: show nothing (404), not even an admin hint
abort(404)
@app.context_processor
def inject_auth():
return {"logged_in": is_logged_in()}
# --------------------
# Routes
# --------------------
@app.route("/")
def index():
# Redirect unauthenticated users to login; authenticated users to admin.
if not is_logged_in():
return redirect(url_for("login"))
return redirect(url_for("admin"))
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
username = request.form.get("username", "")
password = request.form.get("password", "")
if (
username == app.config["ADMIN_USERNAME"]
and password == app.config["ADMIN_PASSWORD"]
):
session["logged_in"] = True
flash("Успешный вход в админку.", "success")
return redirect(url_for("admin"))
flash("Неверные имя пользователя или пароль.", "error")
return render_template("login.html")
@app.route("/logout", methods=["POST"])
def logout():
session.clear()
# After logout, nothing is visible. Return 404-like behavior.
abort(404)
@app.route("/admin", methods=["GET", "POST"])
def admin():
login_required()
db = get_db()
if request.method == "POST":
html = request.form.get("html", "").strip()
if not html:
flash("HTML не может быть пустым.", "error")
else:
uid = uuid_lib.uuid4().hex
db.execute(
"INSERT INTO pages (uuid, html, created_at) VALUES (?, ?, ?)",
(uid, html, datetime.utcnow().isoformat(timespec="seconds")),
)
db.commit()
flash("Страница опубликована.", "success")
return redirect(url_for("admin"))
pages = db.execute(
"SELECT id, uuid, created_at FROM pages ORDER BY id DESC"
).fetchall()
base_url = request.host_url.rstrip("/")
return render_template("admin.html", pages=pages, base_url=base_url)
@app.route("/p/<string:uid>")
def view_page(uid: str):
db = get_db()
row = db.execute("SELECT html FROM pages WHERE uuid = ?", (uid,)).fetchone()
if row is None:
abort(404)
# Show a floating admin button for authenticated users, otherwise serve raw HTML
html: str = row["html"]
if is_logged_in():
admin_url = url_for("admin")
toolbar = (
'<div style="position:fixed;top:16px;left:16px;z-index:2147483647;">'
f'<a href="{admin_url}" '
'style="background:linear-gradient(135deg,#111,#333);color:#fff;padding:10px 14px;'
'border-radius:10px;box-shadow:0 8px 20px rgba(0,0,0,0.25);'
'text-decoration:none;font-weight:600;letter-spacing:.2px;'
'font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;"'
'>Админка</a></div>'
)
lower = html.lower()
if "</body>" in lower:
idx = lower.rfind("</body>")
html = html[:idx] + toolbar + html[idx:]
else:
html = html + toolbar
return Response(html, mimetype="text/html; charset=utf-8")
# Optional: simple 404 page to keep things minimal
@app.errorhandler(404)
def not_found(_):
return ("", 404)
return app
app = create_app()
if __name__ == "__main__":
# Run development server
app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 5000)), debug=True)