This commit is contained in:
Radek
2025-03-31 10:46:49 +01:00
parent 9b778e1cc9
commit dafa85963c
11 changed files with 279 additions and 0 deletions

19
app/__init__.py Normal file
View File

@@ -0,0 +1,19 @@
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
db = SQLAlchemy()
login_manager = LoginManager()
def create_app():
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///webapp.db'
db.init_app(app)
login_manager.init_app(app)
with app.app_context():
from . import routes, models
db.create_all()
return app

9
app/forms.py Normal file
View File

@@ -0,0 +1,9 @@
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired, Length, EqualTo
class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired(), Length(min=4, max=150)])
password = PasswordField('Password', validators=[DataRequired()])
remember_me = BooleanField('Remember Me')
submit = SubmitField('Login')

8
app/models.py Normal file
View File

@@ -0,0 +1,8 @@
from . import db
from flask_login import UserMixin
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(150), unique=True, nullable=False)
password = db.Column(db.String(150), nullable=False)
is_admin = db.Column(db.Boolean, default=False)

38
app/routes.py Normal file
View File

@@ -0,0 +1,38 @@
from flask import render_template, redirect, url_for, flash, request
from flask_login import login_user, logout_user, login_required, current_user
from app import app, db
from app.forms import LoginForm
from app.models import User
@app.route('/')
@app.route('/home')
def home():
return render_template('home.html')
@app.route('/admin')
@login_required
def admin():
if not current_user.is_admin:
flash('You do not have permission to access this page.')
return redirect(url_for('home'))
return render_template('admin.html')
@app.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('home'))
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if user and user.check_password(form.password.data):
login_user(user, remember=form.remember_me.data)
next_page = request.args.get('next')
return redirect(next_page) if next_page else redirect(url_for('home'))
else:
flash('Invalid username or password')
return render_template('login.html', form=form)
@app.route('/logout')
def logout():
logout_user()
return redirect(url_for('home'))

37
app/static/css/styles.css Normal file
View File

@@ -0,0 +1,37 @@
/* Base styles */
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f4;
}
/* Navigation styles */
nav {
@apply bg-gray-800 text-white p-4;
}
nav a {
@apply text-white mx-4 no-underline;
}
/* Form styles */
form {
@apply max-w-md mx-auto my-8 p-6 bg-white rounded-lg shadow-md;
}
form label {
@apply block mb-2;
}
form input {
@apply w-full p-2 mb-4 border border-gray-300 rounded;
}
form button {
@apply py-2 px-4 bg-gray-800 text-white rounded cursor-pointer;
}
form button:hover {
@apply bg-gray-700;
}

13
app/static/js/scripts.js Normal file
View File

@@ -0,0 +1,13 @@
// JavaScript for handling form submissions and other dynamic behavior
// Example function to handle form submission
document.addEventListener('DOMContentLoaded', function() {
const form = document.querySelector('form');
if (form) {
form.addEventListener('submit', function(event) {
event.preventDefault();
// Add form validation or submission logic here
alert('Form submitted!');
});
}
});

47
app/templates/admin.html Normal file
View File

@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Dashboard</title>
<link href="{{ url_for('static', filename='css/styles.css') }}" rel="stylesheet">
</head>
<body>
<header>
<nav>
<ul>
<li><a href="{{ url_for('home') }}">Home</a></li>
<li><a href="{{ url_for('admin') }}">Admin</a></li>
<li><a href="{{ url_for('logout') }}">Logout</a></li>
</ul>
</nav>
</header>
<main>
<h1>Admin Dashboard</h1>
<section>
<h2>Manage Users</h2>
<table>
<thead>
<tr>
<th>Username</th>
<th>Role</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{ user.username }}</td>
<td>{{ user.role }}</td>
<td>
<button onclick="editUser('{{ user.username }}')">Edit</button>
<button onclick="deleteUser('{{ user.username }}')">Delete</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</section>
</main>
</body>
</html>

7
app/templates/base.html Normal file
View File

@@ -0,0 +1,7 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Flask App{% endblock %}</title>
<link href="{{ url_for('static', filename='css/styles.css') }}" rel="stylesheet">

13
app/templates/home.html Normal file
View File

@@ -0,0 +1,13 @@
{% extends "base.html" %}
{% block title %}Home{% endblock %}
{% block content %}
<h1>Welcome to the Flask App</h1>
<p>This is the homepage of the Flask application.</p>
<nav>
<ul>
<li><a href="{{ url_for('home') }}">Home</a></li>
<li><a href="{{ url_for('admin') }}">Admin</a></li>
<li><a href="{{ url_for('login') }}">Login</a></li>
</ul>
</nav>
{% endblock %}

32
app/templates/login.html Normal file
View File

@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login</title>
<link href="{{ url_for('static', filename='css/styles.css') }}" rel="stylesheet">
</head>
<body>
<header>
<nav>
<ul>
<li><a href="{{ url_for('home') }}">Home</a></li>
<li><a href="{{ url_for('admin') }}">Admin</a></li>
<li><a href="{{ url_for('logout') }}">Logout</a></li>
</ul>
</nav>
</header>
<main>
<h1>Login</h1>
<form method="POST" action="{{ url_for('login') }}">
<label for="username">Username:</label>
<input type="text" id="username" name="username" required>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required>
<label for="otp">OTP:</label>
<input type="text" id="otp" name="otp" required>
<button type="submit">Login</button>
</form>
</main>
</body>
</html>

56
plan.md Normal file
View File

@@ -0,0 +1,56 @@
# Flask Application Boilerplate Plan
## Overview
This plan outlines the steps to create a comprehensive Flask application boilerplate that integrates Tailwind CSS for styling, HTML templates for the frontend, and includes the following features:
1. **Homepage**: A welcoming landing page with basic navigation.
2. **Admin Page**: A secure admin dashboard for managing users, accessible only to authenticated administrators.
3. **Login Page**: A secure login page with two-factor authentication (2FA) for enhanced security.
4. **Database**: Use SQLite for the database, with the database file named `webapp.db` and the first username set to `admin`.
## Project Structure
The application will be structured with proper routing, authentication mechanisms, and session management. Necessary configurations for Tailwind CSS and HTML templates will be included.
## Steps to Implement the Plan
### 1. Set Up the Flask Application
- Create the basic Flask application structure.
- Set up the necessary configurations for Tailwind CSS and HTML templates.
### 2. Create the Homepage
- Design a welcoming landing page with basic navigation.
- Implement the necessary routes and templates.
### 3. Create the Admin Page
- Design a secure admin dashboard for managing users.
- Implement authentication mechanisms to restrict access to authenticated administrators.
- Implement the necessary routes and templates.
### 4. Create the Login Page
- Design a secure login page with two-factor authentication (2FA).
- Implement the necessary routes and templates.
- Set up the authentication mechanisms for the login page.
### 5. Set Up the Database
- Use SQLite for the database.
- Create the necessary database schema.
- Implement the database connection and queries.
### 6. Provide Instructions for Setting Up and Running the Application
- Write clear instructions for setting up and running the application.
## Mermaid Diagram
```mermaid
graph TD
A[Start] --> B[Set Up Flask Application]
B --> C[Create Homepage]
C --> D[Create Admin Page]
D --> E[Create Login Page]
E --> F[Set Up Database]
F --> G[Provide Instructions]
G --> H[End]
```
## Next Steps
- Switch to another mode to implement the solution based on this plan.