im so tired man

This commit is contained in:
Wowieee4
2026-05-28 11:27:06 +07:00
parent 66da2473e1
commit 6272ece15d
86 changed files with 1759 additions and 4090 deletions
@@ -8,4 +8,5 @@ public class LocationUpdateRequest {
private Double accuracy;
private Double speed;
private Double heading;
private Integer batteryLevel;
}
@@ -4,6 +4,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import java.util.List;
import java.util.Map;
@Data
@NoArgsConstructor
@@ -22,6 +23,8 @@ public class DashboardResponse {
// Status
private long unreadSosCount;
private long unreadNotifCount;
private long obstaclesToday;
private Map<String, Object> userStatus;
// Recent activity (5 terbaru)
private List<ActivityLogResponse> recentActivity;
@@ -16,5 +16,6 @@ public class LocationResponse {
private Double accuracy;
private Double speed;
private Double heading;
private Integer batteryLevel;
private LocalDateTime createdAt;
}
@@ -29,6 +29,9 @@ public class LocationHistory {
private Double speed; // m/s
private Double heading; // derajat 0-360
@Column(name = "battery_level")
private Integer batteryLevel;
@Column(name = "created_at", nullable = false, updatable = false)
private LocalDateTime createdAt;
@@ -39,8 +39,13 @@ public class GlobalExceptionHandler {
}
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<ApiResponse<Object>> handleRuntime(RuntimeException ex) {
String message = ex.getMessage();
if ("Email tidak terdaftar".equals(message) || "Password salah".equals(message)) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(ApiResponse.error("AUTH_INVALID", message));
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ApiResponse.error("INTERNAL_ERROR", ex.getMessage()));
.body(ApiResponse.error("INTERNAL_ERROR", message));
}
@ExceptionHandler(Exception.class)
@@ -4,7 +4,11 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
@Repository
public interface ObstacleLogRepository extends JpaRepository<ObstacleLog, Long> {
Page<ObstacleLog> findByUserIdOrderByCreatedAtDesc(Long userId, Pageable pageable);
long countByUserIdAndCreatedAtAfter(Long userId, LocalDateTime createdAt);
}
@@ -8,7 +8,10 @@ import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@@ -82,7 +85,34 @@ public class JwtUtil {
}
private Key getSignInKey() {
byte[] keyBytes = Decoders.BASE64.decode(secretKey);
byte[] keyBytes = decodeSecret(secretKey);
return Keys.hmacShaKeyFor(keyBytes);
}
private byte[] decodeSecret(String configuredSecret) {
String trimmed = configuredSecret == null ? "" : configuredSecret.trim();
if (trimmed.isEmpty()) {
throw new IllegalStateException("JWT secret must not be empty");
}
byte[] keyBytes;
try {
keyBytes = Decoders.BASE64.decode(trimmed);
} catch (RuntimeException base64Error) {
try {
keyBytes = Decoders.BASE64URL.decode(trimmed);
} catch (RuntimeException base64UrlError) {
keyBytes = trimmed.getBytes(StandardCharsets.UTF_8);
}
}
if (keyBytes.length >= 32) {
return keyBytes;
}
try {
return MessageDigest.getInstance("SHA-256").digest(keyBytes);
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("SHA-256 is not available", e);
}
}
}
@@ -8,6 +8,11 @@ import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.HashMap;
import java.util.Map;
@Service
@RequiredArgsConstructor
public class GuardianDashboardService {
@@ -17,6 +22,7 @@ public class GuardianDashboardService {
private final ActivityLogService activityLogService;
private final SosEventRepository sosEventRepository;
private final GuardianNotificationRepository notifRepository;
private final ObstacleLogRepository obstacleLogRepository;
public DashboardResponse getDashboard(Long guardianId) {
var pairing = pairingRelationRepository
@@ -40,6 +46,21 @@ public class GuardianDashboardService {
long unreadSos = sosEventRepository.findByUserIdOrderByCreatedAtDesc(userId, PageRequest.of(0, 100))
.stream().filter(s -> s.getStatus().name().equals("TRIGGERED")).count();
long unreadNotif = notifRepository.countByUserIdAndIsReadFalse(userId);
long obstaclesToday = obstacleLogRepository.countByUserIdAndCreatedAtAfter(
userId,
LocalDateTime.of(LocalDateTime.now().toLocalDate(), LocalTime.MIN)
);
Map<String, Object> userStatus = new HashMap<>();
userStatus.put("displayName", user.getDisplayName());
userStatus.put("email", user.getEmail());
userStatus.put("online", lastLocation != null
&& lastLocation.getCreatedAt() != null
&& lastLocation.getCreatedAt().isAfter(LocalDateTime.now().minusMinutes(2)));
userStatus.put("lastSeenAt", lastLocation != null ? lastLocation.getCreatedAt() : null);
userStatus.put("battery", lastLocation != null ? lastLocation.getBatteryLevel() : null);
userStatus.put("lastSpeed", lastLocation != null ? lastLocation.getSpeed() : null);
userStatus.put("obstaclesToday", obstaclesToday);
return DashboardResponse.builder()
.pairedUserId(userId)
@@ -49,6 +70,8 @@ public class GuardianDashboardService {
.lastLocation(lastLocation)
.unreadSosCount(unreadSos)
.unreadNotifCount(unreadNotif)
.obstaclesToday(obstaclesToday)
.userStatus(userStatus)
.recentActivity(recentActivity)
.build();
}
@@ -42,6 +42,7 @@ public class LocationService {
.accuracy(req.getAccuracy())
.speed(req.getSpeed())
.heading(req.getHeading())
.batteryLevel(req.getBatteryLevel())
.build();
loc = locationHistoryRepository.save(loc);
@@ -136,6 +137,7 @@ public class LocationService {
return LocationResponse.builder()
.id(l.getId()).lat(l.getLat()).lng(l.getLng())
.accuracy(l.getAccuracy()).speed(l.getSpeed()).heading(l.getHeading())
.batteryLevel(l.getBatteryLevel())
.createdAt(l.getCreatedAt()).build();
}
}