import os
from datetime import datetime
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify
from slugify import slugify
from werkzeug.middleware.proxy_fix import ProxyFix
from config import config
from models import db, User, BlogPost, ForumCategory, ForumTopic, ForumReply, ContactMessage
def create_app(config_name=None):
if config_name is None:
config_name = os.environ.get('FLASK_CONFIG', 'default')
app = Flask(__name__)
app.config.from_object(config[config_name])
# Trust proxy headers from Nginx Proxy Manager → local nginx (2 proxies)
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=2, x_proto=1, x_host=1, x_prefix=1)
db.init_app(app)
with app.app_context():
db.create_all()
seed_data()
# Context processor for templates
@app.context_processor
def inject_now():
return {'now': datetime.utcnow()}
# ==================== Company Pages ====================
@app.route('/')
def index():
latest_posts = BlogPost.query.filter_by(published=True)\
.order_by(BlogPost.created_at.desc())\
.limit(3).all()
return render_template('index.html', latest_posts=latest_posts)
@app.route('/about')
def about():
return render_template('about.html')
@app.route('/services')
def services():
return render_template('services.html')
@app.route('/contact')
def contact():
return render_template('contact.html')
@app.route('/contact/submit', methods=['POST'])
def contact_submit():
name = request.form.get('name')
email = request.form.get('email')
subject = request.form.get('subject')
message = request.form.get('message')
if not all([name, email, subject, message]):
return '
Please fill in all fields.
'
contact_message = ContactMessage(
name=name,
email=email,
subject=subject,
message=message
)
db.session.add(contact_message)
db.session.commit()
return '''
Thank you! Your message has been sent. We'll get back to you soon.
'''
# ==================== Blog ====================
@app.route('/blog')
def blog_index():
page = request.args.get('page', 1, type=int)
posts = BlogPost.query.filter_by(published=True)\
.order_by(BlogPost.created_at.desc())\
.paginate(page=page, per_page=10, error_out=False)
return render_template('blog/index.html', posts=posts.items)
@app.route('/blog/search')
def blog_search():
query = request.args.get('search', '')
if query:
posts = BlogPost.query.filter_by(published=True)\
.filter(BlogPost.title.ilike(f'%{query}%') | BlogPost.content.ilike(f'%{query}%'))\
.order_by(BlogPost.created_at.desc())\
.limit(10).all()
else:
posts = BlogPost.query.filter_by(published=True)\
.order_by(BlogPost.created_at.desc())\
.limit(10).all()
return render_template('blog/partials/post_list.html', posts=posts)
@app.route('/blog/load-more/')
def blog_load_more(page):
posts = BlogPost.query.filter_by(published=True)\
.order_by(BlogPost.created_at.desc())\
.paginate(page=page, per_page=10, error_out=False)
return render_template('blog/partials/post_list.html', posts=posts.items)
@app.route('/blog/')
def blog_post(slug):
post = BlogPost.query.filter_by(slug=slug, published=True).first_or_404()
return render_template('blog/post.html', post=post)
# ==================== Forum ====================
@app.route('/forum')
def forum_index():
categories = ForumCategory.query.order_by(ForumCategory.order).all()
recent_topics = ForumTopic.query\
.order_by(ForumTopic.created_at.desc())\
.limit(5).all()
return render_template('forum/index.html', categories=categories, recent_topics=recent_topics)
@app.route('/forum/search')
def forum_search():
query = request.args.get('q', '')
if query:
topics = ForumTopic.query\
.filter(ForumTopic.title.ilike(f'%{query}%') | ForumTopic.content.ilike(f'%{query}%'))\
.order_by(ForumTopic.created_at.desc())\
.limit(10).all()
if topics:
html = ''
return html
else:
return 'No topics found matching your search.
'
return ''
@app.route('/forum/')
def forum_category(slug):
category = ForumCategory.query.filter_by(slug=slug).first_or_404()
page = request.args.get('page', 1, type=int)
topics = ForumTopic.query.filter_by(category_id=category.id)\
.order_by(ForumTopic.is_pinned.desc(), ForumTopic.created_at.desc())\
.paginate(page=page, per_page=20, error_out=False)
return render_template('forum/category.html', category=category, topics=topics)
@app.route('/forum//new', methods=['POST'])
def forum_create_topic(category_slug):
category = ForumCategory.query.filter_by(slug=category_slug).first_or_404()
title = request.form.get('title')
content = request.form.get('content')
if not title or not content:
return 'Please fill in all fields.
'
# Get or create a default user for demo purposes
user = User.query.first()
if not user:
user = User(username='Guest', email='guest@example.com')
user.set_password('guest')
db.session.add(user)
db.session.commit()
topic = ForumTopic(
title=title,
content=content,
category_id=category.id,
author_id=user.id
)
db.session.add(topic)
db.session.commit()
# Return updated topic list
topics = ForumTopic.query.filter_by(category_id=category.id)\
.order_by(ForumTopic.is_pinned.desc(), ForumTopic.created_at.desc())\
.paginate(page=1, per_page=20, error_out=False)
return render_template('forum/partials/topic_list.html', category=category, topics=topics)
@app.route('/forum//')
def forum_topic(category_slug, topic_id):
topic = ForumTopic.query.get_or_404(topic_id)
replies = topic.replies.order_by(ForumReply.created_at.asc()).all()
return render_template('forum/topic.html', topic=topic, replies=replies)
@app.route('/forum///reply', methods=['POST'])
def forum_add_reply(category_slug, topic_id):
topic = ForumTopic.query.get_or_404(topic_id)
if topic.is_locked:
return 'This topic is locked.
'
content = request.form.get('content')
if not content:
return 'Please enter a reply.
'
# Get or create a default user for demo purposes
user = User.query.first()
if not user:
user = User(username='Guest', email='guest@example.com')
user.set_password('guest')
db.session.add(user)
db.session.commit()
reply = ForumReply(
content=content,
topic_id=topic.id,
author_id=user.id
)
db.session.add(reply)
db.session.commit()
# Return the new reply as HTML
return f'''
{reply.author.username[0].upper()}
{reply.author.username}
{reply.created_at.strftime('%B %d, %Y at %H:%M')}
{reply.content}
'''
return app
def seed_data():
"""Seed the database with sample data if empty."""
if User.query.first() is None:
# Create admin user
admin = User(username='admin', email='admin@directlx.dev', is_admin=True)
admin.set_password('admin123')
db.session.add(admin)
# Create sample blog posts
posts = [
{
'title': 'Getting Started with Flask and HTMX',
'slug': 'getting-started-flask-htmx',
'excerpt': 'Learn how to build modern, interactive web applications using Flask and HTMX.',
'content': '''Flask and HTMX make a powerful combination for building interactive web applications without the complexity of a JavaScript framework.
Why HTMX?
HTMX allows you to access modern browser features directly from HTML, making it easy to build dynamic user interfaces with minimal JavaScript.
Getting Started
First, create a new Flask project and install the required dependencies. Then, add HTMX to your templates and start building interactive features.
''',
'published': True
},
{
'title': 'Building Scalable APIs with Python',
'slug': 'building-scalable-apis-python',
'excerpt': 'Best practices for designing and building scalable REST APIs using Python.',
'content': '''Building scalable APIs requires careful planning and adherence to best practices.
Design Principles
Follow REST principles, use proper HTTP methods, and design your endpoints to be intuitive and consistent.
Performance Considerations
Implement caching, pagination, and rate limiting to ensure your API can handle high traffic.
''',
'published': True
},
{
'title': 'The Future of Web Development',
'slug': 'future-web-development',
'excerpt': 'Exploring emerging trends and technologies shaping the future of web development.',
'content': '''The web development landscape is constantly evolving. Let's explore what's next.
Emerging Trends
From WebAssembly to edge computing, new technologies are changing how we build web applications.
What This Means for Developers
Staying current with these trends will help you build better applications and advance your career.
''',
'published': True
}
]
for post_data in posts:
post = BlogPost(author_id=1, **post_data)
db.session.add(post)
# Create forum categories
categories = [
{'name': 'General Discussion', 'slug': 'general', 'description': 'General topics and discussions about software development.', 'order': 1},
{'name': 'Help & Support', 'slug': 'help', 'description': 'Get help with technical problems and questions.', 'order': 2},
{'name': 'Showcase', 'slug': 'showcase', 'description': 'Share your projects and get feedback from the community.', 'order': 3},
{'name': 'Off Topic', 'slug': 'off-topic', 'description': 'Everything else that doesn\'t fit elsewhere.', 'order': 4}
]
for cat_data in categories:
category = ForumCategory(**cat_data)
db.session.add(category)
db.session.commit()
# Create sample forum topics
general = ForumCategory.query.filter_by(slug='general').first()
if general:
topic = ForumTopic(
title='Welcome to the DirectLX Community!',
content='Welcome everyone! This is a place to discuss software development, share knowledge, and connect with other developers. Feel free to introduce yourself!',
category_id=general.id,
author_id=1,
is_pinned=True
)
db.session.add(topic)
db.session.commit()
# Create the application instance
app = create_app()
if __name__ == '__main__':
app.run(debug=True, port=5000)