Phase 0: monorepo skeleton (hub, live-map, api, packages, infra, CI)

This commit is contained in:
Mavis
2026-06-02 15:47:28 +00:00
commit 7b19c54943
59 changed files with 2391 additions and 0 deletions
+84
View File
@@ -0,0 +1,84 @@
-- Schema bootstrap for the kerbal-rt Postgres + Timescale install.
-- Runs automatically on first container start.
--
-- See kerbalrealtime-clone-plan.md §Component B for the full data model.
-- This is the Phase 0 minimum; Phase 1 fills in the rest.
CREATE EXTENSION IF NOT EXISTS timescaledb;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Bodies (celestial body catalog; small, mostly static)
CREATE TABLE IF NOT EXISTS bodies (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
kind TEXT NOT NULL CHECK (kind IN ('star','planet','moon','asteroid','comet')),
parent_id TEXT REFERENCES bodies(id),
radius DOUBLE PRECISION NOT NULL,
sphere_of_influence DOUBLE PRECISION NOT NULL,
gravitational_parameter DOUBLE PRECISION NOT NULL,
rotation_period DOUBLE PRECISION NOT NULL,
axial_tilt DOUBLE PRECISION NOT NULL,
elements JSONB NOT NULL
);
-- Vessels (slowly-changing dimension)
CREATE TABLE IF NOT EXISTS vessels (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
type TEXT,
owner TEXT,
status TEXT NOT NULL CHECK (status IN ('ACTIVE','DECOMMISSIONED','LOST')) DEFAULT 'ACTIVE',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
retired_at TIMESTAMPTZ
);
CREATE INDEX IF NOT EXISTS vessels_status_idx ON vessels(status);
-- Telemetry snapshots (high-volume time series)
CREATE TABLE IF NOT EXISTS telemetry_snapshots (
ts TIMESTAMPTZ NOT NULL,
ut DOUBLE PRECISION NOT NULL,
payload JSONB NOT NULL
);
SELECT create_hypertable('telemetry_snapshots', 'ts', if_not_exists => TRUE);
CREATE INDEX IF NOT EXISTS telemetry_snapshots_ut_idx ON telemetry_snapshots(ut DESC);
-- Mission events
CREATE TABLE IF NOT EXISTS mission_events (
id BIGSERIAL PRIMARY KEY,
mission TEXT NOT NULL,
vessel_id TEXT REFERENCES vessels(id),
title TEXT NOT NULL,
description TEXT,
scheduled_at TIMESTAMPTZ NOT NULL,
ut_at_event DOUBLE PRECISION,
duration_s INTEGER,
kind TEXT NOT NULL CHECK (kind IN ('LAUNCH','BURN','FLYBY','LANDING','DOCKING','EVA','ECLIPSE','OPERATION','OTHER')),
created_by TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS mission_events_scheduled_idx ON mission_events(scheduled_at);
-- Media
CREATE TABLE IF NOT EXISTS media (
id BIGSERIAL PRIMARY KEY,
kind TEXT NOT NULL CHECK (kind IN ('image','youtube')),
url TEXT NOT NULL,
thumbnail_url TEXT,
caption TEXT,
tags TEXT[] NOT NULL DEFAULT '{}',
mission_id TEXT,
vessel_id TEXT REFERENCES vessels(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS media_tags_idx ON media USING GIN(tags);
CREATE INDEX IF NOT EXISTS media_created_idx ON media(created_at DESC);
-- Ground stations
CREATE TABLE IF NOT EXISTS ground_stations (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
body_id TEXT NOT NULL REFERENCES bodies(id),
lat DOUBLE PRECISION NOT NULL,
lon DOUBLE PRECISION NOT NULL,
alt DOUBLE PRECISION NOT NULL
);
+37
View File
@@ -0,0 +1,37 @@
-- Seed data: the Kerbol system (KSP stock).
-- Numbers sourced from the KSP wiki and the in-game config files.
-- Gravitational parameter μ in m^3/s^2.
-- This is enough to render the system in the live map before
-- the real telemetry bridge is wired in.
INSERT INTO bodies (id, name, kind, parent_id, radius, sphere_of_influence, gravitational_parameter, rotation_period, axial_tilt, elements) VALUES
('kerbol', 'Kerbol', 'star', NULL, 261600000, 'Infinity'::float8::float8, 1.172332794e18, 432000, 0, '{"semiMajorAxis":0,"eccentricity":0,"inclination":0,"longitudeOfAscendingNode":0,"argumentOfPeriapsis":0,"meanAnomalyAtEpoch":0,"epoch":0}'),
('moho', 'Moho', 'planet', 'kerbol', 250000, 9646663, 1.686842e11, 1210000, 0.05, '{"semiMajorAxis":5263138304,"eccentricity":0.2,"inclination":0.075,"longitudeOfAscendingNode":1.5,"argumentOfPeriapsis":2.8,"meanAnomalyAtEpoch":1.0,"epoch":0}'),
('eve', 'Eve', 'planet', 'kerbol', 700000, 85109365, 8.1717302e12, 80500, 0.1, '{"semiMajorAxis":9832684544,"eccentricity":0.01,"inclination":0.1,"longitudeOfAscendingNode":0,"argumentOfPeriapsis":0,"meanAnomalyAtEpoch":3.14,"epoch":0}'),
('kerbin', 'Kerbin', 'planet', 'kerbol', 600000, 84159286, 3.5316e12, 21600, 0, '{"semiMajorAxis":13599840256,"eccentricity":0.05,"inclination":0,"longitudeOfAscendingNode":0,"argumentOfPeriapsis":0,"meanAnomalyAtEpoch":0.7,"epoch":0}'),
('duna', 'Duna', 'planet', 'kerbol', 320000, 47921949, 3.0136321e11, 65518, 0.06, '{"semiMajorAxis":20726155264,"eccentricity":0.05,"inclination":0.02,"longitudeOfAscendingNode":0,"argumentOfPeriapsis":0,"meanAnomalyAtEpoch":1.5,"epoch":0}'),
('dres', 'Dres', 'planet', 'kerbol', 138000, 32832840, 2.1484489e10, 34800, 0.08, '{"semiMajorAxis":40839348203,"eccentricity":0.14,"inclination":0.08,"longitudeOfAscendingNode":0,"argumentOfPeriapsis":0,"meanAnomalyAtEpoch":2.1,"epoch":0}'),
('jool', 'Jool', 'planet', 'kerbol', 6000000, 2450055988, 2.82528e14, 36000, 0.05, '{"semiMajorAxis":68773560320,"eccentricity":0.05,"inclination":0.04,"longitudeOfAscendingNode":0,"argumentOfPeriapsis":0,"meanAnomalyAtEpoch":4.2,"epoch":0}'),
('eeloo', 'Eeloo', 'planet', 'kerbol', 210000, 119082940, 7.4410815e10, 19460, 0.1, '{"semiMajorAxis":90118820000,"eccentricity":0.26,"inclination":0.13,"longitudeOfAscendingNode":0,"argumentOfPeriapsis":0,"meanAnomalyAtEpoch":5.5,"epoch":0}'),
('gilly', 'Gilly', 'moon', 'eve', 13000, 126123, 8289449.8, 28260, 0.05, '{"semiMajorAxis":31500000,"eccentricity":0.18,"inclination":0.2,"longitudeOfAscendingNode":0,"argumentOfPeriapsis":0,"meanAnomalyAtEpoch":0.5,"epoch":0}'),
('mun', 'Mun', 'moon', 'kerbin', 200000, 2429559, 6.514e10, 138984, 0, '{"semiMajorAxis":12000000,"eccentricity":0,"inclination":0,"longitudeOfAscendingNode":0,"argumentOfPeriapsis":0,"meanAnomalyAtEpoch":1.0,"epoch":0}'),
('minmus', 'Minmus', 'moon', 'kerbin', 60000, 2247428, 1.765e9, 40400, 0.04, '{"semiMajorAxis":47000000,"eccentricity":0.0,"inclination":0.075,"longitudeOfAscendingNode":0,"argumentOfPeriapsis":0,"meanAnomalyAtEpoch":2.5,"epoch":0}'),
('ike', 'Ike', 'moon', 'duna', 130000, 1048598, 1.856e9, 65518, 0.05, '{"semiMajorAxis":3200000,"eccentricity":0.0,"inclination":0,"longitudeOfAscendingNode":0,"argumentOfPeriapsis":0,"meanAnomalyAtEpoch":1.5,"epoch":0}'),
('laythe', 'Laythe', 'moon', 'jool', 500000, 3723645, 1.840e11, 52980, 0, '{"semiMajorAxis":27184000,"eccentricity":0.0,"inclination":0,"longitudeOfAscendingNode":0,"argumentOfPeriapsis":0,"meanAnomalyAtEpoch":2.0,"epoch":0}'),
('vall', 'Vall', 'moon', 'jool', 240000, 2406401, 2.061e10, 106200, 0, '{"semiMajorAxis":43152000,"eccentricity":0.0,"inclination":0,"longitudeOfAscendingNode":0,"argumentOfPeriapsis":0,"meanAnomalyAtEpoch":2.5,"epoch":0}'),
('tylo', 'Tylo', 'moon', 'jool', 375000, 10856418, 2.122e11, 84600, 0, '{"semiMajorAxis":68500000,"eccentricity":0.0,"inclination":0,"longitudeOfAscendingNode":0,"argumentOfPeriapsis":0,"meanAnomalyAtEpoch":3.0,"epoch":0}'),
('bop', 'Bop', 'moon', 'jool', 65000, 1220600, 2.486e8, 360, 0.05, '{"semiMajorAxis":128500000,"eccentricity":0.23,"inclination":0.2,"longitudeOfAscendingNode":0,"argumentOfPeriapsis":0,"meanAnomalyAtEpoch":4.0,"epoch":0}'),
('pol', 'Pol', 'moon', 'jool', 44000, 1042138, 7.214e7, 340, 0.05, '{"semiMajorAxis":179890000,"eccentricity":0.17,"inclination":0.15,"longitudeOfAscendingNode":0,"argumentOfPeriapsis":0,"meanAnomalyAtEpoch":4.5,"epoch":0}')
ON CONFLICT (id) DO UPDATE SET
name = EXCLUDED.name,
radius = EXCLUDED.radius,
sphere_of_influence = EXCLUDED.sphere_of_influence,
gravitational_parameter = EXCLUDED.gravitational_parameter,
rotation_period = EXCLUDED.rotation_period,
axial_tilt = EXCLUDED.axial_tilt,
elements = EXCLUDED.elements;
-- Sample ground station: the project's "Montana DSN" (real site has this).
INSERT INTO ground_stations (id, name, body_id, lat, lon, alt) VALUES
('montana', 'Montana DSN', 'kerbin', 47.0, -110.0, 1200)
ON CONFLICT (id) DO NOTHING;