Update app.py
Browse files
app.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
import os
|
2 |
import json
|
3 |
-
from flask import Flask, request, g, Response, render_template, redirect, url_for, flash # Thêm
|
4 |
import psycopg2
|
5 |
from dotenv import load_dotenv
|
6 |
from werkzeug.security import generate_password_hash, check_password_hash
|
@@ -21,8 +21,8 @@ DB_PORT = os.getenv('DB_PORT')
|
|
21 |
# Biến môi trường cho mật khẩu trang
|
22 |
PAGE_PASSWORD = os.getenv('PAGE_PASSWORD')
|
23 |
|
24 |
-
# Cấu hình secret key cho Flask (
|
25 |
-
# Trong môi trường production, hãy tạo một chuỗi ngẫu nhiên mạnh và lưu vào biến môi trường
|
26 |
app.config['SECRET_KEY'] = os.getenv('FLASK_SECRET_KEY', 'mot_chuoi_bi_mat_rat_ngau_nhien_va_dai')
|
27 |
|
28 |
if not PAGE_PASSWORD:
|
@@ -52,7 +52,7 @@ def close_db_connection(exception):
|
|
52 |
db.close()
|
53 |
print("Đã đóng kết nối Supabase PostgreSQL.")
|
54 |
|
55 |
-
# --- Hàm tiện ích để trả về JSON với tiếng Việt (
|
56 |
def custom_jsonify(data, status_code=200):
|
57 |
response = Response(
|
58 |
response=json.dumps(data, ensure_ascii=False),
|
@@ -65,25 +65,58 @@ def custom_jsonify(data, status_code=200):
|
|
65 |
def require_page_password(f):
|
66 |
@wraps(f)
|
67 |
def decorated_function(*args, **kwargs):
|
68 |
-
# Kiểm tra
|
69 |
-
|
|
|
70 |
|
71 |
-
# Nếu
|
|
|
72 |
if request.method == 'POST' and not provided_password:
|
73 |
provided_password = request.headers.get('X-Page-Password')
|
74 |
|
75 |
if not PAGE_PASSWORD:
|
76 |
flash("Lỗi cấu hình server: Mật khẩu trang chưa được thiết lập.", "error")
|
77 |
-
return redirect(url_for('home'))
|
78 |
|
79 |
if provided_password == PAGE_PASSWORD:
|
80 |
-
|
|
|
|
|
|
|
81 |
else:
|
82 |
flash("Truy cập bị từ chối: Mật khẩu trang không hợp lệ.", "error")
|
83 |
-
|
84 |
-
return redirect(request.referrer or url_for('home'))
|
85 |
return decorated_function
|
86 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
87 |
# --- Các tuyến đường (Routes) của ứng dụng ---
|
88 |
|
89 |
@app.route('/')
|
@@ -91,43 +124,51 @@ def home():
|
|
91 |
"""Trang chủ của ứng dụng."""
|
92 |
return render_template('index.html')
|
93 |
|
94 |
-
|
95 |
-
@
|
|
|
96 |
def test_db():
|
97 |
"""Kiểm tra kết nối đến cơ sở dữ liệu và hiển thị kết quả."""
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
|
|
|
|
104 |
try:
|
105 |
cur = conn.cursor()
|
106 |
cur.execute("SELECT version();")
|
107 |
db_version = cur.fetchone()[0]
|
108 |
cur.close()
|
109 |
flash("Kết nối cơ sở dữ liệu thành công!", "success")
|
110 |
-
|
|
|
111 |
except Exception as e:
|
112 |
flash(f"Lỗi truy vấn cơ sở dữ liệu: {e}", "error")
|
113 |
-
|
114 |
-
|
|
|
|
|
115 |
|
116 |
-
@app.route('/create_table', methods=['GET'
|
117 |
-
@require_page_password
|
118 |
def create_table():
|
119 |
"""Tạo bảng 'users' nếu chưa tồn tại và hiển thị kết quả."""
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
|
|
|
|
126 |
try:
|
127 |
cur = conn.cursor()
|
128 |
cur.execute("""
|
129 |
CREATE TABLE IF NOT EXISTS users (
|
130 |
-
id SERIAL PRIMARY KEY,
|
131 |
name VARCHAR(100) NOT NULL,
|
132 |
email VARCHAR(100) UNIQUE NOT NULL,
|
133 |
password VARCHAR(255) NOT NULL
|
@@ -136,15 +177,18 @@ def create_table():
|
|
136 |
conn.commit()
|
137 |
cur.close()
|
138 |
flash("Bảng 'users' đã được tạo (nếu chưa tồn tại) với cột mật khẩu.", "success")
|
139 |
-
|
|
|
140 |
except Exception as e:
|
141 |
conn.rollback()
|
142 |
flash(f"Lỗi khi tạo bảng: {e}", "error")
|
143 |
-
|
144 |
-
|
|
|
|
|
145 |
|
146 |
@app.route('/add_user', methods=['GET', 'POST'])
|
147 |
-
@require_page_password
|
148 |
def add_user():
|
149 |
"""Thêm người dùng mới vào cơ sở dữ liệu với mật khẩu đã băm."""
|
150 |
if request.method == 'POST':
|
@@ -184,9 +228,9 @@ def add_user():
|
|
184 |
return render_template('add_user.html')
|
185 |
return render_template('add_user.html')
|
186 |
|
187 |
-
@app.route('/
|
188 |
-
@require_page_password
|
189 |
-
def get_users():
|
190 |
"""Lấy danh sách tất cả người dùng từ cơ sở dữ liệu."""
|
191 |
conn = get_db_connection()
|
192 |
users_list = []
|
|
|
1 |
import os
|
2 |
import json
|
3 |
+
from flask import Flask, request, g, Response, render_template, redirect, url_for, flash, session # Thêm 'session'
|
4 |
import psycopg2
|
5 |
from dotenv import load_dotenv
|
6 |
from werkzeug.security import generate_password_hash, check_password_hash
|
|
|
21 |
# Biến môi trường cho mật khẩu trang
|
22 |
PAGE_PASSWORD = os.getenv('PAGE_PASSWORD')
|
23 |
|
24 |
+
# Cấu hình secret key cho Flask (CẦN THIẾT cho flash messages và session)
|
25 |
+
# Trong môi trường production, hãy tạo một chuỗi ngẫu nhiên mạnh và lưu vào biến môi trường FLASK_SECRET_KEY
|
26 |
app.config['SECRET_KEY'] = os.getenv('FLASK_SECRET_KEY', 'mot_chuoi_bi_mat_rat_ngau_nhien_va_dai')
|
27 |
|
28 |
if not PAGE_PASSWORD:
|
|
|
52 |
db.close()
|
53 |
print("Đã đóng kết nối Supabase PostgreSQL.")
|
54 |
|
55 |
+
# --- Hàm tiện ích để trả về JSON với tiếng Việt (giữ cho các API nếu cần) ---
|
56 |
def custom_jsonify(data, status_code=200):
|
57 |
response = Response(
|
58 |
response=json.dumps(data, ensure_ascii=False),
|
|
|
65 |
def require_page_password(f):
|
66 |
@wraps(f)
|
67 |
def decorated_function(*args, **kwargs):
|
68 |
+
# Kiểm tra session trước
|
69 |
+
if session.get('page_logged_in'):
|
70 |
+
return f(*args, **kwargs)
|
71 |
|
72 |
+
# Nếu chưa đăng nhập qua session, kiểm tra mật khẩu từ form/query/header
|
73 |
+
provided_password = request.form.get('page_password') or request.args.get('page_password')
|
74 |
if request.method == 'POST' and not provided_password:
|
75 |
provided_password = request.headers.get('X-Page-Password')
|
76 |
|
77 |
if not PAGE_PASSWORD:
|
78 |
flash("Lỗi cấu hình server: Mật khẩu trang chưa được thiết lập.", "error")
|
79 |
+
return redirect(url_for('home'))
|
80 |
|
81 |
if provided_password == PAGE_PASSWORD:
|
82 |
+
session['page_logged_in'] = True # Đặt session là đã đăng nhập
|
83 |
+
# Để tránh lỗi chuyển hướng lại chính trang đó với POST,
|
84 |
+
# hãy chuyển hướng đến chính URL hiện tại nhưng là GET request
|
85 |
+
return redirect(request.url)
|
86 |
else:
|
87 |
flash("Truy cập bị từ chối: Mật khẩu trang không hợp lệ.", "error")
|
88 |
+
return render_template('page_password_form.html', next_url=request.url) # Hiển thị form mật khẩu riêng
|
|
|
89 |
return decorated_function
|
90 |
|
91 |
+
# --- Tuyến đường mới để hiển thị form mật khẩu trang và xử lý đăng nhập ---
|
92 |
+
@app.route('/page_password', methods=['GET', 'POST'])
|
93 |
+
def page_password_form():
|
94 |
+
next_url = request.args.get('next') or url_for('home') # Lấy URL gốc muốn truy cập sau khi nhập mật khẩu
|
95 |
+
|
96 |
+
if request.method == 'POST':
|
97 |
+
provided_password = request.form.get('page_password_input') # Lấy từ form riêng này
|
98 |
+
|
99 |
+
if not PAGE_PASSWORD:
|
100 |
+
flash("Lỗi cấu hình server: Mật khẩu trang chưa được thiết lập.", "error")
|
101 |
+
return redirect(url_for('home'))
|
102 |
+
|
103 |
+
if provided_password == PAGE_PASSWORD:
|
104 |
+
session['page_logged_in'] = True
|
105 |
+
flash("Mật khẩu trang đã được xác thực thành công!", "success")
|
106 |
+
return redirect(next_url)
|
107 |
+
else:
|
108 |
+
flash("Mật khẩu trang không hợp lệ. Vui lòng thử lại.", "error")
|
109 |
+
return render_template('page_password_form.html', next_url=next_url)
|
110 |
+
|
111 |
+
# Nếu là GET request, hiển thị form mật khẩu
|
112 |
+
return render_template('page_password_form.html', next_url=next_url)
|
113 |
+
|
114 |
+
@app.route('/page_logout')
|
115 |
+
def page_logout():
|
116 |
+
session.pop('page_logged_in', None)
|
117 |
+
flash("Bạn đã đăng xuất khỏi phiên mật khẩu trang.", "info")
|
118 |
+
return redirect(url_for('home'))
|
119 |
+
|
120 |
# --- Các tuyến đường (Routes) của ứng dụng ---
|
121 |
|
122 |
@app.route('/')
|
|
|
124 |
"""Trang chủ của ứng dụng."""
|
125 |
return render_template('index.html')
|
126 |
|
127 |
+
# Áp dụng decorator cho các trang cần bảo vệ
|
128 |
+
@app.route('/test_db', methods=['GET']) # Đổi thành GET
|
129 |
+
@require_page_password # Áp dụng bảo vệ
|
130 |
def test_db():
|
131 |
"""Kiểm tra kết nối đến cơ sở dữ liệu và hiển thị kết quả."""
|
132 |
+
conn = get_db_connection()
|
133 |
+
db_version_info = "Chưa kiểm tra"
|
134 |
+
status_info = "none"
|
135 |
+
if conn is None:
|
136 |
+
flash("Không thể kết nối đến cơ sở dữ liệu.", "error")
|
137 |
+
db_version_info = "Lỗi kết nối"
|
138 |
+
status_info = "error"
|
139 |
+
else:
|
140 |
try:
|
141 |
cur = conn.cursor()
|
142 |
cur.execute("SELECT version();")
|
143 |
db_version = cur.fetchone()[0]
|
144 |
cur.close()
|
145 |
flash("Kết nối cơ sở dữ liệu thành công!", "success")
|
146 |
+
db_version_info = db_version
|
147 |
+
status_info = "success"
|
148 |
except Exception as e:
|
149 |
flash(f"Lỗi truy vấn cơ sở dữ liệu: {e}", "error")
|
150 |
+
db_version_info = "Lỗi truy vấn"
|
151 |
+
status_info = "error"
|
152 |
+
return render_template('test_db.html', db_version=db_version_info, status=status_info)
|
153 |
+
|
154 |
|
155 |
+
@app.route('/create_table', methods=['GET']) # Đổi thành GET
|
156 |
+
@require_page_password # Áp dụng bảo vệ
|
157 |
def create_table():
|
158 |
"""Tạo bảng 'users' nếu chưa tồn tại và hiển thị kết quả."""
|
159 |
+
conn = get_db_connection()
|
160 |
+
message_info = "Chưa tạo"
|
161 |
+
status_info = "none"
|
162 |
+
if conn is None:
|
163 |
+
flash("Không thể kết nối đến cơ sở dữ liệu.", "error")
|
164 |
+
message_info = "Lỗi kết nối"
|
165 |
+
status_info = "error"
|
166 |
+
else:
|
167 |
try:
|
168 |
cur = conn.cursor()
|
169 |
cur.execute("""
|
170 |
CREATE TABLE IF NOT EXISTS users (
|
171 |
+
id SERIAL PRIMARY음을 PRIMARY KEY,
|
172 |
name VARCHAR(100) NOT NULL,
|
173 |
email VARCHAR(100) UNIQUE NOT NULL,
|
174 |
password VARCHAR(255) NOT NULL
|
|
|
177 |
conn.commit()
|
178 |
cur.close()
|
179 |
flash("Bảng 'users' đã được tạo (nếu chưa tồn tại) với cột mật khẩu.", "success")
|
180 |
+
message_info = "Tạo bảng thành công"
|
181 |
+
status_info = "success"
|
182 |
except Exception as e:
|
183 |
conn.rollback()
|
184 |
flash(f"Lỗi khi tạo bảng: {e}", "error")
|
185 |
+
message_info = "Lỗi tạo bảng"
|
186 |
+
status_info = "error"
|
187 |
+
return render_template('create_table.html', message=message_info, status=status_info)
|
188 |
+
|
189 |
|
190 |
@app.route('/add_user', methods=['GET', 'POST'])
|
191 |
+
@require_page_password # Áp dụng bảo vệ
|
192 |
def add_user():
|
193 |
"""Thêm người dùng mới vào cơ sở dữ liệu với mật khẩu đã băm."""
|
194 |
if request.method == 'POST':
|
|
|
228 |
return render_template('add_user.html')
|
229 |
return render_template('add_user.html')
|
230 |
|
231 |
+
@app.route('/get_users', methods=['GET']) # Đổi thành GET
|
232 |
+
@require_page_password # Áp dụng bảo vệ
|
233 |
+
def get_users(): # Đổi tên hàm thành get_users cho rõ ràng
|
234 |
"""Lấy danh sách tất cả người dùng từ cơ sở dữ liệu."""
|
235 |
conn = get_db_connection()
|
236 |
users_list = []
|