Skip to content

Shippy is a web-based command center linking ship owners, port managers, administrators, and public stakeholders around shared maritime data. It offers authenticated dashboards, moderation workflows, and real-time ETA visibility, with a public lookup for casual visitors.

License

Notifications You must be signed in to change notification settings

aami-dodon/Shippy

Shippy

Shippy is a web-based command center that connects ship owners, port managers, administrators, and public stakeholders around shared maritime data. The platform delivers authenticated dashboards, moderation workflows, and real-time ETA visibility while keeping a public lookup available for casual visitors.

Functional scope

  • Ship owners register and maintain detailed vessel records (type, IMO number, capacities, current position).
  • Port managers create and update port directories with geographic coordinates and operational status.
  • Administrators moderate both domains, approve submissions, and enforce data quality.
  • All authenticated personas query the ETA service while the public can run read-only lookups.
  • Every mutating action generates audit trails and notification payloads.

Demo Website

https://shippy.dodon.in

Screenshot

Landing Page

Mobile App

Expo Go

  • Install Expo Go on your iOS or Android device.
  • Open Expo Go and scan the QR code above to launch (using your camera for IOS) the published Shippy mobile app demo.

Implemented features

  • 🔐 JWT authentication & RBAC for Admin, Ship Owner, Port Manager, and Viewer personas using a shared React context.
  • 🧭 Self-service registration so ship owners, port managers, and viewers can onboard themselves and receive a JWT instantly.
  • 🚢 Ship management UI + API with creation, editing, status transitions, and admin approval controls.
  • Port management UI + API mirroring the ship experience with geographic coordinate capture.
  • ⏱️ ETA integration that calls the external adapter, caches results, and exposes both private and public lookups with live arrival status reporting.
  • 🛡️ Admin moderation hub showing pending records with approve/delete actions and an audit history view.
  • ✉️ Notification hooks that log outgoing email events (confirmation, approval, admin alerts) via the demo email client.

Getting started

Install dependencies

npm install --prefix apps/server
npm install --prefix apps/web
npm install --prefix apps/mobile

API (Express)

npm run dev --prefix apps/server

The API listens on http://localhost:4000.

Web dashboard (Vite)

npm run dev --prefix apps/web

The web app runs at http://localhost:5173.

Mobile app (Expo)

npm run start --prefix apps/mobile

Expo Dev Tools open in your browser; scan the QR code with Expo Go or press i/a to launch the iOS and Android simulators. Create apps/mobile/.env to override defaults:

EXPO_PUBLIC_API_BASE_URL=http://localhost:4000/api
EXPO_PUBLIC_SUPPORT_EMAIL=support@shippy.test

Use your LAN IP in EXPO_PUBLIC_API_BASE_URL when testing on a physical device (http://<LAN-IP>:4000/api) and make sure the API is running first.

Sharing via Expo Go

  1. Install the EAS CLI if needed: npm install -g eas-cli (or run the commands with npx eas update ...).
  2. Authenticate once with eas login.
  3. Publish an update: eas update --branch production --message "initial publish".
  4. Share the link printed by the CLI or copy it from https://expo.dev/accounts/<account>/projects/<slug>.

Environment variables are documented in .env.example. The client relies on VITE_API_BASE_URL; the API expects PORT, ALLOWED_ORIGINS, JWT_SECRET, ETA_API_BASE_URL, ETA_API_TIMEOUT (in milliseconds), ETA_API_ENDPOINT_PATH (defaults to /getETA), and the ship location sync variables detailed below.

  • For local npm workflows, change VITE_API_BASE_URL to http://localhost:4000/api and access the React dev server at http://localhost:5173.
  • When building for Docker Compose, leave the default VITE_API_BASE_URL=http://localhost:9002/api so the browser hits the host-mapped API port while the Express app continues to listen on 4000 inside the container.
  • ALLOWED_ORIGINS already whitelists both http://localhost:5173 (npm) and http://localhost:9003 (Docker Compose).
  • The Expo mobile client reads EXPO_PUBLIC_API_BASE_URL; point it at http://<LAN-IP>:4000/api when running on a physical device so the app reaches your dev server.

Running with Docker

The repository includes a production-oriented Docker setup for both the API and the client. Copy the sample environment file so Docker Compose can load the configuration for each service:

cp .env.example .env

Before building, confirm .env retains the Docker default (VITE_API_BASE_URL=http://localhost:9002/api). The compose file exposes host ports 9002 and 9003 while the API itself keeps listening on 4000; the client build only needs the host-facing API URL to line up with that mapping. Switch back to http://localhost:4000/api only when you return to npm-based development.

Build and launch the stack:

docker compose up --build
  • The API will be available at http://localhost:9002 on the host while continuing to run on port 4000 inside the container.
  • The React client is published at http://localhost:9003, leaving the Vite dev default (5173) untouched for npm-based workflows.
  • Update .env whenever you need to change ports or downstream integrations; both containers reuse the same file.

Stop the stack with docker compose down.

Ship location sync service

A background worker refreshes ship locations every five minutes by calling the configured location provider. The job starts automatically when the API boots and can be disabled or tuned via environment variables.

Configuration

Variable Description Example
SHIP_LOCATION_SYNC_ENABLED Enables (true) or disables (false) the background updater. Defaults to true. false
SHIP_LOCATION_SYNC_INTERVAL_MS Override for the polling cadence in milliseconds. Defaults to 300000 (five minutes). 120000
SHIP_LOCATION_API_BASE_URL Base URL for the upstream provider serving live positions. https://location.example.com
SHIP_LOCATION_API_ENDPOINT_PATH Path appended to the base URL for the request. Defaults to /location. /v2/location
SHIP_LOCATION_API_TIMEOUT Timeout in milliseconds applied to outbound HTTP calls. Defaults to 5000. 8000

The external API must accept a GET request with the query parameter imoNumber=<IMO_number> and return either a currentPosition string or latitude/longitude values. Responses lacking usable coordinates are ignored with a warning log.

ETA API integration

The ETA module calls an external HTTP API to resolve the expected arrival time for a ship/port pair. The integration is fully driven by environment variables so that different providers can be plugged in without code changes.

Configuration

Variable Description Example
ETA_API_BASE_URL Base URL of the upstream ETA provider. https://eta.example.com
ETA_API_ENDPOINT_PATH Path appended to the base URL when performing lookups. Defaults to /getETA. /getETA
ETA_API_TIMEOUT Request timeout in milliseconds applied to the outbound call. Defaults to 5000. 8000

Request payload

  • HTTP method: GET
  • URL: ${ETA_API_BASE_URL}${ETA_API_ENDPOINT_PATH}
  • Query parameters:
    • shipId – UUID for the ship requesting an ETA.
    • portId – UUID for the destination port.

Example request:

GET https://eta.example.com/getETA?shipId=37d1d0a5-1dbf-4b58-9d1a-5a2d4b92eb0c&portId=1d639a1f-86b1-4d72-8fa5-536f8ae12b2a

Expected response

The provider should respond with a JSON payload containing the calculated ETA, the most recent actual arrival (when available), and an arrival status of delayed, on_time, or early so clients can present accurate messaging. Any additional metadata is ignored by Shippy, but it can be present.

{
  "shipId": "37d1d0a5-1dbf-4b58-9d1a-5a2d4b92eb0c",
  "portId": "1d639a1f-86b1-4d72-8fa5-536f8ae12b2a",
  "scheduledArrival": "2024-03-25T12:00:00.000Z",
  "expectedArrival": "2024-03-25T14:30:00.000Z",
  "actualArrival": "2024-03-25T13:58:00.000Z",
  "arrivalStatus": "early",
  "source": "provider-name"
}

If the upstream service is unavailable, Shippy will fall back to a simulated ETA three hours in the future and cache the response for 15 minutes. The final API response delivered to clients always includes shipId, portId, scheduledArrival (when supplied by the provider), expectedArrival, actualArrival (when known), arrivalStatus, and lastUpdated.

Seeded demo accounts

Role Email Password
Admin admin@shippy.test Password123!
Ship Owner owner@shippy.test Password123!
Port Manager manager@shippy.test Password123!
Viewer viewer@shippy.test Password123!

Viewer accounts have read-only access and are redirected to the public ETA workspace.


Data contracts

Ship

Field Type Notes
id UUID Primary key
name string Required
type string Required
IMO_number string Unique
DWT number Deadweight tonnage
capacity number Cargo capacity
ownerId UUID References User.id
currentPosition string Lat/long or port code
status enum(pending,approved,inactive,under_maintenance) Workflow state
createdAt / updatedAt ISO string Audit timestamps

Port

Field Type Notes
id string External port identifier
name string Required
country string Required
coordinates object { latitude: number, longitude: number }
capacity number? Optional throughput
managerId UUID References User.id
status enum(pending,operational,closed,under_maintenance) Operational posture
createdAt / updatedAt ISO string Audit timestamps

ETA record

Field Type Notes
shipId UUID References Ship.id
portId UUID References Port.id
scheduledArrival ISO string | null Timetabled arrival when provided by upstream
expectedArrival ISO string Calculated ETA
actualArrival ISO string | null Actual arrival reported by provider
lastUpdated ISO string Cache timestamp

Audit log entry

Field Type Notes
id UUID Primary key
entityType string ship or port
entityId UUID Target entity
action string create, update, approve, delete
actorId UUID References User.id
timestamp ISO string Event time
changes object? Diff payload

REST API overview

All authenticated endpoints require Authorization: Bearer <token>.

Authentication

  • POST /api/auth/login
  • POST /api/auth/register
  • GET /api/auth/roles

Ships

  • GET /api/ships (supports ?status=)
  • GET /api/ships/:id
  • POST /api/ships
  • PUT /api/ships/:id
  • PATCH /api/ships/:id/approve
  • DELETE /api/ships/:id

Ports

  • GET /api/ports (supports ?status=)
  • GET /api/ports/:id
  • POST /api/ports
  • PUT /api/ports/:id
  • PATCH /api/ports/:id/approve
  • DELETE /api/ports/:id

ETA

  • GET /api/eta?shipId={id}&portId={id} – authenticated lookup
  • GET /api/public/eta?shipId={id}&portId={id} – public lookup

Public catalogue

  • GET /api/public/ship/:id
  • GET /api/public/port/:id

Audit

  • GET /api/audit

Health

  • GET /health

Client routes

Route Access Description
/ Auth & public Landing for guests, metrics dashboard for authenticated users
/login Public Email/password login with demo shortcuts
/ships Ship Owner, Admin Ship management workspace
/ports Port Manager, Admin Port management workspace
/eta Ship Owner, Port Manager, Admin Authenticated ETA lookup
/admin Admin Moderation queues
/audit Admin Audit trail viewer
/public/eta Public Read-only ETA search

Notifications & metrics

  • Ship submission confirmation (queued email notification).
  • Port submission confirmation (queued email notification).
  • Admin alert for new pending records (queued notification).
  • Metrics tracked in-memory: number of ships/ports, pending counts, ETA cache hits.

Tech stack

Frontend

  • React 18 + Vite + Material UI.
  • React Router DOM with a custom RequireAuth guard.
  • Native fetch helper for REST integration.
  • Auth state persisted via a custom context provider.

Backend

  • Node.js + Express with modular routers per domain.
  • JWT authentication, bcrypt password hashing, and RBAC middleware.
  • In-memory seed store (Prisma + PostgreSQL planned for persistence).
  • Winston + Morgan structured logging and rate limiting via express-rate-limit.
  • Axios-based ETA integration client with graceful degradation.
  • Vitest + Supertest integration tests.

Cross-cutting concerns

  • Shared type documentation in packages/types for ships, ports, audit entries, and ETA payloads.
  • Centralized audit log service feeding the /api/audit endpoint.
  • Demo email client capturing notifications for testing.

Roadmap

  1. Swap the in-memory store for Prisma/PostgreSQL and wire Swagger documentation.
  2. Extend email notifications to a real provider and add user-managed subscriptions.
  3. Introduce analytics dashboards (ETA success rates, lookup trends).
  4. Add React Testing Library coverage for core UI workflows.

About

Shippy is a web-based command center linking ship owners, port managers, administrators, and public stakeholders around shared maritime data. It offers authenticated dashboards, moderation workflows, and real-time ETA visibility, with a public lookup for casual visitors.

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  

Languages