Update
This commit is contained in:
parent
c5e2e70332
commit
f097a8fdb3
18 changed files with 114 additions and 89 deletions
|
@ -5,28 +5,13 @@ from dotenv import load_dotenv
|
|||
from flask import jsonify
|
||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||
from argon2 import PasswordHasher
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
DEFAULTS = {
|
||||
'SECRET_KEY': '',
|
||||
'BEHIND_PROXY': False
|
||||
}
|
||||
|
||||
def get(key, type: type = str):
|
||||
value = os.environ.get(key)
|
||||
|
||||
if value is None:
|
||||
return DEFAULTS[key]
|
||||
|
||||
if type == bool:
|
||||
value = value.lower() == 'true'
|
||||
|
||||
return value
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['SECRET_KEY'] = get('SECRET_KEY')
|
||||
app.config['BEHIND_PROXY'] = get('BEHIND_PROXY', bool)
|
||||
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')
|
||||
app.config['BEHIND_PROXY'] = os.environ.get('BEHIND_PROXY') == 'true'
|
||||
|
||||
app.url_map.strict_slashes = False
|
||||
|
||||
|
@ -72,10 +57,6 @@ register_blueprints(app)
|
|||
from .scheduler import initialize_scheduler
|
||||
initialize_scheduler(app)
|
||||
|
||||
# TODO find a better way to do this
|
||||
from .roles import Role
|
||||
app.jinja_env.globals['Role'] = Role
|
||||
|
||||
# Flask CLI command to run migrations
|
||||
@app.cli.command("run-migrations")
|
||||
def run_migrations_command():
|
||||
|
|
|
@ -31,7 +31,7 @@ def index():
|
|||
@login_required
|
||||
@require_role(Role.ADMIN)
|
||||
def dashboard():
|
||||
return render_template('dashboard.html')
|
||||
return render_template('admin_dashboard.html')
|
||||
|
||||
@blueprint.route('/manage_user/<id>', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
|
@ -49,4 +49,4 @@ def manage_user(id: int):
|
|||
form.email.data = user.email
|
||||
form.username.data = user.username
|
||||
|
||||
return render_template('manage_user.html', user=user)
|
||||
return render_template('admin_manage_user.html', user=user)
|
|
@ -67,11 +67,11 @@ def login():
|
|||
if user and user.verify_password(password):
|
||||
login_user(user)
|
||||
|
||||
return redirect_to_next(default=url_for('.login_success'))
|
||||
return redirect_to_next()
|
||||
else:
|
||||
flash('Invalid username or password', 'error')
|
||||
|
||||
return render_template('login.html', form=form)
|
||||
return render_template('auth_login.html', form=form)
|
||||
|
||||
@blueprint.route('/register', methods=['GET', 'POST'])
|
||||
def register():
|
||||
|
@ -92,15 +92,11 @@ def register():
|
|||
|
||||
return redirect(url_for('.register_success'))
|
||||
|
||||
return render_template('register.html', form=form)
|
||||
return render_template('auth_register.html', form=form)
|
||||
|
||||
@blueprint.route('/register_success')
|
||||
def register_success():
|
||||
return render_template('register_success.html')
|
||||
|
||||
@blueprint.route('/login_success')
|
||||
def login_success():
|
||||
return redirect_to_next()
|
||||
return render_template('auth_register_success.html')
|
||||
|
||||
@blueprint.route('/logout')
|
||||
def logout():
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from flask import Blueprint, jsonify, Flask, request, render_template, redirect, url_for, flash
|
||||
from wtforms import Form, StringField, IntegerField, validators
|
||||
from flask_login import login_required
|
||||
from wtforms import StringField, IntegerField, validators, SubmitField
|
||||
from flask_wtf import FlaskForm
|
||||
from flask_login import login_required, current_user
|
||||
|
||||
from buybuilds.models.resource import Resource
|
||||
from buybuilds import db
|
||||
|
@ -17,9 +18,11 @@ blueprint = Blueprint(
|
|||
def register_routes(app: Flask):
|
||||
app.register_blueprint(blueprint)
|
||||
|
||||
class CreateResourceForm(Form):
|
||||
class CreateResourceForm(FlaskForm):
|
||||
name = StringField('Name', [validators.Length(min=2, max=30)])
|
||||
price = IntegerField('Price', [validators.NumberRange(min=0, max=100)])
|
||||
description = StringField('Short description', [validators.Length(max=70)])
|
||||
submit = SubmitField('Submit')
|
||||
|
||||
@blueprint.route('/', methods=['GET'])
|
||||
def index():
|
||||
|
@ -29,7 +32,7 @@ def index():
|
|||
@login_required
|
||||
@require_role(Role.PUBLISHER)
|
||||
def dashboard():
|
||||
return render_template('dashboard.html')
|
||||
return render_template('publisher_dashboard.html')
|
||||
|
||||
@blueprint.route('/create_resource', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
|
@ -40,13 +43,17 @@ def create_resource():
|
|||
if form.validate_on_submit():
|
||||
name = form.name.data
|
||||
price = form.price.data
|
||||
description = form.description.data
|
||||
|
||||
resource = Resource(name=name, price=price * 100)
|
||||
db.session.add(resource)
|
||||
db.session.commit()
|
||||
resource = Resource.create(
|
||||
name=name,
|
||||
price=int(price * 100),
|
||||
description=form.description.data,
|
||||
user=current_user
|
||||
)
|
||||
|
||||
flash("Resource created successfully", "success")
|
||||
return redirect(url_for('resource.resource', resource_id=resource.id))
|
||||
|
||||
return render_template('create_resource.html', form=form)
|
||||
return render_template('publisher_create_resource.html', form=form)
|
||||
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Publisher Dashboard{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
<h2>Publisher Dashboard</h2>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h3>All Resources</h3>
|
||||
|
||||
{% if resources %}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Price</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for resource in resources %}
|
||||
<tr>
|
||||
<td>{{ resource.id }}</td>
|
||||
<td>{{ resource.name }}</td>
|
||||
<td>{{ resource.price }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>No resources found. <a href="{{ url_for('publisher.create_resource') }}">Create a new resource</a></p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,33 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Publisher Dashboard{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
<h2>Publisher Dashboard</h2>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h3>Your Resources</h3>
|
||||
|
||||
{% if current_user.resources %}
|
||||
<div class="row">
|
||||
{% for resource in current_user.resources %}
|
||||
<div class="col-sm-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ resource.name }}</h5>
|
||||
<p class="card-text">
|
||||
{{ resource.description }}
|
||||
<br>
|
||||
{{ render_icon('tag-fill') }} {{ resource.price / 100 }}$
|
||||
</p>
|
||||
<a href="{{ url_for('resource.resource', resource_id=resource.id) }}" class="btn btn-primary">View / Edit</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p>No resources found. <a href="{{ url_for('publisher.create_resource') }}">Create a new resource</a></p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -5,6 +5,7 @@ from buybuilds.models.resource import Resource
|
|||
blueprint = Blueprint(
|
||||
name='resource',
|
||||
import_name=__name__,
|
||||
template_folder='templates',
|
||||
url_prefix='/resource'
|
||||
)
|
||||
|
||||
|
|
|
@ -7,8 +7,38 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>ID: {{ resource.id }}</p>
|
||||
<p>Name: {{ resource.name }}</p>
|
||||
<p>Price: {{ resource.price }}</p>
|
||||
<p>Publisher: {{ resource.user.username }}</p>
|
||||
<div class="row">
|
||||
<div class="col-sm-9">
|
||||
<p>Lorem ipsum</p>
|
||||
<p>Lorem ipsum</p>
|
||||
<p>Lorem ipsum</p>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title">{{ resource.name }}</h3>
|
||||
<figure class="card-text">
|
||||
<blockquote>
|
||||
<p>{{ resource.description }}</p>
|
||||
</blockquote>
|
||||
<figcaption class="blockquote-footer text-end">
|
||||
<cite title="Source Title">{{ resource.user.username }}</cite>
|
||||
</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<a class="btn btn-primary">Buy Now</a>
|
||||
</div>
|
||||
<div class="col d-flex align-items-center text-right">
|
||||
<span>{{ render_icon('tags-fill') }} {{ resource.price }}$</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -27,12 +27,12 @@ def create_database(app: Flask) -> SQLAlchemy:
|
|||
|
||||
def initialize_database(app: Flask, db: SQLAlchemy):
|
||||
"""Create database tables and initialize default data."""
|
||||
from .models import User
|
||||
from .models import User, Resource
|
||||
from .roles import Role
|
||||
|
||||
with app.app_context():
|
||||
app.logger.info("Creating database tables...")
|
||||
db.create_all() # Should now create all tables including Inquiry
|
||||
db.create_all()
|
||||
|
||||
# Run migrations after creating tables
|
||||
# This ensures any new columns added to existing tables are properly added
|
||||
|
|
|
@ -14,6 +14,10 @@ def create_login_manager(app):
|
|||
@login_manager.user_loader
|
||||
def user_loader(id):
|
||||
return User.query.get(id)
|
||||
|
||||
|
||||
# TODO find a better way to do this
|
||||
app.jinja_env.globals['Role'] = Role
|
||||
|
||||
return login_manager
|
||||
|
||||
|
|
|
@ -3,15 +3,20 @@ from .user import User
|
|||
|
||||
class Resource(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
name = db.Column(db.String(30), unique=True, nullable=False, index=True)
|
||||
price = db.Column(db.Integer, nullable=False)
|
||||
|
||||
description = db.Column(db.String(70))
|
||||
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False, index=True)
|
||||
user = db.relationship('User', back_populates='resources')
|
||||
|
||||
@classmethod
|
||||
def create(cls, name: str, price: int, user: User) -> 'Resource':
|
||||
resource = cls(name=name, price=price, user=user)
|
||||
db.session.add(resource)
|
||||
def create(cls, name: str, price: int, user: User, description: str = None) -> 'Resource':
|
||||
resource = cls(name=name, price=price, description=description)
|
||||
user.resources.append(resource)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
return resource
|
|
@ -1,6 +1,6 @@
|
|||
{% from 'bootstrap5/form.html' import render_form %}
|
||||
{% from 'bootstrap5/nav.html' import render_nav_item %}
|
||||
{% from 'bootstrap5/utils.html' import render_messages %}
|
||||
{% from 'bootstrap5/utils.html' import render_messages, render_icon %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
@ -23,7 +23,9 @@
|
|||
{% if current_user.is_authenticated %}
|
||||
{% if current_user.role >= Role.ADMIN %}
|
||||
{{ render_nav_item('admin.dashboard', 'Admin Dashboard') }}
|
||||
{% elif current_user.role >= Role.PUBLISHER %}
|
||||
{% endif %}
|
||||
|
||||
{% if current_user.role >= Role.PUBLISHER %}
|
||||
{{ render_nav_item('publisher.dashboard', 'Publisher Dashboard') }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
@ -32,17 +34,17 @@
|
|||
{% if current_user.is_authenticated %}
|
||||
<div class="dropdown">
|
||||
<a class="btn btn-dark dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
{{ current_user.username }}
|
||||
{{ current_user.role }}
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="dropdown-item">[{{ current_user.role }}] {{ current_user.username }}</li>
|
||||
<li class="dropdown-item">{{ render_icon('person-fill') }} {{ current_user.username }}</li>
|
||||
|
||||
<li class="dropdown-divider"></li>
|
||||
<a class="dropdown-item" href="{{ url_for('auth.logout') }}">Logout</a>
|
||||
<a class="dropdown-item" href="{{ url_for('auth.logout') }}">{{ render_icon('door-open-fill') }} Logout</a>
|
||||
</ul>
|
||||
</div>
|
||||
{% else %}
|
||||
<a class="btn btn-success" href="{{ url_for('auth.login') }}">Login</a>
|
||||
<a class="btn btn-success" href="{{ url_for('auth.login') }}">{{ render_icon('door-open-fill') }} Login</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue