A modern, feature-rich notes management system built with Spring Boot 3, PostgreSQL, and advanced search capabilities.
- JWT-based authentication with refresh tokens
- Email verification with OTP
- Password reset functionality
- Secure user isolation
- Create, read, update, delete notes
- Full-text search with PostgreSQL FTS (Indonesian language support)
- Advanced filtering by category, tags, and date range
- Rich text content support
- Organize notes with categories and tags
- Color-coded tags with hex color support
- Search and filter tags/categories
- Usage tracking and cascade protection
- PostgreSQL Full-Text Search with ranking
- Multi-criteria filtering
- Real-time search suggestions
- Search result highlighting
- Comprehensive pagination with metadata
- Structured logging with correlation IDs
- Email notifications with beautiful HTML templates
- Queue-based background processing
- Caching with Memcached
- API documentation with Swagger/OpenAPI
- Backend: Spring Boot 3.5.5, Java 21
- Database: PostgreSQL 17 with Full-Text Search
- Cache: Memcached
- Queue: Redis/Valkey
- Security: Spring Security with JWT
- Documentation: Swagger/OpenAPI 3
- Build: Maven
- Containerization: Docker & Docker Compose
- Java 21+
- Docker & Docker Compose
- Maven 3.9+
git clone <repository-url>
cd notes
make docker-up
# or
docker-compose up -d
make run
# or
./mvnw spring-boot:run
- API: http://localhost:8080
- Swagger UI: http://localhost:8080/swagger-ui.html
- Health Check: http://localhost:8080/actuator/health
POST /api/auth/register # Register new user
POST /api/auth/login # User login
POST /api/auth/verify-otp # Verify email with OTP
POST /api/auth/resend-otp # Resend OTP
POST /api/auth/refresh-token # Refresh access token
POST /api/auth/forgot-password # Request password reset
POST /api/auth/reset-password # Reset password with OTP
GET /api/notes # List notes with filters
POST /api/notes # Create note
GET /api/notes/{id} # Get note by ID
PUT /api/notes/{id} # Update note
DELETE /api/notes/{id} # Delete note
GET /api/notes/search # Full-text search
GET /api/notes/category/{id} # Notes by category
GET /api/notes/tag/{id} # Notes by tag
GET /api/categories # List categories
POST /api/categories # Create category
GET /api/categories/{id} # Get category
PUT /api/categories/{id} # Update category
DELETE /api/categories/{id} # Delete category
GET /api/tags # List tags with filters
POST /api/tags # Create tag
GET /api/tags/all # All user tags
GET /api/tags/{id} # Get tag
PUT /api/tags/{id} # Update tag
DELETE /api/tags/{id} # Delete tag
Create .env
file:
# Database
DATABASE_USER=notes
DATABASE_PASSWORD=notes
DATABASE_DB=notes
# Email (Gmail App Password required)
MAIL_HOST=smtp.gmail.com
MAIL_USERNAME=[email protected]
MAIL_PASSWORD=your-app-password
# Redis/Valkey
SPRING_REDIS_HOST=localhost
SPRING_REDIS_PORT=6379
# Memcached
MEMCACHED_HOST=localhost
MEMCACHED_PORT=11211
default
- Development with mock emaildocker
- Docker environmentprod
- Production settings
# Register new user
curl -X POST http://localhost:8080/api/auth/register \
-H "Content-Type: application/json" \
-d '{
"username": "johndoe",
"email": "[email protected]",
"password": "password123"
}'
# Verify OTP (check console logs for OTP in dev mode)
curl -X POST "http://localhost:8080/api/auth/[email protected]&otpCode=123456"
# Login
curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"usernameOrEmail": "johndoe",
"password": "password123"
}'
# Create category
curl -X POST http://localhost:8080/api/categories \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Work",
"description": "Work related notes"
}'
# Create tag
curl -X POST http://localhost:8080/api/tags \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Important",
"color": "#ff0000"
}'
# Create note
curl -X POST http://localhost:8080/api/notes \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "My Important Note",
"content": "This is a work-related note with important information.",
"categoryId": "category-uuid",
"tagIds": ["tag-uuid"]
}'
# Full-text search
curl "http://localhost:8080/api/notes/search?query=important&page=0&size=10" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
# Advanced filtering
curl "http://localhost:8080/api/notes?search=work&categoryId=uuid&tagIds=uuid1,uuid2&startDate=2025-01-01T00:00:00&page=0&size=10&sort=updatedAt,desc" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
make help # Show all commands
make setup # Setup development environment
make dev # Quick development cycle
make build # Build application
make test # Run tests
make docker-up # Start Docker services
make docker-down # Stop Docker services
make db-reset # Reset database
make logs # Show application logs
make api-test # Test API endpoints
make db-migrate # Run Flyway migrations
make db-info # Show migration status
make format # Format code with Spotless
make format-check # Check code formatting
src/
βββ main/java/blog/sammi/lab/notes/
β βββ application/ # Use cases & DTOs
β βββ domain/ # Entities & repositories
β βββ infrastructure/ # External services & config
β βββ presentation/ # Controllers & DTOs
βββ main/resources/
βββ db/migration/ # Flyway migrations
βββ templates/ # Email templates
- Clean Architecture - Separation of concerns
- CQRS - Command Query Responsibility Segregation
- Repository Pattern - Data access abstraction
- MapStruct - Object mapping
- Strategy Pattern - Email service implementations
- Indonesian Language Support - Proper stemming and stop words
- Weighted Search - Title has higher relevance than content
- Ranking - Results sorted by relevance score
- Performance - GIN indexes for fast search
-- Simple search
SELECT * FROM notes WHERE search_vector @@ to_tsquery('indonesian', 'catatan');
-- Phrase search
SELECT * FROM notes WHERE search_vector @@ to_tsquery('indonesian', 'catatan & penting');
-- Ranked results
SELECT *, ts_rank(search_vector, query) as rank
FROM notes, to_tsquery('indonesian', 'catatan') query
WHERE search_vector @@ query
ORDER BY rank DESC;
- Enable 2-Factor Authentication
- Generate App Password:
- Go to Google App Passwords
- Select "Mail" β "Other (Notes App)"
- Copy 16-character password
- Update
.env
with credentials
Set app.email.mock: true
to see OTP codes in console logs instead of sending emails.
make prod-build
docker run -p 8080:8080 notes-app:latest
version: '3.8'
services:
app:
image: notes-app:latest
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=docker
depends_on:
- db
- redis
- memcached
/actuator/health
- Application health/actuator/info
- Application info/actuator/metrics
- Application metrics
- Structured Logging - JSON format with correlation IDs
- Security Events - Authentication and authorization logs
- Performance Metrics - Request timing and database queries
- Fork the repository
- Create feature branch (
git checkout -b feature/amazing-feature
) - Commit changes (
git commit -m 'Add amazing feature'
) - Push to branch (
git push origin feature/amazing-feature
) - Open Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Documentation: Swagger UI
- Issues: Create GitHub issue
- Email: [email protected]
Built with β€οΈ using Spring Boot 3 and modern Java practices