A modern, cloud-powered Image & GIF Gallery — built with Flask, AWS S3, Neon PostgreSQL, and Tailwind CSS.
- 🔐 Secure Authentication — Sign up, log in, and log out with hashed passwords (Werkzeug + Flask-Login)
- ☁️ Cloud Storage — Images uploaded directly to AWS S3 with public-read URLs
- 🏷️ Tag System — Add up to 20 tags per image for easy discovery
- 🔍 Live Search — Dynamic, case-insensitive partial tag search without page reloads
- 👍 Like / Dislike — One reaction per user per image with smart toggle logic
- 🗑️ Owner-Only Deletion — Only the uploader can delete an image, with a confirmation modal
- 👑 Admin Role — Admin can delete any user's image for moderation
- 🌓 Dark / Light Mode — Persistent theme toggle via
localStorage - 📱 Fully Responsive — Pinterest-style masonry grid that adapts from mobile to desktop
- ⚡ Fast — Search returns results in under 2 seconds for up to 10,000 records
| Layer | Technology |
|---|---|
| Backend | Python 3.11, Flask 3.0, SQLAlchemy 2.0 |
| Database | Neon PostgreSQL (production) / SQLite (local dev) |
| Storage | Amazon AWS S3 (Boto3) |
| Auth | Flask-Login + Werkzeug password hashing |
| Frontend | HTML5, Tailwind CSS (CDN), Vanilla JavaScript |
| Hosting | Render (Web Service) |
| Testing | pytest, Hypothesis (property-based tests) |
| Production Server | Gunicorn |
- Python 3.10 or higher
- An AWS account with an S3 bucket (configured for public read access)
- An IAM user with
s3:PutObject,s3:DeleteObject,s3:GetObjectpermissions pip3(Python package manager)
git clone https://github.com/YOUR_USERNAME/cms-image-gallery.git
cd cms-image-gallerypip3 install -r requirements.txtCreate a .env file in the project root with the following keys:
# Flask secret key — generate with: python3 -c "import secrets; print(secrets.token_hex(32))"
SECRET_KEY=your-secret-key
# AWS S3 credentials
AWS_ACCESS_KEY_ID=your-aws-access-key-id
AWS_SECRET_ACCESS_KEY=your-aws-secret-access-key
AWS_S3_BUCKET_NAME=your-bucket-name
AWS_S3_REGION=us-east-1| Variable | Description |
|---|---|
SECRET_KEY |
Flask session signing key — generate one with the command below |
AWS_ACCESS_KEY_ID |
Your AWS IAM access key ID |
AWS_SECRET_ACCESS_KEY |
Your AWS IAM secret access key |
AWS_S3_BUCKET_NAME |
Name of your S3 bucket |
AWS_S3_REGION |
AWS region (e.g. us-east-1) |
Generate a secret key:
python3 -c "import secrets; print(secrets.token_hex(32))"python3 app.pyTo enable debug mode:
FLASK_DEBUG=1 python3 app.py| Setting | Value |
|---|---|
| Object Ownership | ACLs enabled |
| Block Public Access | Disabled (all 4 unchecked) |
| Versioning | Disabled |
| Encryption | SSE-S3 (default) |
Allow public read for uploaded images:
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::YOUR_BUCKET_NAME/*"
}]
}[{
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "PUT", "POST", "DELETE"],
"AllowedOrigins": ["*"],
"ExposeHeaders": ["ETag"]
}]{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:PutObjectAcl", "s3:GetObject", "s3:DeleteObject"],
"Resource": "arn:aws:s3:::YOUR_BUCKET_NAME/*"
},
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::YOUR_BUCKET_NAME"
}
]
}Run the full test suite (174 tests covering services, routes, and edge cases):
python3 -m pytest -vRun with coverage:
python3 -m pytest --cov=. --cov-report=html- 174 passing tests with 100% pass rate
- Unit tests: All backend services (bulk operations, filters, image processing, search, undo queue)
- Integration tests: API routes, template integration, progressive loading
- Property-based tests: Tag validation, upload handling, like/dislike logic
- End-to-end tests: Complete user workflows and feature interactions
This app is deployed on Render (hosting) + Neon (PostgreSQL database) + AWS S3 (image storage).
- Push this repo to GitHub
- Create a free Neon project at neon.tech → copy the pooled connection string
- Create a Render Web Service at render.com → connect this repo
- Configure Render:
- Build Command:
pip install -r requirements.txt - Start Command:
gunicorn app:application --workers 1 --timeout 120 - Instance Type: Free
- Build Command:
- Add Environment Variables in Render:
DATABASE_URL= your Neon connection stringSECRET_KEY= a random 64-char hex stringAWS_ACCESS_KEY_ID= your AWS keyAWS_SECRET_ACCESS_KEY= your AWS secretAWS_S3_BUCKET_NAME= your bucket nameAWS_S3_REGION= your bucket regionRENDER=1
- Deploy — your app goes live in ~3 minutes
Other supported platforms: Railway, Fly.io, PythonAnywhere, AWS Elastic Beanstalk
cms-image-gallery/
├── app.py # Flask app factory + entry point
├── models.py # SQLAlchemy models (User, Image, Tag, Likes)
├── routes.py # All HTTP routes + API endpoints
├── auth_service.py # Registration + login
├── upload_service.py # File validation + S3 upload + tag parsing
├── s3_service.py # Boto3 S3 integration
├── search_service.py # Tag-based search + query builder
├── like_service.py # Like/dislike toggle logic
├── delete_service.py # Authorized deletion with cascade
├── bulk_service.py # Bulk delete and download operations
├── filter_service.py # Real-time image filter processing
├── image_service.py # Thumbnail generation and upload
├── query_builder.py # Advanced search query construction
├── undo_queue.py # Undo delete queue management
├── templates/ # Jinja2 HTML templates
│ ├── base.html # Base template with mobile navigation
│ ├── index.html # Gallery homepage with all features
│ ├── login.html
│ ├── register.html
│ ├── upload.html
│ └── error.html
├── static/ # Static assets
│ ├── js/
│ │ ├── gallery.js # Core gallery functionality
│ │ ├── image-viewer.js # Lightbox image viewer
│ │ └── progressive-loading.js # Blur-up image loading
│ ├── css/
│ │ └── (Tailwind CSS via CDN)
│ └── logo.png
├── tests/ # pytest test suite (174 tests)
│ ├── test_like.py # Like/dislike tests
│ ├── test_search.py # Search and query builder tests
│ ├── test_upload.py # Upload validation tests
│ ├── test_bulk_service.py # Bulk operations tests
│ ├── test_filter_service.py # Image filter tests
│ ├── test_image_service.py # Thumbnail generation tests
│ ├── test_undo_queue.py # Undo queue tests
│ ├── test_query_builder.py # Advanced search tests
│ └── test_progressive_loading*.py # Progressive loading tests
├── requirements.txt # Python dependencies (pinned)
├── Procfile # Production start command
└── README.md
- ✅ Passwords are hashed using Werkzeug's
generate_password_hash(PBKDF2-SHA256) - ✅ AWS credentials are loaded from environment variables only — never hardcoded
- ✅
.envis excluded from version control via.gitignore - ✅ Owner-only authorization on all destructive operations
- ✅ Generic error messages on login to prevent user enumeration
- ✅ Unique S3 keys (UUID-based) prevent overwrite attacks
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
![]() Ishant Sahu |
![]() Nitin Nirmalkar |
![]() Rakesh Parate |
![]() Khushraj Varghat |
- Built as a Cloud Computing college project
- Logo represents a lotus (creativity) opening into a book (content)
- Color palette inspired by traditional Indian aesthetics







