Skip to content

Commit 0068d36

Browse files
committed
Housekeeping
1 parent 51e55f2 commit 0068d36

File tree

8 files changed

+73
-58
lines changed

8 files changed

+73
-58
lines changed

bun.lock

Lines changed: 0 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
"pino": "^10.3.1",
3030
"pino-loki": "^3.0.0",
3131
"pino-pretty": "^13.1.3",
32-
"prom-client": "^15.1.3",
3332
"tle": "^3.0.1"
3433
}
3534
}

src/routes/norad.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import { Elysia } from "elysia";
22

33
import tleGetter from "../utils/tleGetter";
4+
import limiter from "../utils/ratelimiter";
45

5-
const noradRoute = new Elysia({ prefix: "/norad" }).get("/:id", async ({ params }) => {
6-
const noradId = parseInt(params.id, 10);
7-
const tleData = await tleGetter(noradId);
8-
return new Response(tleData, { headers: { "Content-Type": "text/plain", "Cache-Control": "max-age=3600" } });
9-
});
6+
const noradRoute = new Elysia({ prefix: "/norad" })
7+
.use(limiter)
8+
.get("/:id", async ({ params }) => {
9+
const noradId = parseInt(params.id, 10);
10+
const tleData = await tleGetter(noradId);
11+
return new Response(tleData, { headers: { "Content-Type": "text/plain", "Cache-Control": "max-age=3600" } });
12+
});
1013

1114
export default noradRoute;

src/utils/groupHandler.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ async function handleGroupRequest(group: string, lastFetchedHeader: number, form
2424
log.debug(`No cached GP data for group "${group}", format "${format}". Fetching from Celestrak...`);
2525
tle = await tleFetcher(group, format);
2626
timestamp = now;
27-
kv.set(`${group}_${format}`, tle);
28-
kv.set(`${group}_timestamp_${format}`, timestamp);
27+
await kv.set(`${group}_${format}`, tle);
28+
await kv.set(`${group}_timestamp_${format}`, timestamp);
2929
} else if (isStale) {
3030
log.debug(`GP data for group "${group}", format "${format}" are stale. Fetching fresh TLEs...`);
3131
tle = await tleFetcher(group, format);
3232
timestamp = now;
33-
kv.set(`${group}_${format}`, tle);
34-
kv.set(`${group}_timestamp_${format}`, timestamp);
33+
await kv.set(`${group}_${format}`, tle);
34+
await kv.set(`${group}_timestamp_${format}`, timestamp);
3535
} else {
3636
log.debug(`Serving cached GP data for group "${group}", format "${format}".`);
3737
}

src/utils/metrics.ts

Lines changed: 0 additions & 15 deletions
This file was deleted.

src/utils/ratelimiter.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ import { Elysia } from "elysia";
22

33
import kv from "./kv";
44
import log from "./logger";
5+
import config from "./config";
56

67
const limiter = new Elysia({ name: "rate-limiter" }).onRequest(async (ctx) => {
78
const ip = ctx.request.headers.get("x-forwarded-for") || ctx.request.headers.get("cf-connecting-ip") || ctx.request.headers.get("true-client-ip") || ctx.request.headers.get("x-real-ip") || "unknown";
89
const key = `rate_limit:${ip}`;
910
const now = Date.now();
10-
const windowSize = process.env.RATE_LIMIT_WINDOW ? parseInt(process.env.RATE_LIMIT_WINDOW) * 1000 : 60 * 1000;
11-
const maxRequests = process.env.RATE_LIMIT_MAX_REQUESTS ? parseInt(process.env.RATE_LIMIT_MAX_REQUESTS) : 60;
11+
const windowSize = config.rateLimitWindow;
12+
const maxRequests = config.rateLimitMaxRequests;
1213

1314
let clientInfo = await kv.get(key);
1415
if (!clientInfo) {

src/utils/tleFetcher.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,20 @@ async function fetchTle(group: string, format: "tle" | "json" | "csv" = "tle"):
1414
"User-Agent": `ReTLEctor/${version} (https://github.com/MrTalon63/ReTLEctor)`,
1515
},
1616
});
17+
18+
if (response.status === 304) {
19+
const cached = (await kv.get(`${group}_${format}`)) as string | null;
20+
if (cached) {
21+
log.debug(`Celestrak returned 304 for group "${group}", format "${format}". Serving cached data.`);
22+
return cached;
23+
}
24+
throw new Error(`Celestrak returned 304 but no cached data exists for group "${group}", format "${format}"`);
25+
}
1726
if (!response.ok) {
1827
throw new Error(`Failed to fetch TLEs: ${response.status} ${response.statusText}`);
1928
}
2029
const tleData = await response.text();
30+
2131
await kv.set(`${group}_${format}`, tleData);
2232
await kv.set(`${group}_timestamp_${format}`, Date.now());
2333
log.debug(`Successfully cached TLEs for group "${group}" in format "${format}".`);

src/utils/tleGetter.ts

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,33 +14,58 @@ async function getObjectsTle(noradId: number) {
1414

1515
if (!tleData || isStale) {
1616
let allTles = (await kv.get("active_tle")) as string | null;
17-
if (!allTles) {
17+
const activeTimestamp: number | undefined = await kv.get("active_timestamp_tle");
18+
const activeIsStale = activeTimestamp ? now - activeTimestamp > config.cacheActiveDuration : true;
19+
20+
if (!allTles || activeIsStale) {
21+
// Active group is missing or stale — fetch fresh data and re-cache all satellites
1822
await fetchTle("active");
19-
}
20-
allTles = (await kv.get("active_tle")) as string | null;
23+
allTles = (await kv.get("active_tle")) as string | null;
2124

22-
// If we still don't have the active TLEs then something went wrong with fetching, so we throw an error
23-
if (!allTles) {
24-
log.error("Failed to fetch active TLEs from Celestrak");
25-
throw new Error("Failed to fetch active TLEs from Celestrak");
26-
}
27-
const lines = allTles.split("\n");
28-
const setPromises: Promise<boolean>[] = [];
29-
for (let i = 0; i < lines.length; i += 3) {
30-
const idLine = lines[i + 0];
31-
const tleLine1 = lines[i + 1];
32-
const tleLine2 = lines[i + 2];
33-
34-
if (idLine && tleLine1 && tleLine2) {
35-
const parsed = tle.parse(`${idLine}\n${tleLine1}\n${tleLine2}`);
36-
const tleString = `${idLine}\n${tleLine1}\n${tleLine2}`;
37-
if (parsed.number === noradId) {
38-
tleData = tleString;
25+
// If we still don't have the active TLEs then something went wrong with fetching, so we throw an error
26+
if (!allTles) {
27+
log.error("Failed to fetch active TLEs from Celestrak");
28+
throw new Error("Failed to fetch active TLEs from Celestrak");
29+
}
30+
31+
const lines = allTles.split("\n");
32+
const setPromises: Promise<boolean>[] = [];
33+
for (let i = 0; i < lines.length; i += 3) {
34+
const idLine = lines[i + 0];
35+
const tleLine1 = lines[i + 1];
36+
const tleLine2 = lines[i + 2];
37+
38+
if (idLine && tleLine1 && tleLine2) {
39+
const parsed = tle.parse(`${idLine}\n${tleLine1}\n${tleLine2}`);
40+
const tleString = `${idLine}\n${tleLine1}\n${tleLine2}`;
41+
if (parsed.number === noradId) {
42+
tleData = tleString;
43+
}
44+
setPromises.push(kv.set(`tle_${parsed.number}`, tleString));
45+
setPromises.push(kv.set(`tle_${parsed.number}_timestamp`, now));
46+
}
47+
}
48+
await Promise.all(setPromises);
49+
} else {
50+
// Active group is still fresh — just scan for the requested satellite without re-writing everything
51+
log.debug(`Active TLE group is fresh. Scanning for NORAD ID ${noradId} without re-caching all satellites.`);
52+
const lines = allTles.split("\n");
53+
for (let i = 0; i < lines.length; i += 3) {
54+
const idLine = lines[i + 0];
55+
const tleLine1 = lines[i + 1];
56+
const tleLine2 = lines[i + 2];
57+
58+
if (idLine && tleLine1 && tleLine2) {
59+
const parsed = tle.parse(`${idLine}\n${tleLine1}\n${tleLine2}`);
60+
if (parsed.number === noradId) {
61+
tleData = `${idLine}\n${tleLine1}\n${tleLine2}`;
62+
await kv.set(`tle_${noradId}`, tleData);
63+
await kv.set(`tle_${noradId}_timestamp`, now);
64+
break;
65+
}
3966
}
40-
setPromises.push(kv.set(`tle_${parsed.number}`, tleString));
4167
}
4268
}
43-
await Promise.all(setPromises);
4469
}
4570

4671
// If we're here then active group doesn't contain the requested NORAD ID, try fetching the TLE directly from Celestrak, but be aware of rate limits so we don't get blocked
@@ -77,6 +102,7 @@ async function getObjectsTle(noradId: number) {
77102

78103
tleData = (await response.text()) as string;
79104
await kv.set(`tle_${noradId}`, tleData);
105+
await kv.set(`tle_${noradId}_timestamp`, Date.now());
80106
await kv.set(`celestrakTries`, tries + 1);
81107
await kv.set(`celestrakLastTry`, Date.now());
82108
log.debug(`Successfully fetched TLE for NORAD ID ${noradId} from Celestrak.`);

0 commit comments

Comments
 (0)