diff --git a/.gitignore b/.gitignore
index 49b8309..ed227fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,8 +40,12 @@ build/
.env
*.env
+walkguide-backend/demo/secrets.properties
walkguide-backend/demo/hs_err_pid*.log
+walkguide-backend/demo/src/main/resources/firebase/*.json
+walkguide-mobile/walkguide_app/android/app/google-services.json
+walkguide-mobile/walkguide_app/ios/Runner/GoogleService-Info.plist
# Android SDK path (generated by Android Studio)
walkguide-mobile/walkguide_app/android/local.properties
diff --git a/Exam Guide.md b/Exam Guide.md
new file mode 100644
index 0000000..2cb48ca
--- /dev/null
+++ b/Exam Guide.md
@@ -0,0 +1,411 @@
+# 📱 Final Exam: Integrated Mobile Application Project
+### Flutter × Spring Boot × Object-Oriented Analysis and Design
+#### Group Assignment (3 Members) — Industry-Grade Level
+
+---
+
+## Overview
+
+This final exam requires your group to design, develop, and deliver a **fully integrated mobile application system** consisting of:
+
+- A **Flutter mobile frontend** that consumes a RESTful API
+- A **Spring Boot backend** that exposes the API with proper layering, security, and persistence
+- A rigorous **OOAD process** — designed before coding, then verified against the final implementation
+
+The project is evaluated across three distinct dimensions with different grade weights. This is intentional: **design thinking (OOAD) is the foundation**, engineering quality (Flutter + Spring Boot) is the execution.
+
+---
+
+## Group Formation & Role Distribution
+
+Each group consists of **exactly 3 members**. Each member owns one primary pillar but all members must understand and contribute to all three.
+
+| Role | Primary Pillar | Core Responsibilities |
+|---|---|---|
+| **OOAD Lead** | Object-Oriented Analysis & Design | Leads all design artifacts (use case, class, sequence, state diagrams), enforces design pattern compliance across both codebases, owns the design traceability matrix |
+| **Flutter Engineer** | Mobile Frontend | Clean Architecture implementation, state management, UI/UX, performance benchmarking |
+| **Backend Engineer** | Spring Boot API | REST API design, service/repository layers, database schema, security (JWT), API documentation, backend testing |
+
+> **Important:** Role ownership means accountability, not isolation. All members must commit code to both repositories. The OOAD Lead reviews both codebases for pattern compliance — this is a graded responsibility.
+
+---
+
+## Project Topic
+
+Your group is free to choose any application domain, provided it:
+
+- Models a real-world problem with identifiable actors, use cases, and entities
+- Requires meaningful backend logic (not just CRUD — include at least one business rule or workflow)
+- Has a clear primary user and at least one secondary actor (admin, system, or external service)
+
+**Example domains** *(create your own — do not copy)*:
+- Hospital appointment and queue management
+- Campus asset borrowing and return tracking
+- Community marketplace with seller verification flow
+- Event ticketing with seat allocation logic
+- Employee attendance with approval workflow
+
+---
+
+## Pillar 1 — Object-Oriented Analysis & Design (OOAD)
+
+OOAD is evaluated in **two phases**: design artifacts produced before coding, and a traceability audit conducted against the final code.
+
+### Phase 1A: Pre-Development Design Artifacts
+
+All diagrams must be produced using **PlantUML, draw.io, or StarUML** and submitted as part of the Week 2–3 checkpoint. Diagrams drawn by hand are not accepted.
+
+| Artifact | Diagram Type | Requirement |
+|---|---|---|
+| Requirements model | Use Case Diagram | All actors, use cases, include/extend relationships |
+| Structural model | Class Diagram | All domain classes with attributes, methods, visibility, and relationships (association, aggregation, composition, inheritance) |
+| Behavioral model | Sequence Diagrams | At least 3 key interactions (e.g., login, create resource, approval flow) showing object collaboration |
+| State model | State Machine Diagram | At least 1 entity with meaningful state transitions (e.g., Order: PENDING → CONFIRMED → COMPLETED → CANCELLED) |
+| Data model | ERD (Crow's Foot notation) | All entities, PKs/FKs, cardinality — must align with the class diagram |
+| Architecture model | Component Diagram | Flutter app, Spring Boot layers, database, and external services as components with interfaces |
+
+### Phase 1B: Design Pattern Compliance
+
+Your system must implement **at least 4 GoF design patterns** across the full stack, with at least 1 from each category:
+
+| Category | Required Count | Examples |
+|---|---|---|
+| Creational | ≥ 1 | Factory Method, Builder, Singleton |
+| Structural | ≥ 1 | Adapter, Facade, Decorator, Proxy |
+| Behavioral | ≥ 2 | Strategy, Observer, Command, Template Method, Chain of Responsibility |
+
+Each pattern must be documented with:
+1. Pattern name and category
+2. Which class/component implements it (with file path)
+3. UML class diagram showing the pattern in context
+4. Justification — why this pattern was chosen over alternatives
+
+### Phase 1C: Design Traceability Audit (Post-Development)
+
+After development is complete, the OOAD Lead conducts a **traceability audit** comparing the pre-development design to the final code:
+
+- For each class in the original class diagram: does it exist in code? If not, explain why.
+- For each design pattern: show the exact code that implements it (file + line reference).
+- For each sequence diagram: trace the method call chain in the actual code.
+- Document all **design deviations** — cases where implementation diverged from design — with a written rationale for each deviation.
+
+> A perfect match between design and code is not required. Thoughtful, documented deviations are acceptable. Undocumented deviations are penalized.
+
+---
+
+## Pillar 2 — Flutter Mobile Frontend
+
+### Technical Requirements
+
+| Category | Requirement |
+|---|---|
+| **Flutter Version** | Flutter 3.x (Stable channel) |
+| **Architecture** | Clean Architecture — strict 4-layer separation: `domain / data / application / presentation` |
+| **State Management** | BLoC or Riverpod (consistent throughout; mixing is not allowed) |
+| **Navigation** | Go Router with at least 6 distinct screens and route guards for authenticated routes |
+| **API Communication** | `Dio` with interceptors for JWT token injection, refresh token handling, and error normalization |
+| **Local Persistence** | Hive or SQLite for offline caching of at least one core data entity |
+| **Authentication** | JWT-based login/register consuming the Spring Boot auth endpoint |
+| **UI/UX** | Custom widget library (min. 5 reusable widgets), responsive layout, consistent design system |
+| **Error Handling** | Typed failure classes using `Either` (dartz) or equivalent; no raw `try/catch` in UI layer |
+
+### Folder Structure (Enforced)
+
+```
+lib/
+├── core/ # shared utilities, theme, constants, error types
+├── features/
+│ └── [feature]/
+│ ├── domain/ # entities, repository interfaces, use cases
+│ ├── data/ # repository implementations, DTOs, data sources
+│ └── presentation/ # BLoC/Cubit, pages, widgets
+└── main.dart
+```
+
+Any structure deviating from feature-first modular layout must be approved in writing before Week 4.
+
+### Advanced Features (Choose at least 2)
+
+- Real-time updates via WebSocket or Server-Sent Events from Spring Boot
+- Push notifications triggered by backend events (FCM)
+- Offline-first with background sync to Spring Boot API
+- Animated transitions using custom `PageRouteBuilder` or Lottie
+- Internationalization (i18n) with at least 2 languages
+- Biometric authentication (fingerprint/face ID) as second factor
+
+### Flutter Testing & Benchmarking
+
+#### Functional Testing
+
+| Type | Tool | Minimum |
+|---|---|---|
+| Unit Testing | `flutter_test` | All use cases and repository implementations |
+| Widget Testing | `flutter_test` | At least 5 core UI components |
+| Integration Testing | `integration_test` | At least 3 end-to-end flows against the live Spring Boot API |
+
+#### Performance Benchmarking
+
+Run all benchmarks on a **physical Android device in profile mode** (`flutter run --profile`). Emulator results alone are not accepted.
+
+| Metric | Tool | Pass Threshold |
+|---|---|---|
+| Memory — baseline | DevTools → Memory tab | Report heap at launch (MB) |
+| Memory — leak check | DevTools → Memory tab | No steady growth over 10 repeated navigations |
+| Frame rate / jank | DevTools → Performance tab | ≥ 90% frames < 16ms (60fps target) |
+| CPU profile | DevTools → CPU Profiler | Flame graph for top 3 CPU-heavy operations |
+| API latency (client-side) | Dio interceptor logs | All core endpoints < 1500ms |
+| Cold start time | `--trace-startup --profile` | `timeToFirstFrame` < 3000ms |
+| APK size | `flutter build apk --analyze-size` | Release APK < 50MB |
+
+Each benchmark must be reported with: objective, tool, method, results table, threshold comparison, and DevTools screenshot.
+
+**Regression requirement:** Run benchmarks at Week 5 (mid-sprint) and at Week 7 (final). Submit a delta table comparing both runs. Any metric that degrades > 20% must include a root cause analysis and remediation.
+
+---
+
+## Pillar 3 — Spring Boot Backend
+
+### Technical Requirements
+
+| Category | Requirement |
+|---|---|
+| **Java Version** | Java 17+ |
+| **Framework** | Spring Boot 3.x |
+| **Architecture** | Layered: `Controller → Service → Repository` (no logic in Controller, no DB calls in Service) |
+| **Database** | PostgreSQL or MySQL with JPA/Hibernate; schema migrations via Flyway |
+| **Security** | Spring Security with JWT (access token + refresh token); role-based access control (RBAC) |
+| **API Design** | RESTful conventions; versioned endpoints (`/api/v1/...`); proper HTTP status codes |
+| **Validation** | Bean Validation (`@Valid`) on all request DTOs; global exception handler via `@ControllerAdvice` |
+| **Documentation** | Swagger/OpenAPI 3.0 via `springdoc-openapi`; all endpoints documented with schemas |
+| **Configuration** | Environment-separated configs (`application-dev.yml`, `application-prod.yml`); no hardcoded secrets |
+
+### API Contract Requirements
+
+- Minimum **10 distinct REST endpoints** covering the full application domain
+- All endpoints must return a **consistent response envelope**:
+
+```json
+{
+ "success": true,
+ "data": {},
+ "message": "Operation successful",
+ "timestamp": "2025-01-01T00:00:00Z"
+}
+```
+
+- Error responses must include: `success: false`, `errorCode`, `message`, and `timestamp`
+- API contract must be defined as an **OpenAPI 3.0 YAML file** committed to the repository before development begins (design-first)
+
+### Backend Testing & Benchmarking
+
+#### Functional Testing
+
+| Type | Tool | Minimum |
+|---|---|---|
+| Unit Testing | JUnit 5 + Mockito | All service classes; mock repository layer |
+| Integration Testing | `@SpringBootTest` + MockMvc | All controller endpoints; test DB via Testcontainers |
+| Code Coverage | JaCoCo | ≥ 70% line coverage on `service` and `controller` packages |
+
+#### Load Benchmarking
+
+| Metric | Tool | Pass Threshold |
+|---|---|---|
+| API throughput | Apache JMeter or k6 | ≥ 100 req/s under 50 concurrent users |
+| p95 latency | JMeter or k6 | < 500ms under load |
+| Error rate under load | JMeter or k6 | < 1% at 50 concurrent users |
+| DB query performance | Spring Actuator + slow query log | No query > 200ms for standard operations |
+| JVM memory under load | Actuator `/actuator/metrics` | No heap exhaustion during 5-min load test |
+
+Load test scenario: simulate 50 concurrent users performing a realistic user journey (login → fetch list → create resource → logout) for 5 minutes. Export JMeter `.jtl` report or k6 summary as evidence.
+
+---
+
+## Deliverables
+
+### 1. 📁 GitHub Repositories (2 repos)
+
+**Flutter Repository** (`[GroupName]-[AppName]-mobile-final`):
+- Feature-first clean architecture folder structure
+- GitHub Actions workflow (`.github/workflows/flutter.yml`) — green at submission
+- `README.md`: setup instructions, environment variables, APK download link
+- All 3 members must have commits; branching strategy enforced
+
+**Spring Boot Repository** (`[GroupName]-[AppName]-backend`):
+- Layered package structure (`controller`, `service`, `repository`, `domain`, `dto`, `config`)
+- Flyway migration scripts in `resources/db/migration/`
+- OpenAPI YAML committed before Week 4
+- `README.md`: setup instructions, environment variables, how to run locally
+- JaCoCo HTML coverage report committed or published via CI
+
+### 2. 📦 APK File
+
+- Release build named `[GroupName]_[AppName]_FinalExam.apk`
+- Must connect to a **live, publicly deployed** Spring Boot backend (not localhost)
+- Acceptable deployment platforms: Railway, Render, Fly.io, or any public URL
+
+### 3. 📄 Written Report
+
+Format: PDF, minimum **25 pages** (excluding cover and references). Language: English or Bahasa Indonesia.
+
+| # | Section | Description |
+|---|---|---|
+| 1 | Cover Page | System name, group name, member names & student IDs, course name, date |
+| 2 | Abstract | 200–250 words covering the system, tech stack, and key findings |
+| 3 | Introduction | Problem background, objectives, target users, scope and limitations |
+| 4 | OOAD — Pre-Development | All design artifacts (use case, class, sequence, state, ERD, component diagrams) |
+| 5 | OOAD — Design Patterns | Documentation of all 4+ patterns with UML and code references |
+| 6 | OOAD — Traceability Audit | Design-to-code mapping table; documented deviations with rationale |
+| 7 | System Architecture | Flutter Clean Architecture, Spring Boot layers, API communication flow diagram |
+| 8 | API Contract | OpenAPI summary, endpoint table, request/response examples |
+| 9 | Flutter Implementation | Key features, state management flow, custom widgets, advanced features |
+| 10 | Spring Boot Implementation | Service layer logic, security config, DB schema, Flyway migrations |
+| 11 | Flutter Testing & Benchmarking | Test results, all 7 benchmark metrics with evidence and delta table |
+| 12 | Backend Testing & Benchmarking | JUnit/integration test results, JMeter/k6 load test report |
+| 13 | Team Contribution | Per-member task table with percentage, cross-verified with Git commit history |
+| 14 | Conclusion | Achievements, design lessons learned, challenges, future improvements |
+| 15 | References | IEEE format |
+
+### 4. Presentation
+
+- Duration: **15–20 minutes**
+- Structure:
+ - Team introduction + system overview (2 min)
+ - OOAD design walkthrough — diagrams and pattern explanation (4–5 min)
+ - Flutter app live demo — all major flows (5–6 min)
+ - Spring Boot API demo — Swagger UI + one live API call (3 min)
+ - Benchmark results summary (2 min)
+- All 3 members must present a section
+- Upload to YouTube (unlisted) or Google Drive
+
+> **Live Session:** Each member will be questioned individually on the section they presented and on OOAD concepts. Individual scores may differ from the group score.
+
+---
+
+## Timeline
+
+| Phase | Activity | Deadline |
+|---|---|---|
+| Week 1 | Group registration + topic proposal + actor/use case list | Day 7 |
+| Week 2–3 | All OOAD Phase 1A artifacts submitted + OpenAPI YAML drafted | Day 21 |
+| Week 4 | Architecture approved; development sprint begins | Day 28 |
+| Week 5 | Mid-sprint benchmark run (Flutter + Backend) submitted | Day 35 |
+| Week 6–7 | Feature freeze; Flutter ↔ Spring Boot integration testing | Day 49 |
+| Week 7 | Final benchmark run; delta table completed | Day 49 |
+| Week 8 | OOAD traceability audit completed; report writing + video | Day 56 |
+| **Final** | **All deliverables submitted** | **Day 60, 23:59** |
+| Final+1 | Live presentation & individual Q&A | Scheduled by lecturer |
+
+---
+
+## Grading Rubric
+
+Each pillar is graded **independently out of 100 points**. Students receive three separate scores — one per pillar. There is no combined final grade: each score stands on its own and is recorded separately.
+
+---
+
+### 🥇 Pillar 1 — OOAD Score (/ 100)
+
+| Component | Points | Criteria |
+|---|---|---|
+| Pre-development design artifacts | 35 | Completeness of all 6 required diagrams, correctness of notation, diagram tool used (no hand-drawn), submitted before coding begins |
+| Design pattern implementation | 25 | Correct application of ≥ 4 GoF patterns (min 1 per category), UML documented per pattern, each traceable to code with file path |
+| Traceability audit | 25 | Coverage of class-to-code mapping, quality and honesty of deviation documentation, sequence diagram trace accuracy |
+| Cross-pillar design consistency | 15 | Alignment between class diagram, ERD, Flutter domain layer entities, and Spring Boot domain/entity classes |
+
+**OOAD Penalty:**
+
+| Violation | Deduction |
+|---|---|
+| OOAD artifacts submitted after Week 3 (after coding begins) | −20 points |
+| Diagram produced with unpermitted tool (e.g., hand-drawn, screenshot of AI output) | −15 points |
+| Design pattern claimed but not traceable in code | −8 points per pattern |
+| Traceability audit missing for > 30% of class diagram classes | −10 points |
+
+---
+
+### 🥈 Pillar 2 — Flutter Mobile Score (/ 100)
+
+| Component | Points | Criteria |
+|---|---|---|
+| Clean Architecture compliance | 25 | Strict 4-layer separation enforced, no cross-layer violations, feature-first folder structure correct, dependency direction correct |
+| Features & UX quality | 20 | All required screens functional, JWT auth flow works against live API, custom widget library present, error states handled |
+| Testing — unit & widget | 15 | All use cases and repositories covered, at least 5 widget tests, test quality (meaningful assertions, not just coverage padding) |
+| Testing — integration | 10 | At least 3 end-to-end flows tested against live Spring Boot API, not mocked |
+| Performance benchmarking | 20 | All 7 metrics reported on physical device in profile mode, DevTools screenshots provided, delta table (mid vs final), root cause for any regression > 20% |
+| Report clarity | 10 | Report is complete and have clear explanation |
+
+**Flutter Penalty:**
+
+| Violation | Deduction |
+|---|---|
+| Responsive Design fail | −10 points |
+| Benchmarks run on emulator only (no physical device) | −10 points |
+| Missing delta table (mid-sprint vs final benchmark) | −8 points |
+| State management inconsistency (mixing BLoC and Riverpod) | −10 points |
+| Raw `try/catch` found in presentation layer | −5 points per occurrence (max −15) |
+| APK connects to localhost instead of deployed backend | −15 points |
+
+---
+
+### 🥉 Pillar 3 — Spring Boot Score (/ 100)
+
+| Component | Points | Criteria |
+|---|---|---|
+| API design & OpenAPI contract | 25 | ≥ 10 endpoints, consistent response envelope, versioned routes, OpenAPI 3.0 YAML committed before Week 4, Swagger UI accessible |
+| Layered architecture & security | 25 | Strict Controller → Service → Repository separation, JWT with access + refresh token, RBAC with at least 2 roles, no hardcoded secrets |
+| Testing — unit & integration | 25 | JUnit 5 + Mockito for all service classes, MockMvc + Testcontainers for all controllers, JaCoCo ≥ 70% on `service` and `controller` packages |
+| Load benchmarking | 25 | All 5 metrics reported (throughput, p95 latency, error rate, DB query time, JVM heap), JMeter `.jtl` or k6 summary exported, analysis against pass thresholds |
+
+**Spring Boot Penalty:**
+
+| Violation | Deduction |
+|---|---|
+| Hardcoded secrets (API keys, DB passwords) in any file | −15 points |
+| JaCoCo coverage below 70% | −10 points |
+| No Flyway migrations (schema managed manually) | −8 points |
+| OpenAPI YAML committed after Week 4 | −10 points |
+| Load test run with < 50 concurrent users | −10 points |
+| Business logic found directly in Controller class | −8 points per occurrence (max −16) |
+
+---
+
+### Universal Penalty (Applied to All Three Pillar Scores)
+
+| Violation | Deduction |
+|---|---|
+| Late submission (per day, applied to all pillars) | −5 points per pillar |
+| Missing deliverable | −15 points from the relevant pillar |
+| Plagiarized code (any source) | 0 on all three pillars |
+| Member with < 10% commits and no other contribution evidence | That member's individual pillar scores reduced by 20 points each |
+
+---
+
+## Academic Integrity
+
+- All code must be original. Open-source libraries are permitted with proper attribution in both READMEs and the report.
+- Use of AI coding assistants is **permitted but must be disclosed** in a dedicated "AI Tool Usage" section in the report, listing which tools were used, for which tasks, and how outputs were reviewed and understood.
+- Design artifacts must be produced by the group. AI-generated diagrams submitted without annotation will be identified during the live Q&A.
+- Plagiarism between groups or from public repositories results in **zero marks for all involved groups**.
+
+---
+
+## Submission Checklist
+
+- [ ] Flutter GitHub repository (Actions pipeline green, branch protection active, 3+ merged PRs)
+- [ ] Spring Boot GitHub repository (JaCoCo report committed, OpenAPI YAML present, Flyway migrations included)
+- [ ] APK file connecting to live deployed backend (`[GroupName]_FinalExam.apk`)
+- [ ] Written report PDF (≥ 25 pages, all 16 sections complete, benchmark delta table included)
+- [ ] Demo video link (YouTube unlisted or Google Drive, all 3 members presenting)
+- [ ] OOAD traceability matrix (Section 6 of report)
+- [ ] Mid-sprint benchmark results (Sections 11 & 12 of report)
+- [ ] JMeter/k6 load test export (appendix or Drive link)
+
+---
+
+## Contact & Questions
+
+All questions must be submitted through the official course channel. Questions submitted at least **48 hours before any deadline** are guaranteed a response. Design artifact reviews (Week 2–3) require a scheduled appointment — contact the lecturer by Week 1 to book a slot.
+
+---
+
+*Build systems you can defend, designs you can explain, and code that reflects your thinking. 🚀*
diff --git a/walkguide-backend/demo/backend-run.err.log b/walkguide-backend/demo/backend-run.err.log
new file mode 100644
index 0000000..a7fbef3
--- /dev/null
+++ b/walkguide-backend/demo/backend-run.err.log
@@ -0,0 +1,7 @@
+Set-Location : A positional parameter cannot be found that accepts argument 'PROGRAM'.
+At line:1 char:1
++ Set-Location C:\COBA PROGRAM SEMESTER 4\UAS FLUTTER FT. SPRINGBOOT da ...
++ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ + CategoryInfo : InvalidArgument: (:) [Set-Location], ParameterBindingException
+ + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.SetLocationCommand
+
diff --git a/walkguide-backend/demo/backend-run.log b/walkguide-backend/demo/backend-run.log
new file mode 100644
index 0000000..4af7315
--- /dev/null
+++ b/walkguide-backend/demo/backend-run.log
@@ -0,0 +1,2867 @@
+[INFO] Scanning for projects...
+[INFO]
+[INFO] -------------------------< com.walkguide:demo >-------------------------
+[INFO] Building walkguide 0.0.1-SNAPSHOT
+[INFO] from pom.xml
+[INFO] --------------------------------[ jar ]---------------------------------
+[INFO]
+[INFO] >>> spring-boot:3.2.5:run (default-cli) > test-compile @ demo >>>
+[INFO]
+[INFO] --- jacoco:0.8.11:prepare-agent (default) @ demo ---
+[INFO] argLine set to "-javaagent:C:\\Users\\Robertus\\.m2\\repository\\org\\jacoco\\org.jacoco.agent\\0.8.11\\org.jacoco.agent-0.8.11-runtime.jar=destfile=C:\\COBA PROGRAM SEMESTER 4\\UAS FLUTTER FT. SPRINGBOOT dan OOD\\Final-08-Evan-Jap-Bambang-WalkGuide-AI\\walkguide-backend\\demo\\target\\jacoco.exec"
+[INFO]
+[INFO] --- resources:3.3.1:resources (default-resources) @ demo ---
+[INFO] Copying 3 resources from src\main\resources to target\classes
+[INFO] Copying 20 resources from src\main\resources to target\classes
+[INFO]
+[INFO] --- compiler:3.11.0:compile (default-compile) @ demo ---
+[INFO] Nothing to compile - all classes are up to date
+[INFO]
+[INFO] --- resources:3.3.1:testResources (default-testResources) @ demo ---
+[INFO] skip non existing resourceDirectory C:\COBA PROGRAM SEMESTER 4\UAS FLUTTER FT. SPRINGBOOT dan OOD\Final-08-Evan-Jap-Bambang-WalkGuide-AI\walkguide-backend\demo\src\test\resources
+[INFO]
+[INFO] --- compiler:3.11.0:testCompile (default-testCompile) @ demo ---
+[INFO] Changes detected - recompiling the module! :source
+[INFO] Compiling 28 source files with javac [debug release 21] to target\test-classes
+[INFO]
+[INFO] <<< spring-boot:3.2.5:run (default-cli) < test-compile @ demo <<<
+[INFO]
+[INFO]
+[INFO] --- spring-boot:3.2.5:run (default-cli) @ demo ---
+[INFO] Attaching agents: []
+Standard Commons Logging discovery in action with spring-jcl: please remove commons-logging.jar from classpath in order to avoid potential conflicts
+
+ . ____ _ __ _ _
+ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
+( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
+ \\/ ___)| |_)| | | | | || (_| | ) ) ) )
+ ' |____| .__|_| |_|_| |_\__, | / / / /
+ =========|_|==============|___/=/_/_/_/
+ :: Spring Boot :: (v3.2.5)
+
+2026-05-27T19:15:04.478+07:00 INFO 14960 --- [ main] com.walkguide.WalkGuideApplication : Starting WalkGuideApplication using Java 21.0.9 with PID 14960 (C:\COBA PROGRAM SEMESTER 4\UAS FLUTTER FT. SPRINGBOOT dan OOD\Final-08-Evan-Jap-Bambang-WalkGuide-AI\walkguide-backend\demo\target\classes started by Robertus in C:\COBA PROGRAM SEMESTER 4\UAS FLUTTER FT. SPRINGBOOT dan OOD\Final-08-Evan-Jap-Bambang-WalkGuide-AI\walkguide-backend\demo)
+2026-05-27T19:15:04.481+07:00 DEBUG 14960 --- [ main] com.walkguide.WalkGuideApplication : Running with Spring Boot v3.2.5, Spring v6.1.6
+2026-05-27T19:15:04.482+07:00 INFO 14960 --- [ main] com.walkguide.WalkGuideApplication : The following 1 profile is active: "dev"
+2026-05-27T19:15:05.632+07:00 INFO 14960 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
+2026-05-27T19:15:05.781+07:00 INFO 14960 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 127 ms. Found 13 JPA repository interfaces.
+2026-05-27T19:15:06.394+07:00 INFO 14960 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)
+2026-05-27T19:15:06.419+07:00 INFO 14960 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
+2026-05-27T19:15:06.419+07:00 INFO 14960 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.20]
+2026-05-27T19:15:06.507+07:00 INFO 14960 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
+2026-05-27T19:15:06.509+07:00 INFO 14960 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1964 ms
+Standard Commons Logging discovery in action with spring-jcl: please remove commons-logging.jar from classpath in order to avoid potential conflicts
+2026-05-27T19:15:06.564+07:00 DEBUG 14960 --- [ main] com.walkguide.security.JwtAuthFilter : Filter 'jwtAuthFilter' configured for use
+2026-05-27T19:15:06.683+07:00 INFO 14960 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
+2026-05-27T19:15:06.741+07:00 INFO 14960 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.4.4.Final
+2026-05-27T19:15:06.774+07:00 INFO 14960 --- [ main] o.h.c.internal.RegionFactoryInitiator : HHH000026: Second-level cache disabled
+2026-05-27T19:15:07.001+07:00 INFO 14960 --- [ main] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer
+2026-05-27T19:15:07.029+07:00 INFO 14960 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
+2026-05-27T19:15:09.789+07:00 INFO 14960 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
+2026-05-27T19:15:18.903+07:00 WARN 14960 --- [ main] org.hibernate.orm.deprecation : HHH90000025: PostgreSQLDialect does not need to be specified explicitly using 'hibernate.dialect' (remove the property setting and it will be selected by default)
+2026-05-27T19:15:20.462+07:00 INFO 14960 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
+2026-05-27T19:15:22.089+07:00 INFO 14960 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
+2026-05-27T19:15:22.497+07:00 INFO 14960 --- [ main] com.walkguide.config.FirebaseConfig : [FIREBASE] Firebase Admin initialized from classpath:firebase/google-services-admin.json
+2026-05-27T19:15:23.016+07:00 WARN 14960 --- [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
+2026-05-27T19:15:23.047+07:00 WARN 14960 --- [ main] .s.s.UserDetailsServiceAutoConfiguration :
+
+Using generated security password: 95518d96-3ca0-4c5c-b99a-94ed7eb4c8e2
+
+This generated password is for development use only. Your security configuration must be updated before running your application in production.
+
+2026-05-27T19:15:23.171+07:00 DEBUG 14960 --- [ main] o.s.w.s.s.s.WebSocketHandlerMapping : Patterns [/ws/**] in 'stompWebSocketHandlerMapping'
+2026-05-27T19:15:23.336+07:00 INFO 14960 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@63b4a896, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@3feeab43, org.springframework.security.web.context.SecurityContextHolderFilter@5f59b6c5, org.springframework.security.web.header.HeaderWriterFilter@566dc0c3, org.springframework.web.filter.CorsFilter@6178ac9d, org.springframework.security.web.authentication.logout.LogoutFilter@5096be74, com.walkguide.security.JwtAuthFilter@113ee1ce, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@1872a7fe, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@70720b78, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@7413c41, org.springframework.security.web.session.SessionManagementFilter@535b016, org.springframework.security.web.access.ExceptionTranslationFilter@50c462b8, org.springframework.security.web.access.intercept.AuthorizationFilter@6cb59aa]
+2026-05-27T19:15:23.407+07:00 DEBUG 14960 --- [ main] .WebSocketAnnotationMethodMessageHandler :
+ c.w.c.AuthController:
+
+2026-05-27T19:15:23.408+07:00 DEBUG 14960 --- [ main] .WebSocketAnnotationMethodMessageHandler :
+ c.w.c.CallController:
+
+2026-05-27T19:15:23.409+07:00 DEBUG 14960 --- [ main] .WebSocketAnnotationMethodMessageHandler :
+ c.w.c.GuardianController:
+
+2026-05-27T19:15:23.410+07:00 DEBUG 14960 --- [ main] .WebSocketAnnotationMethodMessageHandler :
+ c.w.c.PairingController:
+
+2026-05-27T19:15:23.410+07:00 DEBUG 14960 --- [ main] .WebSocketAnnotationMethodMessageHandler :
+ c.w.c.UserController:
+
+2026-05-27T19:15:23.411+07:00 DEBUG 14960 --- [ main] .WebSocketAnnotationMethodMessageHandler :
+ o.s.b.a.w.s.e.BasicErrorController:
+
+2026-05-27T19:15:23.413+07:00 DEBUG 14960 --- [ main] .WebSocketAnnotationMethodMessageHandler :
+ o.s.w.a.OpenApiWebMvcResource:
+
+2026-05-27T19:15:23.414+07:00 DEBUG 14960 --- [ main] .WebSocketAnnotationMethodMessageHandler :
+ o.s.w.u.SwaggerWelcomeWebMvc:
+
+2026-05-27T19:15:23.415+07:00 DEBUG 14960 --- [ main] .WebSocketAnnotationMethodMessageHandler :
+ o.s.w.u.SwaggerConfigResource:
+
+2026-05-27T19:15:23.710+07:00 INFO 14960 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path ''
+2026-05-27T19:15:23.724+07:00 DEBUG 14960 --- [ main] o.s.m.s.ExecutorSubscribableChannel : clientOutboundChannel added SubProtocolWebSocketHandler[StompSubProtocolHandler[v10.stomp, v11.stomp, v12.stomp]]
+2026-05-27T19:15:23.725+07:00 DEBUG 14960 --- [ main] o.s.m.s.ExecutorSubscribableChannel : clientInboundChannel added WebSocketAnnotationMethodMessageHandler[prefixes=[/app/]]
+2026-05-27T19:15:23.726+07:00 INFO 14960 --- [ main] o.s.m.s.b.SimpleBrokerMessageHandler : Starting...
+2026-05-27T19:15:23.726+07:00 DEBUG 14960 --- [ main] o.s.m.s.ExecutorSubscribableChannel : clientInboundChannel added SimpleBrokerMessageHandler [org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry@61ee9672]
+2026-05-27T19:15:23.726+07:00 DEBUG 14960 --- [ main] o.s.m.s.ExecutorSubscribableChannel : brokerChannel added SimpleBrokerMessageHandler [org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry@61ee9672]
+2026-05-27T19:15:23.728+07:00 INFO 14960 --- [ main] o.s.m.s.b.SimpleBrokerMessageHandler : BrokerAvailabilityEvent[available=true, SimpleBrokerMessageHandler [org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry@61ee9672]]
+2026-05-27T19:15:23.729+07:00 INFO 14960 --- [ main] o.s.m.s.b.SimpleBrokerMessageHandler : Started.
+2026-05-27T19:15:23.730+07:00 DEBUG 14960 --- [ main] o.s.m.s.ExecutorSubscribableChannel : clientInboundChannel added UserDestinationMessageHandler[DefaultUserDestinationResolver[prefix=/user/]]
+2026-05-27T19:15:23.730+07:00 DEBUG 14960 --- [ main] o.s.m.s.ExecutorSubscribableChannel : brokerChannel added UserDestinationMessageHandler[DefaultUserDestinationResolver[prefix=/user/]]
+2026-05-27T19:15:23.737+07:00 INFO 14960 --- [ main] com.walkguide.WalkGuideApplication : Started WalkGuideApplication in 19.764 seconds (process running for 20.253)
+Hibernate:
+ select
+ count(*)
+ from
+ users u1_0
+DataSeeder: Database sudah ada data, skip seeding.
+2026-05-27T19:15:38.814+07:00 INFO 14960 --- [0.0-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
+2026-05-27T19:15:38.814+07:00 INFO 14960 --- [0.0-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
+2026-05-27T19:15:38.815+07:00 INFO 14960 --- [0.0-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
+2026-05-27T19:16:23.704+07:00 INFO 14960 --- [MessageBroker-1] o.s.w.s.c.WebSocketMessageBrokerStats : WebSocketSession[0 current WS(0)-HttpStream(0)-HttpPoll(0), 0 total, 0 closed abnormally (0 connect failure, 0 send limit, 0 transport error)], stompSubProtocol[processed CONNECT(0)-CONNECTED(0)-DISCONNECT(0)], stompBrokerRelay[null], inboundChannel[pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0], outboundChannel[pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0], sockJsScheduler[pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]
+2026-05-27T19:18:42.664+07:00 WARN 14960 --- [0.0-8080-exec-7] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: 08001
+2026-05-27T19:18:42.676+07:00 ERROR 14960 --- [0.0-8080-exec-7] o.h.engine.jdbc.spi.SqlExceptionHelper : HikariPool-1 - Connection is not available, request timed out after 10011ms.
+2026-05-27T19:18:42.676+07:00 ERROR 14960 --- [0.0-8080-exec-7] o.h.engine.jdbc.spi.SqlExceptionHelper : The connection attempt failed.
+Hibernate:
+ select
+ rt1_0.id,
+ rt1_0.created_at,
+ rt1_0.expires_at,
+ rt1_0.token,
+ rt1_0.user_id
+ from
+ refresh_tokens rt1_0
+ where
+ rt1_0.user_id is null
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.email=?
+Hibernate:
+ select
+ rt1_0.id,
+ rt1_0.created_at,
+ rt1_0.expires_at,
+ rt1_0.token,
+ rt1_0.user_id
+ from
+ refresh_tokens rt1_0
+ where
+ rt1_0.user_id=?
+Hibernate:
+ insert
+ into
+ activity_logs
+ (created_at, description, log_type, metadata, user_id)
+ values
+ (?, ?, ?, ?, ?)
+Hibernate:
+ insert
+ into
+ refresh_tokens
+ (created_at, expires_at, token, user_id)
+ values
+ (?, ?, ?, ?)
+Hibernate:
+ delete
+ from
+ refresh_tokens
+ where
+ id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ update
+ users
+ set
+ display_name=?,
+ email=?,
+ fcm_token=?,
+ pairing_code=?,
+ pairing_code_expires_at=?,
+ password=?,
+ role=?,
+ unique_user_id=?,
+ updated_at=?
+ where
+ id=?
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users g1_0
+ on g1_0.id=pr1_0.guardian_id
+ where
+ g1_0.id=?
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users g1_0
+ on g1_0.id=pr1_0.guardian_id
+ where
+ g1_0.id=?
+ and pr1_0.status=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users g1_0
+ on g1_0.id=pr1_0.guardian_id
+ where
+ g1_0.id=?
+ and pr1_0.status=?
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users g1_0
+ on g1_0.id=pr1_0.guardian_id
+ where
+ g1_0.id=?
+ and pr1_0.status=?
+Hibernate:
+ select
+ lh1_0.id,
+ lh1_0.accuracy,
+ lh1_0.created_at,
+ lh1_0.heading,
+ lh1_0.lat,
+ lh1_0.lng,
+ lh1_0.speed,
+ lh1_0.user_id
+ from
+ location_history lh1_0
+ where
+ lh1_0.user_id=?
+ order by
+ lh1_0.created_at desc
+ fetch
+ first ? rows only
+2026-05-27T19:28:59.450+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:28:59.456+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ al1_0.id,
+ al1_0.created_at,
+ al1_0.description,
+ al1_0.log_type,
+ al1_0.metadata,
+ al1_0.user_id
+ from
+ activity_logs al1_0
+ left join
+ users u1_0
+ on u1_0.id=al1_0.user_id
+ where
+ u1_0.id=?
+ order by
+ al1_0.created_at desc
+ offset
+ ? rows
+ fetch
+ first ? rows only
+Hibernate:
+ select
+ count(al1_0.id)
+ from
+ activity_logs al1_0
+ left join
+ users u1_0
+ on u1_0.id=al1_0.user_id
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ se1_0.id,
+ se1_0.acknowledged_at,
+ se1_0.created_at,
+ se1_0.lat,
+ se1_0.lng,
+ se1_0.status,
+ se1_0.trigger_type,
+ se1_0.user_id
+ from
+ sos_events se1_0
+ where
+ se1_0.user_id=?
+ order by
+ se1_0.created_at desc
+ offset
+ ? rows
+ fetch
+ first ? rows only
+Hibernate:
+ select
+ count(gn1_0.id)
+ from
+ guardian_notifications gn1_0
+ where
+ gn1_0.user_id=?
+ and not(gn1_0.is_read)
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ al1_0.id,
+ al1_0.created_at,
+ al1_0.description,
+ al1_0.log_type,
+ al1_0.metadata,
+ al1_0.user_id
+ from
+ activity_logs al1_0
+ left join
+ users u1_0
+ on u1_0.id=al1_0.user_id
+ where
+ u1_0.id=?
+ order by
+ al1_0.created_at desc
+ offset
+ ? rows
+ fetch
+ first ? rows only
+Hibernate:
+ select
+ count(al1_0.id)
+ from
+ activity_logs al1_0
+ left join
+ users u1_0
+ on u1_0.id=al1_0.user_id
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ se1_0.id,
+ se1_0.acknowledged_at,
+ se1_0.created_at,
+ se1_0.lat,
+ se1_0.lng,
+ se1_0.status,
+ se1_0.trigger_type,
+ se1_0.user_id
+ from
+ sos_events se1_0
+ where
+ se1_0.user_id=?
+ order by
+ se1_0.created_at desc
+ offset
+ ? rows
+ fetch
+ first ? rows only
+2026-05-27T19:29:04.606+07:00 DEBUG 14960 --- [0.0-8080-exec-6] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:29:04.607+07:00 DEBUG 14960 --- [0.0-8080-exec-6] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:29:09.779+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:29:09.786+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:29:14.951+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:29:14.951+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.email=?
+Hibernate:
+ select
+ rt1_0.id,
+ rt1_0.created_at,
+ rt1_0.expires_at,
+ rt1_0.token,
+ rt1_0.user_id
+ from
+ refresh_tokens rt1_0
+ where
+ rt1_0.user_id=?
+Hibernate:
+ insert
+ into
+ activity_logs
+ (created_at, description, log_type, metadata, user_id)
+ values
+ (?, ?, ?, ?, ?)
+Hibernate:
+ insert
+ into
+ refresh_tokens
+ (created_at, expires_at, token, user_id)
+ values
+ (?, ?, ?, ?)
+Hibernate:
+ delete
+ from
+ refresh_tokens
+ where
+ id=?
+2026-05-27T19:29:20.100+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:29:20.113+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ vcc1_0.id,
+ vcc1_0.command_key,
+ vcc1_0.enabled,
+ vcc1_0.guardian_id,
+ vcc1_0.trigger_phrase,
+ vcc1_0.updated_at,
+ vcc1_0.user_id
+ from
+ voice_command_configs vcc1_0
+ where
+ vcc1_0.user_id=?
+Hibernate:
+ select
+ hs1_0.id,
+ hs1_0.button_code,
+ hs1_0.button_name,
+ hs1_0.enabled,
+ hs1_0.guardian_id,
+ hs1_0.shortcut_key,
+ hs1_0.updated_at,
+ hs1_0.user_id
+ from
+ hardware_shortcuts hs1_0
+ where
+ hs1_0.user_id=?
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users u1_0
+ on u1_0.id=pr1_0.user_id
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:29:22.109+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:29:22.110+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:29:27.233+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:29:27.234+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:29:32.402+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:29:32.414+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:29:37.567+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:29:37.579+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:29:42.732+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:29:42.744+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:29:47.899+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:29:47.911+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:29:53.062+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:29:53.063+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.email=?
+Hibernate:
+ select
+ rt1_0.id,
+ rt1_0.created_at,
+ rt1_0.expires_at,
+ rt1_0.token,
+ rt1_0.user_id
+ from
+ refresh_tokens rt1_0
+ where
+ rt1_0.user_id=?
+Hibernate:
+ insert
+ into
+ activity_logs
+ (created_at, description, log_type, metadata, user_id)
+ values
+ (?, ?, ?, ?, ?)
+Hibernate:
+ insert
+ into
+ refresh_tokens
+ (created_at, expires_at, token, user_id)
+ values
+ (?, ?, ?, ?)
+Hibernate:
+ delete
+ from
+ refresh_tokens
+ where
+ id=?
+2026-05-27T19:29:58.228+07:00 DEBUG 14960 --- [0.0-8080-exec-7] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:29:58.241+07:00 DEBUG 14960 --- [0.0-8080-exec-7] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ update
+ users
+ set
+ display_name=?,
+ email=?,
+ fcm_token=?,
+ pairing_code=?,
+ pairing_code_expires_at=?,
+ password=?,
+ role=?,
+ unique_user_id=?,
+ updated_at=?
+ where
+ id=?
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users g1_0
+ on g1_0.id=pr1_0.guardian_id
+ where
+ g1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users g1_0
+ on g1_0.id=pr1_0.guardian_id
+ where
+ g1_0.id=?
+ and pr1_0.status=?
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users g1_0
+ on g1_0.id=pr1_0.guardian_id
+ where
+ g1_0.id=?
+ and pr1_0.status=?
+Hibernate:
+ select
+ lh1_0.id,
+ lh1_0.accuracy,
+ lh1_0.created_at,
+ lh1_0.heading,
+ lh1_0.lat,
+ lh1_0.lng,
+ lh1_0.speed,
+ lh1_0.user_id
+ from
+ location_history lh1_0
+ where
+ lh1_0.user_id=?
+ order by
+ lh1_0.created_at desc
+ fetch
+ first ? rows only
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users g1_0
+ on g1_0.id=pr1_0.guardian_id
+ where
+ g1_0.id=?
+ and pr1_0.status=?
+Hibernate:
+ select
+ al1_0.id,
+ al1_0.created_at,
+ al1_0.description,
+ al1_0.log_type,
+ al1_0.metadata,
+ al1_0.user_id
+ from
+ activity_logs al1_0
+ left join
+ users u1_0
+ on u1_0.id=al1_0.user_id
+ where
+ u1_0.id=?
+ order by
+ al1_0.created_at desc
+ offset
+ ? rows
+ fetch
+ first ? rows only
+2026-05-27T19:30:00.506+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:30:00.507+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ count(al1_0.id)
+ from
+ activity_logs al1_0
+ left join
+ users u1_0
+ on u1_0.id=al1_0.user_id
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ se1_0.id,
+ se1_0.acknowledged_at,
+ se1_0.created_at,
+ se1_0.lat,
+ se1_0.lng,
+ se1_0.status,
+ se1_0.trigger_type,
+ se1_0.user_id
+ from
+ sos_events se1_0
+ where
+ se1_0.user_id=?
+ order by
+ se1_0.created_at desc
+ offset
+ ? rows
+ fetch
+ first ? rows only
+Hibernate:
+ select
+ count(gn1_0.id)
+ from
+ guardian_notifications gn1_0
+ where
+ gn1_0.user_id=?
+ and not(gn1_0.is_read)
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ al1_0.id,
+ al1_0.created_at,
+ al1_0.description,
+ al1_0.log_type,
+ al1_0.metadata,
+ al1_0.user_id
+ from
+ activity_logs al1_0
+ left join
+ users u1_0
+ on u1_0.id=al1_0.user_id
+ where
+ u1_0.id=?
+ order by
+ al1_0.created_at desc
+ offset
+ ? rows
+ fetch
+ first ? rows only
+Hibernate:
+ select
+ count(al1_0.id)
+ from
+ activity_logs al1_0
+ left join
+ users u1_0
+ on u1_0.id=al1_0.user_id
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ se1_0.id,
+ se1_0.acknowledged_at,
+ se1_0.created_at,
+ se1_0.lat,
+ se1_0.lng,
+ se1_0.status,
+ se1_0.trigger_type,
+ se1_0.user_id
+ from
+ sos_events se1_0
+ where
+ se1_0.user_id=?
+ order by
+ se1_0.created_at desc
+ offset
+ ? rows
+ fetch
+ first ? rows only
+2026-05-27T19:30:03.397+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:30:03.408+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users g1_0
+ on g1_0.id=pr1_0.guardian_id
+ where
+ g1_0.id=?
+2026-05-27T19:30:05.610+07:00 DEBUG 14960 --- [0.0-8080-exec-7] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:30:05.622+07:00 DEBUG 14960 --- [0.0-8080-exec-7] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:30:07.814+07:00 INFO 14960 --- [0.0-8080-exec-4] c.walkguide.service.AgoraTokenService : [AGORA] Token generated untuk channel=call_2067_2068 uid=2068 expires=1779888607
+2026-05-27T19:30:07.817+07:00 INFO 14960 --- [0.0-8080-exec-4] c.walkguide.controller.CallController : [CALL] Token generated | caller=2068 receiver=2067 channel=call_2067_2068
+2026-05-27T19:30:08.564+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:30:08.565+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:30:10.773+07:00 DEBUG 14960 --- [0.0-8080-exec-7] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:30:10.785+07:00 DEBUG 14960 --- [0.0-8080-exec-7] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:30:13.728+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:30:13.739+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:30:15.944+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:30:15.956+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ se1_0.id,
+ se1_0.acknowledged_at,
+ se1_0.created_at,
+ se1_0.lat,
+ se1_0.lng,
+ se1_0.status,
+ se1_0.trigger_type,
+ se1_0.user_id
+ from
+ sos_events se1_0
+ where
+ se1_0.user_id=?
+ order by
+ se1_0.created_at desc
+ offset
+ ? rows
+ fetch
+ first ? rows only
+2026-05-27T19:30:18.908+07:00 DEBUG 14960 --- [0.0-8080-exec-2] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:30:18.919+07:00 DEBUG 14960 --- [0.0-8080-exec-2] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users u1_0
+ on u1_0.id=pr1_0.user_id
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:30:21.106+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:30:21.117+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:30:24.059+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:30:24.098+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:30:26.273+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:30:26.274+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users g1_0
+ on g1_0.id=pr1_0.guardian_id
+ where
+ g1_0.id=?
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users g1_0
+ on g1_0.id=pr1_0.guardian_id
+ where
+ g1_0.id=?
+ and pr1_0.status=?
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users g1_0
+ on g1_0.id=pr1_0.guardian_id
+ where
+ g1_0.id=?
+ and pr1_0.status=?
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users g1_0
+ on g1_0.id=pr1_0.guardian_id
+ where
+ g1_0.id=?
+ and pr1_0.status=?
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users u1_0
+ on u1_0.id=pr1_0.user_id
+ where
+ u1_0.id=?
+2026-05-27T19:30:29.237+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:30:29.238+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ lh1_0.id,
+ lh1_0.accuracy,
+ lh1_0.created_at,
+ lh1_0.heading,
+ lh1_0.lat,
+ lh1_0.lng,
+ lh1_0.speed,
+ lh1_0.user_id
+ from
+ location_history lh1_0
+ where
+ lh1_0.user_id=?
+ order by
+ lh1_0.created_at desc
+ fetch
+ first ? rows only
+Hibernate:
+ select
+ al1_0.id,
+ al1_0.created_at,
+ al1_0.description,
+ al1_0.log_type,
+ al1_0.metadata,
+ al1_0.user_id
+ from
+ activity_logs al1_0
+ left join
+ users u1_0
+ on u1_0.id=al1_0.user_id
+ where
+ u1_0.id=?
+ order by
+ al1_0.created_at desc
+ offset
+ ? rows
+ fetch
+ first ? rows only
+Hibernate:
+ select
+ count(al1_0.id)
+ from
+ activity_logs al1_0
+ left join
+ users u1_0
+ on u1_0.id=al1_0.user_id
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ se1_0.id,
+ se1_0.acknowledged_at,
+ se1_0.created_at,
+ se1_0.lat,
+ se1_0.lng,
+ se1_0.status,
+ se1_0.trigger_type,
+ se1_0.user_id
+ from
+ sos_events se1_0
+ where
+ se1_0.user_id=?
+ order by
+ se1_0.created_at desc
+ offset
+ ? rows
+ fetch
+ first ? rows only
+Hibernate:
+ select
+ count(gn1_0.id)
+ from
+ guardian_notifications gn1_0
+ where
+ gn1_0.user_id=?
+ and not(gn1_0.is_read)
+2026-05-27T19:30:31.440+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:30:31.441+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ al1_0.id,
+ al1_0.created_at,
+ al1_0.description,
+ al1_0.log_type,
+ al1_0.metadata,
+ al1_0.user_id
+ from
+ activity_logs al1_0
+ left join
+ users u1_0
+ on u1_0.id=al1_0.user_id
+ where
+ u1_0.id=?
+ order by
+ al1_0.created_at desc
+ offset
+ ? rows
+ fetch
+ first ? rows only
+Hibernate:
+ select
+ count(al1_0.id)
+ from
+ activity_logs al1_0
+ left join
+ users u1_0
+ on u1_0.id=al1_0.user_id
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ se1_0.id,
+ se1_0.acknowledged_at,
+ se1_0.created_at,
+ se1_0.lat,
+ se1_0.lng,
+ se1_0.status,
+ se1_0.trigger_type,
+ se1_0.user_id
+ from
+ sos_events se1_0
+ where
+ se1_0.user_id=?
+ order by
+ se1_0.created_at desc
+ offset
+ ? rows
+ fetch
+ first ? rows only
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:30:34.390+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:30:34.391+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:30:36.101+07:00 INFO 14960 --- [0.0-8080-exec-4] c.walkguide.service.AgoraTokenService : [AGORA] Token generated untuk channel=call_2067_2068 uid=2067 expires=1779888636
+2026-05-27T19:30:36.112+07:00 INFO 14960 --- [0.0-8080-exec-4] c.walkguide.controller.CallController : [CALL] Token generated | caller=2067 receiver=2068 channel=call_2067_2068
+2026-05-27T19:30:36.606+07:00 DEBUG 14960 --- [0.0-8080-exec-7] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:30:36.618+07:00 DEBUG 14960 --- [0.0-8080-exec-7] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:30:39.554+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:30:39.555+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:30:41.778+07:00 DEBUG 14960 --- [0.0-8080-exec-8] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:30:41.790+07:00 DEBUG 14960 --- [0.0-8080-exec-8] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users u1_0
+ on u1_0.id=pr1_0.user_id
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users g1_0
+ on g1_0.id=pr1_0.guardian_id
+ where
+ g1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:30:44.718+07:00 DEBUG 14960 --- [.0-8080-exec-10] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:30:44.724+07:00 DEBUG 14960 --- [.0-8080-exec-10] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:30:46.928+07:00 INFO 14960 --- [0.0-8080-exec-4] c.walkguide.service.AgoraTokenService : [AGORA] Token generated untuk channel=call_2067_2068 uid=2068 expires=1779888646
+2026-05-27T19:30:46.939+07:00 INFO 14960 --- [0.0-8080-exec-4] c.walkguide.controller.CallController : [CALL] Token generated | caller=2068 receiver=2067 channel=call_2067_2068
+2026-05-27T19:30:46.939+07:00 DEBUG 14960 --- [0.0-8080-exec-6] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:30:46.939+07:00 DEBUG 14960 --- [0.0-8080-exec-6] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:30:48.914+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.m.s.b.SimpleBrokerMessageHandler : Processing MESSAGE destination=/queue/call/2067 session=null payload={"receiverId":"2067","channelName":"call_2067_2068","callerId":"2068","type":"IN...(truncated)
+2026-05-27T19:30:48.920+07:00 INFO 14960 --- [0.0-8080-exec-1] c.w.websocket.LocationBroadcaster : [WS] Call broadcast -> /queue/call/2067 | type=INCOMING_CALL status=RINGING channel=call_2067_2068
+2026-05-27T19:30:49.898+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:30:49.899+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:30:52.103+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:30:52.115+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:30:53.589+07:00 INFO 14960 --- [0.0-8080-exec-1] com.walkguide.service.FcmService : [FCM] Sent high-priority notification successfully: projects/walkguide-549b3/messages/0:1779885052504510%084e7484084e7484
+2026-05-27T19:30:55.060+07:00 DEBUG 14960 --- [.0-8080-exec-10] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:30:55.062+07:00 DEBUG 14960 --- [.0-8080-exec-10] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:30:55.909+07:00 WARN 14960 --- [0.0-8080-exec-1] com.walkguide.service.FcmService : [FIRESTORE] Notification audit skipped: com.google.api.gax.rpc.PermissionDeniedException: io.grpc.StatusRuntimeException: PERMISSION_DENIED: Cloud Firestore API has not been used in project walkguide-549b3 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/firestore.googleapis.com/overview?project=walkguide-549b3 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
+2026-05-27T19:30:55.909+07:00 INFO 14960 --- [0.0-8080-exec-1] c.w.service.CallNotificationService : [CALL] Incoming call notification sent | caller=2068 receiver=2067 channel=call_2067_2068
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:30:57.011+07:00 INFO 14960 --- [0.0-8080-exec-7] c.walkguide.service.AgoraTokenService : [AGORA] Token generated untuk channel=call_2067_2068 uid=2067 expires=1779888657
+2026-05-27T19:30:57.023+07:00 INFO 14960 --- [0.0-8080-exec-7] c.walkguide.controller.CallController : [CALL] Token generated | caller=2067 receiver=2068 channel=call_2067_2068
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:30:57.281+07:00 DEBUG 14960 --- [0.0-8080-exec-6] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:30:57.282+07:00 DEBUG 14960 --- [0.0-8080-exec-6] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:30:58.242+07:00 DEBUG 14960 --- [0.0-8080-exec-8] o.s.m.s.b.SimpleBrokerMessageHandler : Processing MESSAGE destination=/queue/call/2068 session=null payload={"receiverId":"2067","receiverName":"suami kobo","channelName":"call_2067_2068",...(truncated)
+2026-05-27T19:30:58.253+07:00 INFO 14960 --- [0.0-8080-exec-8] c.w.websocket.LocationBroadcaster : [WS] Call broadcast -> /queue/call/2068 | type=CALL_ACCEPTED status=ACCEPTED channel=call_2067_2068
+2026-05-27T19:30:58.254+07:00 DEBUG 14960 --- [0.0-8080-exec-8] o.s.m.s.b.SimpleBrokerMessageHandler : Processing MESSAGE destination=/queue/call/2067 session=null payload={"receiverId":"2067","receiverName":"suami kobo","channelName":"call_2067_2068",...(truncated)
+2026-05-27T19:30:58.254+07:00 INFO 14960 --- [0.0-8080-exec-8] c.w.websocket.LocationBroadcaster : [WS] Call broadcast -> /queue/call/2067 | type=CALL_ACCEPTED status=ACCEPTED channel=call_2067_2068
+2026-05-27T19:30:58.254+07:00 INFO 14960 --- [0.0-8080-exec-8] c.w.service.CallNotificationService : [CALL] Call accepted | caller=2068 receiver=2067 channel=call_2067_2068
+2026-05-27T19:31:00.222+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:00.224+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:31:02.443+07:00 DEBUG 14960 --- [0.0-8080-exec-8] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:02.453+07:00 DEBUG 14960 --- [0.0-8080-exec-8] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:31:05.389+07:00 DEBUG 14960 --- [0.0-8080-exec-6] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:05.401+07:00 DEBUG 14960 --- [0.0-8080-exec-6] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:31:07.637+07:00 DEBUG 14960 --- [0.0-8080-exec-8] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:07.648+07:00 DEBUG 14960 --- [0.0-8080-exec-8] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:31:10.554+07:00 DEBUG 14960 --- [0.0-8080-exec-6] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:10.566+07:00 DEBUG 14960 --- [0.0-8080-exec-6] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:31:12.771+07:00 DEBUG 14960 --- [0.0-8080-exec-8] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:12.782+07:00 DEBUG 14960 --- [0.0-8080-exec-8] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:31:15.726+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:15.737+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:31:17.935+07:00 DEBUG 14960 --- [0.0-8080-exec-2] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:17.947+07:00 DEBUG 14960 --- [0.0-8080-exec-2] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:31:20.889+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:20.901+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:31:23.105+07:00 DEBUG 14960 --- [0.0-8080-exec-2] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:23.112+07:00 DEBUG 14960 --- [0.0-8080-exec-2] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:31:26.058+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:26.070+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:31:28.271+07:00 DEBUG 14960 --- [0.0-8080-exec-2] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:28.283+07:00 DEBUG 14960 --- [0.0-8080-exec-2] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:31:31.219+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:31.231+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:31:33.436+07:00 DEBUG 14960 --- [0.0-8080-exec-2] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:33.437+07:00 DEBUG 14960 --- [0.0-8080-exec-2] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:31:36.382+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:36.393+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:31:38.600+07:00 DEBUG 14960 --- [0.0-8080-exec-2] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:38.601+07:00 DEBUG 14960 --- [0.0-8080-exec-2] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:31:41.551+07:00 DEBUG 14960 --- [0.0-8080-exec-6] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:41.564+07:00 DEBUG 14960 --- [0.0-8080-exec-6] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:31:43.769+07:00 DEBUG 14960 --- [0.0-8080-exec-8] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:43.781+07:00 DEBUG 14960 --- [0.0-8080-exec-8] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:31:46.718+07:00 DEBUG 14960 --- [0.0-8080-exec-6] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:46.719+07:00 DEBUG 14960 --- [0.0-8080-exec-6] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:31:48.933+07:00 DEBUG 14960 --- [0.0-8080-exec-8] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:48.935+07:00 DEBUG 14960 --- [0.0-8080-exec-8] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:31:51.884+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:51.885+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:31:52.614+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.m.s.b.SimpleBrokerMessageHandler : Processing MESSAGE destination=/queue/call/2068 session=null payload={"otherId":"2068","receiverName":"suami kobo","endedBy":"2067","type":"CALL_ENDE...(truncated)
+2026-05-27T19:31:52.626+07:00 INFO 14960 --- [0.0-8080-exec-4] c.w.websocket.LocationBroadcaster : [WS] Call broadcast -> /queue/call/2068 | type=CALL_ENDED status=ENDED channel=call_2067_2068
+2026-05-27T19:31:52.627+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.m.s.b.SimpleBrokerMessageHandler : Processing MESSAGE destination=/queue/call/2067 session=null payload={"otherId":"2068","receiverName":"suami kobo","endedBy":"2067","type":"CALL_ENDE...(truncated)
+2026-05-27T19:31:52.628+07:00 INFO 14960 --- [0.0-8080-exec-4] c.w.websocket.LocationBroadcaster : [WS] Call broadcast -> /queue/call/2067 | type=CALL_ENDED status=ENDED channel=call_2067_2068
+2026-05-27T19:31:54.122+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:54.135+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:31:56.658+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.m.s.b.SimpleBrokerMessageHandler : Processing MESSAGE destination=/queue/call/2068 session=null payload={"otherId":"2068","receiverName":"suami kobo","endedBy":"2067","type":"CALL_ENDE...(truncated)
+2026-05-27T19:31:56.670+07:00 INFO 14960 --- [0.0-8080-exec-1] c.w.websocket.LocationBroadcaster : [WS] Call broadcast -> /queue/call/2068 | type=CALL_ENDED status=ENDED channel=call_2067_2068
+2026-05-27T19:31:56.671+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.m.s.b.SimpleBrokerMessageHandler : Processing MESSAGE destination=/queue/call/2067 session=null payload={"otherId":"2068","receiverName":"suami kobo","endedBy":"2067","type":"CALL_ENDE...(truncated)
+2026-05-27T19:31:56.671+07:00 INFO 14960 --- [0.0-8080-exec-1] c.w.websocket.LocationBroadcaster : [WS] Call broadcast -> /queue/call/2067 | type=CALL_ENDED status=ENDED channel=call_2067_2068
+2026-05-27T19:31:57.050+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:57.052+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:31:59.268+07:00 DEBUG 14960 --- [0.0-8080-exec-2] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:31:59.280+07:00 DEBUG 14960 --- [0.0-8080-exec-2] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:31:59.991+07:00 INFO 14960 --- [0.0-8080-exec-4] com.walkguide.service.FcmService : [FCM] Sent normal notification successfully: projects/walkguide-549b3/messages/0:1779885120460350%084e7484084e7484
+2026-05-27T19:32:00.045+07:00 WARN 14960 --- [0.0-8080-exec-4] com.walkguide.service.FcmService : [FIRESTORE] Notification audit skipped: com.google.api.gax.rpc.PermissionDeniedException: io.grpc.StatusRuntimeException: PERMISSION_DENIED: Cloud Firestore API has not been used in project walkguide-549b3 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/firestore.googleapis.com/overview?project=walkguide-549b3 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:32:00.576+07:00 INFO 14960 --- [0.0-8080-exec-1] com.walkguide.service.FcmService : [FCM] Sent normal notification successfully: projects/walkguide-549b3/messages/0:1779885121120572%084e7484084e7484
+2026-05-27T19:32:00.777+07:00 WARN 14960 --- [0.0-8080-exec-1] com.walkguide.service.FcmService : [FIRESTORE] Notification audit skipped: com.google.api.gax.rpc.PermissionDeniedException: io.grpc.StatusRuntimeException: PERMISSION_DENIED: Cloud Firestore API has not been used in project walkguide-549b3 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/firestore.googleapis.com/overview?project=walkguide-549b3 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
+Hibernate:
+ select
+ vcc1_0.id,
+ vcc1_0.command_key,
+ vcc1_0.enabled,
+ vcc1_0.guardian_id,
+ vcc1_0.trigger_phrase,
+ vcc1_0.updated_at,
+ vcc1_0.user_id
+ from
+ voice_command_configs vcc1_0
+ where
+ vcc1_0.user_id=?
+Hibernate:
+ select
+ hs1_0.id,
+ hs1_0.button_code,
+ hs1_0.button_name,
+ hs1_0.enabled,
+ hs1_0.guardian_id,
+ hs1_0.shortcut_key,
+ hs1_0.updated_at,
+ hs1_0.user_id
+ from
+ hardware_shortcuts hs1_0
+ where
+ hs1_0.user_id=?
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users u1_0
+ on u1_0.id=pr1_0.user_id
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:32:02.214+07:00 DEBUG 14960 --- [0.0-8080-exec-2] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:32:02.226+07:00 DEBUG 14960 --- [0.0-8080-exec-2] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:32:04.463+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:32:04.465+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:32:07.397+07:00 DEBUG 14960 --- [0.0-8080-exec-2] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:32:07.399+07:00 DEBUG 14960 --- [0.0-8080-exec-2] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:32:09.446+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.m.s.b.SimpleBrokerMessageHandler : Processing MESSAGE destination=/queue/call/2067 session=null payload={"otherId":"2067","receiverName":"suami kobo","endedBy":"2068","type":"CALL_ENDE...(truncated)
+2026-05-27T19:32:09.447+07:00 INFO 14960 --- [0.0-8080-exec-4] c.w.websocket.LocationBroadcaster : [WS] Call broadcast -> /queue/call/2067 | type=CALL_ENDED status=ENDED channel=call_2067_2068
+2026-05-27T19:32:09.447+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.m.s.b.SimpleBrokerMessageHandler : Processing MESSAGE destination=/queue/call/2068 session=null payload={"otherId":"2067","receiverName":"suami kobo","endedBy":"2068","type":"CALL_ENDE...(truncated)
+2026-05-27T19:32:09.447+07:00 INFO 14960 --- [0.0-8080-exec-4] c.w.websocket.LocationBroadcaster : [WS] Call broadcast -> /queue/call/2068 | type=CALL_ENDED status=ENDED channel=call_2067_2068
+2026-05-27T19:32:09.599+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:32:09.600+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:32:11.061+07:00 INFO 14960 --- [0.0-8080-exec-4] com.walkguide.service.FcmService : [FCM] Sent normal notification successfully: projects/walkguide-549b3/messages/0:1779885131535053%084e7484084e7484
+2026-05-27T19:32:11.118+07:00 WARN 14960 --- [0.0-8080-exec-4] com.walkguide.service.FcmService : [FIRESTORE] Notification audit skipped: com.google.api.gax.rpc.PermissionDeniedException: io.grpc.StatusRuntimeException: PERMISSION_DENIED: Cloud Firestore API has not been used in project walkguide-549b3 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/firestore.googleapis.com/overview?project=walkguide-549b3 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
+2026-05-27T19:32:12.550+07:00 DEBUG 14960 --- [0.0-8080-exec-6] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:32:12.551+07:00 DEBUG 14960 --- [0.0-8080-exec-6] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users g1_0
+ on g1_0.id=pr1_0.guardian_id
+ where
+ g1_0.id=?
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users g1_0
+ on g1_0.id=pr1_0.guardian_id
+ where
+ g1_0.id=?
+ and pr1_0.status=?
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users g1_0
+ on g1_0.id=pr1_0.guardian_id
+ where
+ g1_0.id=?
+ and pr1_0.status=?
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users g1_0
+ on g1_0.id=pr1_0.guardian_id
+ where
+ g1_0.id=?
+ and pr1_0.status=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:32:14.805+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:32:14.817+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ lh1_0.id,
+ lh1_0.accuracy,
+ lh1_0.created_at,
+ lh1_0.heading,
+ lh1_0.lat,
+ lh1_0.lng,
+ lh1_0.speed,
+ lh1_0.user_id
+ from
+ location_history lh1_0
+ where
+ lh1_0.user_id=?
+ order by
+ lh1_0.created_at desc
+ fetch
+ first ? rows only
+Hibernate:
+ select
+ al1_0.id,
+ al1_0.created_at,
+ al1_0.description,
+ al1_0.log_type,
+ al1_0.metadata,
+ al1_0.user_id
+ from
+ activity_logs al1_0
+ left join
+ users u1_0
+ on u1_0.id=al1_0.user_id
+ where
+ u1_0.id=?
+ order by
+ al1_0.created_at desc
+ offset
+ ? rows
+ fetch
+ first ? rows only
+Hibernate:
+ select
+ count(al1_0.id)
+ from
+ activity_logs al1_0
+ left join
+ users u1_0
+ on u1_0.id=al1_0.user_id
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ se1_0.id,
+ se1_0.acknowledged_at,
+ se1_0.created_at,
+ se1_0.lat,
+ se1_0.lng,
+ se1_0.status,
+ se1_0.trigger_type,
+ se1_0.user_id
+ from
+ sos_events se1_0
+ where
+ se1_0.user_id=?
+ order by
+ se1_0.created_at desc
+ offset
+ ? rows
+ fetch
+ first ? rows only
+Hibernate:
+ select
+ count(gn1_0.id)
+ from
+ guardian_notifications gn1_0
+ where
+ gn1_0.user_id=?
+ and not(gn1_0.is_read)
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:32:17.715+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:32:17.728+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ al1_0.id,
+ al1_0.created_at,
+ al1_0.description,
+ al1_0.log_type,
+ al1_0.metadata,
+ al1_0.user_id
+ from
+ activity_logs al1_0
+ left join
+ users u1_0
+ on u1_0.id=al1_0.user_id
+ where
+ u1_0.id=?
+ order by
+ al1_0.created_at desc
+ offset
+ ? rows
+ fetch
+ first ? rows only
+Hibernate:
+ select
+ count(al1_0.id)
+ from
+ activity_logs al1_0
+ left join
+ users u1_0
+ on u1_0.id=al1_0.user_id
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ se1_0.id,
+ se1_0.acknowledged_at,
+ se1_0.created_at,
+ se1_0.lat,
+ se1_0.lng,
+ se1_0.status,
+ se1_0.trigger_type,
+ se1_0.user_id
+ from
+ sos_events se1_0
+ where
+ se1_0.user_id=?
+ order by
+ se1_0.created_at desc
+ offset
+ ? rows
+ fetch
+ first ? rows only
+2026-05-27T19:32:19.928+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:32:19.929+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:32:22.887+07:00 DEBUG 14960 --- [0.0-8080-exec-7] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:32:22.926+07:00 DEBUG 14960 --- [0.0-8080-exec-7] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:32:25.333+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:32:25.345+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users g1_0
+ on g1_0.id=pr1_0.guardian_id
+ where
+ g1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:32:27.296+07:00 INFO 14960 --- [0.0-8080-exec-2] c.walkguide.service.AgoraTokenService : [AGORA] Token generated untuk channel=call_2067_2068 uid=2068 expires=1779888747
+2026-05-27T19:32:27.297+07:00 INFO 14960 --- [0.0-8080-exec-2] c.walkguide.controller.CallController : [CALL] Token generated | caller=2068 receiver=2067 channel=call_2067_2068
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:32:28.047+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:32:28.048+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:32:28.525+07:00 DEBUG 14960 --- [0.0-8080-exec-7] o.s.m.s.b.SimpleBrokerMessageHandler : Processing MESSAGE destination=/queue/call/2067 session=null payload={"receiverId":"2067","channelName":"call_2067_2068","callerId":"2068","type":"IN...(truncated)
+2026-05-27T19:32:28.526+07:00 INFO 14960 --- [0.0-8080-exec-7] c.w.websocket.LocationBroadcaster : [WS] Call broadcast -> /queue/call/2067 | type=INCOMING_CALL status=RINGING channel=call_2067_2068
+2026-05-27T19:32:28.775+07:00 INFO 14960 --- [0.0-8080-exec-7] com.walkguide.service.FcmService : [FCM] Sent high-priority notification successfully: projects/walkguide-549b3/messages/0:1779885149223672%084e7484084e7484
+2026-05-27T19:32:28.822+07:00 WARN 14960 --- [0.0-8080-exec-7] com.walkguide.service.FcmService : [FIRESTORE] Notification audit skipped: com.google.api.gax.rpc.PermissionDeniedException: io.grpc.StatusRuntimeException: PERMISSION_DENIED: Cloud Firestore API has not been used in project walkguide-549b3 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/firestore.googleapis.com/overview?project=walkguide-549b3 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
+2026-05-27T19:32:28.823+07:00 INFO 14960 --- [0.0-8080-exec-7] c.w.service.CallNotificationService : [CALL] Incoming call notification sent | caller=2068 receiver=2067 channel=call_2067_2068
+2026-05-27T19:32:30.510+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:32:30.511+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:32:33.210+07:00 DEBUG 14960 --- [0.0-8080-exec-7] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:32:33.211+07:00 DEBUG 14960 --- [0.0-8080-exec-7] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:32:35.696+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:32:35.697+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:32:37.382+07:00 INFO 14960 --- [0.0-8080-exec-4] c.walkguide.service.AgoraTokenService : [AGORA] Token generated untuk channel=call_2067_2068 uid=2067 expires=1779888757
+2026-05-27T19:32:37.394+07:00 INFO 14960 --- [0.0-8080-exec-4] c.walkguide.controller.CallController : [CALL] Token generated | caller=2067 receiver=2068 channel=call_2067_2068
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:32:38.370+07:00 DEBUG 14960 --- [0.0-8080-exec-6] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:32:38.379+07:00 DEBUG 14960 --- [0.0-8080-exec-6] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:32:38.861+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.m.s.b.SimpleBrokerMessageHandler : Processing MESSAGE destination=/queue/call/2068 session=null payload={"receiverId":"2067","receiverName":"suami kobo","channelName":"call_2067_2068",...(truncated)
+2026-05-27T19:32:38.861+07:00 INFO 14960 --- [0.0-8080-exec-5] c.w.websocket.LocationBroadcaster : [WS] Call broadcast -> /queue/call/2068 | type=CALL_ACCEPTED status=ACCEPTED channel=call_2067_2068
+2026-05-27T19:32:38.862+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.m.s.b.SimpleBrokerMessageHandler : Processing MESSAGE destination=/queue/call/2067 session=null payload={"receiverId":"2067","receiverName":"suami kobo","channelName":"call_2067_2068",...(truncated)
+2026-05-27T19:32:38.862+07:00 INFO 14960 --- [0.0-8080-exec-5] c.w.websocket.LocationBroadcaster : [WS] Call broadcast -> /queue/call/2067 | type=CALL_ACCEPTED status=ACCEPTED channel=call_2067_2068
+2026-05-27T19:32:38.862+07:00 INFO 14960 --- [0.0-8080-exec-5] c.w.service.CallNotificationService : [CALL] Call accepted | caller=2068 receiver=2067 channel=call_2067_2068
+2026-05-27T19:32:40.845+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:32:40.857+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:32:43.541+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:32:43.554+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:32:46.009+07:00 DEBUG 14960 --- [0.0-8080-exec-8] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:32:46.021+07:00 DEBUG 14960 --- [0.0-8080-exec-8] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:32:48.719+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:32:48.721+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:32:51.268+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:32:51.270+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:32:53.878+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:32:53.879+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:32:56.431+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:32:56.433+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:32:59.043+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:32:59.044+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:33:01.751+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:33:01.752+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:33:04.236+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:33:04.238+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:33:06.926+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:33:06.928+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:33:09.387+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:33:09.389+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:33:10.594+07:00 DEBUG 14960 --- [0.0-8080-exec-7] o.s.m.s.b.SimpleBrokerMessageHandler : Processing MESSAGE destination=/queue/call/2067 session=null payload={"otherId":"2067","receiverName":"suami kobo","endedBy":"2068","type":"CALL_ENDE...(truncated)
+2026-05-27T19:33:10.595+07:00 INFO 14960 --- [0.0-8080-exec-7] c.w.websocket.LocationBroadcaster : [WS] Call broadcast -> /queue/call/2067 | type=CALL_ENDED status=ENDED channel=call_2067_2068
+2026-05-27T19:33:10.596+07:00 DEBUG 14960 --- [0.0-8080-exec-7] o.s.m.s.b.SimpleBrokerMessageHandler : Processing MESSAGE destination=/queue/call/2068 session=null payload={"otherId":"2067","receiverName":"suami kobo","endedBy":"2068","type":"CALL_ENDE...(truncated)
+2026-05-27T19:33:10.596+07:00 INFO 14960 --- [0.0-8080-exec-7] c.w.websocket.LocationBroadcaster : [WS] Call broadcast -> /queue/call/2068 | type=CALL_ENDED status=ENDED channel=call_2067_2068
+2026-05-27T19:33:12.096+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:33:12.097+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ vcc1_0.id,
+ vcc1_0.command_key,
+ vcc1_0.enabled,
+ vcc1_0.guardian_id,
+ vcc1_0.trigger_phrase,
+ vcc1_0.updated_at,
+ vcc1_0.user_id
+ from
+ voice_command_configs vcc1_0
+ where
+ vcc1_0.user_id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ hs1_0.id,
+ hs1_0.button_code,
+ hs1_0.button_name,
+ hs1_0.enabled,
+ hs1_0.guardian_id,
+ hs1_0.shortcut_key,
+ hs1_0.updated_at,
+ hs1_0.user_id
+ from
+ hardware_shortcuts hs1_0
+ where
+ hs1_0.user_id=?
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users u1_0
+ on u1_0.id=pr1_0.user_id
+ where
+ u1_0.id=?
+2026-05-27T19:33:13.056+07:00 INFO 14960 --- [0.0-8080-exec-7] com.walkguide.service.FcmService : [FCM] Sent normal notification successfully: projects/walkguide-549b3/messages/0:1779885193529669%084e7484084e7484
+2026-05-27T19:33:13.110+07:00 WARN 14960 --- [0.0-8080-exec-7] com.walkguide.service.FcmService : [FIRESTORE] Notification audit skipped: com.google.api.gax.rpc.PermissionDeniedException: io.grpc.StatusRuntimeException: PERMISSION_DENIED: Cloud Firestore API has not been used in project walkguide-549b3 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/firestore.googleapis.com/overview?project=walkguide-549b3 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:33:14.632+07:00 DEBUG 14960 --- [.0-8080-exec-10] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:33:14.637+07:00 DEBUG 14960 --- [.0-8080-exec-10] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users g1_0
+ on g1_0.id=pr1_0.guardian_id
+ where
+ g1_0.id=?
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users g1_0
+ on g1_0.id=pr1_0.guardian_id
+ where
+ g1_0.id=?
+ and pr1_0.status=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users g1_0
+ on g1_0.id=pr1_0.guardian_id
+ where
+ g1_0.id=?
+ and pr1_0.status=?
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users g1_0
+ on g1_0.id=pr1_0.guardian_id
+ where
+ g1_0.id=?
+ and pr1_0.status=?
+Hibernate:
+ select
+ lh1_0.id,
+ lh1_0.accuracy,
+ lh1_0.created_at,
+ lh1_0.heading,
+ lh1_0.lat,
+ lh1_0.lng,
+ lh1_0.speed,
+ lh1_0.user_id
+ from
+ location_history lh1_0
+ where
+ lh1_0.user_id=?
+ order by
+ lh1_0.created_at desc
+ fetch
+ first ? rows only
+Hibernate:
+ select
+ al1_0.id,
+ al1_0.created_at,
+ al1_0.description,
+ al1_0.log_type,
+ al1_0.metadata,
+ al1_0.user_id
+ from
+ activity_logs al1_0
+ left join
+ users u1_0
+ on u1_0.id=al1_0.user_id
+ where
+ u1_0.id=?
+ order by
+ al1_0.created_at desc
+ offset
+ ? rows
+ fetch
+ first ? rows only
+Hibernate:
+ select
+ count(al1_0.id)
+ from
+ activity_logs al1_0
+ left join
+ users u1_0
+ on u1_0.id=al1_0.user_id
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ se1_0.id,
+ se1_0.acknowledged_at,
+ se1_0.created_at,
+ se1_0.lat,
+ se1_0.lng,
+ se1_0.status,
+ se1_0.trigger_type,
+ se1_0.user_id
+ from
+ sos_events se1_0
+ where
+ se1_0.user_id=?
+ order by
+ se1_0.created_at desc
+ offset
+ ? rows
+ fetch
+ first ? rows only
+Hibernate:
+ select
+ count(gn1_0.id)
+ from
+ guardian_notifications gn1_0
+ where
+ gn1_0.user_id=?
+ and not(gn1_0.is_read)
+2026-05-27T19:33:17.259+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:33:17.260+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ al1_0.id,
+ al1_0.created_at,
+ al1_0.description,
+ al1_0.log_type,
+ al1_0.metadata,
+ al1_0.user_id
+ from
+ activity_logs al1_0
+ left join
+ users u1_0
+ on u1_0.id=al1_0.user_id
+ where
+ u1_0.id=?
+ order by
+ al1_0.created_at desc
+ offset
+ ? rows
+ fetch
+ first ? rows only
+Hibernate:
+ select
+ count(al1_0.id)
+ from
+ activity_logs al1_0
+ left join
+ users u1_0
+ on u1_0.id=al1_0.user_id
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ se1_0.id,
+ se1_0.acknowledged_at,
+ se1_0.created_at,
+ se1_0.lat,
+ se1_0.lng,
+ se1_0.status,
+ se1_0.trigger_type,
+ se1_0.user_id
+ from
+ sos_events se1_0
+ where
+ se1_0.user_id=?
+ order by
+ se1_0.created_at desc
+ offset
+ ? rows
+ fetch
+ first ? rows only
+2026-05-27T19:33:19.819+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:33:19.822+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:33:22.428+07:00 DEBUG 14960 --- [0.0-8080-exec-7] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:33:22.431+07:00 DEBUG 14960 --- [0.0-8080-exec-7] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:33:24.965+07:00 DEBUG 14960 --- [0.0-8080-exec-2] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:33:24.966+07:00 DEBUG 14960 --- [0.0-8080-exec-2] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:33:27.589+07:00 DEBUG 14960 --- [0.0-8080-exec-8] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:33:27.594+07:00 DEBUG 14960 --- [0.0-8080-exec-8] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:33:30.077+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:33:30.081+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ pr1_0.id,
+ pr1_0.guardian_id,
+ pr1_0.invited_at,
+ pr1_0.responded_at,
+ pr1_0.status,
+ pr1_0.user_id
+ from
+ pairing_relations pr1_0
+ left join
+ users u1_0
+ on u1_0.id=pr1_0.user_id
+ where
+ u1_0.id=?
+2026-05-27T19:33:32.755+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:33:32.756+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:33:35.214+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:33:35.217+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:33:35.438+07:00 INFO 14960 --- [0.0-8080-exec-3] c.walkguide.service.AgoraTokenService : [AGORA] Token generated untuk channel=call_2067_2068 uid=2067 expires=1779888815
+2026-05-27T19:33:35.441+07:00 INFO 14960 --- [0.0-8080-exec-3] c.walkguide.controller.CallController : [CALL] Token generated | caller=2067 receiver=2068 channel=call_2067_2068
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:33:36.919+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.m.s.b.SimpleBrokerMessageHandler : Processing MESSAGE destination=/queue/call/2068 session=null payload={"receiverId":"2068","channelName":"call_2067_2068","callerId":"2067","type":"IN...(truncated)
+2026-05-27T19:33:36.920+07:00 INFO 14960 --- [0.0-8080-exec-5] c.w.websocket.LocationBroadcaster : [WS] Call broadcast -> /queue/call/2068 | type=INCOMING_CALL status=RINGING channel=call_2067_2068
+2026-05-27T19:33:37.163+07:00 INFO 14960 --- [0.0-8080-exec-5] com.walkguide.service.FcmService : [FCM] Sent high-priority notification successfully: projects/walkguide-549b3/messages/0:1779885217624637%084e7484084e7484
+2026-05-27T19:33:37.220+07:00 WARN 14960 --- [0.0-8080-exec-5] com.walkguide.service.FcmService : [FIRESTORE] Notification audit skipped: com.google.api.gax.rpc.PermissionDeniedException: io.grpc.StatusRuntimeException: PERMISSION_DENIED: Cloud Firestore API has not been used in project walkguide-549b3 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/firestore.googleapis.com/overview?project=walkguide-549b3 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
+2026-05-27T19:33:37.221+07:00 INFO 14960 --- [0.0-8080-exec-5] c.w.service.CallNotificationService : [CALL] Incoming call notification sent | caller=2067 receiver=2068 channel=call_2067_2068
+2026-05-27T19:33:37.919+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:33:37.920+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:33:40.376+07:00 DEBUG 14960 --- [0.0-8080-exec-2] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:33:40.379+07:00 DEBUG 14960 --- [0.0-8080-exec-2] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:33:43.099+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:33:43.100+07:00 DEBUG 14960 --- [0.0-8080-exec-3] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:33:43.804+07:00 INFO 14960 --- [.0-8080-exec-10] c.walkguide.service.AgoraTokenService : [AGORA] Token generated untuk channel=call_2067_2068 uid=2068 expires=1779888823
+2026-05-27T19:33:43.804+07:00 INFO 14960 --- [.0-8080-exec-10] c.walkguide.controller.CallController : [CALL] Token generated | caller=2068 receiver=2067 channel=call_2067_2068
+2026-05-27T19:33:45.613+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:33:45.615+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:33:48.293+07:00 DEBUG 14960 --- [0.0-8080-exec-8] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:33:48.295+07:00 DEBUG 14960 --- [0.0-8080-exec-8] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:33:49.690+07:00 WARN 14960 --- [0.0-8080-exec-9] com.zaxxer.hikari.pool.PoolBase : HikariPool-1 - Failed to validate connection org.postgresql.jdbc.PgConnection@2f6912a6 (This connection has been closed.). Possibly consider using a shorter maxLifetime value.
+2026-05-27T19:33:50.729+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:33:50.731+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+Hibernate:
+ select
+ u1_0.id,
+ u1_0.created_at,
+ u1_0.display_name,
+ u1_0.email,
+ u1_0.fcm_token,
+ u1_0.pairing_code,
+ u1_0.pairing_code_expires_at,
+ u1_0.password,
+ u1_0.role,
+ u1_0.unique_user_id,
+ u1_0.updated_at
+ from
+ users u1_0
+ where
+ u1_0.id=?
+2026-05-27T19:33:53.427+07:00 DEBUG 14960 --- [0.0-8080-exec-6] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:33:53.429+07:00 DEBUG 14960 --- [0.0-8080-exec-6] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:33:53.890+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.m.s.b.SimpleBrokerMessageHandler : Processing MESSAGE destination=/queue/call/2067 session=null payload={"receiverId":"2068","receiverName":"GuardianRobert","channelName":"call_2067_20...(truncated)
+2026-05-27T19:33:53.890+07:00 INFO 14960 --- [0.0-8080-exec-9] c.w.websocket.LocationBroadcaster : [WS] Call broadcast -> /queue/call/2067 | type=CALL_ACCEPTED status=ACCEPTED channel=call_2067_2068
+2026-05-27T19:33:53.891+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.m.s.b.SimpleBrokerMessageHandler : Processing MESSAGE destination=/queue/call/2068 session=null payload={"receiverId":"2068","receiverName":"GuardianRobert","channelName":"call_2067_20...(truncated)
+2026-05-27T19:33:53.892+07:00 INFO 14960 --- [0.0-8080-exec-9] c.w.websocket.LocationBroadcaster : [WS] Call broadcast -> /queue/call/2068 | type=CALL_ACCEPTED status=ACCEPTED channel=call_2067_2068
+2026-05-27T19:33:53.893+07:00 INFO 14960 --- [0.0-8080-exec-9] c.w.service.CallNotificationService : [CALL] Call accepted | caller=2067 receiver=2068 channel=call_2067_2068
+2026-05-27T19:33:55.880+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:33:55.880+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:33:58.584+07:00 DEBUG 14960 --- [.0-8080-exec-10] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:33:58.585+07:00 DEBUG 14960 --- [.0-8080-exec-10] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:34:01.039+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:34:01.042+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:34:03.749+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:34:03.751+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:34:06.209+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:34:06.210+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:34:08.918+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:34:08.919+07:00 DEBUG 14960 --- [0.0-8080-exec-9] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:34:11.369+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:34:11.371+07:00 DEBUG 14960 --- [0.0-8080-exec-1] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:34:14.082+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:34:14.083+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:34:16.535+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:34:16.535+07:00 DEBUG 14960 --- [0.0-8080-exec-4] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
+2026-05-27T19:34:19.253+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@268eefe7
+2026-05-27T19:34:19.258+07:00 DEBUG 14960 --- [0.0-8080-exec-5] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://192.168.1.68:8080/ws
diff --git a/walkguide-backend/demo/pom.xml b/walkguide-backend/demo/pom.xml
index 624f7b5..1e0aeb2 100644
--- a/walkguide-backend/demo/pom.xml
+++ b/walkguide-backend/demo/pom.xml
@@ -104,6 +104,13 @@
+
+
+ com.google.firebase
+ firebase-admin
+ 9.3.0
+
+
org.springframework.boot
diff --git a/walkguide-backend/demo/src/main/java/com/walkguide/config/FirebaseConfig.java b/walkguide-backend/demo/src/main/java/com/walkguide/config/FirebaseConfig.java
new file mode 100644
index 0000000..c4af300
--- /dev/null
+++ b/walkguide-backend/demo/src/main/java/com/walkguide/config/FirebaseConfig.java
@@ -0,0 +1,52 @@
+package com.walkguide.config;
+
+import com.google.auth.oauth2.GoogleCredentials;
+import com.google.firebase.FirebaseApp;
+import com.google.firebase.FirebaseOptions;
+import jakarta.annotation.PostConstruct;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.stereotype.Component;
+
+import java.io.InputStream;
+
+@Component
+@RequiredArgsConstructor
+@Slf4j
+public class FirebaseConfig {
+
+ private final ResourceLoader resourceLoader;
+
+ @Value("${firebase.credentials-path:classpath:firebase/google-services-admin.json}")
+ private String credentialsPath;
+
+ @PostConstruct
+ void initializeFirebase() {
+ if (!FirebaseApp.getApps().isEmpty()) {
+ log.info("[FIREBASE] FirebaseApp already initialized");
+ return;
+ }
+
+ try {
+ Resource resource = resourceLoader.getResource(credentialsPath);
+ if (!resource.exists() || !resource.isReadable()) {
+ log.warn("[FIREBASE] Credential not found/readable at {}. FCM runs in log-only fallback.", credentialsPath);
+ return;
+ }
+
+ try (InputStream in = resource.getInputStream()) {
+ FirebaseOptions options = FirebaseOptions.builder()
+ .setCredentials(GoogleCredentials.fromStream(in))
+ .build();
+ FirebaseApp.initializeApp(options);
+ }
+
+ log.info("[FIREBASE] Firebase Admin initialized from {}", credentialsPath);
+ } catch (Exception e) {
+ log.warn("[FIREBASE] Failed to initialize Firebase Admin. FCM fallback active: {}", e.getMessage());
+ }
+ }
+}
diff --git a/walkguide-backend/demo/src/main/java/com/walkguide/controller/CallController.java b/walkguide-backend/demo/src/main/java/com/walkguide/controller/CallController.java
index 36d35b5..fbeab8d 100644
--- a/walkguide-backend/demo/src/main/java/com/walkguide/controller/CallController.java
+++ b/walkguide-backend/demo/src/main/java/com/walkguide/controller/CallController.java
@@ -14,9 +14,12 @@ import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.DeleteMapping;
+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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@@ -36,35 +39,76 @@ public class CallController {
@Operation(summary = "Generate Agora token", description = "Caller requests a token before joining Agora")
public ResponseEntity> generateToken(
@Valid @RequestBody CallTokenRequest req) {
-
Long callerId = SecurityHelper.getCurrentUserId();
AgoraTokenResponse response = agoraTokenService.generateToken(callerId, req.getReceiverId());
-
log.info("[CALL] Token generated | caller={} receiver={} channel={}",
callerId, req.getReceiverId(), response.getChannelName());
-
return ResponseEntity.ok(ApiResponse.ok(response, "Token Agora berhasil digenerate"));
}
@PostMapping("/notify")
@Operation(summary = "Notify receiver of incoming call")
- public ResponseEntity> notifyCall(
- @Valid @RequestBody CallNotifyRequest req) {
-
+ public ResponseEntity> notifyCall(@Valid @RequestBody CallNotifyRequest req) {
Long callerId = SecurityHelper.getCurrentUserId();
String message = callNotificationService.notifyIncomingCall(callerId, req);
return ResponseEntity.ok(ApiResponse.ok(null, message));
}
+ @PostMapping("/accept")
+ @Operation(summary = "Receiver accepts incoming call")
+ public ResponseEntity>> acceptCall(@RequestBody Map body) {
+ Long receiverId = SecurityHelper.getCurrentUserId();
+ Long callerId = Long.parseLong(body.get("callerId"));
+ String channelName = body.get("channelName");
+ return ResponseEntity.ok(ApiResponse.ok(
+ callNotificationService.acceptCall(receiverId, callerId, channelName),
+ "Call accepted"
+ ));
+ }
+
+ @GetMapping("/pending")
+ @Operation(summary = "Get pending incoming call for logged-in receiver")
+ public ResponseEntity>> pendingCall() {
+ Long receiverId = SecurityHelper.getCurrentUserId();
+ return ResponseEntity.ok(ApiResponse.ok(callNotificationService.getPendingCall(receiverId), "Pending call"));
+ }
+
+ @DeleteMapping("/pending")
+ @Operation(summary = "Clear pending incoming call for logged-in receiver")
+ public ResponseEntity> clearPendingCall() {
+ Long receiverId = SecurityHelper.getCurrentUserId();
+ callNotificationService.clearPendingCall(receiverId);
+ return ResponseEntity.ok(ApiResponse.ok(null, "Pending call cleared"));
+ }
+
+ @GetMapping("/accepted")
+ @Operation(summary = "Get accepted call for logged-in caller")
+ public ResponseEntity>> acceptedCall() {
+ Long callerId = SecurityHelper.getCurrentUserId();
+ return ResponseEntity.ok(ApiResponse.ok(callNotificationService.getAcceptedCall(callerId), "Accepted call"));
+ }
+
+ @DeleteMapping("/accepted")
+ @Operation(summary = "Clear accepted call for logged-in caller")
+ public ResponseEntity> clearAcceptedCall() {
+ Long callerId = SecurityHelper.getCurrentUserId();
+ callNotificationService.clearAcceptedCall(callerId);
+ return ResponseEntity.ok(ApiResponse.ok(null, "Accepted call cleared"));
+ }
+
+ @GetMapping("/state")
+ @Operation(summary = "Get call state by Agora channel")
+ public ResponseEntity>> callState(@RequestParam String channelName) {
+ return ResponseEntity.ok(ApiResponse.ok(callNotificationService.getCallState(channelName), "Call state"));
+ }
+
@PostMapping("/end")
@Operation(summary = "Notify end of call")
- public ResponseEntity> endCall(
- @RequestBody Map body) {
-
+ public ResponseEntity> endCall(@RequestBody Map body) {
Long callerId = SecurityHelper.getCurrentUserId();
- Long otherId = body.get("otherId");
- callNotificationService.notifyCallEnded(callerId, otherId);
-
+ Long otherId = Long.parseLong(body.get("otherId"));
+ String channelName = body.get("channelName");
+ callNotificationService.notifyCallEnded(callerId, otherId, channelName);
return ResponseEntity.ok(ApiResponse.ok(null, "Call ended"));
}
-}
+}
\ No newline at end of file
diff --git a/walkguide-backend/demo/src/main/java/com/walkguide/exception/GlobalExceptionHandler.java b/walkguide-backend/demo/src/main/java/com/walkguide/exception/GlobalExceptionHandler.java
index 5d307bf..11740ba 100644
--- a/walkguide-backend/demo/src/main/java/com/walkguide/exception/GlobalExceptionHandler.java
+++ b/walkguide-backend/demo/src/main/java/com/walkguide/exception/GlobalExceptionHandler.java
@@ -1,6 +1,7 @@
package com.walkguide.exception;
import com.walkguide.dto.ApiResponse;
+import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
@@ -29,6 +30,13 @@ public class GlobalExceptionHandler {
.body(ApiResponse.error("VALIDATION_ERROR", msg));
}
+
+ @ExceptionHandler(DataIntegrityViolationException.class)
+ public ResponseEntity> handleDataIntegrity(DataIntegrityViolationException ex) {
+ return ResponseEntity.status(HttpStatus.CONFLICT)
+ .body(ApiResponse.error("DATA_CONFLICT",
+ "Data pairing lama masih bentrok. Refresh status atau unpair dulu, lalu coba lagi."));
+ }
@ExceptionHandler(RuntimeException.class)
public ResponseEntity> handleRuntime(RuntimeException ex) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
diff --git a/walkguide-backend/demo/src/main/java/com/walkguide/service/CallNotificationService.java b/walkguide-backend/demo/src/main/java/com/walkguide/service/CallNotificationService.java
index fc45d02..2bb3924 100644
--- a/walkguide-backend/demo/src/main/java/com/walkguide/service/CallNotificationService.java
+++ b/walkguide-backend/demo/src/main/java/com/walkguide/service/CallNotificationService.java
@@ -4,11 +4,14 @@ import com.walkguide.dto.request.CallNotifyRequest;
import com.walkguide.entity.User;
import com.walkguide.exception.ResourceNotFoundException;
import com.walkguide.repository.UserRepository;
+import com.walkguide.websocket.LocationBroadcaster;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
+import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
@Service
@RequiredArgsConstructor
@@ -17,29 +20,38 @@ public class CallNotificationService {
private final FcmService fcmService;
private final UserRepository userRepository;
+ private final LocationBroadcaster locationBroadcaster;
+ private final Map> pendingCalls = new ConcurrentHashMap<>();
+ private final Map> acceptedCalls = new ConcurrentHashMap<>();
+ private final Map> callStates = new ConcurrentHashMap<>();
public String notifyIncomingCall(Long callerId, CallNotifyRequest req) {
User caller = userRepository.findById(callerId)
.orElseThrow(() -> new ResourceNotFoundException("Caller not found"));
-
User receiver = userRepository.findById(req.getReceiverId())
.orElseThrow(() -> new ResourceNotFoundException("Receiver not found"));
+ String callerName = caller.getDisplayName() != null ? caller.getDisplayName() : caller.getEmail();
+ Map payload = new HashMap<>();
+ payload.put("type", "INCOMING_CALL");
+ payload.put("status", "RINGING");
+ payload.put("callerId", String.valueOf(callerId));
+ payload.put("receiverId", String.valueOf(receiver.getId()));
+ payload.put("callerName", callerName);
+ payload.put("channelName", req.getChannelName());
+ payload.put("agoraToken", req.getAgoraToken() != null ? req.getAgoraToken() : "");
+ payload.put("receiverUid", String.valueOf(req.getReceiverUid()));
+
+ pendingCalls.put(receiver.getId(), payload);
+ acceptedCalls.remove(callerId);
+ callStates.put(req.getChannelName(), payload);
+ locationBroadcaster.broadcastCall(receiver.getId(), payload);
+
if (receiver.getFcmToken() == null || receiver.getFcmToken().isBlank()) {
log.warn("[CALL] Receiver {} has no FCM token; push notification skipped", req.getReceiverId());
- return "Panggilan dikirim (receiver mungkin tidak menerima push notification)";
+ return "Panggilan dikirim via realtime fallback.";
}
- String callerName = caller.getDisplayName() != null ? caller.getDisplayName() : caller.getEmail();
- Map payload = Map.of(
- "type", "INCOMING_CALL",
- "callerId", String.valueOf(callerId),
- "callerName", callerName,
- "channelName", req.getChannelName(),
- "agoraToken", req.getAgoraToken() != null ? req.getAgoraToken() : "",
- "receiverUid", String.valueOf(req.getReceiverUid())
- );
-
fcmService.sendHighPriority(
receiver.getFcmToken(),
"Panggilan Masuk",
@@ -52,22 +64,111 @@ public class CallNotificationService {
return "Notifikasi panggilan berhasil dikirim";
}
+ public Map acceptCall(Long receiverId, Long callerId, String channelName) {
+ User receiver = userRepository.findById(receiverId)
+ .orElseThrow(() -> new ResourceNotFoundException("Receiver not found"));
+ userRepository.findById(callerId)
+ .orElseThrow(() -> new ResourceNotFoundException("Caller not found"));
+
+ pendingCalls.remove(receiverId);
+ String receiverName = receiver.getDisplayName() != null ? receiver.getDisplayName() : receiver.getEmail();
+ Map payload = new HashMap<>(getCallState(channelName));
+ payload.put("type", "CALL_ACCEPTED");
+ payload.put("status", "ACCEPTED");
+ payload.put("callerId", String.valueOf(callerId));
+ payload.put("receiverId", String.valueOf(receiverId));
+ payload.put("receiverName", receiverName);
+ payload.put("channelName", channelName != null ? channelName : "");
+ payload.put("acceptedBy", String.valueOf(receiverId));
+ payload.put("acceptedAt", String.valueOf(System.currentTimeMillis()));
+
+ acceptedCalls.put(callerId, payload);
+ if (channelName != null && !channelName.isBlank()) {
+ callStates.put(channelName, payload);
+ }
+ locationBroadcaster.broadcastCall(callerId, payload);
+ locationBroadcaster.broadcastCall(receiverId, payload);
+ log.info("[CALL] Call accepted | caller={} receiver={} channel={}", callerId, receiverId, channelName);
+ return payload;
+ }
+
+ public Map getPendingCall(Long receiverId) {
+ return pendingCalls.get(receiverId);
+ }
+
+ public void clearPendingCall(Long receiverId) {
+ pendingCalls.remove(receiverId);
+ }
+
+ public Map getAcceptedCall(Long callerId) {
+ return acceptedCalls.get(callerId);
+ }
+
+ public void clearAcceptedCall(Long callerId) {
+ acceptedCalls.remove(callerId);
+ }
+
+ public Map getCallState(String channelName) {
+ if (channelName == null || channelName.isBlank()) {
+ return new HashMap<>();
+ }
+ return callStates.getOrDefault(channelName, new HashMap<>());
+ }
+
public void notifyCallEnded(Long callerId, Long otherId) {
+ notifyCallEnded(callerId, otherId, null);
+ }
+
+ public void notifyCallEnded(Long callerId, Long otherId, String channelName) {
if (otherId == null) {
return;
}
+ clearPendingCall(otherId);
+ clearPendingCall(callerId);
+ clearAcceptedCall(callerId);
+ clearAcceptedCall(otherId);
+
+ String resolvedChannel = channelName;
+ if (resolvedChannel == null || resolvedChannel.isBlank()) {
+ resolvedChannel = findActiveChannel(callerId, otherId);
+ }
+ Map payload = new HashMap<>(getCallState(resolvedChannel));
+ payload.put("type", "CALL_ENDED");
+ payload.put("status", "ENDED");
+ payload.put("callerId", String.valueOf(callerId));
+ payload.put("otherId", String.valueOf(otherId));
+ payload.put("channelName", resolvedChannel != null ? resolvedChannel : "");
+ payload.put("endedBy", String.valueOf(callerId));
+ payload.put("endedAt", String.valueOf(System.currentTimeMillis()));
+ if (resolvedChannel != null && !resolvedChannel.isBlank()) {
+ callStates.put(resolvedChannel, payload);
+ }
+
+ locationBroadcaster.broadcastCall(otherId, payload);
+ locationBroadcaster.broadcastCall(callerId, payload);
+
userRepository.findById(otherId).ifPresent(other -> {
if (other.getFcmToken() == null || other.getFcmToken().isBlank()) {
return;
}
-
fcmService.sendToToken(
other.getFcmToken(),
"Panggilan Berakhir",
"Panggilan telah berakhir",
- Map.of("type", "CALL_ENDED", "callerId", String.valueOf(callerId))
+ payload
);
});
}
-}
+
+ private String findActiveChannel(Long userA, Long userB) {
+ String a = String.valueOf(userA);
+ String b = String.valueOf(userB);
+ return callStates.entrySet().stream()
+ .filter(e -> a.equals(e.getValue().get("callerId")) && b.equals(e.getValue().get("receiverId"))
+ || b.equals(e.getValue().get("callerId")) && a.equals(e.getValue().get("receiverId")))
+ .map(Map.Entry::getKey)
+ .findFirst()
+ .orElse(null);
+ }
+}
\ No newline at end of file
diff --git a/walkguide-backend/demo/src/main/java/com/walkguide/service/FcmService.java b/walkguide-backend/demo/src/main/java/com/walkguide/service/FcmService.java
index 466a707..b39bd6e 100644
--- a/walkguide-backend/demo/src/main/java/com/walkguide/service/FcmService.java
+++ b/walkguide-backend/demo/src/main/java/com/walkguide/service/FcmService.java
@@ -1,50 +1,130 @@
package com.walkguide.service;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
+import com.google.cloud.Timestamp;
+import com.google.cloud.firestore.Firestore;
+import com.google.firebase.FirebaseApp;
+import com.google.firebase.cloud.FirestoreClient;
+import com.google.firebase.messaging.AndroidConfig;
+import com.google.firebase.messaging.AndroidNotification;
+import com.google.firebase.messaging.FirebaseMessaging;
+import com.google.firebase.messaging.Message;
+import com.google.firebase.messaging.Notification;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
+import java.time.Instant;
+import java.util.HashMap;
import java.util.Map;
/**
- * FCM Service untuk push notification.
- * Saat ini dalam mode LOG-ONLY agar tidak butuh Firebase credentials dulu.
- * Untuk enable FCM nyata: uncomment bagian Firebase dan tambah dependency Firebase Admin SDK.
+ * FCM Service untuk push notification dan audit notifikasi ke Firestore.
+ * Jika Firebase credential belum tersedia, service tetap aman berjalan dalam mode log-only.
*/
@Service
-@RequiredArgsConstructor
-@Slf4j
public class FcmService {
- public void sendToToken(String fcmToken, String title, String body, Map data) {
- if (fcmToken == null || fcmToken.isBlank()) {
- log.warn("[FCM] Token kosong, skip notifikasi: {} - {}", title, body);
- return;
- }
- // LOG ONLY untuk sekarang
- log.info("[FCM] TO={} | TITLE={} | BODY={} | DATA={}", fcmToken, title, body, data);
+ private static final Logger log = LoggerFactory.getLogger(FcmService.class);
- // TODO: uncomment ini setelah tambah Firebase Admin SDK ke pom.xml
- // dan taruh google-services-admin.json di src/main/resources/firebase/
- //
- // try {
- // Message message = Message.builder()
- // .setToken(fcmToken)
- // .setNotification(Notification.builder().setTitle(title).setBody(body).build())
- // .putAllData(data != null ? data : Map.of())
- // .setAndroidConfig(AndroidConfig.builder()
- // .setPriority(AndroidConfig.Priority.HIGH)
- // .build())
- // .build();
- // String response = FirebaseMessaging.getInstance().send(message);
- // log.info("[FCM] Sent successfully: {}", response);
- // } catch (FirebaseMessagingException e) {
- // log.error("[FCM] Failed to send: {}", e.getMessage());
- // }
+ public void sendToToken(String fcmToken, String title, String body, Map data) {
+ sendInternal(fcmToken, title, body, data, false);
}
public void sendHighPriority(String fcmToken, String title, String body, Map data) {
- // SOS dan incoming call pakai ini - sama untuk sekarang
- sendToToken(fcmToken, title, body, data);
+ sendInternal(fcmToken, title, body, data, true);
+ }
+
+ @Value("${firebase.notifications-collection:notifications}")
+ private String notificationsCollection;
+
+ private void sendInternal(String fcmToken, String title, String body, Map data, boolean highPriority) {
+ Map safeData = data != null ? data : Map.of();
+ String status = "SKIPPED";
+ String messageId = null;
+
+ if (fcmToken == null || fcmToken.isBlank()) {
+ log.warn("[FCM] Token kosong, skip notifikasi: {} - {}", title, body);
+ saveNotificationAudit(fcmToken, title, body, safeData, highPriority, status, null);
+ return;
+ }
+
+ if (FirebaseApp.getApps().isEmpty()) {
+ status = "LOG_ONLY";
+ log.info("[FCM] LOG_ONLY TO={} | TITLE={} | BODY={} | DATA={}",
+ maskToken(fcmToken), title, body, safeData);
+ saveNotificationAudit(fcmToken, title, body, safeData, highPriority, status, null);
+ return;
+ }
+
+ try {
+ AndroidConfig.Priority priority = highPriority
+ ? AndroidConfig.Priority.HIGH
+ : AndroidConfig.Priority.NORMAL;
+
+ AndroidNotification androidNotification = AndroidNotification.builder()
+ .setChannelId(highPriority ? "walkguide_urgent" : "walkguide_alerts")
+ .setPriority(highPriority
+ ? AndroidNotification.Priority.MAX
+ : AndroidNotification.Priority.DEFAULT)
+ .build();
+
+ Message message = Message.builder()
+ .setToken(fcmToken)
+ .setNotification(Notification.builder()
+ .setTitle(title != null ? title : "WalkGuide")
+ .setBody(body != null ? body : "")
+ .build())
+ .putAllData(safeData)
+ .setAndroidConfig(AndroidConfig.builder()
+ .setPriority(priority)
+ .setNotification(androidNotification)
+ .build())
+ .build();
+
+ messageId = FirebaseMessaging.getInstance().send(message);
+ status = "SENT";
+ log.info("[FCM] Sent {} notification successfully: {}", highPriority ? "high-priority" : "normal", messageId);
+ } catch (Exception e) {
+ status = "FAILED";
+ log.error("[FCM] Failed to send notification: {}", e.getMessage());
+ } finally {
+ saveNotificationAudit(fcmToken, title, body, safeData, highPriority, status, messageId);
+ }
+ }
+
+ private void saveNotificationAudit(String fcmToken, String title, String body, Map data,
+ boolean highPriority, String status, String messageId) {
+ if (FirebaseApp.getApps().isEmpty()) {
+ return;
+ }
+
+ try {
+ Firestore firestore = FirestoreClient.getFirestore();
+ Map doc = new HashMap<>();
+ doc.put("title", title);
+ doc.put("body", body);
+ doc.put("type", data.getOrDefault("type", "GENERAL"));
+ doc.put("data", data);
+ doc.put("priority", highPriority ? "HIGH" : "NORMAL");
+ doc.put("status", status);
+ doc.put("messageId", messageId);
+ doc.put("recipientTokenMasked", maskToken(fcmToken));
+ doc.put("createdAt", Timestamp.ofTimeSecondsAndNanos(Instant.now().getEpochSecond(), 0));
+
+ firestore.collection(notificationsCollection).add(doc).get();
+ log.debug("[FIRESTORE] Notification audit saved | type={} status={}", doc.get("type"), status);
+ } catch (Exception e) {
+ log.warn("[FIRESTORE] Notification audit skipped: {}", e.getMessage());
+ }
+ }
+
+ private String maskToken(String token) {
+ if (token == null || token.isBlank()) {
+ return "";
+ }
+ int visible = Math.min(6, token.length());
+ return "***" + token.substring(token.length() - visible);
}
}
+
diff --git a/walkguide-backend/demo/src/main/java/com/walkguide/service/PairingService.java b/walkguide-backend/demo/src/main/java/com/walkguide/service/PairingService.java
index 69f0445..3a9f8e1 100644
--- a/walkguide-backend/demo/src/main/java/com/walkguide/service/PairingService.java
+++ b/walkguide-backend/demo/src/main/java/com/walkguide/service/PairingService.java
@@ -7,7 +7,6 @@ import com.walkguide.enums.*;
import com.walkguide.exception.PairingException;
import com.walkguide.exception.ResourceNotFoundException;
import com.walkguide.repository.*;
-import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -18,7 +17,6 @@ import java.util.List;
import java.util.Map;
@Service
-@RequiredArgsConstructor
public class PairingService {
private final PairingRelationRepository pairingRelationRepository;
@@ -34,6 +32,22 @@ public class PairingService {
private static final int PAIRING_CODE_TTL_MINUTES = 15;
private static final SecureRandom RANDOM = new SecureRandom();
+ public PairingService(PairingRelationRepository pairingRelationRepository,
+ UserRepository userRepository,
+ VoiceCommandConfigRepository voiceCommandConfigRepository,
+ HardwareShortcutRepository hardwareShortcutRepository,
+ AiConfigRepository aiConfigRepository,
+ ActivityLogService activityLogService,
+ FcmService fcmService) {
+ this.pairingRelationRepository = pairingRelationRepository;
+ this.userRepository = userRepository;
+ this.voiceCommandConfigRepository = voiceCommandConfigRepository;
+ this.hardwareShortcutRepository = hardwareShortcutRepository;
+ this.aiConfigRepository = aiConfigRepository;
+ this.activityLogService = activityLogService;
+ this.fcmService = fcmService;
+ }
+
@Transactional
public PairingCodeResponse getOrCreatePairingCode(Long userId) {
User user = userRepository.findById(userId)
@@ -69,7 +83,6 @@ public class PairingService {
@Transactional
public PairingStatusResponse inviteUser(Long guardianId, String submittedCode) {
- // Guardian tidak boleh punya pairing ACTIVE atau PENDING
if (pairingRelationRepository.existsByGuardian_IdAndStatus(guardianId, PairingStatus.ACTIVE)) {
throw new PairingException("Kamu sudah memiliki user yang dipair. Unpair dulu sebelum invite user baru.");
}
@@ -88,6 +101,52 @@ public class PairingService {
throw new PairingException("User ini sudah dipair dengan Guardian lain.");
}
+ var existingGuardianPairing = pairingRelationRepository.findByGuardian_Id(guardianId);
+ if (existingGuardianPairing.isPresent()) {
+ PairingRelation existing = existingGuardianPairing.get();
+ if (existing.getStatus() == PairingStatus.ACTIVE) {
+ if (existing.getUser().getId().equals(user.getId())) {
+ return buildStatus(existing, guardian, existing.getUser(), "GUARDIAN");
+ }
+ throw new PairingException(
+ "Guardian sudah pairing aktif dengan User lain. Unpair dulu sebelum invite User baru.");
+ }
+ if (existing.getStatus() == PairingStatus.PENDING) {
+ if (existing.getUser().getId().equals(user.getId())) {
+ sendPairingInviteNotification(existing, guardian, user);
+ return buildStatus(existing, guardian, user, "GUARDIAN");
+ }
+ throw new PairingException(
+ "Guardian masih punya undangan pairing yang menunggu respons User.");
+ }
+ }
+
+ var existingUserPairing = pairingRelationRepository.findByUser_Id(user.getId());
+ if (existingUserPairing.isPresent()) {
+ PairingRelation existing = existingUserPairing.get();
+ if (existing.getStatus() == PairingStatus.ACTIVE) {
+ throw new PairingException("User ini sudah dipair dengan Guardian lain.");
+ }
+ if (existing.getStatus() == PairingStatus.PENDING) {
+ if (existing.getGuardian().getId().equals(guardianId)) {
+ sendPairingInviteNotification(existing, guardian, user);
+ return buildStatus(existing, guardian, user, "GUARDIAN");
+ }
+ throw new PairingException("User ini masih punya undangan pairing dari Guardian lain.");
+ }
+ }
+
+ if (existingGuardianPairing.isPresent()) {
+ pairingRelationRepository.delete(existingGuardianPairing.get());
+ pairingRelationRepository.flush();
+ }
+ if (existingUserPairing.isPresent()
+ && (existingGuardianPairing.isEmpty()
+ || !existingUserPairing.get().getId().equals(existingGuardianPairing.get().getId()))) {
+ pairingRelationRepository.delete(existingUserPairing.get());
+ pairingRelationRepository.flush();
+ }
+
PairingRelation pairing = PairingRelation.builder()
.guardian(guardian)
.user(user)
@@ -99,11 +158,7 @@ public class PairingService {
user.setPairingCodeExpiresAt(null);
userRepository.save(user);
- // Kirim FCM ke user
- fcmService.sendToToken(user.getFcmToken(),
- "Pairing Request",
- "Guardian " + guardian.getDisplayName() + " mengundang kamu untuk terhubung",
- Map.of("type", "PAIRING_INVITE", "guardianName", guardian.getDisplayName()));
+ sendPairingInviteNotification(pairing, guardian, user);
activityLogService.createLog(guardian, ActivityLogType.PAIRING_INVITE_SENT,
"Guardian mengirim invite ke " + user.getDisplayName(), null);
@@ -195,6 +250,13 @@ public class PairingService {
// ========== PRIVATE ==========
private void seedDefaults(Long guardianId, Long userId) {
+ voiceCommandConfigRepository.deleteByUserId(userId);
+ hardwareShortcutRepository.deleteByUserId(userId);
+ aiConfigRepository.findByUserId(userId).ifPresent(aiConfigRepository::delete);
+ voiceCommandConfigRepository.flush();
+ hardwareShortcutRepository.flush();
+ aiConfigRepository.flush();
+
// Voice commands default
List defaults = List.of(
vc(guardianId, userId, VoiceCommandKey.OPEN_WALKGUIDE, "Open Walkguide"),
@@ -261,6 +323,15 @@ public class PairingService {
return user;
}
+ private void sendPairingInviteNotification(PairingRelation pairing, User guardian, User user) {
+ fcmService.sendToToken(user.getFcmToken(),
+ "Pairing Request",
+ "Guardian " + guardian.getDisplayName() + " mengundang kamu untuk terhubung",
+ Map.of(
+ "type", "PAIRING_INVITE",
+ "pairingId", pairing.getId().toString(),
+ "guardianName", guardian.getDisplayName()));
+ }
private void assignNewPairingCode(User user, LocalDateTime now) {
String candidate;
do {
@@ -307,3 +378,4 @@ public class PairingService {
.build();
}
}
+
diff --git a/walkguide-backend/demo/src/main/java/com/walkguide/service/SosService.java b/walkguide-backend/demo/src/main/java/com/walkguide/service/SosService.java
index bd68ab0..6210717 100644
--- a/walkguide-backend/demo/src/main/java/com/walkguide/service/SosService.java
+++ b/walkguide-backend/demo/src/main/java/com/walkguide/service/SosService.java
@@ -7,6 +7,7 @@ import com.walkguide.entity.User;
import com.walkguide.enums.ActivityLogType;
import com.walkguide.enums.PairingStatus;
import com.walkguide.enums.SosStatus;
+import com.walkguide.exception.PairingException;
import com.walkguide.exception.ResourceNotFoundException;
import com.walkguide.repository.*;
import com.walkguide.websocket.LocationBroadcaster;
@@ -36,6 +37,14 @@ public class SosService {
@Transactional
public SosEventResponse triggerSos(Long userId, SosRequest req) {
+ User user = userRepository.findById(userId)
+ .orElseThrow(() -> new ResourceNotFoundException("User tidak ditemukan"));
+
+ var activePairing = pairingRelationRepository
+ .findByUser_IdAndStatus(userId, PairingStatus.ACTIVE)
+ .orElseThrow(() -> new PairingException(
+ "SOS hanya bisa dikirim setelah User terhubung dengan Guardian aktif."));
+
SosEvent sos = SosEvent.builder()
.userId(userId)
.triggerType(req.getTriggerType() != null ? req.getTriggerType() : "MANUAL")
@@ -46,18 +55,13 @@ public class SosService {
sos = sosEventRepository.save(sos);
final SosEvent savedSos = sos;
- User user = userRepository.findById(userId)
- .orElseThrow(() -> new ResourceNotFoundException("User tidak ditemukan"));
-
activityLogService.createLog(user, ActivityLogType.SOS_TRIGGERED,
"SOS dikirim via " + sos.getTriggerType(), null);
SosEventResponse sosResponse = toResponse(savedSos);
// Kirim ke Guardian via FCM (background) + WebSocket (foreground)
- pairingRelationRepository.findByUser_IdAndStatus(userId, PairingStatus.ACTIVE)
- .ifPresent(pairing -> {
- User guardian = pairing.getGuardian();
+ User guardian = activePairing.getGuardian();
String guardianFcm = guardian.getFcmToken();
String locStr = req.getLat() != null
? String.format("Lat:%.4f,Lng:%.4f", req.getLat(), req.getLng())
@@ -78,7 +82,6 @@ public class SosService {
log.info("[SOS] Alert sent to Guardian={} for User={} | trigger={}",
guardian.getId(), userId, savedSos.getTriggerType());
- });
return sosResponse;
}
diff --git a/walkguide-backend/demo/src/main/java/com/walkguide/websocket/LocationBroadcaster.java b/walkguide-backend/demo/src/main/java/com/walkguide/websocket/LocationBroadcaster.java
index a73d88d..3b4f83f 100644
--- a/walkguide-backend/demo/src/main/java/com/walkguide/websocket/LocationBroadcaster.java
+++ b/walkguide-backend/demo/src/main/java/com/walkguide/websocket/LocationBroadcaster.java
@@ -3,68 +3,49 @@ package com.walkguide.websocket;
import com.walkguide.dto.response.LocationResponse;
import com.walkguide.dto.response.NotificationResponse;
import com.walkguide.dto.response.SosEventResponse;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
-/**
- * Service untuk broadcast pesan real-time via WebSocket (STOMP).
- *
- * Dipakai oleh:
- * - LocationService → broadcast GPS ke Guardian
- * - SosService → broadcast SOS ke Guardian
- * - NotificationService→ broadcast notif ke User
- *
- * PATTERN: Observer — Guardian/User subscribe ke topic,
- * LocationBroadcaster push data saat ada update.
- */
+import java.util.Map;
+
@Service
-@RequiredArgsConstructor
-@Slf4j
public class LocationBroadcaster {
+ private static final Logger log = LoggerFactory.getLogger(LocationBroadcaster.class);
+
private final SimpMessagingTemplate messagingTemplate;
- /**
- * Broadcast lokasi GPS user ke Guardian yang subscribe.
- * Guardian Flutter subscribe ke: /topic/location/{userId}
- *
- * @param userId ID dari ROLE_USER (bukan guardian)
- * @param location Response lokasi terbaru
- */
+ public LocationBroadcaster(SimpMessagingTemplate messagingTemplate) {
+ this.messagingTemplate = messagingTemplate;
+ }
+
public void broadcastLocation(Long userId, LocationResponse location) {
String destination = "/topic/location/" + userId;
messagingTemplate.convertAndSend(destination, location);
- log.debug("[WS] Location broadcast → {} | lat={} lng={}",
+ log.debug("[WS] Location broadcast -> {} | lat={} lng={}",
destination, location.getLat(), location.getLng());
}
- /**
- * Broadcast SOS event ke Guardian secara real-time.
- * Guardian Flutter subscribe ke: /queue/sos/{guardianId}
- *
- * @param guardianId ID dari ROLE_GUARDIAN
- * @param sos SOS event yang baru di-trigger
- */
public void broadcastSos(Long guardianId, SosEventResponse sos) {
String destination = "/queue/sos/" + guardianId;
messagingTemplate.convertAndSend(destination, sos);
- log.info("[WS] SOS broadcast → {} | userId={} status={}",
+ log.info("[WS] SOS broadcast -> {} | userId={} status={}",
destination, sos.getUserId(), sos.getStatus());
}
- /**
- * Broadcast notifikasi dari Guardian ke User secara real-time.
- * User Flutter subscribe ke: /queue/notif/{userId}
- *
- * @param userId ID dari ROLE_USER yang menerima notif
- * @param notification Notifikasi yang baru dikirim Guardian
- */
public void broadcastNotification(Long userId, NotificationResponse notification) {
String destination = "/queue/notif/" + userId;
messagingTemplate.convertAndSend(destination, notification);
- log.debug("[WS] Notification broadcast → {} | type={}",
+ log.debug("[WS] Notification broadcast -> {} | type={}",
destination, notification.getNotifType());
}
+
+ public void broadcastCall(Long receiverId, Map payload) {
+ String destination = "/queue/call/" + receiverId;
+ messagingTemplate.convertAndSend(destination, payload);
+ log.info("[WS] Call broadcast -> {} | type={} status={} channel={}",
+ destination, payload.get("type"), payload.get("status"), payload.get("channelName"));
+ }
}
diff --git a/walkguide-backend/demo/src/main/resources/application-dev.yml b/walkguide-backend/demo/src/main/resources/application-dev.yml
index 41a46e8..58e9101 100644
--- a/walkguide-backend/demo/src/main/resources/application-dev.yml
+++ b/walkguide-backend/demo/src/main/resources/application-dev.yml
@@ -8,7 +8,16 @@ spring:
datasource:
url: ${DB_URL:jdbc:postgresql://202.46.28.160:2002/uas_5803024001}
username: ${DB_USERNAME:5803024001}
- password: ${DB_PASSWORD:pw5803024001}
+ password: ${DB_PASSWORD:pw5803024001}
+ hikari:
+ maximum-pool-size: ${DB_POOL_MAX:1}
+ minimum-idle: ${DB_POOL_MIN_IDLE:0}
+ connection-timeout: ${DB_CONNECTION_TIMEOUT:10000}
+ idle-timeout: ${DB_IDLE_TIMEOUT:30000}
+ max-lifetime: ${DB_MAX_LIFETIME:120000}
+
+ flyway:
+ enabled: ${FLYWAY_ENABLED:false}
jpa:
show-sql: true
@@ -21,8 +30,8 @@ jwt:
expiration: ${JWT_EXPIRATION:86400000}
agora:
- app-id: ${AGORA_APP_ID:}
- app-certificate: ${AGORA_APP_CERTIFICATE:}
+ app-id: ${AGORA_APP_ID:e36c2b6592e34cfda1f6ea6432a5e68d}
+ app-certificate: ${AGORA_APP_CERTIFICATE:70a4288475734a8c92ff8686c66cbc77}
logging:
level:
diff --git a/walkguide-backend/demo/src/main/resources/application.properties b/walkguide-backend/demo/src/main/resources/application.properties
index c5cea7b..3fc8d3b 100644
--- a/walkguide-backend/demo/src/main/resources/application.properties
+++ b/walkguide-backend/demo/src/main/resources/application.properties
@@ -1,11 +1,19 @@
# ===== SERVER =====
+spring.config.import=optional:file:./secrets.properties
server.port=${SERVER_PORT:8080}
+server.address=${SERVER_ADDRESS:0.0.0.0}
# ===== POSTGRESQL CONNECTION =====
spring.datasource.url=${DB_URL:jdbc:postgresql://202.46.28.160:2002/uas_5803024001}
spring.datasource.username=${DB_USERNAME:5803024001}
spring.datasource.password=${DB_PASSWORD:pw5803024001}
spring.datasource.driver-class-name=org.postgresql.Driver
+# ===== HIKARI POOL (keep DB classroom slots low) =====
+spring.datasource.hikari.maximum-pool-size=${DB_POOL_MAX:1}
+spring.datasource.hikari.minimum-idle=${DB_POOL_MIN_IDLE:0}
+spring.datasource.hikari.connection-timeout=${DB_CONNECTION_TIMEOUT:10000}
+spring.datasource.hikari.idle-timeout=${DB_IDLE_TIMEOUT:30000}
+spring.datasource.hikari.max-lifetime=${DB_MAX_LIFETIME:120000}
# ===== JPA / HIBERNATE =====
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
@@ -27,9 +35,13 @@ springdoc.swagger-ui.path=/swagger-ui.html
springdoc.api-docs.path=/v3/api-docs
# ===== AGORA RTC =====
-agora.app-id=${AGORA_APP_ID:}
+agora.app-id=${AGORA_APP_ID:e36c2b6592e34cfda1f6ea6432a5e68d}
agora.app-certificate=${AGORA_APP_CERTIFICATE:}
+# ===== FIREBASE =====
+firebase.credentials-path=${FIREBASE_CREDENTIALS_PATH:classpath:firebase/google-services-admin.json}
+firebase.notifications-collection=${FIREBASE_NOTIFICATIONS_COLLECTION:notifications}
+
# ===== WEBSOCKET =====
# WebSocket auto-dikonfigurasi oleh WebSocketConfig.java
diff --git a/walkguide-backend/demo/src/test/java/com/walkguide/service/SosServiceTest.java b/walkguide-backend/demo/src/test/java/com/walkguide/service/SosServiceTest.java
index 7bf6329..b33cb55 100644
--- a/walkguide-backend/demo/src/test/java/com/walkguide/service/SosServiceTest.java
+++ b/walkguide-backend/demo/src/test/java/com/walkguide/service/SosServiceTest.java
@@ -4,10 +4,11 @@ import com.walkguide.dto.request.SosRequest;
import com.walkguide.dto.response.SosEventResponse;
import com.walkguide.entity.PairingRelation;
import com.walkguide.entity.SosEvent;
-import com.walkguide.entity.User;
-import com.walkguide.enums.PairingStatus;
-import com.walkguide.enums.SosStatus;
-import com.walkguide.exception.ResourceNotFoundException;
+import com.walkguide.entity.User;
+import com.walkguide.enums.PairingStatus;
+import com.walkguide.enums.SosStatus;
+import com.walkguide.exception.PairingException;
+import com.walkguide.exception.ResourceNotFoundException;
import com.walkguide.repository.*;
import com.walkguide.websocket.LocationBroadcaster;
import org.junit.jupiter.api.BeforeEach;
@@ -79,10 +80,10 @@ class SosServiceTest {
req.setLat(-7.257);
req.setLng(112.752);
- when(sosEventRepository.save(any(SosEvent.class))).thenReturn(savedSos);
- when(userRepository.findById(2L)).thenReturn(Optional.of(user));
- when(pairingRelationRepository.findByUser_IdAndStatus(2L, PairingStatus.ACTIVE))
- .thenReturn(Optional.empty()); // tidak ada guardian → skip FCM
+ when(sosEventRepository.save(any(SosEvent.class))).thenReturn(savedSos);
+ when(userRepository.findById(2L)).thenReturn(Optional.of(user));
+ when(pairingRelationRepository.findByUser_IdAndStatus(2L, PairingStatus.ACTIVE))
+ .thenReturn(Optional.of(activePairing));
doNothing().when(activityLogService).createLog(any(), any(), any(), any());
SosEventResponse result = sosService.triggerSos(2L, req);
@@ -103,10 +104,10 @@ class SosServiceTest {
req.setLat(-7.257);
req.setLng(112.752);
- when(sosEventRepository.save(any(SosEvent.class))).thenReturn(savedSos);
- when(userRepository.findById(2L)).thenReturn(Optional.of(user));
- when(pairingRelationRepository.findByUser_IdAndStatus(2L, PairingStatus.ACTIVE))
- .thenReturn(Optional.empty());
+ when(sosEventRepository.save(any(SosEvent.class))).thenReturn(savedSos);
+ when(userRepository.findById(2L)).thenReturn(Optional.of(user));
+ when(pairingRelationRepository.findByUser_IdAndStatus(2L, PairingStatus.ACTIVE))
+ .thenReturn(Optional.of(activePairing));
doNothing().when(activityLogService).createLog(any(), any(), any(), any());
ArgumentCaptor captor = ArgumentCaptor.forClass(SosEvent.class);
@@ -147,12 +148,27 @@ class SosServiceTest {
SosRequest req = new SosRequest();
req.setTriggerType("MANUAL");
- when(sosEventRepository.save(any(SosEvent.class))).thenReturn(savedSos);
- when(userRepository.findById(99L)).thenReturn(Optional.empty());
-
- assertThatThrownBy(() -> sosService.triggerSos(99L, req))
- .isInstanceOf(ResourceNotFoundException.class);
- }
+ when(userRepository.findById(99L)).thenReturn(Optional.empty());
+
+ assertThatThrownBy(() -> sosService.triggerSos(99L, req))
+ .isInstanceOf(ResourceNotFoundException.class);
+ }
+
+ @Test
+ @DisplayName("triggerSos - tanpa pairing aktif: harus throw PairingException dan tidak simpan SOS")
+ void triggerSos_unpaired_shouldThrowPairingException() {
+ SosRequest req = new SosRequest();
+ req.setTriggerType("MANUAL");
+
+ when(userRepository.findById(2L)).thenReturn(Optional.of(user));
+ when(pairingRelationRepository.findByUser_IdAndStatus(2L, PairingStatus.ACTIVE))
+ .thenReturn(Optional.empty());
+
+ assertThatThrownBy(() -> sosService.triggerSos(2L, req))
+ .isInstanceOf(PairingException.class)
+ .hasMessageContaining("Guardian aktif");
+ verify(sosEventRepository, never()).save(any(SosEvent.class));
+ }
// ===== acknowledgeSos TESTS =====
diff --git a/walkguide-mobile/walkguide_app/android/app/build.gradle.kts b/walkguide-mobile/walkguide_app/android/app/build.gradle.kts
index 24d296b..cfd2b78 100644
--- a/walkguide-mobile/walkguide_app/android/app/build.gradle.kts
+++ b/walkguide-mobile/walkguide_app/android/app/build.gradle.kts
@@ -5,6 +5,10 @@ plugins {
id("dev.flutter.flutter-gradle-plugin")
}
+if (file("google-services.json").exists()) {
+ apply(plugin = "com.google.gms.google-services")
+}
+
android {
namespace = "com.example.walkguide_app"
compileSdk = flutter.compileSdkVersion
diff --git a/walkguide-mobile/walkguide_app/android/gradle.properties b/walkguide-mobile/walkguide_app/android/gradle.properties
index 7ed25f5..d80fffc 100644
--- a/walkguide-mobile/walkguide_app/android/gradle.properties
+++ b/walkguide-mobile/walkguide_app/android/gradle.properties
@@ -1,4 +1,7 @@
-org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
+org.gradle.jvmargs=-Xmx2G -XX:MaxMetaspaceSize=1G -XX:ReservedCodeCacheSize=256m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+org.gradle.workers.max=2
+org.gradle.parallel=false
+org.gradle.daemon=false
android.useAndroidX=true
android.enableJetifier=true
-kotlin.incremental=false
+kotlin.incremental=false
\ No newline at end of file
diff --git a/walkguide-mobile/walkguide_app/android/settings.gradle.kts b/walkguide-mobile/walkguide_app/android/settings.gradle.kts
index fb605bc..a907d73 100644
--- a/walkguide-mobile/walkguide_app/android/settings.gradle.kts
+++ b/walkguide-mobile/walkguide_app/android/settings.gradle.kts
@@ -21,6 +21,7 @@ plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.9.1" apply false
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
+ id("com.google.gms.google-services") version "4.4.2" apply false
}
include(":app")
diff --git a/walkguide-mobile/walkguide_app/lib/app/app.dart b/walkguide-mobile/walkguide_app/lib/app/app.dart
index ebf00e5..b72fbdd 100644
--- a/walkguide-mobile/walkguide_app/lib/app/app.dart
+++ b/walkguide-mobile/walkguide_app/lib/app/app.dart
@@ -4,13 +4,14 @@ import 'package:google_fonts/google_fonts.dart';
import 'app_cubit.dart';
import 'router.dart';
+import '../core/theme/app_colors.dart';
class WalkGuideApp extends StatelessWidget {
const WalkGuideApp({super.key});
@override
Widget build(BuildContext context) {
- const seed = Color(0xFF1A56DB);
+ const seed = AppColors.primary;
return BlocProvider(
create: (_) => AppCubit(),
@@ -23,9 +24,15 @@ class WalkGuideApp extends StatelessWidget {
colorScheme: ColorScheme.fromSeed(
seedColor: seed,
brightness: Brightness.light,
+ primary: seed,
+ secondary: AppColors.accent,
+ error: AppColors.danger,
+ ),
+ scaffoldBackgroundColor: AppColors.surface,
+ textTheme: GoogleFonts.interTextTheme().apply(
+ bodyColor: AppColors.text,
+ displayColor: AppColors.text,
),
- scaffoldBackgroundColor: const Color(0xFFF4F7FB),
- textTheme: GoogleFonts.interTextTheme(),
pageTransitionsTheme: const PageTransitionsTheme(
builders: {
TargetPlatform.android: ZoomPageTransitionsBuilder(),
@@ -35,16 +42,41 @@ class WalkGuideApp extends StatelessWidget {
),
appBarTheme: const AppBarTheme(
centerTitle: false,
- backgroundColor: Color(0xFFF4F7FB),
- foregroundColor: Color(0xFF0F172A),
+ backgroundColor: AppColors.surface,
+ foregroundColor: AppColors.text,
elevation: 0,
surfaceTintColor: Colors.transparent,
),
+ cardTheme: CardThemeData(
+ elevation: 0,
+ color: AppColors.surfaceRaised,
+ surfaceTintColor: Colors.transparent,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(8),
+ side: const BorderSide(color: AppColors.border),
+ ),
+ ),
+ dividerTheme: const DividerThemeData(
+ color: AppColors.border,
+ thickness: 1,
+ space: 1,
+ ),
+ iconButtonTheme: IconButtonThemeData(
+ style: IconButton.styleFrom(
+ foregroundColor: AppColors.text,
+ backgroundColor: Colors.white,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(8),
+ side: const BorderSide(color: AppColors.border),
+ ),
+ ),
+ ),
navigationBarTheme: NavigationBarThemeData(
elevation: 0,
height: 76,
- backgroundColor: Colors.white.withValues(alpha: 0.96),
- indicatorColor: const Color(0xFFE0E7FF),
+ backgroundColor: Colors.white,
+ indicatorColor: const Color(0xFFDDEAFE),
+ surfaceTintColor: Colors.transparent,
labelTextStyle: WidgetStateProperty.resolveWith(
(states) => TextStyle(
fontSize: 12,
@@ -61,7 +93,7 @@ class WalkGuideApp extends StatelessWidget {
minimumSize: const Size(0, 50),
textStyle: const TextStyle(fontWeight: FontWeight.w800),
shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(14),
+ borderRadius: BorderRadius.circular(10),
),
),
),
@@ -70,27 +102,38 @@ class WalkGuideApp extends StatelessWidget {
minimumSize: const Size(0, 50),
foregroundColor: seed,
textStyle: const TextStyle(fontWeight: FontWeight.w800),
- side: const BorderSide(color: Color(0xFFCBD5E1)),
+ side: const BorderSide(color: AppColors.border),
shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(14),
+ borderRadius: BorderRadius.circular(10),
),
),
),
+ snackBarTheme: SnackBarThemeData(
+ behavior: SnackBarBehavior.floating,
+ backgroundColor: AppColors.text,
+ contentTextStyle: GoogleFonts.inter(
+ color: Colors.white,
+ fontWeight: FontWeight.w600,
+ ),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(8),
+ ),
+ ),
inputDecorationTheme: InputDecorationTheme(
filled: true,
- fillColor: const Color(0xFFF8FAFC),
+ fillColor: Colors.white,
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
border: OutlineInputBorder(
- borderRadius: BorderRadius.circular(16),
- borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
+ borderRadius: BorderRadius.circular(10),
+ borderSide: const BorderSide(color: AppColors.border),
),
enabledBorder: OutlineInputBorder(
- borderRadius: BorderRadius.circular(16),
- borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
+ borderRadius: BorderRadius.circular(10),
+ borderSide: const BorderSide(color: AppColors.border),
),
focusedBorder: OutlineInputBorder(
- borderRadius: BorderRadius.circular(16),
+ borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: seed, width: 1.5),
),
),
diff --git a/walkguide-mobile/walkguide_app/lib/app/injection_container.dart b/walkguide-mobile/walkguide_app/lib/app/injection_container.dart
index d51cd1b..8550976 100644
--- a/walkguide-mobile/walkguide_app/lib/app/injection_container.dart
+++ b/walkguide-mobile/walkguide_app/lib/app/injection_container.dart
@@ -10,6 +10,7 @@ import '../core/services/haptic_service.dart';
import '../core/services/call_service.dart';
import '../core/services/fcm_service.dart';
import '../core/services/hardware_shortcut_listener.dart';
+import '../core/services/incoming_call_polling_service.dart';
import '../core/services/location_reporter_service.dart';
import '../core/services/offline_queue_service.dart';
import '../core/services/stt_service.dart';
@@ -39,17 +40,24 @@ Future initDependencies() async {
sl.registerLazySingleton(() => SttService());
sl.registerLazySingleton(() => HapticService());
sl.registerLazySingleton(
- () => TtsWithHapticObstacleAlertStrategy(sl(), sl()),
+ () => TtsWithHapticObstacleAlertStrategy(
+ sl(), sl()),
);
sl.registerLazySingleton(() => ObstacleAnalyzer());
- sl.registerLazySingleton(() => YoloDetector(sl()));
+ sl.registerLazySingleton(
+ () => YoloDetector(sl()));
sl.registerLazySingleton(
() => OfflineQueueService(sl()),
);
sl.registerLazySingleton(() => FcmService(sl()));
- sl.registerLazySingleton(() => WebSocketService(sl()));
- sl.registerLazySingleton(() => LocationReporterService(sl(), sl()));
+ sl.registerLazySingleton(
+ () => WebSocketService(sl()));
+ sl.registerLazySingleton(() =>
+ LocationReporterService(sl(), sl()));
sl.registerLazySingleton(() => CallService(sl()));
+ sl.registerLazySingleton(
+ () => IncomingCallPollingService(sl()),
+ );
sl.registerLazySingleton(
() => HardwareShortcutListener(sl()),
);
@@ -59,8 +67,10 @@ Future initDependencies() async {
sl.registerLazySingleton(
() => WalkGuideRepositoryImpl(sl(), sl()),
);
- sl.registerFactory(() => WalkGuideCubit(sl()));
- sl.registerLazySingleton(() => SosRepositoryImpl(sl()));
+ sl.registerFactory(
+ () => WalkGuideCubit(sl()));
+ sl.registerLazySingleton(
+ () => SosRepositoryImpl(sl()));
sl.registerFactory(() => SosCubit(sl()));
sl.registerLazySingleton(
() => NotificationRepositoryImpl(sl(), sl()),
diff --git a/walkguide-mobile/walkguide_app/lib/app/router.dart b/walkguide-mobile/walkguide_app/lib/app/router.dart
index 5eada23..a022a66 100644
--- a/walkguide-mobile/walkguide_app/lib/app/router.dart
+++ b/walkguide-mobile/walkguide_app/lib/app/router.dart
@@ -29,7 +29,8 @@ import '../features/navigation_mode/presentation/screens/navigation_mode_screen.
as nav;
import '../features/notifications/presentation/screens/notification_screen.dart'
as notifications;
-import '../features/pairing/presentation/screens/pairing_screens.dart' as pairing;
+import '../features/pairing/presentation/screens/pairing_screens.dart'
+ as pairing;
import '../features/server_connect/server_connect_server.dart'
as server_connect;
import '../features/settings/presentation/screens/user_settings_screen.dart'
@@ -96,7 +97,17 @@ final GoRouter appRouter = GoRouter(
builder: (_, __) => const auth_register.RegisterScreen()),
GoRoute(
path: '/incoming-call',
- builder: (_, __) => const call.IncomingCallScreen()),
+ builder: (_, state) {
+ final extra = state.extra is Map
+ ? Map.from(state.extra as Map)
+ : {};
+ return call.IncomingCallScreen(
+ callerName: extra['callerName']?.toString() ?? 'Guardian',
+ callerId: int.tryParse(extra['callerId']?.toString() ?? ''),
+ channelName: extra['channelName']?.toString(),
+ agoraToken: extra['agoraToken']?.toString(),
+ );
+ }),
ShellRoute(
builder: (_, __, child) => UserShell(child: child),
routes: [
@@ -161,6 +172,12 @@ final GoRouter appRouter = GoRouter(
path: '/guardian/settings',
builder: (_, __) =>
const guardian_settings.GuardianSettingsScreen()),
+ GoRoute(
+ path: '/guardian/call',
+ builder: (_, __) => const call.CallScreen(
+ targetLabel: 'User',
+ returnRoute: '/guardian/dashboard',
+ )),
GoRoute(
path: '/guardian/benchmark',
builder: (_, __) => const benchmark.AiBenchmarkScreen()),
diff --git a/walkguide-mobile/walkguide_app/lib/core/constants/app_constants.dart b/walkguide-mobile/walkguide_app/lib/core/constants/app_constants.dart
index cc6cbbf..c6d2de0 100644
--- a/walkguide-mobile/walkguide_app/lib/core/constants/app_constants.dart
+++ b/walkguide-mobile/walkguide_app/lib/core/constants/app_constants.dart
@@ -61,7 +61,7 @@ class AppConstants {
await prefs.setString(_selectedYoloModelKey, path);
}
- // Agora App ID diisi saat build: --dart-define=AGORA_APP_ID=...
- static const String agoraAppId =
- String.fromEnvironment('AGORA_APP_ID', defaultValue: '');
+ // Agora App ID tetap bisa dioverride saat build: --dart-define=AGORA_APP_ID=...
+ static const String agoraAppId = String.fromEnvironment('AGORA_APP_ID',
+ defaultValue: 'e36c2b6592e34cfda1f6ea6432a5e68d');
}
diff --git a/walkguide-mobile/walkguide_app/lib/core/errors/friendly_error.dart b/walkguide-mobile/walkguide_app/lib/core/errors/friendly_error.dart
index 14b264f..4334958 100644
--- a/walkguide-mobile/walkguide_app/lib/core/errors/friendly_error.dart
+++ b/walkguide-mobile/walkguide_app/lib/core/errors/friendly_error.dart
@@ -71,6 +71,10 @@ bool _looksTechnical(String message) {
'null check operator',
'nosuchmethod',
'formatexception',
+ 'could not execute statement',
+ 'duplicate key',
+ 'constraint',
+ 'sql [',
];
return blocked.any(lower.contains);
}
diff --git a/walkguide-mobile/walkguide_app/lib/core/services/call_service.dart b/walkguide-mobile/walkguide_app/lib/core/services/call_service.dart
index c52e5f3..06b59c7 100644
--- a/walkguide-mobile/walkguide_app/lib/core/services/call_service.dart
+++ b/walkguide-mobile/walkguide_app/lib/core/services/call_service.dart
@@ -1,5 +1,8 @@
+import 'dart:async';
+
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:flutter/foundation.dart';
+import 'package:permission_handler/permission_handler.dart';
import '../constants/app_constants.dart';
import '../network/api_client.dart';
@@ -7,9 +10,19 @@ import '../network/api_client.dart';
class CallService {
final ApiClient _apiClient;
RtcEngine? _engine;
+ VoidCallback? _onRemoteUserJoined;
+ VoidCallback? _onRemoteUserOffline;
CallService(this._apiClient);
+ void setRemoteUserJoinedCallback(VoidCallback? callback) {
+ _onRemoteUserJoined = callback;
+ }
+
+ void setRemoteUserOfflineCallback(VoidCallback? callback) {
+ _onRemoteUserOffline = callback;
+ }
+
Future