Alpha stage commit

This commit is contained in:
2026-07-02 21:04:05 +02:00
parent c03b183dfb
commit abed21be21
136 changed files with 15531 additions and 15 deletions

196
schemas/database.sql Normal file
View File

@@ -0,0 +1,196 @@
-- BikepackPilot database draft
-- Requires PostgreSQL + PostGIS.
CREATE EXTENSION IF NOT EXISTS postgis;
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE TYPE confidence_level AS ENUM (
'known_good', 'high', 'medium', 'low', 'unknown', 'restricted', 'conflicting'
);
CREATE TYPE warning_severity AS ENUM ('info', 'notice', 'warning', 'critical');
CREATE TYPE poi_category AS ENUM (
'water', 'food', 'sleep', 'repair', 'bailout', 'charging', 'emergency'
);
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT UNIQUE,
display_name TEXT,
default_profile TEXT NOT NULL DEFAULT 'loaded_gravel',
privacy_mode TEXT NOT NULL DEFAULT 'private',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE trips (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
owner_id UUID REFERENCES users(id) ON DELETE SET NULL,
title TEXT NOT NULL,
start_point GEOGRAPHY(Point, 4326) NOT NULL,
end_point GEOGRAPHY(Point, 4326) NOT NULL,
start_date DATE,
end_date DATE,
profile TEXT NOT NULL,
constraints JSONB NOT NULL DEFAULT '{}'::jsonb,
status TEXT NOT NULL DEFAULT 'draft',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE routes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
trip_id UUID NOT NULL REFERENCES trips(id) ON DELETE CASCADE,
provider TEXT NOT NULL,
geometry GEOGRAPHY(LineString, 4326) NOT NULL,
distance_m INTEGER NOT NULL,
ascent_m INTEGER DEFAULT 0,
descent_m INTEGER DEFAULT 0,
surface_breakdown JSONB NOT NULL DEFAULT '{}'::jsonb,
suitability_score NUMERIC(5,2),
warnings JSONB NOT NULL DEFAULT '[]'::jsonb,
attribution TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE route_segments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
route_id UUID NOT NULL REFERENCES routes(id) ON DELETE CASCADE,
seq INTEGER NOT NULL,
start_m INTEGER NOT NULL,
end_m INTEGER NOT NULL,
geometry GEOGRAPHY(LineString, 4326) NOT NULL,
distance_m INTEGER NOT NULL,
ascent_m INTEGER DEFAULT 0,
descent_m INTEGER DEFAULT 0,
avg_grade_percent NUMERIC(5,2),
max_grade_percent NUMERIC(5,2),
surface TEXT,
smoothness TEXT,
highway TEXT,
bicycle_access TEXT,
legal_access_confidence confidence_level NOT NULL DEFAULT 'unknown',
source_confidence confidence_level NOT NULL DEFAULT 'unknown',
traffic_stress NUMERIC(4,2),
official_cycle_route BOOLEAN DEFAULT false,
protected_area_overlap BOOLEAN DEFAULT false,
scores JSONB NOT NULL DEFAULT '{}'::jsonb
);
CREATE INDEX route_segments_route_seq_idx ON route_segments(route_id, seq);
CREATE INDEX route_segments_geom_idx ON route_segments USING GIST(geometry);
CREATE TABLE pois (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
category poi_category NOT NULL,
name TEXT,
location GEOGRAPHY(Point, 4326) NOT NULL,
source TEXT NOT NULL,
source_id TEXT,
confidence confidence_level NOT NULL DEFAULT 'unknown',
opening_hours TEXT,
last_seen_at TIMESTAMPTZ,
last_confirmed_by_user_at TIMESTAMPTZ,
tags JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX pois_category_idx ON pois(category);
CREATE INDEX pois_location_idx ON pois USING GIST(location);
CREATE UNIQUE INDEX pois_source_source_id_idx ON pois(source, source_id) WHERE source_id IS NOT NULL;
CREATE TABLE route_pois (
route_id UUID NOT NULL REFERENCES routes(id) ON DELETE CASCADE,
poi_id UUID NOT NULL REFERENCES pois(id) ON DELETE CASCADE,
meters_from_start INTEGER,
distance_from_route_m INTEGER,
detour_distance_m INTEGER,
relevance_score NUMERIC(5,2),
PRIMARY KEY (route_id, poi_id)
);
CREATE TABLE stages (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
route_id UUID NOT NULL REFERENCES routes(id) ON DELETE CASCADE,
day_index INTEGER NOT NULL,
start_m INTEGER NOT NULL,
end_m INTEGER NOT NULL,
distance_m INTEGER NOT NULL,
ascent_m INTEGER DEFAULT 0,
descent_m INTEGER DEFAULT 0,
endpoint GEOGRAPHY(Point, 4326) NOT NULL,
sleep_candidates JSONB NOT NULL DEFAULT '[]'::jsonb,
water_pois JSONB NOT NULL DEFAULT '[]'::jsonb,
food_pois JSONB NOT NULL DEFAULT '[]'::jsonb,
repair_pois JSONB NOT NULL DEFAULT '[]'::jsonb,
bailout_pois JSONB NOT NULL DEFAULT '[]'::jsonb,
warnings JSONB NOT NULL DEFAULT '[]'::jsonb,
plan_b JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE service_gaps (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
route_id UUID NOT NULL REFERENCES routes(id) ON DELETE CASCADE,
category poi_category NOT NULL,
start_m INTEGER NOT NULL,
end_m INTEGER NOT NULL,
distance_m INTEGER NOT NULL,
severity warning_severity NOT NULL,
explanation TEXT NOT NULL
);
CREATE TABLE rule_cards (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
rule_type TEXT NOT NULL,
jurisdiction TEXT NOT NULL,
status TEXT NOT NULL,
confidence confidence_level NOT NULL DEFAULT 'unknown',
summary TEXT NOT NULL,
source_url TEXT,
source_name TEXT,
last_reviewed_at DATE,
valid_from DATE,
valid_to DATE,
geometry GEOGRAPHY(Geometry, 4326),
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX rule_cards_geom_idx ON rule_cards USING GIST(geometry);
CREATE TABLE local_reports (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
route_id UUID REFERENCES routes(id) ON DELETE SET NULL,
route_segment_id UUID REFERENCES route_segments(id) ON DELETE SET NULL,
report_type TEXT NOT NULL,
location GEOGRAPHY(Point, 4326) NOT NULL,
meters_from_start INTEGER,
observed_at TIMESTAMPTZ NOT NULL,
details TEXT,
payload JSONB NOT NULL DEFAULT '{}'::jsonb,
trust_score NUMERIC(5,2) DEFAULT 0,
expires_at TIMESTAMPTZ,
moderation_status TEXT NOT NULL DEFAULT 'pending',
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX local_reports_location_idx ON local_reports USING GIST(location);
CREATE INDEX local_reports_type_idx ON local_reports(report_type);
CREATE TABLE offline_packs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
trip_id UUID NOT NULL REFERENCES trips(id) ON DELETE CASCADE,
route_id UUID NOT NULL REFERENCES routes(id) ON DELETE CASCADE,
bbox GEOGRAPHY(Polygon, 4326),
route_buffer_km NUMERIC(6,2) NOT NULL DEFAULT 5,
included_layers JSONB NOT NULL DEFAULT '[]'::jsonb,
data_versions JSONB NOT NULL DEFAULT '{}'::jsonb,
files JSONB NOT NULL DEFAULT '[]'::jsonb,
warnings JSONB NOT NULL DEFAULT '[]'::jsonb,
status TEXT NOT NULL DEFAULT 'queued',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
expires_at TIMESTAMPTZ
);