from fastapi import FastAPI import gradio as gr import os import sqlalchemy from sqlalchemy import create_engine, text, Column, Integer, String, DateTime from sqlalchemy.orm import sessionmaker, declarative_base from datetime import datetime import time # --- Cấu hình Biến Database (PHẢI KHỚP VỚI run.sh) --- DB_USER="gradio_user" DB_PASS="local_password_123" DB_NAME="gradio_db" DB_HOST="localhost" # Kết nối cục bộ DB_PORT="5432" # --- Thiết lập Cơ sở dữ liệu (Database Setup) --- # 1. Tạo chuỗi kết nối cục bộ DATABASE_URL = f"postgresql://{DB_USER}:{DB_PASS}@{DB_HOST}:{DB_PORT}/{DB_NAME}" DB_IS_LOCAL = True # 2. Tạo kết nối print("Đang kết nối tới cơ sở dữ liệu PostgreSQL cục bộ...") try: engine = create_engine(DATABASE_URL) # Thử kết nối nhanh with engine.connect() as connection: print("Kết nối PostgreSQL cục bộ thành công.") except Exception as e: print(f"LỖI NGHIÊM TRỌNG: Không thể kết nối tới DB cục bộ: {e}") print("Sử dụng cơ sở dữ liệu SQLite trong bộ nhớ để demo.") engine = create_engine("sqlite:///:memory:") DB_IS_LOCAL = False # Đánh dấu là đang chạy demo Base = declarative_base() # 3. Định nghĩa mô hình bảng (Table Model) class Note(Base): __tablename__ = 'notes' id = Column(Integer, primary_key=True, autoincrement=True) username = Column(String(100), nullable=False) message = Column(String(500), nullable=False) created_at = Column(DateTime, default=datetime.utcnow) # 4. Tạo bảng (nếu chưa tồn tại) try: Base.metadata.create_all(engine) print("Bảng 'notes' đã được kiểm tra/tạo thành công.") except Exception as e: print(f"Lỗi khi tạo bảng (có thể bảng đã tồn tại): {e}") # 5. Tạo một Session để tương tác với DB SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) # --- Hàm xử lý Gradio --- def add_entry(username, message): """Thêm một mục mới vào cơ sở dữ liệu.""" if not DB_IS_LOCAL: print("Chạy ở chế độ demo (SQLite), không thêm vào DB.") return get_entries() if not username or not message: return get_entries() # Chỉ tải lại, không thêm gì try: db = SessionLocal() new_note = Note(username=username, message=message) db.add(new_note) db.commit() print(f"Đã thêm mục mới từ: {username}") except Exception as e: print(f"Lỗi khi thêm mục: {e}") db.rollback() return f"Lỗi khi thêm mục: {e}" finally: db.close() # Sau khi thêm, tải lại các mục return get_entries() def get_entries(): """Lấy tất cả các mục từ cơ sở dữ liệu và định dạng chúng.""" if not DB_IS_LOCAL: return "Ứng dụng đang chạy ở chế độ demo (SQLite). Không thể kết nối với DB cục bộ." output = "" try: db = SessionLocal() # Lấy 20 mục gần nhất notes = db.query(Note).order_by(Note.created_at.desc()).limit(20).all() if not notes: output = "Chưa có tin nhắn nào. Hãy là người đầu tiên!" else: # Định dạng đầu ra (hiển thị từ mới nhất) for note in notes: time_str = note.created_at.strftime('%Y-%m-%d %H:%M') output += f"[{time_str}] {note.username}:\n{note.message}\n" + "-"*20 + "\n" except Exception as e: print(f"Lỗi khi tải mục: {e}") output = f"Lỗi khi tải mục: {e}" finally: db.close() return output with gr.Blocks(title="Gradio + PG (Local)") as gr_interface: gr.Markdown("# Sổ khách (Gradio + PostgreSQL Cục bộ)") gr.Markdown("Để lại tin nhắn và xem các tin nhắn trước đó. CẢNH BÁO: Dữ liệu sẽ bị mất khi Space khởi động lại!") with gr.Row(): with gr.Column(scale=1): username_input = gr.Textbox(label="Tên của bạn") message_input = gr.Textbox(label="Tin nhắn của bạn", lines=4, placeholder="Viết gì đó...") submit_button = gr.Button("Gửi tin nhắn", variant="primary") with gr.Column(scale=2): output_display = gr.Textbox( label="Tin nhắn đã lưu (Tải lại tự động)", lines=12, interactive=False ) # Tải các mục khi giao diện khởi động gr_interface.load(get_entries, outputs=output_display) # Liên kết nút Gửi submit_button.click( fn=add_entry, inputs=[username_input, message_input], outputs=output_display ).then( # Xóa các ô input sau khi gửi lambda: (gr.update(value=""), gr.update(value="")), outputs=[username_input, message_input] ) app = FastAPI() app = gr.mount_gradio_app(app, gr_interface, path="/")