A Next.js application with three runtime surfaces in one codebase:
| Surface | Routes | Purpose |
|---|---|---|
| Frontend portal | /login, /calendar, /day/*, /analysis, /my-entries, /profile |
Stakeholder-facing LNG discharge planning portal for active viewer and company_admin users. |
| Backend admin console | /admin/login, /admin/* |
Hosting-company administration console for active superadmin users only. |
| Backend API | /api/* |
Server-side operational endpoints, including public data synchronization. |
# Install dependencies
npm install
# Copy environment variables and fill in your Supabase credentials
cp .env.example .env.local
# Run the application in development
npm run devThe development server defaults to http://localhost:3000.
- Required Node.js version: 20.19.x (exact tested major/minor line).
- Local development: use
.nvmrc(nvm use) to align your shell. - CI: pin the same
20.19.xline in your Node setup step. - Vercel: set Project Settings → Node.js Version to 20.x so build/runtime stay aligned with
package.jsonengines.
- Create a Supabase project at https://supabase.com.
- Run the migrations in
supabase/migrations/in order against your project. - Enable Storage and create a public bucket named
branding. - Enable Row Level Security on all tables.
Test users are created with the Supabase Admin API so credentials are not committed into migration history or production schema changes. The script is idempotent: it creates or updates the Auth user, the matching profiles row, and a reusable LNG Test Company record for the frontend portal account.
ALLOW_TEST_USER_SEEDING=true \
NEXT_PUBLIC_SUPABASE_URL="https://YOUR_PROJECT.supabase.co" \
SUPABASE_SERVICE_ROLE_KEY="YOUR_SERVICE_ROLE_KEY" \
npm run seed:test-usersDefault non-production accounts created by the script:
| Account | Default password | Role | Expected access | |
|---|---|---|---|---|
| Frontend test user | frontend.test@lng.local |
PortalTest!2026 |
viewer |
Frontend portal only |
| Backend admin test user | admin.test@lng.local |
AdminTest!2026 |
superadmin |
Backend admin console only |
You can override the defaults without changing code:
TEST_PORTAL_EMAIL="portal.user@example.com" \
TEST_PORTAL_PASSWORD="replace-with-a-strong-password" \
TEST_ADMIN_EMAIL="admin.user@example.com" \
TEST_ADMIN_PASSWORD="replace-with-a-strong-password" \
ALLOW_TEST_USER_SEEDING=true \
NEXT_PUBLIC_SUPABASE_URL="https://YOUR_PROJECT.supabase.co" \
SUPABASE_SERVICE_ROLE_KEY="YOUR_SERVICE_ROLE_KEY" \
npm run seed:test-usersDatabase history note: No SQL migration is required for these test users. They are environment-specific operational data. Keep this script restricted to non-production projects unless you intentionally want these accounts in that environment.
| Role | Access |
|---|---|
superadmin |
Backend admin console only; full branding, company, user, audit, and system management. |
company_admin |
Frontend portal; can manage own company's users where supported by RLS/API flows. |
viewer |
Frontend portal; can read public data and add own private entries. |
Access is enforced in multiple layers:
middleware.tsvalidates authenticated users with SupabasegetUser()and checks bothprofiles.roleandprofiles.is_activebefore allowing protected routes.- Login pages call the shared auth context with route-specific allowed roles, so users receive immediate feedback when attempting to sign into the wrong surface.
- Supabase RLS policies in
supabase/migrations/enforce database-level access by role and company.
The app can ingest public schedule data into cruise_schedules and flights via:
POST /api/sync/public-data(manual trigger from Admin → System Settings)- A cron trigger using
Authorization: Bearer ${SYNC_CRON_TOKEN} - Vercel cron is configured in
vercel.jsonto run daily at noon UTC (0 12 * * *). - Schedule is defined strictly in UTC; local execution time will shift with timezone and DST changes.
Configure these environment variables:
SUPABASE_SERVICE_ROLE_KEYSYNC_CRON_TOKENCRON_SECRETis also accepted (useful for Vercel cron auth header)
GIBRALTAR_CRUISE_SCHEDULE_URL(optional override)GIBRALTAR_AIRPORT_FLIGHTS_URL(optional override)AVIATIONSTACK_API_KEY(optional fallback if no key is saved in System Settings)FREE_FLIGHT_API_URL(optional override)
aviation_api_key is managed from System Settings and is used for free flight API ingestion when provided. If that setting is blank, the sync falls back to AVIATIONSTACK_API_KEY.
- Manual ferry schedules and operational events are now supported alongside flights and cruises.
- Planned LNG discharges can be created/updated with vessel, date, target alongside time, quantity, status, and notes.
- Calendar suitability now uses traffic blocking + weather safety scoring (
Green/Orange/Red) with day-level reason codes. - Event/discharge create/update/delete operations are written to
audit_logsthrough database triggers.
Planning warnings are evaluated against the operational limits provided by terminal guidance:
- Berthing limits: Hs > 1.0 m or wind > 10 m/s.
- Alongside directional limits (wave direction + period bands 5s/8s/10s) are applied for additional stop conditions.
- Configure environment variables in
.env.example(Supabase + sync auth keys). - Confirm
app_settingssingleton row exists and timezone is set correctly. - Run all migrations in order (
001,002,003) before first production deploy. - Verify daily sync execution (
/api/sync/public-data) and audit log entries for completed/skipped/failed runs. - Validate UAT scenarios for owner/admin, terminal user, and barge operator workflows.
- Confirm backup/restore process for Supabase project and retention policy for
audit_logs. - Set monitoring alerts for sync failures, auth failures, and sustained weather API errors.
lng-discharge/
src/app/ Next.js App Router pages and API routes
src/components/ Shared UI, portal, and admin components
src/contexts/ Client-side providers and auth state
src/lib/ Supabase clients, shared types, role helpers, utilities
scripts/ Operational scripts such as test-user seeding
supabase/migrations/ PostgreSQL schema, RLS policies, and migration history