A college notes sharing platform built with Java 21 and Spring Boot 3.4.4. Students can browse, upload, and download PDF study notes. The platform features a passkey-gated upload system, an admin panel for content moderation, and a dark/light theme toggle.
💻 Development: In-memory H2 database with JPA, local file storage.
☁️ Production: Backblaze B2 object storage for both PDF files and JSON metadata. No database — all metadata is loaded into an in-memory cache on startup and persisted as JSON files in B2.
| Layer | Technology |
|---|---|
| Backend | Java 21, Spring Boot 3.4.4, Maven |
| Templating | Thymeleaf |
| Frontend | Tailwind CSS (CDN), Alpine.js (CDN) |
| Storage (dev) | Local filesystem + H2 in-memory database |
| Storage (prod) | Backblaze B2 via AWS S3 SDK |
| Containerization | Docker (multi-stage build) |
- 🔍 Browse notes with search and subject filtering
- ⬆️ Upload PDF notes (max 100 MB) protected by a configurable passkey
- 🔐 Admin panel with passkey-gated access for viewing and deleting notes
- 🌓 Dark/light theme toggle persisted in localStorage
- 🚫 Rate limiting on upload passkey attempts (3 strikes, 24-hour IP block)
- ⬇️ Download tracking with pre-signed URLs (5-minute expiry in production)
- ☕ Java 21 (JDK)
- 📦 Maven (or use the included Maven wrapper
./mvnw)
./mvnw spring-boot:runThe application starts on port 8080. Access it at http://localhost:8080.
Default credentials:
| 🔑 Passkey | Value |
|---|---|
| ⬆️ Upload passkey | UPLOAD2026 |
| 🔐 Admin passkey | ADMIN2026 |
These are configurable via upload.passkey and admin.passkey in application.properties.
When deploying with the prod profile, set the following environment variables:
| Variable | Description |
|---|---|
SPRING_PROFILES_ACTIVE=prod |
Enables production profile (disables JPA/H2, enables B2) |
B2_ENDPOINT |
Backblaze B2 endpoint (e.g. https://s3.us-west-004.backblazeb2.com) |
B2_REGION |
B2 region (e.g. us-west-004) |
B2_ACCESS_KEY_ID |
Backblaze B2 key ID |
B2_SECRET_ACCESS_KEY |
Backblaze B2 application key |
B2_BUCKET_NAME |
B2 bucket name |
upload.passkey |
Passkey for uploading notes |
admin.passkey |
Passkey for admin panel access |
PORT |
Server port (default 8080) |
docker build -t notes-hub .
docker run -p 8080:8080 notes-hub📂 src/main/java/com/notes/hub/
├── 📂 controller/
│ └── 📄 NotesController.java # 🌐 All web endpoints
├── 📂 model/
│ ├── 📄 Note.java # 📝 Note entity
│ └── 📄 PasskeyAttempt.java # 🚫 Rate-limit entity
├── 📂 repository/
│ ├── 📄 NoteRepository.java # 🗄️ JPA repository (dev)
│ └── 📄 PasskeyAttemptRepository.java
├── 📂 service/
│ ├── 📄 B2NoteService.java # ☁️ Metadata via B2 JSON (prod)
│ ├── 📄 JpaNoteService.java # 🗄️ Metadata via JPA (dev)
│ ├── 📄 NoteService.java # 📋 Interface
│ └── 📄 PasskeyService.java # 🔐 Passkey verification + rate limiting
├── 📂 storage/
│ ├── 📄 B2Config.java # ⚙️ S3Client + S3Presigner beans
│ ├── 📄 B2StorageService.java # ☁️ B2 file operations (prod)
│ ├── 📄 LocalStorageService.java # 💻 Local file operations (dev)
│ └── 📄 StorageService.java # 📋 Interface
├── 📄 NotesHubApplication.java # 🚀 Entry point
└── 📄 WebConfig.java # ⚙️ Resource handler
📂 src/main/resources/
├── 📄 application.properties # ⚙️ Dev configuration
├── 📄 application-prod.properties # ⚙️ Prod configuration
├── 📂 static/css/
│ └── 📄 theme.css # 🎨 Custom CSS (hero gradient, text colors)
└── 📂 templates/
├── 📂 fragments/
│ ├── 📄 navbar.html # 📍 Shared header + footer with theme toggle
│ └── 📄 theme.html # 🌓 Flash-prevention + theme toggle JS
├── 📄 index.html # 🏠 Home page with hero + recent notes
├── 📄 browse.html # 🔍 Browse/search all notes
├── 📄 upload.html # ⬆️ Upload form
├── 📄 admin.html # 🔐 Admin dashboard
└── 📄 admin-login.html # 🔑 Admin login
The UI uses a GitHub-inspired blue/gray palette controlled via CSS custom properties in theme.css and custom utility classes:
| Role | ☀️ Light Mode | 🌙 Dark Mode |
|---|---|---|
| Background | #FFFFFF |
#0D1117 |
| Surface/Cards | #FFFFFF |
#1C2128 |
| Primary Blue | #0969DA |
#58A6FF |
| Button Blue | #2F81F7 |
#2F81F7 |
| Heading Text | #24292F |
#F0F6FC |
| Muted Text | #57606A |
#8B949E |
The dark/light toggle uses Tailwind's darkMode: 'class' strategy. The user's preference is stored in localStorage with a flash-prevention script in theme.html that applies the correct class before the page renders.