Skip to content

Commit a4fad23

Browse files
committed
Release v1.0.0
1 parent ae3e45a commit a4fad23

13 files changed

+385
-1
lines changed

.gitignore

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Python
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
*.so
6+
.Python
7+
env/
8+
build/
9+
develop-eggs/
10+
dist/
11+
downloads/
12+
eggs/
13+
.eggs/
14+
lib/
15+
lib64/
16+
parts/
17+
sdist/
18+
var/
19+
wheels/
20+
*.egg-info/
21+
.installed.cfg
22+
*.egg
23+
24+
# Flask
25+
instance/
26+
.webassets-cache
27+
app.db
28+
29+
# Virtual Environment
30+
venv/
31+
ENV/
32+
33+
# IDE
34+
.idea/
35+
.vscode/
36+
*.swp
37+
*.swo
38+
39+
# OS
40+
.DS_Store
41+
Thumbs.db

README.md

+63-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,63 @@
1-
# docs-flask-simple-app
1+
# Flask Authentication Project
2+
3+
A simple Flask application demonstrating authentication, SQLAlchemy ORM, and a REST API.
4+
5+
## Features
6+
7+
- User authentication (login/register/logout)
8+
- Public and protected routes
9+
- SQLite database with SQLAlchemy ORM
10+
- REST API endpoint for users
11+
- Bootstrap-styled templates
12+
13+
## Installation
14+
15+
1. Create a virtual environment:
16+
```bash
17+
python -m venv venv
18+
```
19+
20+
2. Activate the virtual environment:
21+
- Windows:
22+
```bash
23+
venv\Scripts\activate
24+
```
25+
- Unix or MacOS:
26+
```bash
27+
source venv/bin/activate
28+
```
29+
30+
3. Install requirements:
31+
```bash
32+
pip install -r requirements.txt
33+
```
34+
35+
4. Run the application:
36+
```bash
37+
python run.py
38+
```
39+
40+
## Routes
41+
42+
- `/` - Public homepage
43+
- `/private` - Protected route (requires login)
44+
- `/api/users` - API endpoint listing all users
45+
- `/#` - User login
46+
- `/register` - User registration
47+
- `/logout` - User logout
48+
49+
## API Usage
50+
51+
Get list of all users:
52+
```bash
53+
curl http://localhost:5000/api/users
54+
```
55+
56+
## Security Note
57+
58+
This is a demonstration project. In a production environment, you should:
59+
- Use a proper secret key
60+
- Enable HTTPS
61+
- Implement proper password validation
62+
- Add rate limiting
63+
- Use environment variables for sensitive data

app/__init__.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from flask import Flask
2+
from flask_sqlalchemy import SQLAlchemy
3+
from flask_login import LoginManager
4+
from app.config import Config
5+
6+
db = SQLAlchemy()
7+
login_manager = LoginManager()
8+
login_manager.login_view = 'auth.login'
9+
10+
def create_app():
11+
app = Flask(__name__)
12+
app.config.from_object(Config)
13+
14+
db.init_app(app)
15+
login_manager.init_app(app)
16+
17+
from app.routes import main, auth
18+
app.register_blueprint(main)
19+
app.register_blueprint(auth)
20+
21+
with app.app_context():
22+
db.create_all()
23+
24+
return app

app/config.py

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
class Config:
2+
SECRET_KEY = 'your-secret-key-here' # Change this to a secure secret key
3+
SQLALCHEMY_DATABASE_URI = 'sqlite:///app.db'
4+
SQLALCHEMY_TRACK_MODIFICATIONS = False

app/models.py

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from flask_login import UserMixin
2+
from werkzeug.security import generate_password_hash, check_password_hash
3+
from app import db, login_manager
4+
5+
class User(UserMixin, db.Model):
6+
id = db.Column(db.Integer, primary_key=True)
7+
username = db.Column(db.String(64), unique=True, nullable=False)
8+
password_hash = db.Column(db.String(128))
9+
10+
def set_password(self, password):
11+
self.password_hash = generate_password_hash(password)
12+
13+
def check_password(self, password):
14+
return check_password_hash(self.password_hash, password)
15+
16+
@login_manager.user_loader
17+
def load_user(id):
18+
return User.query.get(int(id))

app/routes.py

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from flask import Blueprint, render_template, redirect, url_for, request, jsonify
2+
from flask_login import login_user, logout_user, login_required, current_user
3+
from app.models import User, db
4+
5+
main = Blueprint('main', __name__)
6+
auth = Blueprint('auth', __name__)
7+
8+
# Public homepage route
9+
@main.route('/')
10+
def home():
11+
return render_template('home.html')
12+
13+
# Protected route
14+
@main.route('/private')
15+
@login_required
16+
def private():
17+
return render_template('private.html')
18+
19+
# API route to list users
20+
@main.route('/api/users')
21+
def list_users():
22+
users = User.query.all()
23+
return jsonify([{'id': user.id, 'username': user.username} for user in users])
24+
25+
# Authentication routes
26+
@auth.route('/#', methods=['GET', 'POST'])
27+
def login():
28+
if request.method == 'POST':
29+
username = request.form.get('username')
30+
password = request.form.get('password')
31+
user = User.query.filter_by(username=username).first()
32+
33+
if user and user.check_password(password):
34+
login_user(user)
35+
return redirect(url_for('main.private'))
36+
37+
return render_template('login.html')
38+
39+
@auth.route('/register', methods=['GET', 'POST'])
40+
def register():
41+
if request.method == 'POST':
42+
username = request.form.get('username')
43+
password = request.form.get('password')
44+
45+
if User.query.filter_by(username=username).first():
46+
return "Username already exists"
47+
48+
user = User(username=username)
49+
user.set_password(password)
50+
db.session.add(user)
51+
db.session.commit()
52+
53+
return redirect(url_for('auth.login'))
54+
55+
return render_template('register.html')
56+
57+
@auth.route('/logout')
58+
@login_required
59+
def logout():
60+
logout_user()
61+
return redirect(url_for('main.home'))

app/templates/base.html

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>{% block title %}{% endblock %} - Flask App</title>
7+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
8+
</head>
9+
<body>
10+
<nav class="navbar navbar-expand-lg navbar-dark bg-dark mb-4">
11+
<div class="container">
12+
<a class="navbar-brand" href="{{ url_for('main.home') }}">Flask App</a>
13+
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
14+
<span class="navbar-toggler-icon"></span>
15+
</button>
16+
<div class="collapse navbar-collapse" id="navbarNav">
17+
<ul class="navbar-nav me-auto">
18+
<li class="nav-item">
19+
<a class="nav-link" href="{{ url_for('main.home') }}">Home</a>
20+
</li>
21+
{% if current_user.is_authenticated %}
22+
<li class="nav-item">
23+
<a class="nav-link" href="{{ url_for('main.private') }}">Private</a>
24+
</li>
25+
{% endif %}
26+
</ul>
27+
<ul class="navbar-nav">
28+
{% if current_user.is_authenticated %}
29+
<li class="nav-item">
30+
<span class="nav-link">Welcome, {{ current_user.username }}!</span>
31+
</li>
32+
<li class="nav-item">
33+
<a class="nav-link" href="{{ url_for('auth.logout') }}">Logout</a>
34+
</li>
35+
{% else %}
36+
<li class="nav-item">
37+
<a class="nav-link" href="{{ url_for('auth.login') }}">Login</a>
38+
</li>
39+
<li class="nav-item">
40+
<a class="nav-link" href="{{ url_for('auth.register') }}">Register</a>
41+
</li>
42+
{% endif %}
43+
</ul>
44+
</div>
45+
</div>
46+
</nav>
47+
48+
<div class="container">
49+
{% with messages = get_flashed_messages(with_categories=true) %}
50+
{% if messages %}
51+
{% for category, message in messages %}
52+
<div class="alert alert-{{ category }}">{{ message }}</div>
53+
{% endfor %}
54+
{% endif %}
55+
{% endwith %}
56+
57+
{% block content %}{% endblock %}
58+
</div>
59+
60+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
61+
</body>
62+
</html>

app/templates/home.html

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{% extends "base.html" %}
2+
3+
{% block title %}Home{% endblock %}
4+
5+
{% block content %}
6+
<div class="row justify-content-center">
7+
<div class="col-md-8">
8+
<div class="card">
9+
<div class="card-body">
10+
<h1 class="card-title">Welcome to Flask App</h1>
11+
<p class="card-text">This is a public page that anyone can access.</p>
12+
{% if not current_user.is_authenticated %}
13+
<p class="card-text">
14+
<a href="{{ url_for('auth.login') }}" class="btn btn-primary">Login</a> or
15+
<a href="{{ url_for('auth.register') }}" class="btn btn-success">Register</a>
16+
to access private content.
17+
</p>
18+
{% endif %}
19+
</div>
20+
</div>
21+
</div>
22+
</div>
23+
{% endblock %}

app/templates/#.html

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{% extends "base.html" %}
2+
3+
{% block title %}Login{% endblock %}
4+
5+
{% block content %}
6+
<div class="row justify-content-center">
7+
<div class="col-md-6">
8+
<div class="card">
9+
<div class="card-body">
10+
<h2 class="card-title text-center mb-4">Login</h2>
11+
<form method="POST">
12+
<div class="mb-3">
13+
<label for="username" class="form-label">Username</label>
14+
<input type="text" class="form-control" id="username" name="username" required>
15+
</div>
16+
<div class="mb-3">
17+
<label for="password" class="form-label">Password</label>
18+
<input type="password" class="form-control" id="password" name="password" required>
19+
</div>
20+
<div class="d-grid">
21+
<button type="submit" class="btn btn-primary">Login</button>
22+
</div>
23+
</form>
24+
<p class="text-center mt-3">
25+
Don't have an account? <a href="{{ url_for('auth.register') }}">Register here</a>
26+
</p>
27+
</div>
28+
</div>
29+
</div>
30+
</div>
31+
{% endblock %}

app/templates/private.html

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{% extends "base.html" %}
2+
3+
{% block title %}Private Page{% endblock %}
4+
5+
{% block content %}
6+
<div class="row justify-content-center">
7+
<div class="col-md-8">
8+
<div class="card">
9+
<div class="card-body">
10+
<h1 class="card-title">Private Page</h1>
11+
<p class="card-text">Welcome to the private page, {{ current_user.username }}!</p>
12+
<p class="card-text">This content is only visible to authenticated users.</p>
13+
</div>
14+
</div>
15+
</div>
16+
</div>
17+
{% endblock %}

app/templates/register.html

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{% extends "base.html" %}
2+
3+
{% block title %}Register{% endblock %}
4+
5+
{% block content %}
6+
<div class="row justify-content-center">
7+
<div class="col-md-6">
8+
<div class="card">
9+
<div class="card-body">
10+
<h2 class="card-title text-center mb-4">Register</h2>
11+
<form method="POST">
12+
<div class="mb-3">
13+
<label for="username" class="form-label">Username</label>
14+
<input type="text" class="form-control" id="username" name="username" required>
15+
</div>
16+
<div class="mb-3">
17+
<label for="password" class="form-label">Password</label>
18+
<input type="password" class="form-control" id="password" name="password" required>
19+
</div>
20+
<div class="d-grid">
21+
<button type="submit" class="btn btn-success">Register</button>
22+
</div>
23+
</form>
24+
<p class="text-center mt-3">
25+
Already have an account? <a href="{{ url_for('auth.login') }}">Login here</a>
26+
</p>
27+
</div>
28+
</div>
29+
</div>
30+
</div>
31+
{% endblock %}

requirements.txt

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Flask==3.0.0
2+
Flask-SQLAlchemy==3.1.1
3+
Flask-Login==0.6.3
4+
werkzeug==3.0.1

0 commit comments

Comments
 (0)