6.5 KiB
6.5 KiB
Fahrtenbuch - Multi-Target Scala.js Application
A collaborative trip tracking application built with Scala.js, supporting both browser and Node.js environments with real-time peer-to-peer synchronization.
Architecture
This project uses a multi-target architecture with shared business logic and platform-specific implementations:
fahrtenbuch/
├── shared/ # Shared business logic and models
│ └── src/main/scala/fahrtenbuch/
│ ├── model/ # Data models (Entry, EntryId)
│ ├── core/ # Business logic (EntryManager)
│ ├── storage/ # Storage interface
│ └── sync/ # Synchronization interface
├── browser/ # Browser-specific implementation
│ └── src/main/scala/fahrtenbuch/
│ ├── components/ # Laminar UI components
│ ├── storage/ # Dexie (IndexedDB) storage
│ ├── sync/ # Trystero (WebRTC) sync
│ └── BrowserMain.scala
├── nodejs/ # Node.js-specific implementation
│ └── src/main/scala/fahrtenbuch/
│ ├── storage/ # File system storage
│ ├── sync/ # WebSocket sync
│ └── NodeMain.scala
└── dist/ # Build outputs
├── browser/ # Browser build artifacts
└── nodejs/ # Node.js build artifacts
Features
Shared Features
- CRDT-based synchronization: Conflict-free replicated data types ensure consistent state across peers
- Real-time sync: Changes are automatically synchronized between connected peers
- Offline support: Works offline with automatic sync when reconnected
- Trip tracking: Track vehicle trips with distance, cost calculations, and payment status
Browser Features
- Modern UI: Built with Laminar and Bulma CSS framework
- IndexedDB storage: Persistent local storage using Dexie
- WebRTC P2P: Direct peer-to-peer communication via Trystero
- Real-time updates: Live UI updates as data changes
Node.js Features
- Command-line interface: Interactive CLI for managing entries
- File system storage: JSON-based persistent storage
- WebSocket server: Acts as a hub for peer communication
- Statistics reporting: Built-in analytics and reporting
Getting Started
Prerequisites
- Scala: 3.7.1
- Node.js: 16+
- sbt: 1.8+
Installation
- Clone the repository:
git clone <repository-url>
cd fahrtenbuch
- Install dependencies:
npm install
- Build both targets:
npm run build
Development
Browser Development
# Start development server with hot reload
npm run dev
# Build browser version only
npm run build:browser
# Production build
npm run build:browser:prod
Node.js Development
# Build Node.js version
npm run build:nodejs
# Run Node.js application
npm run start:nodejs
# With custom options
node dist/nodejs/main.js --port 8080 --data-dir ./data
SBT Commands
# Compile shared code
sbt sharedJs/compile
# Fast build for browser
sbt browser/fastOptJS
# Optimized build for browser
sbt browser/fullOptJS
# Fast build for Node.js
sbt nodejs/fastOptJS
# Optimized build for Node.js
sbt nodejs/fullOptJS
# Build everything
sbt compile
Usage
Browser Application
- Build and start the browser version:
npm run build:browser
npm run dev
- Open your browser and navigate to the displayed URL
- Share the URL (including the hash) with other users for real-time collaboration
Node.js Application
- Build and start the Node.js version:
npm run build:nodejs
node dist/nodejs/main.js --port 8080
- Use the interactive CLI:
fahrtenbuch> help
fahrtenbuch> add 1000 1050 John Dog
fahrtenbuch> list
fahrtenbuch> stats
fahrtenbuch> peers
- Connect multiple instances:
# Start first instance
node dist/nodejs/main.js --port 8080
# Start second instance and connect to first
node dist/nodejs/main.js --port 8081 --connect localhost:8080
Configuration
Node.js Options
--port <port>: WebSocket server port (default: 8080)--data-dir <dir>: Data storage directory (default: ./data)--connect <host:port>: Connect to peer at host:port--help: Show help message
Browser Configuration
The browser version uses URL fragments for room identification. Users sharing the same URL fragment will be connected in the same sync room.
Data Model
Entry
Each trip entry contains:
id: Unique identifierstartKm: Starting odometer readingendKm: Ending odometer readingdriver: Driver nameanimal: Animal transportedpaid: Payment statusdate: Entry creation dategasPricePerKm: Gas cost per kilometerwearPricePerKm: Wear cost per kilometer
Synchronization
- Uses CRDT (Conflict-free Replicated Data Types) for conflict resolution
- Last-writer-wins semantics for most fields
- Automatic merging of concurrent updates
- Peer-to-peer synchronization without central server
Storage
Browser Storage
- IndexedDB: Via Dexie library
- Schema versioning: Automatic migrations
- Live queries: Real-time UI updates
Node.js Storage
- File system: JSON files in data directory
- Atomic writes: Safe concurrent access
- Backup friendly: Human-readable JSON format
Networking
Browser Networking
- WebRTC: Direct peer-to-peer via Trystero
- TURN/STUN: Configurable relay servers
- Room-based: URL hash determines sync room
Node.js Networking
- WebSocket: Server-client architecture
- Auto-discovery: Automatic peer detection
- Hub model: Can act as relay for browser clients
Development Tips
Adding New Features
- Add shared logic to
shared/src/main/scala/fahrtenbuch/ - Implement platform-specific parts in
browser/andnodejs/ - Update interfaces in
storage/andsync/packages - Test both targets with
npm run build
Debugging
- Browser: Use browser dev tools, network tab for WebRTC
- Node.js: Use
console.logor Node.js debugging tools - Storage: Check browser IndexedDB or Node.js data directory
Performance
- Use
fullOptJSfor production builds - Browser builds are optimized for incremental loading
- Node.js builds are optimized for startup time
Contributing
- Fork the repository
- Create a feature branch
- Make changes to shared code and both platform implementations
- Test both browser and Node.js targets
- Submit a pull request
License
[Add your license here]