- Scala 95.5%
- Shell 2.8%
- Dockerfile 1.7%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
`trip.distance` was purely declarative: whoever booked the car typed in how far they drove, nothing checked it, and 4 of the 22 existing reservations have NULL there. The tracker has been logging since 2026-06-19, so the real figure is knowable. A pass now records it in new gps_* columns beside the declared one, which is never overwritten — /api/trips/total and the ICS feed are untouched. The measured window is deliberately NOT the declared start/end: people take the car early and bring it back late, and measuring [12:00, 12:00] silently loses those kilometres. So a reservation is widened to its whole days in Europe/Paris and then clamped to the midpoint of the gap with the nearest non-overlapping neighbour. A lone midday booking is measured 00:00–24:00; two bookings sharing a day (08:00–12:00 and 14:00–20:00) split it at 13:00 and their windows touch exactly, so every kilometre is counted once and none is lost. That rests on a property of Traccar verified against the live server: its reports/summary clamps at the window bounds, so adjacent windows partition the distance exactly. Splitting 2026-08-17 into halves gave 100 + 156000 = 156100 m, the full day, and the afternoon into thirds gave 72700 + 83300 + 0 = 156000. Design notes: - reports/summary beats both alternatives. Against fetching raw positions and haversine-summing: one small response instead of thousands of rows, the device's own odometer instead of straight lines between sparse fixes, and no exposure to the streamed-body hazard TraccarCarLocationService documents. Against reports/trips: that sums 100 m lower on the 17th, because its trip detection drops sub-threshold movement. - `Accept: application/json` is mandatory on the report endpoints. Without it Traccar answers 200 with an XLSX spreadsheet, which then fails to decode as JSON for reasons that look nothing like the cause. The header now lives in TraccarHttp, extracted from fetchPositions so the load-bearing "read the body inside ZIO.scoped" constraint exists in exactly one place. - CarDistanceService is a new trait rather than a method on CarLocationService: that one answers "where is the car", this one "how far did it go", and keeping them apart leaves the parking tests' FakeCarLocationService untouched. - Only an answer from Traccar stamps gps_computed_at. An empty report (tracker dark) is an answer, so it is stamped with no distance and not retried; an unreachable Traccar writes nothing, so the next pass picks the reservation up. - The "is it finished" test is on the widened window end, not end_date — widening pushes the window past midnight, so a booking that ended at noon today is not measurable until tomorrow. - saveGpsDistance writes only the gps_* columns: no `updated_at = NOW()`, so a measurement never bumps the ICS LAST-MODIFIED stamp, and updateTrip's explicit column list means a client PUTting a Trip cannot spoof a measurement. - The pass reads the whole trip list on purpose: clamping needs the neighbours on either side, including already-measured ones and ones before the cut-off. - First background fiber in the app. catchAllCause sits inside repeat so a Traccar outage costs one pass, not the fiber for the process lifetime. Tests: 8 cases on the window rule (whole-day widening, same-day midpoint split with touching seams, the existing 00:00–23:59 midnight handover losing only 30 s, multi-day, overlapping bookings left unclamped, no neighbour, never collapsing), 10 on the pass (cut-off, unclosed window, the widened window actually being queried, declared value left alone, no re-measure, dark tracker stamped once, failure retried, force), and 5 on Postgres over the new test_trip_gps schema including that updated_at does not move and updateTrip cannot overwrite. Verified end to end against the live tracker: two bookings sharing 2026-08-17 came out 0 km and 156 km over windows meeting at 11:00Z — the day's exact total, with both declared values untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Reviewed-on: #2 |
||
| .github/workflows | ||
| .junie | ||
| domain | ||
| domain-test/src | ||
| native-config | ||
| remote-storage | ||
| remote-storage-test/src | ||
| remote-tracking/src/scala/traccar | ||
| repository-postgres/src | ||
| repository-test-postgres/src | ||
| spike | ||
| test-support/src/scala/zio/test/sbt | ||
| web/src | ||
| .env.exemple | ||
| .gitignore | ||
| .nixpacks.toml | ||
| .scalafix.conf | ||
| .scalafmt.conf | ||
| .sdkmanrc | ||
| AGENTS.md | ||
| bleep.yaml | ||
| CLAUDE.md | ||
| CONFIGURATION_CHANGES.md | ||
| docker-compose.yml | ||
| Dockerfile | ||
| GEMINI.md | ||
| NATIVE_IMAGE_SPIKE.md | ||
| qodana.yaml | ||
| README.md | ||
| REFACTORING_SUGGESTIONS.md | ||
| test.pdf | ||
Car Sharing Backend
This project is a Scala 3 application using PostgreSQL for database management and Bleep for build automation. Test
Prerequisites
- Scala 3
- PostgreSQL
- Bleep
Setup
Install PostgreSQL
Install PostgreSQL on your system and create a database:
createdb car
Install Bleep
Follow the Bleep installation guide to install Bleep on your system.
Configuration
-
PostgreSQL Configuration:
Ensure that PostgreSQL is running and properly configured. Set the following environment variables:
export DATABASE_URL="jdbc:postgresql://localhost:5432/car" export DATABASE_USER="myuser" export DATABASE_PASSWORD="mypassword" -
Application Configuration:
The application uses ZIO Config to load configuration from
application.conf.To set up your configuration:
-
Copy the example configuration file:
cp domain/src/resources/application.conf.example domain/src/resources/application.conf -
Edit the
application.conffile with your settings:- WebDAV/NextCloud connection settings (username, password, URL)
- Authentication settings (secret key, token expiration)
-
For production, you can set environment variables instead:
NEXTCLOUD_USERNAME- Your NextCloud usernameNEXTCLOUD_PASSWORD- Your NextCloud passwordNEXTCLOUD_URL- Your NextCloud URLNEXTCLOUD_BASE_PATH- Base path for files (default: "voiture")AUTH_SECRET_KEY- Secret key for JWT token generationAUTH_TOKEN_EXPIRATION_SECONDS- Token expiration time in seconds
Note: The
application.conffile is excluded from version control to prevent committing sensitive data. -
-
Bleep Configuration:
Initialize Bleep in your project directory:
bleep new
Usage
Compile the Project
To compile the project, run:
bleep compile web
Run the Project
To run the project, use:
bleep run web
Test the Project
To execute tests, run:
bleep test repository-test-postgres
Project Structure
domain/: Core business logic, models, service traitsrepository-postgres/: PostgreSQL persistence using Doobie + Flywayremote-storage/: WebDAV/NextCloud file storageweb/: HTTP API layer using Tapirbleep.yaml: Bleep build configuration file
Database Migrations
This project uses Flyway for database migrations. Migration files are located in repository-postgres/src/resources/db/migration/.
Migrations are automatically applied on application startup.
Additional Resources
- Scala 3 Documentation
- PostgreSQL Documentation
- Bleep Documentation
- Doobie Documentation
- Flyway Documentation
License
This project is licensed under the MIT License. See the LICENSE file for details.