first commit
This commit is contained in:
commit
e7b76acac9
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
@ -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
|
13
app.py
Normal file
13
app.py
Normal file
@ -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)
|
20
app/__init__.py
Normal file
20
app/__init__.py
Normal file
@ -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
|
18
app/models.py
Normal file
18
app/models.py
Normal file
@ -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))
|
70
app/routes.py
Normal file
70
app/routes.py
Normal file
@ -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')
|
9
app/static/css/style.css
Normal file
9
app/static/css/style.css
Normal file
@ -0,0 +1,9 @@
|
||||
body {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
}
|
BIN
app/static/images/logo.png
Normal file
BIN
app/static/images/logo.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 62 KiB |
8
app/templates/about.html
Normal file
8
app/templates/about.html
Normal file
@ -0,0 +1,8 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}關於我們{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>關於我們</h1>
|
||||
<p>這是一個關於我們的介紹頁面。</p>
|
||||
{% endblock %}
|
75
app/templates/base.html
Normal file
75
app/templates/base.html
Normal file
@ -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>
|
8
app/templates/contact.html
Normal file
8
app/templates/contact.html
Normal file
@ -0,0 +1,8 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}聯絡我們{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>聯絡我們</h1>
|
||||
<p>這裡是聯絡我們的資訊頁面。</p>
|
||||
{% endblock %}
|
8
app/templates/index.html
Normal file
8
app/templates/index.html
Normal file
@ -0,0 +1,8 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}首頁{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>歡迎來到首頁</h1>
|
||||
<p>這是我的網站首頁。</p>
|
||||
{% endblock %}
|
23
app/templates/login.html
Normal file
23
app/templates/login.html
Normal file
@ -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 %}
|
23
app/templates/register.html
Normal file
23
app/templates/register.html
Normal file
@ -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 %}
|
9
app/utils.py
Normal file
9
app/utils.py
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user