SalimTag/dar-lemlih-apiculture
π― Full-stack e-commerce platform for Moroccan honey products | React + Spring Boot + Docker + JWT Auth
π― Dar Lemlih Apiculture β Moroccan Terroir E-Commerce Platform
A production-ready, multilingual e-commerce platform for Moroccan terroir products β honey, pollen, and more. Built with Spring Boot, React, JWT authentication, and full RTL/i18n support.

Dar Lemlih storefront β Multilingual with Arabic RTL support
πΈ Screenshots
| Storefront | Product Catalog | Admin Dashboard |
|---|---|---|
![]() |
![]() |
![]() |
β¨ Features
ποΈ Public Storefront
- Multilingual UI β French, English, Arabic with RTL support
- Product Catalog with advanced search and filtering
- Shopping Cart and seamless checkout flow
- Secure Payments β Provider-agnostic (Stripe, Checkout.com, mock)
- Order Tracking β Real-time order history and status
- User Accounts β Authentication, profile, and preferences
π§ Admin Back-Office
- Sales Dashboard with analytics and KPIs
- Product Management β CRUD with media uploads via S3/LocalStack
- Order Fulfillment β Manage, update, and ship orders
- User & Role Management β RBAC with granular permissions
- System Settings β Language, payment provider, upload configuration
π Security
- JWT access/refresh token authentication
- Role-Based Access Control (RBAC)
- BCrypt password hashing
- Rate limiting on auth endpoints
- CORS configuration
- Input validation and sanitization
ποΈ Architecture
βββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT LAYER β
β React + Vite + TypeScript + Tailwind CSS β
β FR π«π· | EN π¬π§ | AR πΈπ¦ β
βββββββββββββββββββ¬ββββββββββββββββββββββββββββ
β REST API (JSON)
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββ
β API LAYER β
β Spring Boot 3 + Java 21 β
β JWT Auth | RBAC | OpenAPI Docs | Validation β
ββββββββββββ¬βββββββββββββββββββ¬ββββββββββββββββ
β β
βΌ βΌ
ββββββββββββββββββββ ββββββββββββββββββββββββββ
β MySQL 8 β β S3 / LocalStack β
β (Data Store) β β (Media Storage) β
ββββββββββββββββββββ ββββββββββββββββββββββββββ
π Quick Start
Prerequisites
| Tool | Version | Purpose |
|---|---|---|
| Docker & Docker Compose | Latest | Run all services |
| Java | 21+ | Backend development |
| Node.js | 20+ | Frontend development |
| MySQL | 8.0 | Database (or via Docker) |
1. Clone the Repository
git clone https://github.com/SalimTag/dar-lemlih-apiculture.git
cd dar-lemlih-apiculture2. Configure Environment Variables
cp apps/api/.env.example apps/api/.env
cp apps/web/.env.example apps/web/.envEdit both .env files with your configuration. See Environment Variables for full reference.
3. Start All Services
docker-compose -f infra/docker/docker-compose.yml up --buildπ Tip: LocalStack is bundled with Docker Compose. The
mediaS3 bucket is created automatically at startup β no manual setup needed.
4. Seed Demo Data
# Wait for services to be healthy, then:
curl -X POST http://localhost:8080/api/admin/seed5. Access the Platform
| Service | URL |
|---|---|
| π₯οΈ Frontend | http://localhost:5173 |
| βοΈ Backend API | http://localhost:8080 |
| π Swagger UI | http://localhost:8080/swagger-ui.html |
| π OpenAPI JSON | http://localhost:8080/v3/api-docs |
π Default Credentials
| Role | Password | |
|---|---|---|
| Admin | admin@darlemlih.ma | Admin!234 |
| Customer | customer@darlemlih.ma | Customer!234 |
β οΈ Important: Change all default credentials before any public deployment.
π Project Structure
dar-lemlih-apiculture/
βββ apps/
β βββ api/ # Spring Boot Backend
β β βββ src/
β β β βββ main/java/
β β β β βββ auth/ # JWT authentication
β β β β βββ catalog/ # Product & category management
β β β β βββ orders/ # Order processing
β β β β βββ payments/ # Payment provider abstraction
β β β β βββ storage/ # S3/filesystem storage
β β β β βββ users/ # User & role management
β β β βββ resources/
β β β βββ application.yml
β β βββ .env.example
β β βββ pom.xml
β β
β βββ web/ # React Frontend
β βββ src/
β β βββ components/ # Reusable UI components
β β βββ pages/ # Route pages
β β βββ features/ # Feature modules
β β βββ services/ # API client
β β βββ i18n/ # Translation files (FR/EN/AR)
β β βββ store/ # State management
β βββ .env.example
β βββ package.json
β
βββ infra/
β βββ docker/
β β βββ docker-compose.yml # Full stack setup
β β βββ Dockerfile.api # Backend image
β β βββ Dockerfile.web # Frontend image
β βββ ci/ # CI/CD pipelines
β
βββ docs/
β βββ demo.gif
β βββ screenshots/
βββ README.md
βοΈ Environment Variables
Backend (apps/api/.env)
# Database
DB_URL=jdbc:mysql://localhost:3306/darlemlih
DB_USERNAME=darlemlih
DB_PASSWORD=yourpassword
# JWT
JWT_SECRET=your-super-secret-jwt-key-min-32-chars
JWT_ACCESS_EXPIRY=3600 # 1 hour
JWT_REFRESH_EXPIRY=604800 # 7 days
# Payment Provider
PAYMENT_PROVIDER=mock # mock | stripe | checkoutcom
STRIPE_SECRET_KEY=sk_test_... # if using stripe
# Email
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USERNAME=your@email.com
SMTP_PASSWORD=your-app-password
# Media Storage - S3 / LocalStack
STORAGE_S3_ENABLED=true
AWS_ENDPOINT_URL=http://localhost:4566
AWS_PUBLIC_ENDPOINT=http://localhost:4566
AWS_REGION=us-east-1
AWS_ACCESS_KEY_ID=test
AWS_SECRET_ACCESS_KEY=test
S3_BUCKET=media
# Media Storage - Filesystem Fallback
UPLOAD_PATH=./uploads
UPLOAD_PUBLIC_BASE_URL=/uploads
UPLOAD_MAX_SIZE=10MB
UPLOAD_MAX_FILES=5
UPLOAD_ALLOWED_EXTENSIONS=jpg,jpeg,png,webp
UPLOAD_ALLOWED_CONTENT_TYPES=image/jpeg,image/png,image/webpFrontend (apps/web/.env)
VITE_API_URL=http://localhost:8080
VITE_APP_DEFAULT_LANG=fr # fr | en | ar
VITE_PAYMENT_PROVIDER=mockπ Internationalization
The platform fully supports three languages with automatic RTL layout for Arabic:
| Language | Code | Direction | Status |
|---|---|---|---|
| π«π· French | fr |
LTR | β Default |
| π¬π§ English | en |
LTR | β Complete |
| πΈπ¦ Arabic | ar |
RTL | β Complete |
Set the default language via VITE_APP_DEFAULT_LANG. Users can switch languages in-app from the header menu.
πΌοΈ Media Storage
Product images go through a dedicated storage abstraction with LocalStack support for local development:
| Mode | Configuration | Files Location |
|---|---|---|
| S3 / LocalStack | STORAGE_S3_ENABLED=true |
media bucket |
| Filesystem | STORAGE_S3_ENABLED=false |
./uploads directory |
- The
mediabucket is auto-created at startup β no manual setup - Validation limits (file size, extensions, count) are configurable via
UPLOAD_*env vars - Switch seamlessly from LocalStack (dev) to real AWS S3 (production) by updating credentials
π§ͺ Testing
Backend Tests (Java / JUnit)
cd apps/api
./mvnw test
# With coverage report
./mvnw test jacoco:report
# Report at: target/site/jacoco/index.htmlFrontend Tests (React / Vitest)
cd apps/web
npm test
# With coverage
npm run test:coverageAPI Testing
Once running, use the interactive Swagger UI at:
http://localhost:8080/swagger-ui.html
Or import the OpenAPI spec into Postman:
http://localhost:8080/v3/api-docs
π¦ Production Deployment
1. Build Production Images
docker build -f infra/docker/Dockerfile.api -t darlemlih-api:latest apps/api
docker build -f infra/docker/Dockerfile.web -t darlemlih-web:latest apps/web2. Update Environment Variables
For production, ensure you update:
-
JWT_SECRETβ Use a strong, randomly generated secret -
DB_PASSWORDβ Use a strong database password -
PAYMENT_PROVIDERβ Set tostripeorcheckoutcom -
STORAGE_S3_ENABLED=trueβ Use real AWS S3 credentials - All default admin credentials
3. Deployment Options
Docker Swarm:
docker stack deploy -c infra/docker/docker-compose.prod.yml darlemlihKubernetes:
kubectl apply -f infra/k8s/Cloud Platforms:
- Frontend: Vercel or Netlify (static hosting)
- Backend: Railway, Render, or AWS ECS
- Database: PlanetScale, AWS RDS, or managed MySQL
π API Documentation
The REST API is fully documented with OpenAPI 3.0.
Key Endpoints
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| POST | /api/auth/login |
Authenticate user | β |
| POST | /api/auth/register |
Register new user | β |
| POST | /api/auth/refresh |
Refresh access token | β |
| GET | /api/products |
List all products | β |
| GET | /api/products/{id} |
Get product details | β |
| POST | /api/cart |
Add item to cart | β |
| POST | /api/orders |
Create new order | β |
| GET | /api/orders/{id} |
Get order status | β |
| POST | /api/admin/products |
Create product | β Admin |
| GET | /api/admin/orders |
List all orders | β Admin |
| GET | /api/admin/users |
Manage users | β Admin |
Authentication Flow
1. POST /api/auth/login β { accessToken, refreshToken }
2. Include header: Authorization: Bearer <accessToken>
3. On expiry: POST /api/auth/refresh β new { accessToken }
π Security
This platform implements industry-standard security practices:
- JWT Tokens β Short-lived access tokens (1h) + refresh tokens (7d)
- BCrypt β Password hashing with configurable strength factor
- RBAC β Role-based access:
CUSTOMER,ADMIN,SUPER_ADMIN - Rate Limiting β Auth endpoints protected against brute force
- CORS β Configurable allowed origins
- Input Validation β All inputs validated and sanitized server-side
- HTTPS Ready β TLS termination supported via reverse proxy
π€ Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add: AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
Please ensure:
- Code follows existing style conventions
- Backend: Java code follows Google Java Style Guide
- Frontend: ESLint and Prettier pass (
npm run lint) - All tests pass before submitting PR
πΊοΈ Roadmap
- Mobile app (React Native)
- Loyalty points system
- Product reviews and ratings
- Wishlist functionality
- Advanced analytics dashboard
- Abandoned cart recovery emails
- Discount codes and promotions
- Multi-vendor support
- WhatsApp order notifications
π License
Β© 2025 Dar Lemlih Apiculture. All rights reserved.
This project is proprietary software. Unauthorized copying, distribution, or use is strictly prohibited.
π§ Contact & Support
Salim Tagemouati β Developer
- π GitHub: @SalimTag
- πΌ LinkedIn: Salim Tagemouati
- π§ Support: support@darlemlih.ma
β Star this repository if you find it useful!
Made with β€οΈ in Morocco π²π¦ by Salim Tagemouati
π― Bringing Moroccan terroir to the world, one jar at a time


