Added next rocket data
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
parent
0c5c3cf2d0
commit
1f43a7c29e
|
|
@ -12,6 +12,9 @@ COPY --from=builder /app/public ./public
|
|||
COPY --from=builder /app/.next/standalone ./
|
||||
COPY --from=builder /app/.next/static ./.next/static
|
||||
COPY --from=builder /app/lib ./lib
|
||||
COPY --from=builder /app/config ./config
|
||||
|
||||
RUN mkdir -p /app/data
|
||||
|
||||
EXPOSE 3000
|
||||
CMD ["node", "server.js"]
|
||||
22
app/page.tsx
22
app/page.tsx
|
|
@ -2,7 +2,6 @@ import MissionControl from '@/components/MissionControl';
|
|||
import db from '@/lib/db';
|
||||
|
||||
export default function Home() {
|
||||
// SQLite handles the 'now' comparison perfectly
|
||||
const issRow = db.prepare(`
|
||||
SELECT pass_time, end_time
|
||||
FROM iss_passes
|
||||
|
|
@ -10,26 +9,41 @@ export default function Home() {
|
|||
ORDER BY datetime(pass_time) ASC LIMIT 1
|
||||
`).get() as { pass_time: string, end_time: string } | undefined;
|
||||
|
||||
// 2. Fetch Moon
|
||||
const moonRow = db.prepare(`
|
||||
SELECT title, event_time
|
||||
FROM global_events
|
||||
WHERE id = 'moon_phase'
|
||||
`).get() as { title: string, event_time: string } | undefined;
|
||||
|
||||
// Prepare Dates
|
||||
const cosmicRow = db.prepare(`
|
||||
SELECT title, event_time
|
||||
FROM global_events
|
||||
WHERE id = 'next_cosmic_event'
|
||||
`).get() as { title: string, event_time: string } | undefined;
|
||||
|
||||
const launchRow = db.prepare(
|
||||
"SELECT title, event_time FROM global_events WHERE id = 'next_launch'"
|
||||
).get() as { title: string, event_time: string } | undefined;
|
||||
|
||||
const launchStart = launchRow ? new Date(launchRow.event_time) : null;
|
||||
const launchEnd = launchStart ? new Date(launchStart.getTime() + 7200000) : null;
|
||||
|
||||
const issStart = issRow ? new Date(issRow.pass_time) : null;
|
||||
const issEnd = issRow ? new Date(issRow.end_time) : null;
|
||||
|
||||
const moonStart = moonRow ? new Date(moonRow.event_time) : null;
|
||||
// Moon "Event" lasts 24 hours
|
||||
const moonEnd = moonStart ? new Date(moonStart.getTime() + 86400000) : null;
|
||||
|
||||
const cosmicStart = cosmicRow ? new Date(cosmicRow.event_time) : null;
|
||||
const cosmicEnd = cosmicStart ? new Date(cosmicStart.getTime() + 14400000) : null;
|
||||
|
||||
return (
|
||||
<main>
|
||||
<MissionControl
|
||||
iss={{ start: issStart, end: issEnd }}
|
||||
moon={{ title: moonRow?.title || "Moon Phase", start: moonStart, end: moonEnd }}
|
||||
cosmic={{ title: cosmicRow?.title || "Cosmic Event", start: cosmicStart, end: cosmicEnd }}
|
||||
launch={{ title: launchRow?.title || "Rocket Launch", start: launchStart, end: launchEnd }}
|
||||
/>
|
||||
</main>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -112,20 +112,17 @@ export default function EventCard({ id, title, targetDate, endDate, icon }: Even
|
|||
|
||||
<div className="text-4xl font-mono text-white flex items-baseline gap-2">
|
||||
{parseInt(timeLeft.d) >= 2 ? (
|
||||
/* Case 1: More than 2 days - Keep it super simple */
|
||||
<>
|
||||
<span>{timeLeft.d}</span>
|
||||
<span className="text-slate-500 text-xl lowercase">Days</span>
|
||||
</>
|
||||
) : parseInt(timeLeft.d) === 1 ? (
|
||||
/* Case 2: Between 24 and 48 hours - Show Day + Hours */
|
||||
<>
|
||||
<span>01</span><span className="text-slate-500 text-sm">d</span>
|
||||
<span className="text-slate-500">:</span>
|
||||
<span>{timeLeft.h}</span><span className="text-slate-500 text-sm">h</span>
|
||||
</>
|
||||
) : (
|
||||
/* Case 3: Under 24 hours - The full high-precision clock */
|
||||
<>
|
||||
<span>{timeLeft.h}</span>
|
||||
<span className="text-slate-500">:</span>
|
||||
|
|
@ -135,9 +132,18 @@ export default function EventCard({ id, title, targetDate, endDate, icon }: Even
|
|||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="text-[10px] text-slate-500 mt-2 font-mono uppercase tracking-widest leading-none">
|
||||
{parseInt(timeLeft.d) > 0 ? "Until event" : "T-Minus to Horizon"}
|
||||
{isLive ? (
|
||||
"Mission in Progress"
|
||||
) : id === 'iss' ? (
|
||||
"T-Minus to Horizon"
|
||||
) : id === 'launch' ? (
|
||||
"T-Minus to Ignition"
|
||||
) : id === 'moon' ? (
|
||||
"Until Lunar Phase"
|
||||
) : (
|
||||
"Days to Peak Phase"
|
||||
)}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -12,10 +12,7 @@ const Starfield = dynamic(() => import('@/components/Starfield'), {
|
|||
});
|
||||
|
||||
|
||||
export default function MissionControl({ iss, moon }: MissionControlProps) {
|
||||
|
||||
const BASE_TIME = 1769610273000;
|
||||
|
||||
export default function MissionControl({ iss, moon, cosmic, launch }: MissionControlProps) {
|
||||
const events = useMemo(() => [
|
||||
{
|
||||
id: 'iss',
|
||||
|
|
@ -32,20 +29,20 @@ export default function MissionControl({ iss, moon }: MissionControlProps) {
|
|||
icon: <Moon size={20} />,
|
||||
},
|
||||
{
|
||||
id: 'starlink',
|
||||
title: "Starlink Train",
|
||||
date: new Date(BASE_TIME + 1000 * 60 * 45),
|
||||
endDate: new Date(BASE_TIME + 1000 * 60 * 55),
|
||||
icon: <Rocket size={20} />,
|
||||
id: 'cosmic',
|
||||
title: cosmic.title,
|
||||
date: cosmic.start,
|
||||
endDate: cosmic.end,
|
||||
icon: <Sparkles size={20} />,
|
||||
},
|
||||
{
|
||||
id: 'meteor',
|
||||
title: "Meteor Shower",
|
||||
date: new Date(BASE_TIME + 1000 * 60 * 60 * 24 * 10),
|
||||
endDate: new Date(BASE_TIME + 1000 * 60 * 60 * 24 * 10.1),
|
||||
icon: <Sparkles size={20} />,
|
||||
id: 'launch',
|
||||
title: launch.title,
|
||||
date: launch.start,
|
||||
endDate: launch.end,
|
||||
icon: <Rocket size={20} />,
|
||||
}
|
||||
], [iss, moon]);
|
||||
], [iss, moon, cosmic, launch]);
|
||||
|
||||
return (
|
||||
<div className="relative min-h-screen flex flex-col items-center p-8 overflow-hidden">
|
||||
|
|
@ -63,7 +60,7 @@ export default function MissionControl({ iss, moon }: MissionControlProps) {
|
|||
<div className="z-10 grid grid-cols-1 md:grid-cols-2 gap-6 w-full max-w-5xl">
|
||||
{events.map((event) => (
|
||||
<EventCard
|
||||
key={`${event.id}-${event.date?.getTime()}`}
|
||||
key={`${event.id}-${event.date?.getTime() || 'none'}`}
|
||||
id={event.id}
|
||||
title={event.title}
|
||||
targetDate={event.date}
|
||||
|
|
|
|||
67
config/events.json
Normal file
67
config/events.json
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
[
|
||||
{
|
||||
"id": "lunar_eclipse_mar_2026",
|
||||
"title": "Total Lunar Eclipse",
|
||||
"event_time": "2026-03-03T11:34:00Z"
|
||||
},
|
||||
{
|
||||
"id": "solar_eclipse_aug_2026",
|
||||
"title": "Solar Eclipse: Partial (85%)",
|
||||
"event_time": "2026-08-12T17:10:00Z"
|
||||
},
|
||||
{
|
||||
"id": "leonids_2026",
|
||||
"title": "Leonid Meteor Shower",
|
||||
"event_time": "2026-11-17T12:00:00Z"
|
||||
},
|
||||
{
|
||||
"id": "solar_eclipse_aug_2027",
|
||||
"title": "Solar Eclipse: Partial",
|
||||
"event_time": "2027-08-02T10:05:00Z"
|
||||
},
|
||||
{
|
||||
"id": "perseids_2027",
|
||||
"title": "Perseid Meteor Shower",
|
||||
"event_time": "2027-08-13T01:00:00Z"
|
||||
},
|
||||
{
|
||||
"id": "lunar_eclipse_dec_2028",
|
||||
"title": "Total Lunar Eclipse",
|
||||
"event_time": "2028-12-31T16:53:00Z"
|
||||
},
|
||||
{
|
||||
"id": "solar_eclipse_jun_2029",
|
||||
"title": "Solar Eclipse: Partial",
|
||||
"event_time": "2029-06-12T04:06:00Z"
|
||||
},
|
||||
{
|
||||
"id": "lunar_eclipse_dec_2029",
|
||||
"title": "Total Lunar Eclipse",
|
||||
"event_time": "2029-12-20T22:42:00Z"
|
||||
},
|
||||
{
|
||||
"id": "geminids_2030",
|
||||
"title": "Geminid Meteor Shower",
|
||||
"event_time": "2030-12-14T07:00:00Z"
|
||||
},
|
||||
{
|
||||
"id": "solar_eclipse_nov_2032",
|
||||
"title": "Solar Eclipse: Partial",
|
||||
"event_time": "2032-11-03T05:34:00Z"
|
||||
},
|
||||
{
|
||||
"id": "lunar_eclipse_apr_2033",
|
||||
"title": "Total Lunar Eclipse",
|
||||
"event_time": "2033-04-14T19:13:00Z"
|
||||
},
|
||||
{
|
||||
"id": "solar_eclipse_mar_2034",
|
||||
"title": "Solar Eclipse: Partial",
|
||||
"event_time": "2034-03-20T10:18:00Z"
|
||||
},
|
||||
{
|
||||
"id": "lunar_eclipse_feb_2035",
|
||||
"title": "Total Lunar Eclipse",
|
||||
"event_time": "2035-02-22T09:05:00Z"
|
||||
}
|
||||
]
|
||||
|
|
@ -1,5 +1,10 @@
|
|||
import db from '../lib/db';
|
||||
import * as satellite from 'satellite.js';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { CosmicEvent } from '@/types/space';
|
||||
|
||||
|
||||
|
||||
const MY_LAT = 55.6683;
|
||||
const MY_LON = 12.5333;
|
||||
|
|
@ -75,44 +80,89 @@ async function updateISSData() {
|
|||
console.log(`✅ Stored ${passesFound.length} future passes with start and end times.`);
|
||||
}
|
||||
|
||||
// Add this to your worker script
|
||||
function getNextMoonPhase() {
|
||||
const LUNAR_CYCLE = 29.53059; // Days
|
||||
const KNOWN_NEW_MOON = new Date('2024-01-11T11:57:00Z'); // A reference New Moon
|
||||
function updateMoonPhase() {
|
||||
const LUNAR_CYCLE = 29.53059;
|
||||
const KNOWN_NEW_MOON = new Date('2024-01-11T11:57:00Z');
|
||||
|
||||
const now = new Date();
|
||||
const msSinceReference = now.getTime() - KNOWN_NEW_MOON.getTime();
|
||||
const daysSinceReference = msSinceReference / (1000 * 60 * 60 * 24);
|
||||
const daysSince = (now.getTime() - KNOWN_NEW_MOON.getTime()) / 86400000;
|
||||
const progress = daysSince % LUNAR_CYCLE;
|
||||
|
||||
const currentCycleProgress = daysSinceReference % LUNAR_CYCLE;
|
||||
const daysToFullMoon = (LUNAR_CYCLE / 2) - currentCycleProgress;
|
||||
const daysToNewMoon = LUNAR_CYCLE - currentCycleProgress;
|
||||
const daysToFull = (LUNAR_CYCLE / 2) - progress;
|
||||
const daysToNew = LUNAR_CYCLE - progress;
|
||||
|
||||
// If we've passed the Full Moon in this cycle, count to New Moon
|
||||
let targetDate, title;
|
||||
if (daysToFullMoon > 0) {
|
||||
targetDate = new Date(now.getTime() + daysToFullMoon * 86400000);
|
||||
title = "Next Full Moon";
|
||||
} else {
|
||||
targetDate = new Date(now.getTime() + daysToNewMoon * 86400000);
|
||||
title = "Next New Moon";
|
||||
const moon = daysToFull > 0
|
||||
? { title: "Next Full Moon", date: new Date(now.getTime() + daysToFull * 86400000) }
|
||||
: { title: "Next New Moon", date: new Date(now.getTime() + daysToNew * 86400000) };
|
||||
|
||||
db.prepare(`
|
||||
INSERT INTO global_events (id, title, event_time) VALUES ('moon_phase', ?, ?)
|
||||
ON CONFLICT(id) DO UPDATE SET title=excluded.title, event_time=excluded.event_time
|
||||
`).run(moon.title, moon.date.toISOString());
|
||||
|
||||
console.log(`🌙 Moon: ${moon.title}`);
|
||||
}
|
||||
|
||||
function updateCosmicEvents() {
|
||||
const configPath = path.resolve(process.cwd(), 'config/events.json');
|
||||
if (!fs.existsSync(configPath)) return;
|
||||
|
||||
const events: CosmicEvent[] = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
||||
const now = new Date();
|
||||
|
||||
const next = events
|
||||
.filter((e) => new Date(e.event_time) > now)
|
||||
.sort((a, b) => new Date(a.event_time).getTime() - new Date(b.event_time).getTime())[0];
|
||||
|
||||
if (next) {
|
||||
db.prepare(`
|
||||
INSERT INTO global_events (id, title, event_time) VALUES ('next_cosmic_event', ?, ?)
|
||||
ON CONFLICT(id) DO UPDATE SET title=excluded.title, event_time=excluded.event_time
|
||||
`).run(next.title, next.event_time);
|
||||
console.log(`✨ Cosmic: ${next.title}`);
|
||||
}
|
||||
}
|
||||
|
||||
return { title, targetDate };
|
||||
async function updateRocketLaunches() {
|
||||
try {
|
||||
const nowISO = new Date().toISOString();
|
||||
const url = `https://lldev.thespacedevs.com/2.2.0/launch/upcoming/?limit=1&mode=list&net__gt=${nowISO}`;
|
||||
|
||||
const response = await fetch(url);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.results && data.results.length > 0) {
|
||||
const launch = data.results[0];
|
||||
|
||||
db.prepare(`
|
||||
INSERT INTO global_events (id, title, event_time)
|
||||
VALUES ('next_launch', ?, ?)
|
||||
ON CONFLICT(id) DO UPDATE SET title=excluded.title, event_time=excluded.event_time
|
||||
`).run(
|
||||
`Launch: ${launch.name}`,
|
||||
launch.net
|
||||
);
|
||||
|
||||
console.log(`🚀 Rocket Sync: ${launch.name}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ Rocket Fetch Error:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function updateAllData() {
|
||||
console.log("📡 Starting Ground Station sync...");
|
||||
|
||||
try {
|
||||
await updateISSData();
|
||||
updateMoonPhase();
|
||||
updateCosmicEvents();
|
||||
await updateRocketLaunches();
|
||||
|
||||
const moon = getNextMoonPhase();
|
||||
const upsertMoon = db.prepare(`
|
||||
INSERT INTO global_events (id, title, event_time)
|
||||
VALUES ('moon_phase', ?, ?)
|
||||
ON CONFLICT(id) DO UPDATE SET title=excluded.title, event_time=excluded.event_time
|
||||
`);
|
||||
upsertMoon.run(moon.title, moon.targetDate.toISOString());
|
||||
|
||||
console.log(`🌙 Updated Moon Phase: ${moon.title}`);
|
||||
console.log("✅ All systems synchronized.");
|
||||
} catch (error) {
|
||||
console.error("❌ Sync failed:", error);
|
||||
}
|
||||
}
|
||||
|
||||
updateAllData().catch(console.error);
|
||||
|
|
|
|||
|
|
@ -32,4 +32,12 @@ export interface EventCardProps {
|
|||
export interface MissionControlProps {
|
||||
iss: { start: Date | null; end: Date | null };
|
||||
moon: { title: string; start: Date | null; end: Date | null };
|
||||
cosmic: { title: string; start: Date | null; end: Date | null };
|
||||
launch: { title: string; start: Date | null; end: Date | null };
|
||||
}
|
||||
|
||||
export interface CosmicEvent {
|
||||
id: string;
|
||||
title: string;
|
||||
event_time: string;
|
||||
}
|
||||
Loading…
Reference in a new issue