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">&copy; 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