im so tired man
This commit is contained in:
+1
@@ -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;
|
||||
|
||||
|
||||
+6
-1
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user