This commit is contained in:
Wowieee4
2026-05-02 02:43:47 +07:00
parent f8ca77eeb9
commit c8d9eefa74
14 changed files with 1207 additions and 209 deletions
@@ -1,48 +1,48 @@
package com.walkguide.config;
import org.springframework.boot.CommandLineRunner;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import com.walkguide.entity.User;
import com.walkguide.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class DataSeeder implements CommandLineRunner {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
@Override
public void run(String... args) throws Exception {
// Mesin enkripsi password
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
// Ngecek kalau tabel users kosong, baru diisi
if (userRepository.count() == 0) {
// 1. Bikin akun Guardian (Pendamping)
// 1. Buat Guardian (tanpa connected_to dulu)
User guardian = User.builder()
.email("guardian@walkguide.com")
.password(passwordEncoder.encode("guardian123")) // Password barunya
.role("ROLE_GUARDIAN") // Role udah diganti
.password(passwordEncoder.encode("guardian123"))
.role("ROLE_GUARDIAN")
.build();
guardian = userRepository.save(guardian);
// 2. Bikin akun User (Tunanetra)
// 2. Buat User Tunanetra, langsung sambungkan ke Guardian
User user = User.builder()
.email("user@walkguide.com")
.password(passwordEncoder.encode("user123"))
.role("ROLE_USER")
.connectedTo(guardian)
.build();
user = userRepository.save(user);
// Simpan ke database
// 3. Update Guardian -> sambungkan balik ke User yang dijaganya
guardian.setConnectedTo(user);
userRepository.save(guardian);
userRepository.save(user);
System.out.println("✅ Data Seeder berhasil: Akun Guardian dan User telah masuk ke Database!");
System.out.println("DataSeeder: Guardian (" + guardian.getId()
+ ") <-> User (" + user.getId() + ") berhasil dihubungkan!");
} else {
System.out.println("DataSeeder: Database sudah ada data, skip seeding.");
}
}
}
}
@@ -1,7 +1,12 @@
package com.walkguide.controller;
import java.util.Map;
import com.walkguide.dto.ApiResponse;
import com.walkguide.entity.User;
import com.walkguide.repository.UserRepository;
import com.walkguide.security.JwtUtil;
import com.walkguide.service.MockDataService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
@@ -9,10 +14,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.walkguide.dto.ApiResponse;
import com.walkguide.service.MockDataService;
import lombok.RequiredArgsConstructor;
import java.util.Map;
@RestController
@RequestMapping("/api/guardian")
@@ -20,24 +22,56 @@ import lombok.RequiredArgsConstructor;
public class GuardianController {
private final MockDataService mockDataService;
private final UserRepository userRepository;
private final JwtUtil jwtUtil;
// 4. Ambil Status User
// 4. Ambil Status User yang dimonitor
@GetMapping("/user-status")
public ResponseEntity<ApiResponse<Map<String, Object>>> getUserStatus() {
return ResponseEntity.ok(new ApiResponse<>(true, mockDataService.getUserStatus(), "Data status user berhasil diambil"));
return ResponseEntity.ok(new ApiResponse<>(true,
mockDataService.getUserStatus(),
"Data status user berhasil diambil"));
}
// 5. Setting Hardware Shortcut
@PutMapping("/settings/shortcuts")
public ResponseEntity<ApiResponse<Map<String, Object>>> updateShortcuts(@RequestBody Map<String, Object> request) {
public ResponseEntity<ApiResponse<Map<String, Object>>> updateShortcuts(
@RequestBody Map<String, Object> request) {
Map<String, Object> updated = mockDataService.updateShortcuts(request);
return ResponseEntity.ok(new ApiResponse<>(true, updated, "Shortcut berhasil diperbarui"));
}
// 6. Setting Sensitivitas AI
@PutMapping("/settings/ai")
public ResponseEntity<ApiResponse<Map<String, Object>>> updateAiSettings(@RequestBody Map<String, Object> request) {
public ResponseEntity<ApiResponse<Map<String, Object>>> updateAiSettings(
@RequestBody Map<String, Object> request) {
Map<String, Object> updated = mockDataService.updateAiSettings(request);
return ResponseEntity.ok(new ApiResponse<>(true, updated, "Setting AI berhasil diperbarui"));
}
}
// 7. Lihat User yang terhubung ke Guardian ini
@GetMapping("/my-user")
public ResponseEntity<ApiResponse<Map<String, Object>>> getMyConnectedUser(
HttpServletRequest request) {
String token = request.getHeader("Authorization").substring(7);
String email = jwtUtil.extractUsername(token);
User guardian = userRepository.findByEmail(email)
.orElseThrow(() -> new RuntimeException("Guardian tidak ditemukan"));
User connectedUser = guardian.getConnectedTo();
if (connectedUser == null) {
return ResponseEntity.ok(new ApiResponse<>(true,
Map.of("message", "Belum ada user yang terhubung"),
"Tidak ada koneksi"));
}
Map<String, Object> data = Map.of(
"userId", connectedUser.getId(),
"userEmail", connectedUser.getEmail(),
"connectedSince", connectedUser.getCreatedAt().toString()
);
return ResponseEntity.ok(new ApiResponse<>(true, data,
"Data user yang dipantau berhasil diambil"));
}
}
@@ -1,24 +1,60 @@
package com.walkguide.controller;
import java.util.Map;
import com.walkguide.dto.ApiResponse;
import com.walkguide.entity.User;
import com.walkguide.repository.UserRepository;
import com.walkguide.security.JwtUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.walkguide.dto.ApiResponse;
import java.util.Map;
@RestController
@RequestMapping("/api/user")
@RequiredArgsConstructor
public class UserController {
// 7. Sinyal Darurat (Voice Command)
private final UserRepository userRepository;
private final JwtUtil jwtUtil;
// Sinyal Darurat (Voice Command)
@PostMapping("/emergency")
public ResponseEntity<ApiResponse<String>> triggerEmergency(@RequestBody Map<String, Object> request) {
// Simulasi mengirim notif ke Guardian
public ResponseEntity<ApiResponse<String>> triggerEmergency(
@RequestBody Map<String, Object> request) {
String triggerType = (String) request.get("triggerType");
return ResponseEntity.ok(new ApiResponse<>(true, "Darurat Terkirim", "Guardian telah diberi peringatan via: " + triggerType));
return ResponseEntity.ok(new ApiResponse<>(true,
"Darurat Terkirim",
"Guardian telah diberi peringatan via: " + triggerType));
}
}
// Lihat Guardian yang terhubung ke User ini
@GetMapping("/my-guardian")
public ResponseEntity<ApiResponse<Map<String, Object>>> getMyGuardian(
HttpServletRequest request) {
String token = request.getHeader("Authorization").substring(7);
String email = jwtUtil.extractUsername(token);
User user = userRepository.findByEmail(email)
.orElseThrow(() -> new RuntimeException("User tidak ditemukan"));
User guardian = user.getConnectedTo();
if (guardian == null) {
return ResponseEntity.ok(new ApiResponse<>(true,
Map.of("message", "Belum ada guardian yang terhubung"),
"Tidak ada koneksi"));
}
Map<String, Object> data = Map.of(
"guardianId", guardian.getId(),
"guardianEmail", guardian.getEmail()
);
return ResponseEntity.ok(new ApiResponse<>(true, data,
"Data guardian berhasil diambil"));
}
}
@@ -2,15 +2,22 @@ package com.walkguide.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.PrePersist;
import jakarta.persistence.PreUpdate;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.Instant;
@Entity
@Table(name = "users")
@Data
@@ -18,20 +25,39 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
public class User {
// Ini otomatis bikin ID angka urut (1, 2, 3, dst)
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// Email gak boleh kosong dan gak boleh ada yang kembar
@Column(unique = true, nullable = false)
private String email;
@Column(nullable = false)
private String password;
// Buat nandain dia ini ROLE_USER atau ROLE_ADMIN
@Column(nullable = false)
private String role;
}
private String role;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "connected_to")
private User connectedTo;
@Column(name = "created_at", nullable = false, updatable = false)
private Instant createdAt;
@Column(name = "updated_at", nullable = false)
private Instant updatedAt;
@PrePersist
public void onCreate() {
Instant now = Instant.now();
this.createdAt = now;
this.updatedAt = now;
}
@PreUpdate
public void onUpdate() {
this.updatedAt = Instant.now();
}
}
@@ -1,16 +1,21 @@
package com.walkguide.repository;
import com.walkguide.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.walkguide.entity.User;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// Ini fungsi ajaib Spring Boot. Cuma dari nama fungsinya aja,
// dia udah tau maksudnya "Tolong cariin user di database yang emailnya = X"
Optional<User> findByEmail(String email);
}
@Query("SELECT u FROM User u WHERE u.connectedTo.id = :guardianId AND u.role = 'ROLE_USER'")
Optional<User> findUserByGuardianId(@Param("guardianId") Long guardianId);
@Query("SELECT u FROM User u WHERE u.connectedTo.id = :userId AND u.role = 'ROLE_GUARDIAN'")
Optional<User> findGuardianByUserId(@Param("userId") Long userId);
}
@@ -1,17 +1,27 @@
# Server Port
# ===== SERVER =====
server.port=8080
# === KONEKSI H2 VIRTUAL (GAK PAKE PASSWORD) ===
spring.datasource.url=jdbc:h2:mem:walkguidedb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
# ===== POSTGRESQL CONNECTION =====
spring.datasource.url=jdbc:postgresql://202.46.28.160:2002/uas_5803024001
spring.datasource.username=5803024001
spring.datasource.password=pw5803024001
spring.datasource.driver-class-name=org.postgresql.Driver
# Biar Spring Boot otomatis bikin tabel
spring.jpa.hibernate.ddl-auto=update
# ===== JPA / HIBERNATE =====
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
# JWT Configuration
jwt.secret=WalkGuideSecretKeyYangSangatPanjangMinimal256BitUntukKeamananWalkGuide
jwt.expiration=86400000
# ===== FLYWAY =====
spring.flyway.enabled=true
spring.flyway.locations=classpath:db/migration
spring.flyway.baseline-on-migrate=true
# ===== JWT =====
jwt.secret=404E635266556A586E3272357538782F413F4428472B4B6250645367566B5970
jwt.expiration=86400000
# ===== SWAGGER =====
springdoc.swagger-ui.path=/swagger-ui.html
springdoc.api-docs.path=/v3/api-docs
@@ -0,0 +1,15 @@
-- V1__create_users_table.sql
-- Membuat tabel users dengan relasi self-referencing guardian <-> user
CREATE TABLE IF NOT EXISTS users (
id BIGSERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
role VARCHAR(50) NOT NULL,
connected_to BIGINT REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
CREATE INDEX IF NOT EXISTS idx_users_connected_to ON users(connected_to);
@@ -0,0 +1,22 @@
-- V2__seed_users.sql
-- Insert akun Guardian dan User default (password sudah di-hash BCrypt)
-- guardian123 → BCrypt hash
-- user123 → BCrypt hash
INSERT INTO users (email, password, role, connected_to)
VALUES (
'guardian@walkguide.com',
'$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iAt6WKGK', -- guardian123
'ROLE_GUARDIAN',
NULL -- akan di-update setelah user dibuat
)
ON CONFLICT (email) DO NOTHING;
INSERT INTO users (email, password, role, connected_to)
VALUES (
'user@walkguide.com',
'$2a$10$TJ.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iAt6WK', -- user123
'ROLE_USER',
NULL
)
ON CONFLICT (email) DO NOTHING;
@@ -0,0 +1,18 @@
-- V2__link_guardian_user.sql
-- Menghubungkan Guardian <-> User setelah DataSeeder Java insert kedua akun
-- Script ini dijalankan SETELAH aplikasi sudah seed data via DataSeeder.java
-- Jika DataSeeder yang handle relasi, migration ini bisa jadi no-op safety net
DO $$
DECLARE
v_guardian_id BIGINT;
v_user_id BIGINT;
BEGIN
SELECT id INTO v_guardian_id FROM users WHERE email = 'guardian@walkguide.com';
SELECT id INTO v_user_id FROM users WHERE email = 'user@walkguide.com';
IF v_guardian_id IS NOT NULL AND v_user_id IS NOT NULL THEN
UPDATE users SET connected_to = v_user_id WHERE id = v_guardian_id;
UPDATE users SET connected_to = v_guardian_id WHERE id = v_user_id;
END IF;
END $$;