commit e7b76acac95cedb2c9296d0449a43ffe6fa38913 Author: Wensheng <wensheng9131@gmail.com> Date: Wed Oct 9 17:35:57 2024 +0800 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..94b5324 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +venv/ +.venv/ +env/ +__pycache__/ +*.pyc +*.db +*.sqlite +*.log +.env +.vscode/ +.idea/ +.DS_Store +Thumbs.db +config.py +secrets.json +personal_notes.txt diff --git a/app.py b/app.py new file mode 100644 index 0000000..8ad43e0 --- /dev/null +++ b/app.py @@ -0,0 +1,13 @@ +from app import create_app, db +from app.utils import check_db_connection + +app = create_app() + +if __name__ == '__main__': + with app.app_context(): + if check_db_connection(): + db.create_all() + print("數據庫連接成功,並創建了所有表。") + else: + print("警告:無法連接到數據庫。請檢查你的數據庫設置。") + app.run(debug=True) diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..f3cd7e8 --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,20 @@ +from flask import Flask +from flask_sqlalchemy import SQLAlchemy +from flask_login import LoginManager +from config import Config + +db = SQLAlchemy() +login_manager = LoginManager() +login_manager.login_view = 'main.login' + +def create_app(): + app = Flask(__name__) + app.config.from_object(Config) + + db.init_app(app) + login_manager.init_app(app) + + from app.routes import bp as main_bp + app.register_blueprint(main_bp) + + return app diff --git a/app/models.py b/app/models.py new file mode 100644 index 0000000..606d6f6 --- /dev/null +++ b/app/models.py @@ -0,0 +1,18 @@ +from app import db, login_manager +from flask_login import UserMixin +from werkzeug.security import generate_password_hash, check_password_hash + +class User(UserMixin, db.Model): + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(80), unique=True, nullable=False) + password = db.Column(db.String(120), nullable=False) + + def set_password(self, password): + self.password = generate_password_hash(password) + + def check_password(self, password): + return check_password_hash(self.password, password) + +@login_manager.user_loader +def load_user(user_id): + return User.query.get(int(user_id)) diff --git a/app/routes.py b/app/routes.py new file mode 100644 index 0000000..64c0fd7 --- /dev/null +++ b/app/routes.py @@ -0,0 +1,70 @@ +from flask import Blueprint, render_template, request, redirect, url_for, flash +from flask_login import login_user, login_required, logout_user, current_user +from app import db +from app.models import User +from app.utils import check_db_connection + +bp = Blueprint('main', __name__) + +@bp.route('/') +def index(): + return render_template('index.html') + + +@bp.route('/about') +def about(): + return render_template('about.html') + + +@bp.route('/contact') +def contact(): + return render_template('contact.html') + + +@bp.route('/login', methods=['GET', 'POST']) +def login(): + if not check_db_connection(): + flash('無法連接到數據庫,請稍後再試。', 'danger') + return render_template('login.html') + + if request.method == 'POST': + username = request.form.get('username') + password = request.form.get('password') + user = User.query.filter_by(username=username).first() + if user and user.check_password(password): + login_user(user) + flash('登入成功!', 'success') + return redirect(url_for('main.index')) + else: + flash('登入失敗。請檢查您的用戶名和密碼。', 'danger') + return render_template('login.html') + + +@bp.route('/logout') +@login_required +def logout(): + logout_user() + flash('您已成功登出。', 'success') + return redirect(url_for('main.index')) + + +@bp.route('/register', methods=['GET', 'POST']) +def register(): + if not check_db_connection(): + flash('無法連接到數據庫,請稍後再試。', 'danger') + return render_template('register.html') + + if request.method == 'POST': + username = request.form.get('username') + password = request.form.get('password') + existing_user = User.query.filter_by(username=username).first() + if existing_user: + flash('該用戶名已被使用。', 'danger') + else: + new_user = User(username=username) + new_user.set_password(password) + db.session.add(new_user) + db.session.commit() + flash('註冊成功!請登入。', 'success') + return redirect(url_for('main.login')) + return render_template('register.html') diff --git a/app/static/css/style.css b/app/static/css/style.css new file mode 100644 index 0000000..d785820 --- /dev/null +++ b/app/static/css/style.css @@ -0,0 +1,9 @@ +body { + min-height: 100vh; + display: flex; + flex-direction: column; +} + +main { + flex: 1; +} diff --git a/app/static/images/logo.png b/app/static/images/logo.png new file mode 100644 index 0000000..0dc31ea Binary files /dev/null and b/app/static/images/logo.png differ diff --git a/app/templates/about.html b/app/templates/about.html new file mode 100644 index 0000000..3c261aa --- /dev/null +++ b/app/templates/about.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} + +{% block title %}關於我們{% endblock %} + +{% block content %} +<h1>關於我們</h1> +<p>這是一個關於我們的介紹頁面。</p> +{% endblock %} \ No newline at end of file diff --git a/app/templates/base.html b/app/templates/base.html new file mode 100644 index 0000000..ae0150e --- /dev/null +++ b/app/templates/base.html @@ -0,0 +1,75 @@ +<!DOCTYPE html> +<html lang="zh"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>{% block title %}我的網站{% endblock %}</title> + <!-- Bootstrap CSS --> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"> + <!-- 自定義 CSS --> + <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}"> + {% block extra_css %}{% endblock %} +</head> +<body> + <nav class="navbar navbar-expand-lg navbar-light bg-light"> + <div class="container"> + <a class="navbar-brand" href="{{ url_for('main.index') }}"> + <img src="{{ url_for('static', filename='images/logo.png') }}" alt="Logo" width="30" height="30" class="d-inline-block align-top"> + </a> + <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> + <span class="navbar-toggler-icon"></span> + </button> + <div class="collapse navbar-collapse" id="navbarNav"> + <ul class="navbar-nav ms-auto"> + <li class="nav-item"> + <a class="nav-link" href="{{ url_for('main.index') }}">首頁</a> + </li> + <li class="nav-item"> + <a class="nav-link" href="{{ url_for('main.about') }}">關於我們</a> + </li> + <li class="nav-item"> + <a class="nav-link" href="{{ url_for('main.contact') }}">聯絡我們</a> + </li> + {% if current_user.is_authenticated %} + <li class="nav-item"> + <a class="nav-link" href="{{ url_for('main.logout') }}">登出</a> + </li> + {% else %} + <li class="nav-item"> + <a class="nav-link" href="{{ url_for('main.login') }}">登入</a> + </li> + <li class="nav-item"> + <a class="nav-link" href="{{ url_for('main.register') }}">註冊</a> + </li> + {% endif %} + </ul> + </div> + </div> + </nav> + + <main class="container mt-4"> + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} + <div class="alert alert-{{ category }} alert-dismissible fade show" role="alert"> + {{ message }} + <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> + </div> + {% endfor %} + {% endif %} + {% endwith %} + + {% block content %}{% endblock %} + </main> + + <footer class="footer mt-auto py-3 bg-light"> + <div class="container text-center"> + <span class="text-muted">© 2024 我的網站. All rights reserved.</span> + </div> + </footer> + + <!-- Bootstrap Bundle with Popper --> + <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script> + {% block extra_js %}{% endblock %} +</body> +</html> diff --git a/app/templates/contact.html b/app/templates/contact.html new file mode 100644 index 0000000..0dc322c --- /dev/null +++ b/app/templates/contact.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} + +{% block title %}聯絡我們{% endblock %} + +{% block content %} +<h1>聯絡我們</h1> +<p>這裡是聯絡我們的資訊頁面。</p> +{% endblock %} \ No newline at end of file diff --git a/app/templates/index.html b/app/templates/index.html new file mode 100644 index 0000000..3dc94bc --- /dev/null +++ b/app/templates/index.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} + +{% block title %}首頁{% endblock %} + +{% block content %} +<h1>歡迎來到首頁</h1> +<p>這是我的網站首頁。</p> +{% endblock %} \ No newline at end of file diff --git a/app/templates/login.html b/app/templates/login.html new file mode 100644 index 0000000..650141e --- /dev/null +++ b/app/templates/login.html @@ -0,0 +1,23 @@ +{% extends "base.html" %} + +{% block title %}登入 - 我的網站{% endblock %} + +{% block content %} +<div class="row justify-content-center"> + <div class="col-md-6"> + <h2 class="mb-4">登入</h2> + <form method="POST"> + <div class="mb-3"> + <label for="username" class="form-label">用戶名</label> + <input type="text" class="form-control" id="username" name="username" required> + </div> + <div class="mb-3"> + <label for="password" class="form-label">密碼</label> + <input type="password" class="form-control" id="password" name="password" required> + </div> + <button type="submit" class="btn btn-primary">登入</button> + </form> + <p class="mt-3">還沒有帳號?<a href="{{ url_for('main.register') }}">註冊</a></p> + </div> +</div> +{% endblock %} diff --git a/app/templates/register.html b/app/templates/register.html new file mode 100644 index 0000000..43775b5 --- /dev/null +++ b/app/templates/register.html @@ -0,0 +1,23 @@ +{% extends "base.html" %} + +{% block title %}註冊 - 我的網站{% endblock %} + +{% block content %} +<div class="row justify-content-center"> + <div class="col-md-6"> + <h2 class="mb-4">註冊</h2> + <form method="POST"> + <div class="mb-3"> + <label for="username" class="form-label">用戶名</label> + <input type="text" class="form-control" id="username" name="username" required> + </div> + <div class="mb-3"> + <label for="password" class="form-label">密碼</label> + <input type="password" class="form-control" id="password" name="password" required> + </div> + <button type="submit" class="btn btn-primary">註冊</button> + </form> + <p class="mt-3">已有帳號?<a href="{{ url_for('main.login') }}">登入</a></p> + </div> +</div> +{% endblock %} diff --git a/app/utils.py b/app/utils.py new file mode 100644 index 0000000..6127986 --- /dev/null +++ b/app/utils.py @@ -0,0 +1,9 @@ +from app import db +from sqlalchemy.exc import SQLAlchemyError + +def check_db_connection(): + try: + db.session.query("1").from_statement("SELECT 1").all() + return True + except SQLAlchemyError: + return False