Skip to content

πŸ› Odyhub Backend: A RESTful API built with Spring Boot for a public complaint management system, featuring OTP-based authentication, JWT security, geolocation tracking, and real-time status updates β€” empowering citizen engagement through technology.

License

Notifications You must be signed in to change notification settings

Aldayanday1/odyhub_be

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

7 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ›οΈ

Odyhub Backend

Sistem Pengaduan Masyarakat

Java Spring Boot MySQL Flutter

Backend API yang powerful untuk aplikasi mobile Odyhub - Platform Pengaduan Masyarakat berbasis Flutter

Features β€’ Tech Stack β€’ API Documentation β€’ Installation β€’ Architecture

πŸ“± Tentang Odyhub

Odyhub adalah platform digital yang memungkinkan masyarakat untuk melaporkan berbagai permasalahan dan keluhan di lingkungan mereka secara cepat dan transparan. Backend ini menyediakan RESTful API yang robust untuk mendukung aplikasi mobile Flutter dengan fitur-fitur modern seperti:

  • βœ… Autentikasi Multi-Layer dengan OTP Email & JWT Token
  • πŸ” Keamanan Tinggi menggunakan Argon2 Password Hashing
  • πŸ“ Geolocation Support untuk tracking lokasi pengaduan
  • πŸ“Έ Multi-Image Upload dengan optimisasi storage
  • πŸ“Š Real-time Status Tracking untuk setiap laporan
  • πŸ“§ Email Notification System untuk verifikasi dan notifikasi
  • πŸ‘₯ Role-Based Access Control (User & Admin)
  • πŸ“ˆ Analytics Dashboard untuk monitoring pengaduan

✨ Features

πŸ” Authentication & Authorization

  • User Registration dengan verifikasi OTP via email
  • Secure Login dengan Two-Factor Authentication (2FA)
  • JWT Token Management dengan blacklist support
  • Admin Authentication tanpa OTP untuk akses cepat
  • Session Management dengan auto-expiry
  • Password Hashing menggunakan Argon2 (state-of-the-art)

πŸ“‹ Manajemen Pengaduan

  • Create Report dengan lokasi GPS, foto, dan kategori
  • Update Report untuk edit pengaduan yang dibuat
  • Delete Report dengan validasi kepemilikan
  • View My Reports untuk tracking pengaduan pribadi
  • Search & Filter berdasarkan judul, kategori, dan status
  • Real-time Updates untuk perubahan status laporan

🎯 Kategorisasi Laporan

Mendukung 10 kategori pengaduan:

  • πŸ—οΈ Infrastruktur (jalan rusak, lampu mati, dll)
  • 🌳 Lingkungan (sampah, polusi, dll)
  • πŸš— Transportasi (macet, parkir liar, dll)
  • πŸ›‘οΈ Keamanan (pencurian, kriminal, dll)
  • πŸ₯ Kesehatan (fasilitas kesehatan, dll)
  • πŸ“š Pendidikan (sekolah, fasilitas pendidikan, dll)
  • πŸ‘₯ Sosial (kemiskinan, kesejahteraan, dll)
  • πŸ“„ Izin & Perizinan
  • πŸ›οΈ Birokrasi & Pelayanan Publik
  • πŸ“Œ Lainnya

πŸ‘¨β€πŸ’Ό Panel Admin

  • Dashboard Analytics dengan statistik harian
  • Status Management untuk update progress laporan
  • Response System untuk memberikan tanggapan
  • Filter by Status (Pending, In Progress, Done)
  • Daily Report Chart untuk monitoring trend

πŸ‘€ User Profile Management

  • Profile Customization dengan foto profil & background
  • Image Upload & Storage yang teroptimasi
  • Profile Settings untuk update informasi pribadi

πŸ“§ Email Service

  • OTP Email dengan template HTML yang menarik
  • Registration Confirmation dengan expiry time
  • Login Verification untuk keamanan ekstra
  • Responsive Email Design yang mobile-friendly

πŸ› οΈ Tech Stack

Backend Framework

  • Spring Boot 3.2.3 - Modern Java framework
  • Spring Data JPA - ORM untuk database operations
  • Spring Web - RESTful API development
  • Spring Mail - Email integration

Security

  • JWT (JSON Web Tokens) - Stateless authentication
  • Argon2 Password Encoder - Secure password hashing
  • Spring Security Crypto - Cryptographic operations

Database

  • MySQL 8.0 - Primary database
  • Hibernate - JPA implementation
  • Connection Pooling - Optimized database connections

File Storage

  • Local File System - Optimized file management
  • Multi-part File Upload - Support large files up to 10MB
  • Image Processing - Automatic optimization

Email Service

  • JavaMail API - Email functionality
  • SMTP Gmail Integration - Reliable email delivery
  • HTML Email Templates - Professional email design

Tools & Utilities

  • Maven - Dependency management
  • Jackson - JSON processing
  • SLF4J & Logback - Logging framework
  • JUnit - Unit testing

πŸ“Š Architecture

System Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      MOBILE APP (Flutter)                   β”‚
β”‚                           Odyhub                            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                             β”‚ 
                             β”‚  HTTP/HTTPS
                             β”‚  REST API
                             β–Ό 
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    SPRING BOOT BACKEND                      β”‚
β”‚                                                             β”‚
β”‚     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”‚
β”‚     β”‚ Controllers β”‚  β”‚   Services   β”‚  β”‚ Repositoriesβ”‚      β”‚
β”‚     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚
β”‚            β”‚                β”‚                 β”‚             β”‚
β”‚            β”‚                β”‚                 β”‚             β”‚
β”‚            β–Ό                β–Ό                 β–Ό             β”‚
β”‚     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”‚
β”‚     β”‚  JWT Auth   β”‚  β”‚ Email Serviceβ”‚  β”‚   MySQL DB  β”‚      β”‚
β”‚     β”‚   Filter    β”‚  β”‚    (SMTP)    β”‚  β”‚    (JPA)    β”‚      β”‚
β”‚     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚
β”‚                                                             β”‚
β”‚     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”‚
β”‚     β”‚File Storage β”‚  β”‚  Scheduled   β”‚  β”‚    Util     β”‚      β”‚
β”‚     β”‚  Service    β”‚  β”‚    Tasks     β”‚  β”‚   Classes   β”‚      β”‚ 
β”‚     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Database Schema

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚    User       β”‚     β”‚   Pengaduan     β”‚     β”‚  StatusLaporan   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ ─     β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€     β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ id (PK)       │────<β”‚ user_id (FK)    β”‚>─── β”‚ pengaduan_id (FK)β”‚
β”‚ nama          β”‚     β”‚ id (PK)         β”‚     β”‚ id (PK)          β”‚
β”‚ email         β”‚     β”‚ judul           β”‚     β”‚ status_sebelumnyaβ”‚
β”‚ password      β”‚     β”‚ alamat          β”‚     β”‚ status_baru      β”‚
β”‚ role          β”‚     β”‚ deskripsi       β”‚     β”‚ tanggapan        β”‚
β”‚ otp_code      β”‚     β”‚ kategori        β”‚     β”‚ gambar           β”‚
β”‚ otp_expiry    β”‚     β”‚ gambar          β”‚     β”‚ changed_at       β”‚
β”‚ is_registeringβ”‚     β”‚ latitude        β”‚     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚ longitude       β”‚
                      β”‚ created_at      β”‚     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                      β”‚ updated_at      β”‚     β”‚  UserProfile     β”‚
                      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
                                              β”‚ id (PK)          β”‚
                                              β”‚ user_id (FK)     β”‚
                                              β”‚ profile_image    β”‚
                                              β”‚ background_image β”‚
                                              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸš€ Installation

Prerequisites

  • β˜• Java JDK 17+ (Recommended: JDK 20)
  • πŸ—„οΈ MySQL 8.0+
  • πŸ“¦ Maven 3.6+ (or use IDE built-in)
  • πŸ“§ Gmail Account (for SMTP email service)

Step 1: Clone Repository

git clone https://github.com/Aldayanday1/odyhub_be.git
cd odyhub_be

Step 2: Setup Database

  1. Start MySQL Service:

    # Windows
    net start MySQL80
    
    # Linux/Mac
    sudo systemctl start mysql
  2. Create Database:

    mysql -u root -p
    CREATE DATABASE sistem_pengaduan;
    exit;

Step 3: Configure Application

Option A: Menggunakan Environment Variables (RECOMMENDED)

  1. Copy template environment variables:

    cp .env.example .env
  2. Edit file .env dengan credentials Anda:

    # Database
    DB_USERNAME=root
    DB_PASSWORD=your_mysql_password
    
    # Email SMTP
    MAIL_USERNAME=your-email@gmail.com
    MAIL_PASSWORD=your-gmail-app-password
    
    # JWT Secret (generate dengan: openssl rand -base64 32)
    SECRET_KEY=your-jwt-secret-key
    
    # Upload folder
    UPLOAD_FOLDER=./status-laporan-images
  3. Generate Gmail App Password:

  4. File .env sudah di-ignore oleh Git - aman untuk menyimpan credentials

Option B: Set Environment Variables Manual

Windows PowerShell:

$env:DB_PASSWORD="your_password"
$env:MAIL_USERNAME="your-email@gmail.com"
$env:MAIL_PASSWORD="your-app-password"
$env:SECRET_KEY="your-secret-key"

Linux/Mac:

export DB_PASSWORD="your_password"
export MAIL_USERNAME="your-email@gmail.com"
export MAIL_PASSWORD="your-app-password"
export SECRET_KEY="your-secret-key"

Step 4: Create Upload Folders

# Create folders for image storage
mkdir uploads
mkdir status-laporan-images

Step 5: Run Application

Option 1: Using Maven

mvn spring-boot:run

Option 2: Using IDE

  • NetBeans: Right-click project β†’ Run
  • IntelliJ IDEA: Click Run button
  • Eclipse: Right-click β†’ Run As β†’ Spring Boot App

Option 3: Using JAR

mvn clean package
java -jar target/sistem_pengaduan-0.0.1-SNAPSHOT.jar

Step 6: Verify Installation

# Check if server is running
curl http://localhost:8080/api/users/all

βœ… Backend is ready! Now you can connect your Flutter mobile app.


πŸ“š API Documentation

Base URL

http://localhost:8080/api/users

Authentication Endpoints

1. Register User

POST /register
Content-Type: application/json

{
  "nama": "John Doe",
  "email": "john@example.com",
  "password": "SecurePass123"
}

Response:

{
  "message": "Registrasi berhasil. Silakan cek email Anda untuk kode OTP."
}

2. Verify OTP (Registration)

POST /verify-otp?otp=1234

Response:

{
  "message": "Verifikasi OTP berhasil. Silakan login."
}

3. Login User

POST /login?email=john@example.com&password=SecurePass123

Response:

{
  "message": "OTP telah dikirimkan ke email Anda."
}

4. Verify OTP (Login)

POST /login-with-otp
Content-Type: application/json

{
  "otp": "1234"
}

Response:

{
  "status": "success",
  "message": "Login berhasil",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

5. Login Admin

POST /admin-login
Content-Type: application/json

{
  "nama": "Admin",
  "password": "AdminPass123"
}

Response:

{
  "status": "success",
  "message": "Login berhasil",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

6. Logout

POST /logout
Authorization: Bearer <token>

Pengaduan (Report) Endpoints

1. Create Pengaduan

POST /add
Authorization: Bearer <token>
Content-Type: multipart/form-data

Form Data:
- judul: "Jalan Rusak di Depan Kantor"
- alamat: "Jl. Merdeka No. 123"
- deskripsi: "Jalan berlubang dan berbahaya"
- kategori: "INFRASTRUKTUR"
- latitude: -6.200000
- longitude: 106.816666
- gambar: [file]

Response:

{
  "id": 1,
  "judul": "Jalan Rusak di Depan Kantor",
  "alamat": "Jl. Merdeka No. 123",
  "deskripsi": "Jalan berlubang dan berbahaya",
  "kategori": "INFRASTRUKTUR",
  "gambar": "http://192.168.56.1:8080/api/users/uploads/image.jpg",
  "latitude": -6.200000,
  "longitude": 106.816666,
  "status": "PENDING",
  "namaPembuat": "John Doe",
  "profileImagePembuat": "http://192.168.56.1:8080/images/profile.png",
  "createdAt": "2025-10-01T10:30:00",
  "updatedAt": "2025-10-01T10:30:00"
}

2. Get All Pengaduan

GET /all

3. Get My Pengaduan

GET /my-pengaduan
Authorization: Bearer <token>

4. Get Pengaduan by ID

GET /{id}

5. Update Pengaduan

PUT /update/{id}
Authorization: Bearer <token>
Content-Type: multipart/form-data

Form Data:
- judul: "Updated Title"
- deskripsi: "Updated Description"
- gambar: [file] (optional)

6. Delete Pengaduan

DELETE /delete/{id}
Authorization: Bearer <token>

7. Search Pengaduan

GET /search?judul=jalan

8. Filter by Category

GET /kategori/INFRASTRUKTUR
GET /kategori/LINGKUNGAN
GET /kategori/TRANSPORTASI

Status Laporan Endpoints (Admin Only)

1. Update Status Laporan

PUT /update-status/{pengaduanId}
Authorization: Bearer <admin-token>
Content-Type: multipart/form-data

Form Data:
- statusBaru: "PROGRESS" | "DONE"
- tanggapan: "Sedang ditangani oleh tim"
- gambar: [file] (optional)

Response:

{
  "id": 1,
  "pengaduan": { ... },
  "statusSebelumnya": "PENDING",
  "statusBaru": "PROGRESS",
  "tanggapan": "Sedang ditangani oleh tim",
  "gambar": "http://192.168.56.1:8080/api/users/uploads/response.jpg",
  "changedAt": "2025-10-01T11:00:00"
}

2. Get Pengaduan by Status (Admin)

GET /pengaduan-by-status/PENDING
GET /pengaduan-by-status/PROGRESS
GET /pengaduan-by-status/DONE
Authorization: Bearer <admin-token>

3. Get Daily Count (Admin Analytics)

GET /daily-count
Authorization: Bearer <admin-token>

Response:

{
  "MONDAY": 15,
  "TUESDAY": 23,
  "WEDNESDAY": 18,
  "THURSDAY": 20,
  "FRIDAY": 25,
  "SATURDAY": 12,
  "SUNDAY": 8
}

User Profile Endpoints

1. Get Profile

GET /profile
Authorization: Bearer <token>

Response:

{
  "nama": "John Doe",
  "email": "john@example.com",
  "profileImage": "http://192.168.56.1:8080/images/profile.png",
  "backgroundImage": "http://192.168.56.1:8080/images/background.jpg"
}

2. Update Profile

PUT /profile/update
Authorization: Bearer <token>
Content-Type: multipart/form-data

Form Data:
- profileImage: [file] (optional)
- backgroundImage: [file] (optional)

File Access Endpoint

Get Uploaded Image

GET /uploads/{filename}

Example:

http://localhost:8080/api/users/uploads/image.jpg

πŸ” Security Features

Password Security

  • Argon2 Algorithm dengan parameter custom:
    • Memory: 16 MB
    • Iterations: 32
    • Parallelism: 1
    • Salt Length: 4096 bytes
    • Hash Length: 64 bytes

JWT Token Security

  • Token Expiration: 40 menit (2400 seconds)
  • Token Blacklist: Logout akan masukkan token ke blacklist
  • Role-Based Claims: Token menyimpan role (USER/ADMIN)
  • Secret Key: Disimpan di environment variable

OTP Security

  • Expiry Time: 1 menit
  • Auto-Cleanup: Scheduled task menghapus OTP expired
  • Single Use: OTP dihapus setelah verifikasi berhasil
  • Random Generation: 4-digit random number

API Security

  • Authorization Header: Bearer token untuk protected endpoints
  • Role Validation: Middleware memeriksa role untuk admin endpoints
  • Token Validation: Setiap request divalidasi JWT-nya
  • CORS Configuration: Atur allowed origins sesuai kebutuhan

🎯 Flutter Integration Guide

Setup HTTP Client (Flutter)

// lib/services/api_service.dart
import 'package:http/http.dart' as http;
import 'dart:convert';

class ApiService {
  static const String baseUrl = 'http://YOUR_IP:8080/api/users';
  static String? authToken;

  // Register User
  static Future<Map<String, dynamic>> register({
    required String nama,
    required String email,
    required String password,
  }) async {
    final response = await http.post(
      Uri.parse('$baseUrl/register'),
      headers: {'Content-Type': 'application/json'},
      body: jsonEncode({
        'nama': nama,
        'email': email,
        'password': password,
      }),
    );
    return jsonDecode(response.body);
  }

  // Verify OTP
  static Future<String> verifyOtp(String otp) async {
    final response = await http.post(
      Uri.parse('$baseUrl/verify-otp?otp=$otp'),
    );
    return response.body;
  }

  // Login
  static Future<Map<String, dynamic>> login({
    required String email,
    required String password,
  }) async {
    final response = await http.post(
      Uri.parse('$baseUrl/login?email=$email&password=$password'),
    );
    return {'message': response.body};
  }

  // Login with OTP
  static Future<Map<String, dynamic>> loginWithOtp(String otp) async {
    final response = await http.post(
      Uri.parse('$baseUrl/login-with-otp'),
      headers: {'Content-Type': 'application/json'},
      body: jsonEncode({'otp': otp}),
    );
    final data = jsonDecode(response.body);
    if (data['status'] == 'success') {
      authToken = data['token'];
    }
    return data;
  }

  // Create Pengaduan
  static Future<Map<String, dynamic>> createPengaduan({
    required String judul,
    required String alamat,
    required String deskripsi,
    required String kategori,
    required double latitude,
    required double longitude,
    required File gambar,
  }) async {
    var request = http.MultipartRequest(
      'POST',
      Uri.parse('$baseUrl/add'),
    );
    
    request.headers['Authorization'] = 'Bearer $authToken';
    request.fields['judul'] = judul;
    request.fields['alamat'] = alamat;
    request.fields['deskripsi'] = deskripsi;
    request.fields['kategori'] = kategori;
    request.fields['latitude'] = latitude.toString();
    request.fields['longitude'] = longitude.toString();
    request.files.add(await http.MultipartFile.fromPath('gambar', gambar.path));
    
    final response = await request.send();
    final responseData = await response.stream.bytesToString();
    return jsonDecode(responseData);
  }

  // Get All Pengaduan
  static Future<List<dynamic>> getAllPengaduan() async {
    final response = await http.get(Uri.parse('$baseUrl/all'));
    return jsonDecode(response.body);
  }

  // Get My Pengaduan
  static Future<List<dynamic>> getMyPengaduan() async {
    final response = await http.get(
      Uri.parse('$baseUrl/my-pengaduan'),
      headers: {'Authorization': 'Bearer $authToken'},
    );
    return jsonDecode(response.body);
  }
}

Example Usage in Flutter Widget

// Example: Login Screen
class LoginScreen extends StatelessWidget {
  final TextEditingController emailController = TextEditingController();
  final TextEditingController passwordController = TextEditingController();

  void handleLogin() async {
    try {
      // Step 1: Request OTP
      await ApiService.login(
        email: emailController.text,
        password: passwordController.text,
      );
      
      // Step 2: Navigate to OTP screen
      Navigator.push(context, MaterialPageRoute(
        builder: (context) => OtpVerificationScreen(),
      ));
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Login failed: $e')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    // Your UI implementation
  }
}

πŸ—‚οΈ Project Structure

sistem_pengaduan/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ main/
β”‚   β”‚   β”œβ”€β”€ java/sistem_pengaduan/
β”‚   β”‚   β”‚   β”œβ”€β”€ SistemPengaduanApplication.java
β”‚   β”‚   β”‚   └── demo/
β”‚   β”‚   β”‚       β”œβ”€β”€ config/
β”‚   β”‚   β”‚       β”‚   └── SecurityConfig.java
β”‚   β”‚   β”‚       β”œβ”€β”€ controller/
β”‚   β”‚   β”‚       β”‚   β”œβ”€β”€ UserController.java
β”‚   β”‚   β”‚       β”‚   β”œβ”€β”€ PengaduanController.java
β”‚   β”‚   β”‚       β”‚   β”œβ”€β”€ StatusLaporanController.java
β”‚   β”‚   β”‚       β”‚   β”œβ”€β”€ UserProfileController.java
β”‚   β”‚   β”‚       β”‚   └── FileStorageService.java
β”‚   β”‚   β”‚       β”œβ”€β”€ model/
β”‚   β”‚   β”‚       β”‚   β”œβ”€β”€ User.java
β”‚   β”‚   β”‚       β”‚   β”œβ”€β”€ UserRole.java
β”‚   β”‚   β”‚       β”‚   β”œβ”€β”€ UserProfile.java
β”‚   β”‚   β”‚       β”‚   β”œβ”€β”€ Pengaduan.java
β”‚   β”‚   β”‚       β”‚   β”œβ”€β”€ Kategori.java
β”‚   β”‚   β”‚       β”‚   β”œβ”€β”€ StatusLaporan.java
β”‚   β”‚   β”‚       β”‚   └── Status.java
β”‚   β”‚   β”‚       β”œβ”€β”€ repository/
β”‚   β”‚   β”‚       β”‚   β”œβ”€β”€ UserRepo.java
β”‚   β”‚   β”‚       β”‚   β”œβ”€β”€ UserProfileRepo.java
β”‚   β”‚   β”‚       β”‚   β”œβ”€β”€ PengaduanRepo.java
β”‚   β”‚   β”‚       β”‚   └── StatusLaporanRepo.java
β”‚   β”‚   β”‚       β”œβ”€β”€ service/
β”‚   β”‚   β”‚       β”‚   β”œβ”€β”€ UserService.java
β”‚   β”‚   β”‚       β”‚   β”œβ”€β”€ PengaduanService.java
β”‚   β”‚   β”‚       β”‚   β”œβ”€β”€ StatusLaporanService.java
β”‚   β”‚   β”‚       β”‚   β”œβ”€β”€ EmailServiceRegist.java
β”‚   β”‚   β”‚       β”‚   β”œβ”€β”€ EmailServiceLogin.java
β”‚   β”‚   β”‚       β”‚   β”œβ”€β”€ FileStorageException.java
β”‚   β”‚   β”‚       β”‚   └── ScheduledTasks.java
β”‚   β”‚   β”‚       └── util/
β”‚   β”‚   β”‚           β”œβ”€β”€ JwtUtil.java
β”‚   β”‚   β”‚           └── JwtRequestFilter.java
β”‚   β”‚   └── resources/
β”‚   β”‚       β”œβ”€β”€ application.properties
β”‚   β”‚       └── META-INF/
β”‚   β”‚           └── persistence.xml
β”‚   └── test/
β”‚       └── java/sistem_pengaduan/demo/
β”‚           └── SistemPengaduanApplicationTests.java
β”œβ”€β”€ uploads/                      # User uploaded images
β”œβ”€β”€ status-laporan-images/        # Admin response images
β”œβ”€β”€ pom.xml
└── README.md


πŸ”§ Configuration Files

application.properties

# Server Configuration
server.port=8080

# Database Configuration
spring.datasource.url=jdbc:mysql://localhost:3306/sistem_pengaduan?serverTimezone=Asia/Jakarta
spring.datasource.username=root
spring.datasource.password=YOUR_PASSWORD

# JPA Configuration
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

# File Upload Configuration
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB

# Email Configuration
spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=${MAIL_USERNAME:your-email@gmail.com}
spring.mail.password=${MAIL_PASSWORD:your-app-password}
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true

# Upload Folders
upload.folder=./status-laporan-images

πŸ“ˆ Performance & Optimization

Database Optimization

  • βœ… Connection Pooling dengan HikariCP
  • βœ… Lazy Loading untuk relasi OneToMany
  • βœ… Index Optimization pada foreign keys
  • βœ… Query Optimization dengan JPQL

API Optimization

  • βœ… Compression enabled untuk response
  • βœ… Lazy Initialization untuk dependencies
  • βœ… Efficient JSON Serialization dengan Jackson
  • βœ… Paginated Responses untuk list endpoints

Security Optimization

  • βœ… Argon2 lebih aman dari BCrypt
  • βœ… JWT Stateless mengurangi database hits
  • βœ… Token Blacklist in-memory untuk performa
  • βœ… Scheduled Cleanup untuk expired OTP

πŸ’‘ Troubleshooting

Port Already in Use

# Windows
netstat -ano | findstr :8080
taskkill /PID <PID> /F

# Linux/Mac
lsof -i :8080
kill -9 <PID>

MySQL Connection Failed

# Check MySQL service
net start MySQL80        # Windows
sudo systemctl start mysql  # Linux

# Verify connection
mysql -u root -p

Email Authentication Failed

  • Pastikan 2FA aktif di Gmail
  • Generate App Password baru
  • Update application.properties dengan App Password

Out of Memory Error

# Set Java memory options
export MAVEN_OPTS="-Xms256m -Xmx1024m"
mvn spring-boot:run

πŸ§ͺ Testing

Run Unit Tests

mvn test

Test API with cURL

Register:

curl -X POST http://localhost:8080/api/users/register \
  -H "Content-Type: application/json" \
  -d '{"nama":"Test User","email":"test@example.com","password":"Test123"}'

Login:

curl -X POST "http://localhost:8080/api/users/login?email=test@example.com&password=Test123"

Test with Postman

Import collection dengan endpoint yang ada di API Documentation.


πŸš€ Deployment

Deploy to Production Server

  1. Build JAR:

    mvn clean package -DskipTests
  2. Set Environment Variables:

    export SECRET_KEY="production-secret-key"
    export MAIL_USERNAME="production-email@gmail.com"
    export MAIL_PASSWORD="production-app-password"
    export SERVER_PORT=8080
  3. Run Application:

    java -jar target/sistem_pengaduan-0.0.1-SNAPSHOT.jar
  4. Setup as Service (Linux):

    sudo nano /etc/systemd/system/odyhub-backend.service
    
    [Unit]
    Description=Odyhub Backend Service
    After=mysql.service
    
    [Service]
    User=youruser
    ExecStart=/usr/bin/java -jar /path/to/sistem_pengaduan.jar
    SuccessExitStatus=143
    
    [Install]
    WantedBy=multi-user.target
    
    sudo systemctl enable odyhub-backend
    sudo systemctl start odyhub-backend

πŸ“ API Response Format

Success Response

{
  "status": "success",
  "message": "Operation successful",
  "data": { ... }
}

Error Response

{
  "status": "error",
  "message": "Error description",
  "timestamp": "2025-10-01T10:30:00"
}

🀝 Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository
  2. Create feature branch (git checkout -b feature/AmazingFeature)
  3. Commit changes (git commit -m 'Add AmazingFeature')
  4. Push to branch (git push origin feature/AmazingFeature)
  5. Open Pull Request

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.


πŸ‘¨β€πŸ’» Developer

Aldi Raihan


πŸ™ Acknowledgments

  • Spring Boot Team untuk framework yang luar biasa
  • MySQL Team untuk database yang reliable
  • Gmail SMTP untuk email service
  • Flutter Community untuk mobile app integration

πŸ“ž Support

Jika ada pertanyaan atau issue:


⭐ Star this repository if you find it helpful!

Made with ❀️ for better community engagement

About

πŸ› Odyhub Backend: A RESTful API built with Spring Boot for a public complaint management system, featuring OTP-based authentication, JWT security, geolocation tracking, and real-time status updates β€” empowering citizen engagement through technology.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages