βββ ββββββ βββββββ ββββββ βββ βββ ββββ ββββββ βββββββββββββββββββ βββ
βββ βββββββββββββββββββββββββββ ββββ βββββ ββββββ βββββββββββββββββββββββ
βββ βββββββββββ βββββββββββββββ ββββββ ββββββ βββ βββ βββββββββββ
βββ βββββββββββ βββββββββββββββ βββββββββββββ βββ βββ βββββββββββ
βββββββββββ ββββββββββββββ ββββββ βββ βββ βββββββββββββββ βββ βββ ββββββ
βββββββββββ βββ ββββββββββ ββββββ βββ βββ βββββ βββββββ βββ βββ ββββββ
Transparansi Instan, Keputusan Terinformasi
Overview β’ Demo β’ Features β’ Security β’ Tech Stack β’ Installation β’ Design System β’ API Docs β’ License β’ Team β’ Acknowledgement β’ Support
LacakNutri adalah aplikasi web berbasis Full Stack dan AI yang dirancang untuk memberikan analisis nutrisi instan dan validasi keamanan pangan untuk produk makanan/minuman kemasan. Aplikasi ini menggunakan Vision Language Model (VLM) dari Google Gemini untuk memproses label nutrisi dan komposisi dari gambar, serta mekanisme web scraping untuk memverifikasi status registrasi produk ke database resmi BPOM.
Problem Statement: Minimnya transparansi informasi nilai gizi dan kesulitan memverifikasi keamanan produk BPOM.
Solution: Platform end-to-end yang mengubah label fisik menjadi data terstruktur dengan insight kesehatan personal dalam hitungan detik.
Capture Image β AI Analysis (VLM) β Personalized Health Insights
QR Code β BPOM Validation (Scraping) β BPOM Result
Target Users:
- Konsumen sadar kesehatan
- Keluarga dengan kondisi alergi/diet khusus
- Individu dengan penyakit metabolik (diabetes, hipertensi)
- Masyarakat umum yang peduli keamanan pangan
URL: https://lacaknutri.rbwtech.io
Email: lacaknutri@rbwtech.io
Password: UINIC7.0
Capabilities:
- Full CRUD operations
- Real-time analytics dashboard
- User management
- Content moderation (GiziPedia)
- Scan history monitoring
Email: demo@lacaknutri.com
Password: demo123
Disclaimer:
Fungsi utama /api/scan/analyze menggunakan Gemini AI (VLM) untuk membaca dan menganalisis label dari gambar secara langsung. Ini lebih dari sekadar OCR; ini adalah analisis gambar terstruktur. Tesseract hanya digunakan untuk raw text extraction di endpoint terpisah (/api/scan/ocr-text).
Engine: Google Gemini 2.5 / 2.0 Flash
AI Analysis Pipeline (/api/scan/analyze):
flowchart TD
%% Input
U([User Uploads Image]):::input --> FE[Frontend: Get Recaptcha v3 Token]:::frontend
FE --> API[[API Request + Token]]:::api
%% Security Layer
subgraph SECURITY["Security Layer"]
RL{Rate Limit?}:::decision
CV{Captcha Score?}:::decision
end
API --> RL
RL -- "Too Many (IP)" --> ERR429([429: Too Many Requests]):::error
RL -- "OK" --> CV
CV -- "Score < 0.5" --> ERR403([403: Forbidden]):::error
CV -- "Score >= 0.5" --> GEM["analyze_nutrition_image (GeminiService)"]:::service
%% Main Analysis
GEM -- "Prompt + Image" --> GVLM["Gemini VLM Analysis"]:::ai
GVLM -- "Structured JSON" --> BE[Backend Service]:::backend
BE -- "Extract Ingredients" --> ALRGY{User Allergies Found?}:::decision
ALRGY -- "Allergens Detected" --> HIST[Create ScanHistoryOCR]:::storage
HIST --> SHOW([Display Scan Result]):::result
ALRGY -- "No Allergens" --> SHOW
%% Assignment of classes
class U input;
class FE frontend;
class API api;
class RL,CV,ALRGY decision;
class GEM service;
class GVLM ai;
class BE backend;
class SHOW result;
class ERR429,ERR403 error;
class HIST storage;
class SECURITY SECURITY;
Hasil Structured JSON yang Dikelola oleh AI Service:
| Field | Tipe Data | Deskripsi |
|---|---|---|
nutrition |
JSON Object | Data nutrisi terstruktur (kalori, protein, lemak, gula, sodium, dll.). |
health_score |
Integer (0-100) | Penilaian kesehatan objektif oleh AI. |
grade |
String (A-E) | Kategori nilai kesehatan berdasarkan health_score. |
summary |
String | Ringkasan analisis nutrisi (2-3 kalimat). |
pros, cons |
List of Strings | Keunggulan dan kelemahan nutrisi yang terdeteksi. |
ingredients |
String | Daftar bahan yang diekstraksi. |
warnings |
List of Strings | Daftar potensi peringatan (misalnya, Tinggi Gula, Aditif) ditambah alergen yang terdeteksi dari profil pengguna. |
Untuk menjaga resource AI, endpoint /api/scan/analyze memiliki batasan penggunaan harian:
- Pengguna Tamu (Guest) atau Pengguna Terdaftar Biasa: Maksimal 10x Analisis AI per hari.
- Administrator: Tidak memiliki batasan.
Fitur ini memvalidasi produk menggunakan nomor registrasi BPOM yang diinput (MD/ML/SI/DBL) atau dipindai QR-nya.
Mekanisme: Web Scraping (menggunakan httpx dan BeautifulSoup4) dari situs resmi BPOM (https://cekbpom.pom.go.id).
Alur Validasi BPOM (/api/scan/bpom):
sequenceDiagram
participant User
participant Frontend
participant API as Backend API
participant Google as Google Server
participant CRUD as Database/Cache
participant BPOMScraper
User->>Frontend: Input BPOM / Click Scan
activate Frontend
Frontend->>Google: Request reCAPTCHA v3 Token
Google-->>Frontend: Return Token
Frontend->>+API: POST /scan/bpom (BPOM No + Token)
Note over API: 1. Check Rate Limit (IP Based)<br/>2. Validate Captcha Token
API->>Google: Verify Token (Server-to-Server)
Google-->>API: { success: true, score: 0.9 }
alt Score Low or Rate Limit Hit
API-->>Frontend: 403 Forbidden / 429 Too Many Requests
Frontend-->>User: Show Error Message
else Security Passed
API->>+CRUD: Check cache (ScanHistoryBPOM)
alt Data Cached
CRUD-->>API: Return cached data
API->>CRUD: Create Scan History (No scraping)
else Cache Miss
API->>+BPOMScraper: Search BPOM (Scraping)
BPOMScraper-->>API: Product details
API->>CRUD: Create BPOM Cache
API->>CRUD: Create Scan History
end
API-->>-Frontend: Validation Result JSON
Frontend-->>User: Display Product Data
end
deactivate Frontend
Benefit Caching: Hasil scraping disimpan dalam database (bpom_cache) untuk menghemat resource dan memberikan respons cepat.
Sample Response:
{
"success": true,
"data": {
"registration_number": "MD 272831023097",
"product_name": "Indomie Goreng",
"brand": "PT Indofood CBP Sukses Makmur Tbk",
"status": "TERDAFTAR",
"expiry_date": "2028-12-31",
"cached": false
}
}Endpoint /api/scan/analyze secara otomatis memeriksa bahan-bahan yang diidentifikasi oleh AI terhadap daftar alergi yang tersimpan di profil pengguna.
- Daftar alergi pengguna diambil dari database (
current_user.allergies). - Daftar bahan (
ingredients) dari hasil AI diubah menjadi huruf kecil dan diperiksa keberadaan alergen yang telah diatur oleh pengguna. - Alergen yang terdeteksi ditambahkan ke dalam field
warningssebelum disimpan keScanHistoryOCRdan ditampilkan kepada pengguna.
Akses cepat ke database internal yang berisi 4.122 data produk makanan dan minuman yang telah terkurasi.
Fitur Utama:
- Pencarian Cepat (
/api/food/search): Mencari produk berdasarkan nama atau merek. - Detail Nutrisi: Menampilkan informasi nilai gizi (AKG), komposisi, dan takaran saji tanpa perlu melakukan scan ulang.
- Pagination: Mendukung infinite scroll atau pagination untuk menjelajahi katalog.
Data Point:
- 4.122 entri produk unik.
- Mencakup kategori: Makanan Ringan, Minuman, Produk Susu, Makanan Instan, dll.
Pengguna dapat menyimpan produk hasil scan atau pencarian ke dalam daftar favorit untuk akses cepat di kemudian hari.
- Endpoint:
/api/favorites - Fungsi: Simpan (
POST), Hapus (DELETE), dan Lihat Daftar (GET) produk favorit. - Integrasi: Terhubung langsung dengan Food Catalog dan Scan History.
Content Structure:
- Categories: Zat Gizi Makro, Zat Gizi Mikro, Aditif Makanan, Label & Istilah
- Articles: 100 artikel edukasi
- Format: Markdown-based dengan images
- Search: Full-text search dengan relevance ranking
Features:
- Rich text editor (admin)
- Category filtering
- Bookmark system
- Reading time estimate
- Related articles
Manajemen data alergen yang dinamis.
Allergen Database: Admin dapat mengelola master data alergen (cth: Kacang, Susu, Gluten, Seafood) yang nantinya dipilih oleh user di profil mereka.
Detection Logic (Actual Implementation):
# app/routers/scan.py
user_allergies = [allergy.name.lower() for allergy in current_user.allergies]
ingredients_text = result.get('ingredients', '').lower()
detected_allergens = [
allergy.capitalize()
for allergy in user_allergies
if allergy in ingredients_text
]
# Result: ["Kacang Tanah", "Gluten"]Setiap aktivitas scan (OCR maupun BPOM) disimpan untuk referensi pengguna.
- Riwayat Terpisah: Tab khusus untuk Riwayat OCR dan Riwayat BPOM.
- Detail View: Pengguna dapat melihat kembali detail nutrisi dan hasil analisis AI dari produk yang pernah discan sebelumnya.
- Admin Analytics: Admin dapat memantau jumlah scan harian untuk mengevaluasi penggunaan sistem.
Aplikasi ini menerapkan standar keamanan tinggi untuk mencegah abuse dan brute-force attacks.
Sistem membedakan antara manusia dan bot tanpa mengganggu pengalaman pengguna (tanpa puzzle "pilih lampu merah").
- Frontend: Menggunakan
react-google-recaptcha-v3untuk menghasilkan token berdasarkan interaksi user. - Backend: Middleware memverifikasi token ke server Google sebelum memproses request sensitif.
- Penerapan: Login, Register, Forgot Password, Scan BPOM, dan Scan AI.
Menggunakan slowapi untuk membatasi jumlah request dari satu IP address dalam periode waktu tertentu.
- Login/Register/Reset Password: Maksimal 5 request/menit (Mencegah Brute Force).
- Forgot Password: Maksimal 3 request/jam (Mencegah Spam Email).
- AI Analysis: Maksimal 10 request/hari (Guest / User) untuk menghemat kuota API Gemini.
- Guest Mode: User tanpa login bisa mencoba scan terbatas (dilacak via IP & Session Header).
- Registered User: Riwayat tersimpan permanen.
React 18 (Vite 6)
βββ Routing: React Router v7
βββ State: TanStack Query v5 + Context API
βββ UI: Tailwind CSS 3 + Framer Motion (Animation) + Lucide React (Icons)
βββ Forms: React Hook Form + Zod validation
βββ HTTP: Axios
βββ Scanner: html5-qrcode
βββ Charts: Chart.js + react-chartjs-2
βββ Build: Vite 6 (ES modules)
Key Dependencies (package.json):
{
"react": "^18.3.1",
"vite": "^6.0.5",
"react-router-dom": "^7.1.1",
"@tanstack/react-query": "^5.64.1",
"tailwindcss": "^3.4.17",
"axios": "^1.7.9",
"html5-qrcode": "^2.3.8",
"chart.js": "^4.4.7",
"react-chartjs-2": "^5.3.0",
"react-hook-form": "^7.54.2",
"zod": "^3.24.1",
"framer-motion": "^11.16.0",
"lucide-react": "^0.471.0"
}Folder Structure:
frontend/
βββ public/
βββ vite.config.js
frontend/src/
βββ assets/
βββ components/
β βββ layouts/ # Header, Footer, MainLayout
β βββ scanner/ # Komponen Scanner Modular
β β βββ CameraView.jsx
β β βββ ScanResult.jsx
β βββ ui/ # Reusable UI Components (Button, Card, Modal, dll)
βββ config/ # Konfigurasi Axios/API
βββ context/ # Global State (AuthContext)
βββ hooks/ # Custom Hooks
β βββ useScannerCamera.js
β βββ useSmartCaptcha.js
β βββ ...
βββ i18n/ # Multi-language (ID/EN)
βββ pages/ # Halaman Aplikasi
β βββ admin/ # Dashboard Admin
β βββ Scanner.jsx # Halaman Utama Scanner
β βββ Login.jsx
β βββ ...
βββ routes/ # Routing & Protection (ProtectedRoute)
βββ utils/ # Helper functions
FastAPI + Python 3.11
βββ ORM: SQLAlchemy 2.0
βββ Database: PyMySQL (MariaDB connector)
βββ Validation: Pydantic v2
βββ Auth: JWT (python-jose) + Passlib (bcrypt)
βββ AI: google-genai (Google DeepMind SDK terbaru)
βββ OCR: Pytesseract (Tesseract OCR Wrapper)
βββ Scraping: HTTPX + BeautifulSoup4
βββ Image Processing: Pillow
βββ Server: Uvicorn (ASGI)
Key Dependencies (requirements.txt):
fastapi>=0.100.0
uvicorn>=0.20.0
python-multipart>=0.0.6
sqlalchemy>=2.0.0
pymysql>=1.0.0
pydantic>=2.0.0
pydantic-settings>=2.0.0
pytesseract
pytz
slowapi
email-validator>=2.0.0
python-jose[cryptography]>=3.3.0
passlib>=1.7.4
bcrypt==4.0.1
python-dotenv>=1.0.0
google-genai>=0.1.0
beautifulsoup4>=4.12.0
httpx>=0.24.0
pillow>=10.0.0
requests>=2.31.0Project Structure:
Backend (FastAPI)
backend/
βββ app/
β βββ core/ # Konfigurasi inti (DB, Security, Limiter)
β β βββ config.py
β β βββ database.py
β β βββ limiter.py <-- Rate Limiter Instance
β β βββ security.py
β βββ crud/ # Database Operations (Create, Read, Update, Delete)
β βββ models/ # SQLAlchemy ORM Models (Tabel Database)
β βββ routers/ # API Endpoints (Controller)
β β βββ auth.py <-- Login/Register logic
β β βββ scan.py <-- Logic Scanner Utama
β β βββ ...
β βββ schemas/ # Pydantic Models (Data Validation)
β βββ services/ # External Services Logic
β β βββ ai_service.py <-- Integrasi Google Gemini
β β βββ bpom_endpoint.py <-- Scraper BPOM
β βββ dependencies.py # Dependency Injection (Auth, Captcha)
βββ main.py # Entry Point Aplikasi
βββ requirements.txt
Technology: MariaDB 11.4.4 (via PyMySQL)
Schema Design:
-- Users & Authentication
users (14 columns)
βββ id (PK)
βββ email (UNIQUE), password_hash, full_name
βββ role (enum: user, admin, owner)
βββ gender, age, height, weight
βββ activity_level (enum: sedentary, light, moderate, active, very_active)
βββ reset_token, reset_token_expires
βββ created_at, updated_at
-- Reference Data
allergens (
id, name, code, category, description,
severity_level, created_by, timestamps
)
additives (
id, name, code, category, safety_level,
description, health_risks, timestamps
)
-- User Relations
user_allergies (id, user_id, allergen_id, notes)
favorites (
id, user_id,
food_id (FK -> foods),
scan_ocr_id (FK -> scan_history_ocr),
scan_bpom_id (FK -> scan_history_bpom),
created_at
)
-- Core Data: Scan History
scan_history_ocr (14 columns)
βββ id (PK), user_id (FK)
βββ image_data (LONGTEXT/Base64)
βββ product_name
βββ ocr_raw_data (JSON) -- Stores calories, protein, etc.
βββ ai_analysis (TEXT) -- AI Summary
βββ pros (JSON), cons (JSON)
βββ ingredients (TEXT)
βββ warnings (JSON) -- Includes allergen warnings
βββ health_score (INT), grade (VARCHAR)
βββ is_favorited (BOOL), created_at
scan_history_bpom (10 columns)
βββ id (PK), user_id (FK)
βββ bpom_number, product_name, brand, manufacturer
βββ status (TERDAFTAR, dll)
βββ raw_response (JSON) -- Full scraped data
βββ is_favorited (BOOL), created_at
-- Core Data: Food Catalog
foods (18 columns)
βββ id (PK)
βββ name, brand, barcode
βββ serving_size, calories, proteins, fats, carbs
βββ fiber, sugar, sodium
βββ ingredients, image_url
βββ external_id, data_source
βββ timestamps
-- Caching & Content
bpom_cache (id, bpom_number, data (JSON), timestamps)
articles (id, title, slug, content, category_id, status, views, timestamps)
article_categories (id, name, slug, description)ERD Logic (Simplified):
[foods] <ββββββββ
β
[scan_history_ocr] ββΌβββΊ [favorites] βββ [users] βββΊ [user_allergies] βββΊ [allergens]
β
[scan_history_bpom] ββ
Frontend Hosting:
Platform: VPS (Dedicated Server)
Web Server: Nginx 1.24
SSL: Let's Encrypt
Deployment: Manual Build (dist/) -> Nginx ServeBackend Hosting:
Setup: Systemctl
Runtime: Python 3.11
Server: Uvicorn
Environment: ProductionDatabase:
Server: VPS (aaPanel Managed)
DBMS: MariaDB 11.4.4
Connection: Remote (PyMySQL) via Host IPNode.js 18+
Python 3.11+
MariaDB 11.4.4
Gitgit clone https://github.com/rbwtech/lacak-nutri.git
cd lacak-nutriCreate Database:
CREATE DATABASE lacak_nutri CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;Import Schema:
Pastikan Anda berada di root directory project (lacak-nutri/).
mysql -u root -p lacak_nutri < database/schema.sql
mysql -u root -p lacak_nutri < database/seed.sqlInstall Dependencies:
cd backend
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txtEnvironment Variables (.env):
Buat file .env di dalam folder backend/ dan isi sesuai konfigurasi berikut:
DB_HOST=localhost
DB_PORT=3306
DB_NAME=lacak_nutri
DB_USER=root
DB_PASSWORD=your_password
SECRET_KEY=generate_your_secure_secret_key_here
ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=43200
GEMINI_API_KEY=your_google_gemini_api_key
UPLOAD_DIR=./uploads
MAX_UPLOAD_SIZE=10485760
CORS_ORIGINS=http://localhost:5173
DEBUG=True
HOST=0.0.0.0
PORT=8000
RECAPTCHA_SECRET_KEY=your_recaptcha_v3_server_secret
EMAIL_SENDER_NAME="Lacak Nutri Admin"
EMAIL_SENDER_ADDRESS=admin@lacaknutri.com
SMTP_SERVER=smtp.gmail.com
SMTP_PORT=587
SMTP_USERNAME=your_email@gmail.com
SMTP_PASSWORD=your_app_passwordRun Server:
Pastikan virtual environment aktif dan Anda berada di folder backend/.
uvicorn main:app --reload --host 0.0.0.0 --port 8000Server: http://localhost:8000
API Docs: http://localhost:8000/docs
Install Dependencies:
Buka terminal baru dan masuk ke folder frontend.
cd frontend
npm installEnvironment Variables (.env):
Buat file .env di dalam folder frontend/.
VITE_API_URL=http://localhost:8000/api
VITE_RECAPTCHA_SITE_KEY=your_recaptcha_v3_client_site_keyRun Development Server:
npm run devFrontend: http://localhost:5173
Frontend:
npm run build
# Output akan berada di folder: dist/Backend:
# Menggunakan Gunicorn (Linux/Mac)
gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000
# Atau Uvicorn langsung (Windows/Simple Deploy)
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4Sistem desain LacakNutri menggunakan tema kustom Tailwind CSS dengan dukungan penuh untuk Light Mode dan Dark Mode.
Warna dikelola menggunakan CSS Variables untuk mendukung pergantian tema yang mulus.
| Color Token | Light Mode (Hex) | Dark Mode (Hex) | Penggunaan Utama |
|---|---|---|---|
| Primary | #FF9966 |
#FF9966 |
Brand identity, CTA buttons, links |
| Primary Hover | #FF7A4D |
#FF7A4D |
Hover states |
| Secondary | #6B8E23 |
#8ABE53 |
Status sehat, verifikasi (adjusted for contrast) |
| Accent | #A1D2D5 |
#5D8A8D |
Dekorasi, info cards, highlights |
| Background Base | #FDFDF5 |
#121212 |
Latar belakang utama aplikasi |
| Background Surface | #FFFFF7 |
#1E1E1E |
Kartu (Cards), Modal, Sidebar |
| Text Primary | #333333 |
#E0E0E0 |
Judul, konten utama |
| Text Secondary | #8C8C8C |
#A0A0A0 |
Label, metadata, deskripsi singkat |
| Border | #EBE3D5 |
#333333 |
Garis pemisah, border input |
Status Colors (Hardcoded):
- Success:
#4CAF50 - Warning:
#FFC107 - Error:
#EF5350
Font Family: Menggunakan Manrope dari Google Fonts sebagai font utama, dengan fallback ke Inter dan sans-serif.
font-family: "Manrope", "Inter", "sans-serif";Weights: 400 (Regular), 500 (Medium), 600 (SemiBold), 700 (Bold), 800 (ExtraBold).
Konfigurasi Tailwind (tailwind.config.js) mencakup kustomisasi berikut:
Border Radius:
rounded-xl: 12pxrounded-2xl: 16pxrounded-3xl: 24px
Box Shadow:
shadow-soft:0 8px 24px rgba(0, 0, 0, 0.05)
Animations:
- Blob: Animasi background fluid (
blob 7s infinite). - Fade In: Transisi masuk elemen (
fadeIn 0.5s ease-out).
This project is licensed under the MIT License.
MIT License
Copyright (c) 2025 Trio WakwaW Team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
Trio WakwaW Team - UINIC 7.0 Web Development Competition
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Made with β€οΈ by Trio WakwaW Team
β
β β’ Radipta Basri Wijaya
β β’ Agung Nugraha
β β’ Bayu Wicaksono
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Technologies:
- FastAPI - Modern Python web framework
- React - UI library
- Gemini AI - Generative AI analysis
- BPOM - Public product registration database
Inspiration:
- MyFitnessPal - Nutrition tracking
- Open Food Facts - Open food database
- BPOM Mobile - Product verification
Competition:
- UINIC 7.0 Web Development - Universitas Islam Negeri Sunan Kalijaga Yogyakarta
Issues & Bugs:
Report via GitHub Issues
Feature Requests:
Submit via GitHub Discussions
General Inquiries:
WhatsApp: wa.me/6285182381003
β Star us on GitHub if you find this project useful!
LacakNutri Β© 2025 Trio WakwaW Team