A Flask-based Articles Management System with SEO optimization, admin panel, and maintenance features
Project description
Article Management System
A comprehensive Flask-based Content Management System (CMS) designed for managing articles with advanced SEO optimization, admin panel, and automated maintenance features.
Features
Core Functionality
- Article Management: Create, read, update, and delete articles with server-side rendering
- SEO Optimization:
- Meta tag management (title, description, keywords)
- JSON-LD schema.org structured data
- Self-canonicalization
- Descriptive URLs (auto-generated slugs)
- SEO scoring and recommendations
- HTML Import: Automatically parse and extract article data from HTML with schema.org markup
- Admin Panel: Full-featured admin interface for content management
- Responsive Design: Mobile-friendly horizontal card layout with Bootstrap 5
- Pagination: Efficiently display 9 articles per page with full pagination support
- User Authentication: Secure login/registration system with role-based access control
- Comments System: Integrated with rcomments package for threaded comments
- Likes/Dislikes: User engagement tracking
- View Tracking: Automatic view count incrementation
- Maintenance Automation:
- Auto-delete articles older than configurable threshold (default: 30 days)
- Keep maximum number of articles (default: 90)
- Scheduled daily maintenance with APScheduler
- Manual maintenance trigger
API Endpoints
Full RESTful API for React/TypeScript frontend integration:
GET /api/articles- Paginated article listingGET /api/article/<slug>- Single article detailsGET /api/article/<slug>/comments- Comments with paginationPOST /api/article/<slug>/comment- Add comment (authenticated)DELETE /api/comment/<id>- Delete commentPOST /api/article/<slug>/like- Toggle like/dislikeGET /api/admin/analytics- Admin analytics dashboardPOST /api/admin/maintenance/run- Manual maintenance triggerPOST /api/admin/seo/analyze- SEO score analysis
Installation
Prerequisites
- Python 3.8+
- PostgreSQL 10+
- pip (Python package installer)
Basic Installation
# Install from PyPI
pip install article-management-system
Database Setup
- Create a PostgreSQL database:
CREATE DATABASE article_management;
CREATE USER article_user WITH PASSWORD 'your-password';
GRANT ALL PRIVILEGES ON DATABASE article_management TO article_user;
- Configure environment variables (or use
.envfile):
export DATABASE_URL=postgresql://article_user:your-password@localhost:5432/article_management
export SECRET_KEY=your-secret-key-here
export SITE_URL=http://yourdomain.com
Initialize Database
# Initialize database and create default admin user
flask init-db
# Or using the CLI command
ams-cli init-db
Configuration
Environment Variables
| Variable | Default | Description |
|---|---|---|
SECRET_KEY |
(required) | Flask secret key for sessions |
DATABASE_URL |
(required) | PostgreSQL connection URL |
SITE_URL |
http://localhost:5000 |
Base URL of your site |
SITE_NAME |
Article Management System |
Site name for meta tags |
ARTICLES_PER_PAGE |
9 |
Number of articles per page |
MAINTENANCE_DAYS_THRESHOLD |
30 |
Delete articles older than X days |
MAINTENANCE_MAX_ARTICLES |
90 |
Maximum articles to keep |
RCOMMENTS_ENABLED |
true |
Enable/disable comments |
Database Configuration
The system supports flexible database configuration:
Option 1: Full DATABASE_URL
export DATABASE_URL=postgresql://user:password@localhost:5432/dbname
Option 2: Individual components
export DB_USER=myuser
export DB_PASSWORD=mypassword
export DB_HOST=localhost
export DB_PORT=5432
export DB_NAME=article_management
Option 3: SQLite for development (not recommended for production)
export DATABASE_URL=sqlite:///dev.db
Custom Configuration Class
You can create a custom configuration class to override any setting:
from article_management.config import Config
class CustomConfig(Config):
ARTICLES_PER_PAGE = 12
MAINTENANCE_DAYS_THRESHOLD = 60
SITE_NAME = "My Custom Site"
SITE_URL = "https://mywebsite.com"
# Override any other settings
Then pass it to create_app():
from article_management import create_app
app = create_app('my_module.CustomConfig')
Customization Guide
Custom Authentication (OAuth, SSO, etc.)
The system is designed to be extensible. You can replace or extend the authentication system:
1. Create a custom User model (optional):
# models.py
from article_management import db
from flask_login import UserMixin
class CustomUser(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(120), unique=True, nullable=False)
# Add OAuth fields: oauth_provider, oauth_id, avatar_url, etc.
2. Create custom auth routes:
# routes/custom_auth.py
from flask import Blueprint, redirect, url_for
from flask_login import login_user
custom_auth_bp = Blueprint('custom_auth', __name__)
@custom_auth_bp.route('/login/google')
def google_login():
# Implement OAuth flow
# Redirect to Google OAuth endpoint
pass
@custom_auth_bp.route('/login/google/callback')
def google_callback():
# Handle OAuth callback
# Get or create user
# login_user(user)
return redirect(url_for('main.index'))
3. Override the auth blueprint:
# In your application factory
def create_app(config_class='article_management.config.Config'):
app = Flask(__name__)
app.config.from_object(config_class)
# Don't register the default auth blueprint
# from article_management.routes.auth import bp as auth_bp
# app.register_blueprint(auth_bp, url_prefix='/auth')
# Register your custom auth instead
from myapp.routes.custom_auth import custom_auth_bp
app.register_blueprint(custom_auth_bp, url_prefix='/auth')
4. Use Flask-Dance or Authlib (recommended for OAuth):
from flask_dance.contrib.google import make_google_blueprint
google_bp = make_google_blueprint(
client_id=os.getenv('GOOGLE_CLIENT_ID'),
client_secret=os.getenv('GOOGLE_CLIENT_SECRET'),
scope=["profile", "email"]
)
app.register_blueprint(google_bp, url_prefix="/login")
Custom Templates
Override any template by creating a templates/ directory and setting TEMPLATES_PATH in your config:
class CustomConfig(Config):
TEMPLATES_PATH = ['/path/to/custom/templates', 'article_management/templates']
Flask will search paths in order, allowing you to override specific templates while keeping the rest.
Custom Static Files
Add custom CSS/JS by placing files in your own static directory:
class CustomConfig(Config):
STATIC_FOLDER = '/path/to/custom/static'
Or use Flask's send_static_file override for more control.
Adding New Routes
Create a new blueprint in routes/ and register it in your app factory:
# routes/custom.py
from flask import Blueprint, render_template
custom_bp = Blueprint('custom', __name__)
@custom_bp.route('/custom-page')
def custom_page():
return render_template('custom_page.html')
Then in your app:
from myapp.routes.custom import custom_bp
app.register_blueprint(custom_bp)
Extending the Article Model
Add custom fields to the Article model by creating a migration:
# In a migration file
op.add_column('articles', sa.Column('custom_field', sa.String(500)))
Then update your forms and templates accordingly.
Latest Updates (v1.1.0)
Recent Improvements
-
Enhanced Reading Experience (March 2026)
- Fixed article content width alignment with title (now both 800px)
- Improved typography with serif fonts (Georgia, Times New Roman)
- Better line-height (1.8) and font size (1.125rem)
- Text justification with hyphenation for clean flow
- Enhanced heading hierarchy with visual styling
- Improved blockquotes, lists, code blocks, and tables
- Responsive design with mobile-friendly padding
-
Maintenance Fixes (March 2026)
- Fixed
MaintenanceSettings.run_maintenance()to work regardless ofis_enabledflag - Manual "Run Maintenance Now" button now always executes
- Fixed missing
MaintenanceSettingsimport in API routes - Removed CSRF exemption issue from API endpoint
- Fixed
-
Bug Fixes
- Fixed import errors in API routes
- Improved error handling in maintenance tasks
API Reference
Authentication Endpoints
GET /auth/login- Login pagePOST /auth/login- Login submissionGET /auth/register- Registration pagePOST /auth/register- Registration submissionGET /auth/logout- Logout
Article Endpoints
GET /api/articles- Paginated article listing- Query params:
page,per_page,sort_by(date_published, view_count),order(asc, desc)
- Query params:
GET /api/article/<slug>- Single article details with JSON-LDGET /api/article/<slug>/comments- Paginated commentsPOST /api/article/<slug>/comment- Add comment (requires auth)DELETE /api/comment/<id>- Delete comment (requires admin or comment author)POST /api/article/<slug>/like- Toggle like/dislike (requires auth)
Admin Endpoints
GET /api/admin/analytics- Analytics dashboard dataPOST /api/admin/maintenance/run- Manually trigger maintenance (requires admin)POST /api/admin/seo/analyze- SEO score analysis (requires admin)
CLI Commands
The package includes a powerful CLI:
# Initialize database and create admin
ams-cli init-db
# Create admin user
ams-cli create-admin
# List all articles
ams-cli list-articles
# Delete article by ID
ams-cli delete-article 123
# Run maintenance manually
ams-cli run-maintenance
# Export all articles to JSON
ams-cli export-articles -o backup.json
Project Structure
article_management/
├── __init__.py # Application factory
├── config.py # Configuration classes
├── models.py # Database models
├── seo_utils.py # SEO utilities
├── html_parser.py # HTML content parser
├── forms.py # WTForms definitions
├── decorators.py # Custom decorators
├── cli.py # CLI commands
├── routes/
│ ├── __init__.py
│ ├── main.py # Public routes
│ ├── admin.py # Admin routes
│ ├── auth.py # Authentication routes
│ └── api.py # API endpoints
├── templates/ # Jinja2 templates
│ ├── base.html
│ ├── index.html
│ ├── article_detail.html
│ ├── auth/
│ └── admin/
├── static/
│ ├── css/style.css
│ └── js/main.js
└── wsgi.py # WSGI entry point
Database Schema
Main Tables
- users: User accounts with authentication
- articles: Article content with SEO fields
- comments: Threaded comments (integrated with rcomments)
- likes: User likes/dislikes tracking
- maintenance_settings: Maintenance configuration
Relationships
- User → Comments (one-to-many)
- User → Likes (one-to-many)
- Article → Comments (one-to-many, cascade delete)
- Article → Likes (one-to-many, cascade delete)
- Comment → Replies (self-referential)
Deployment
Production Configuration
- Environment Setup:
export FLASK_ENV=production
export SECRET_KEY=your-very-secure-secret-key
export DATABASE_URL=postgresql://user:pass@host:5432/db
- Use a Production WSGI Server:
# Using Gunicorn
pip install gunicorn
gunicorn -w 4 "article_management.wsgi:app" -b 0.0.0.0:8000
- Set up Systemd service (Linux):
[Unit]
Description=Article Management System
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/path/to/app
Environment="PATH=/path/to/venv/bin"
EnvironmentFile=/path/to/.env
ExecStart=/path/to/venv/bin/gunicorn -w 4 "article_management.wsgi:app" -b 127.0.0.1:8000
[Install]
WantedBy=multi-user.target
- Nginx Configuration:
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Database Migrations
# Initialize migrations
flask db init
# Create migration
flask db migrate -m "Description"
# Apply migration
flask db upgrade
Testing
Run tests with pytest:
pytest tests/
Security Features
- Password hashing with werkzeug
- CSRF protection with Flask-WTF
- SQL injection prevention with SQLAlchemy
- XSS protection in templates
- Authentication required for admin routes
- Role-based access control (admin/user)
- Secure session management
Extending the Package
Custom Templates
Override templates by creating a templates/ directory and setting TEMPLATES_PATH in config.
Custom Static Files
Add custom CSS/JS by placing files in static/ directory.
Adding New Routes
Create a new blueprint in routes/ and register it in __init__.py.
Troubleshooting
Common Issues
-
Database connection error:
- Check PostgreSQL is running
- Verify credentials in
DATABASE_URL - Ensure database exists
-
Template not found:
- Check Flask can find the templates directory
- Verify package installation with
pip install -e .
-
Static files not loading:
- Ensure
static/folder exists - Check file permissions
- Ensure
-
Migrations fail:
- Ensure database user has proper privileges
- Check Flask-Migrate is installed
License
MIT License - see LICENSE file for details
Changelog
v1.1.0 (March 2026)
- Reading Experience: Completely redesigned article layout with consistent 800px width, serif typography, and professional publishing-quality styling
- Maintenance Fixes: Manual maintenance now works correctly regardless of auto-maintenance setting
- Bug Fixes: Fixed missing MaintenanceSettings import in API routes
- Code Quality: Removed problematic CSRF import, improved error handling
v1.0.0
- Initial release
- Full CRUD operations for articles
- SEO optimization with schema.org
- HTML import with automatic parsing
- Admin dashboard
- User authentication
- Comments system (rcomments integration)
- Likes/dislikes
- Maintenance automation
- REST API for frontend integration
- Responsive Bootstrap 5 UI
Usage
Running the Application
# Set Flask app
export FLASK_APP=article_management.wsgi:app
export FLASK_ENV=development
# Run development server
flask run
Or using the WSGI file directly:
python -m article_management.wsgi
Admin Panel
Access the admin panel at /admin after logging in with admin credentials.
Features:
- Dashboard with statistics
- Create articles by uploading HTML or pasting content
- Edit existing articles
- Manage all articles with search/filter
- Configure maintenance settings
- Run maintenance manually
- SEO analysis tool
Creating Articles
Method 1: HTML Upload
Upload an HTML file with schema.org Article markup:
<article itemscope itemtype="https://schema.org/Article">
<header>
<h1 itemprop="headline">Article Title</h1>
<meta itemprop="datePublished" content="2026-03-24">
<meta itemprop="dateModified" content="2026-03-24">
</header>
<div itemprop="articleBody">
<h2>Section Title</h2>
<p>Article content...</p>
</div>
</article>
Method 2: Manual Entry
Fill in all fields manually through the admin form.
Required HTML Structure:
<article itemscope itemtype="https://schema.org/Article"><h1 itemprop="headline">- Article title<meta itemprop="datePublished">- Publication date<meta itemprop="dateModified">- Last modified date<div itemprop="articleBody">- Main content
The system will automatically:
- Extract title, dates, and excerpt from HTML
- Generate SEO-friendly slug from title
- Create JSON-LD schema
- Set canonical URL (self-canonicalization by default)
SEO Features
The system provides comprehensive SEO optimization:
- Meta Tags: Title, description, keywords
- JSON-LD Schema: Automatic generation of schema.org Article markup
- Canonical URLs: Self-canonicalization or custom URLs
- Descriptive Slugs: Auto-generated from title, unique
- SEO Scoring: Real-time analysis with recommendations
- Open Graph: Automatic OG tags for social sharing
- Twitter Cards: Enhanced Twitter sharing
Maintenance Automation
The system automatically cleans up old articles based on your settings:
- Daily Schedule: Runs every day at 2 AM
- Deletion Rules:
- Delete articles older than
MAINTENANCE_DAYS_THRESHOLD - If total articles still exceed
MAINTENANCE_MAX_ARTICLES, delete oldest articles
- Delete articles older than
- Manual Trigger: Run maintenance anytime from admin panel or CLI
API Integration with React/TypeScript
The system provides a complete REST API for frontend integration:
// Example React component
import React, { useEffect, useState } from 'react';
import { Article } from './types';
const ArticleList: React.FC = () => {
const [articles, setArticles] = useState<Article[]>([]);
const [page, setPage] = useState(1);
useEffect(() => {
fetch(`/api/articles?page=${page}`)
.then(res => res.json())
.then(data => setArticles(data.data.articles));
}, [page]);
return (
<div>
{articles.map(article => (
<ArticleCard key={article.id} article={article} />
))}
</div>
);
};
CLI Commands
The package includes a powerful CLI:
# Initialize database and create admin
ams-cli init-db
# Create admin user
ams-cli create-admin
# List all articles
ams-cli list-articles
# Delete article by ID
ams-cli delete-article 123
# Run maintenance manually
ams-cli run-maintenance
# Export all articles to JSON
ams-cli export-articles -o backup.json
Project Structure
article_management/
├── __init__.py # Application factory
├── config.py # Configuration classes
├── models.py # Database models
├── seo_utils.py # SEO utilities
├── html_parser.py # HTML content parser
├── forms.py # WTForms definitions
├── decorators.py # Custom decorators
├── cli.py # CLI commands
├── routes/
│ ├── __init__.py
│ ├── main.py # Public routes
│ ├── admin.py # Admin routes
│ ├── auth.py # Authentication routes
│ └── api.py # API endpoints
├── templates/ # Jinja2 templates
│ ├── base.html
│ ├── index.html
│ ├── article_detail.html
│ ├── auth/
│ └── admin/
├── static/
│ ├── css/style.css
│ └── js/main.js
└── wsgi.py # WSGI entry point
Database Schema
Main Tables
- users: User accounts with authentication
- articles: Article content with SEO fields
- comments: Threaded comments (integrated with rcomments)
- likes: User likes/dislikes tracking
- maintenance_settings: Maintenance configuration
Relationships
- User → Comments (one-to-many)
- User → Likes (one-to-many)
- Article → Comments (one-to-many, cascade delete)
- Article → Likes (one-to-many, cascade delete)
- Comment → Replies (self-referential)
Deployment
Production Configuration
- Environment Setup:
export FLASK_ENV=production
export SECRET_KEY=your-very-secure-secret-key
export DATABASE_URL=postgresql://user:pass@host:5432/db
- Use a Production WSGI Server:
# Using Gunicorn
pip install gunicorn
gunicorn -w 4 "article_management.wsgi:app" -b 0.0.0.0:8000
- Set up Systemd service (Linux):
[Unit]
Description=Article Management System
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/path/to/app
Environment="PATH=/path/to/venv/bin"
EnvironmentFile=/path/to/.env
ExecStart=/path/to/venv/bin/gunicorn -w 4 "article_management.wsgi:app" -b 127.0.0.1:8000
[Install]
WantedBy=multi-user.target
- Nginx Configuration:
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Database Migrations
# Initialize migrations
flask db init
# Create migration
flask db migrate -m "Description"
# Apply migration
flask db upgrade
Testing
Run tests with pytest:
pytest tests/
Security Features
- Password hashing with werkzeug
- CSRF protection with Flask-WTF
- SQL injection prevention with SQLAlchemy
- XSS protection in templates
- Authentication required for admin routes
- Role-based access control (admin/user)
- Secure session management
Extending the Package
Custom Templates
Override templates by creating a templates/ directory and setting TEMPLATES_PATH in config.
Custom Static Files
Add custom CSS/JS by placing files in static/ directory.
Adding New Routes
Create a new blueprint in routes/ and register it in __init__.py.
Troubleshooting
Common Issues
-
Database connection error:
- Check PostgreSQL is running
- Verify credentials in
DATABASE_URL - Ensure database exists
-
Template not found:
- Check Flask can find the templates directory
- Verify package installation with
pip install -e .
-
Static files not loading:
- Ensure
static/folder exists - Check file permissions
- Ensure
-
Migrations fail:
- Ensure database user has proper privileges
- Check Flask-Migrate is installed
License
MIT License - see LICENSE file for details
Changelog
v1.0.0
- Initial release
- Full CRUD operations for articles
- SEO optimization with schema.org
- HTML import with automatic parsing
- Admin dashboard
- User authentication
- Comments system (rcomments integration)
- Likes/dislikes
- Maintenance automation
- REST API for frontend integration
- Responsive Bootstrap 5 UI
- CLI tools
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file article_management_system-1.1.1.tar.gz.
File metadata
- Download URL: article_management_system-1.1.1.tar.gz
- Upload date:
- Size: 57.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
88a2cf659dae97ac33cfbd836e01e92be68695f63743a572aa51be08fe532164
|
|
| MD5 |
0813be0643e726b54f37224d97195361
|
|
| BLAKE2b-256 |
8dbe6d2e0b44fc66d1350e4ec5ca3cbe6726d864ecb817b21e3a35fa93d29ca4
|
File details
Details for the file article_management_system-1.1.1-py3-none-any.whl.
File metadata
- Download URL: article_management_system-1.1.1-py3-none-any.whl
- Upload date:
- Size: 65.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1e50d8d233978a30184b9809e3a623507653a3c5ca6c690b16f9aeb1c50dcaf7
|
|
| MD5 |
8a02900df7132baa3c03978bb0a7461d
|
|
| BLAKE2b-256 |
5750deca9e4bcd5d4b578fd7c67fc72ae4d5733919a6e6d48cf1ac7ba0826f97
|