A backend image hosting service with session-based authentication, cloud object storage, AI-powered image tagging, and full-text tag search - built with Spring Boot.
Live deployment: https://taglens.onrender.com
TagLens lets users register, log in, and upload images that are automatically tagged by an AI vision model. Every uploaded image is analyzed in the background and enriched with structured tags (objects, descriptive tags, and dominant colors), which are then searchable by anyone - no login required for browsing or search.
- 🔐 Session-based authentication - opaque UUID sessions stored in PostgreSQL (not JWT), with bcrypt-hashed passwords
- 📤 Image upload - stored in Backblaze B2 (S3-compatible object storage), 10MB size limit
- 🤖 AI-powered tagging - Google Gemini (
gemini-3.6-flash) analyzes each image and generates structured tags, run asynchronously so uploads never block on AI processing - 🔎 Public search & browsing - anyone can browse the 50 most recent uploads or free-text search across all AI-generated tags, no account needed
- 🗂️ JSONB storage - tags stored as PostgreSQL
JSONBwith a GIN index for efficient querying - 🐳 Containerized & deployed - Dockerized and running live on Render, built via CI/CD
| Layer | Technology |
|---|---|
| Language / Runtime | Java 25 |
| Framework | Spring Boot 4.1 (Spring Web, Spring Security, Spring JDBC) |
| Database | PostgreSQL, versioned with Flyway migrations |
| Object storage | Backblaze B2 (via AWS S3 SDK) |
| AI / tagging | Google Gemini API (gemini-3.6-flash) |
| Auth | Custom session-based auth (UUID sessions + bcrypt), not JWT |
| Logging | Loki (via Logback appender) |
| Containerization | Docker |
| CI/CD | GitHub Actions → GHCR → Render deploy hook |
| Hosting | Render |
- Sessions over JWT: session IDs are opaque UUIDs stored server-side in a
sessionstable, giving immediate session invalidation, per-device auditing, and easy bulk logout - trade-offs a stateless JWT can't offer. - Async AI tagging: tagging is triggered via Spring's
@Asyncafter an image and its metadata are persisted, so the upload response returns immediately. Tags may take a few seconds to appear. - JSONB tag storage: tags are stored as a single
JSONBcolumn ({"objects": [...], "tags": [...], "colors": [...]}) with a GIN index, avoiding a rigid multi-column schema for inherently variable AI output.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST |
/auth/register |
❌ | Register a new user |
POST |
/auth/login |
❌ | Log in, sets session_id cookie |
POST |
/auth/logout |
✅ | Invalidate the current session |
GET |
/auth/me |
✅ | Get the current authenticated user |
Register / Login body:
{
"email": "user@example.com",
"password": "yourpassword"
}| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST |
/images/upload |
✅ | Upload an image (max 10MB). AI tags generate in the background. |
GET |
/images/{id} |
✅ (owner only) | Get a temporary link to view a specific image |
DELETE |
/images/{id} |
✅ (owner only) | Delete an image |
GET |
/images/my |
✅ | List the current user's images |
GET |
/images |
❌ public | Browse the 50 most recently uploaded images, across all users |
GET |
/images?search=<text> |
❌ public | Free-text search across all images' AI-generated tags |
Example tag shape returned with an image:
{
"objects": ["car", "tree", "road"],
"tags": ["outdoor", "sunset", "urban"],
"colors": ["orange", "gray", "blue"]
}Ownership is enforced on
GET /images/{id}andDELETE /images/{id}— a user can only act on their own images.
git clone https://github.com/Barboud/TagLens.git
cd TagLensSet the following environment variables:
DB_URL=jdbc:postgresql://localhost:5432/taglens
DB_USERNAME=your_db_user
DB_PASSWORD=your_db_password
B2_ACCESS_KEY=your_b2_key
B2_SECRET_KEY=your_b2_secret
B2_BUCKET_NAME=your_bucket
GEMINI_API_KEY=your_gemini_key
Then run:
./mvnw spring-boot:runFlyway will run all migrations automatically on startup.
Every push to main triggers a GitHub Actions pipeline that:
- Spins up a PostgreSQL service container and runs the full test suite
- Builds a Docker image and pushes it to GitHub Container Registry (GHCR)
- Triggers a deploy hook on Render, which pulls and runs the new image
This project was built as a multi-week course assignment covering authentication, cloud storage, AI integration, and deployment. Core functionality - auth, upload, AI tagging, public search, and cloud deployment - is complete and live.