Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
# Set these to your actual receiver location

# Latitude in decimal degrees
RECEIVER_LAT=-34.9192
RECEIVER_LAT=37.7644

# Longitude in decimal degrees (note: LON not LONG)
RECEIVER_LON=138.6027
RECEIVER_LON=-122.3954

# Altitude in meters
RECEIVER_ALT=110
Expand All @@ -18,3 +18,6 @@ ADSBLOL_ENABLED=true
# Radius in nautical miles for adsb.lol queries
# Aircraft within this radius of your receiver location will be fetched
ADSBLOL_RADIUS=40

# readsb external feed
READSB_NET_CONNECTOR=192.168.8.183,30005,beast_in
2 changes: 1 addition & 1 deletion .github/workflows/docker_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile.tar1090
file: Dockerfile
platforms: linux/arm64
push: false
load: true
Expand Down
47 changes: 41 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,35 +1,70 @@
FROM debian:bullseye-slim

RUN apt-get update && apt-get install -y \
nginx \
lighttpd \
git \
curl \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*

# Install Node.js 20.x
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
&& apt-get install -y nodejs \
&& rm -rf /var/lib/apt/lists/*

RUN mkdir -p /usr/local/share/tar1090/html

COPY html/ /usr/local/share/tar1090/html/
RUN mkdir -p /run/readsb
COPY data/ /run/readsb/
RUN chmod -R a+r /run/readsb/
COPY *.conf /usr/local/share/tar1090/
COPY *.sh /usr/local/share/tar1090/
RUN chmod +x /usr/local/share/tar1090/*.sh

# Copy lighttpd configs
COPY docker/lighttpd-tar1090.conf /etc/lighttpd/conf-available/89-tar1090.conf
RUN lighttpd-enable-mod tar1090
COPY docker/lighttpd-proxy.conf /etc/lighttpd/conf-available/90-proxy.conf
RUN lighttpd-enable-mod tar1090 && lighttpd-enable-mod proxy

# Copy proxy server
COPY proxy/server.js /opt/proxy/server.js

RUN sed -i 's/server.port\s*=.*/server.port = 8504/' /etc/lighttpd/lighttpd.conf && \
# Configure lighttpd: port 80, document root
RUN sed -i 's/server.port\s*=.*/server.port = 80/' /etc/lighttpd/lighttpd.conf && \
sed -i 's|server.document-root\s*=.*|server.document-root = "/usr/local/share/tar1090/html/"|' /etc/lighttpd/lighttpd.conf

COPY <<'EOF' /usr/local/bin/start.sh
#!/bin/bash
set -e

echo "Starting tar1090 web interface on port 8504..."
lighttpd -D -f /etc/lighttpd/lighttpd.conf
cleanup() {
echo "Shutting down..."
kill $PROXY_PID 2>/dev/null || true
kill $LIGHTTPD_PID 2>/dev/null || true
exit 0
}
trap cleanup SIGTERM SIGINT

echo "Starting aircraft data proxy on port ${PROXY_PORT:-3005}..."
node /opt/proxy/server.js &
PROXY_PID=$!

sleep 1
echo "Starting tar1090 web interface on port 80..."
lighttpd -D -f /etc/lighttpd/lighttpd.conf &
LIGHTTPD_PID=$!

# Wait for either process to exit
wait -n
exit_code=$?
echo "Process exited with code $exit_code, shutting down..."
cleanup
exit $exit_code
EOF

RUN chmod +x /usr/local/bin/start.sh

EXPOSE 8504
EXPOSE 80

CMD ["/usr/local/bin/start.sh"]
18 changes: 0 additions & 18 deletions Dockerfile.tar1090

This file was deleted.

7 changes: 7 additions & 0 deletions data/receiver.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"version": "tar1090-node",
"refresh": 1000,
"history": 120,
"binCraft": false,
"zstd": false
}
8 changes: 5 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ services:
- READSB_RX_LOCATION_ACCURACY=2
- READSB_STATS_RANGE=true
- READSB_NET_ENABLE=true
- DISABLE_WEBAPP=true
- READSB_NET_CONNECTOR=192.168.8.183,30005,beast_in
volumes:
- readsb-autogain:/run/autogain
- readsb-collectd:/run/collectd
- readsb-run:/run/readsb
tmpfs:
- /var/log:size=32M

tar1090:
build:
context: .
dockerfile: Dockerfile.tar1090
dockerfile: Dockerfile
container_name: tar1090
hostname: tar1090
restart: unless-stopped
Expand All @@ -40,13 +40,15 @@ services:
- LONG=${RECEIVER_LON:-0}
- TAR1090_DEFAULTCENTERLAT=${RECEIVER_LAT:-0}
- TAR1090_DEFAULTCENTERLON=${RECEIVER_LON:-0}
- READSB_URL=http://127.0.0.1:80/data/aircraft.json
- ADSBLOL_ENABLED=${ADSBLOL_ENABLED:-false}
- RECEIVER_LAT=${RECEIVER_LAT:-0}
- RECEIVER_LON=${RECEIVER_LON:-0}
- ADSBLOL_RADIUS=${ADSBLOL_RADIUS:-40}
- PROXY_PORT=3005
volumes:
- readsb-run:/run/readsb:ro

volumes:
readsb-autogain:
readsb-collectd:
readsb-run:
20 changes: 0 additions & 20 deletions docker/entrypoint.sh

This file was deleted.

2 changes: 2 additions & 0 deletions docker/lighttpd-proxy.conf
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Note: Port 3005 is hardcoded here and in proxy/server.js (PROXY_PORT default).
# Do not change PROXY_PORT env var - it will break the lighttpd proxy config.
server.modules += ( "mod_proxy" )

$HTTP["url"] =~ "^/data/aircraft\.json" {
Expand Down
60 changes: 40 additions & 20 deletions proxy/server.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
const http = require('http');
const https = require('https');
const fs = require('fs').promises;

const READSB_URL = process.env.READSB_URL || 'http://127.0.0.1:80/data/aircraft.json';
const LOCAL_DATA_PATH = process.env.LOCAL_DATA_PATH || '/run/readsb/aircraft.json';
const ADSBLOL_ENABLED = process.env.ADSBLOL_ENABLED === 'true';
const RECEIVER_LAT = parseFloat(process.env.RECEIVER_LAT || '0');
const RECEIVER_LON = parseFloat(process.env.RECEIVER_LON || '0');
const ADSBLOL_RADIUS = parseInt(process.env.ADSBLOL_RADIUS || '40');
const PORT = parseInt(process.env.PROXY_PORT || '3000');
const PORT = parseInt(process.env.PROXY_PORT || '3005');

const ADSBLOL_API = `https://api.adsb.lol/v2/lat/${RECEIVER_LAT}/lon/${RECEIVER_LON}/dist/${ADSBLOL_RADIUS}`;

Expand Down Expand Up @@ -80,31 +81,50 @@ function convertAdsbLolToReadsb(adsbLolData) {
};
}

async function getAircraftData() {
async function fetchAdsbLol() {
console.log('Fetching from adsb.lol...');
const adsbLolData = await fetchUrl(ADSBLOL_API);
const convertedData = convertAdsbLolToReadsb(adsbLolData);
console.log(`adsb.lol: ${convertedData.aircraft?.length || 0} aircraft`);
return { data: convertedData, source: 'adsb.lol' };
}

async function readLocalFile() {
try {
console.log('Attempting to fetch from local readsb...');
const localData = await fetchUrl(READSB_URL);
console.log(`✓ Local readsb: ${localData.aircraft?.length || 0} aircraft`);
return { data: localData, source: 'local' };
const data = await fs.readFile(LOCAL_DATA_PATH, 'utf8');
return JSON.parse(data);
} catch (error) {
console.log(`✗ Local readsb failed: ${error.message}`);
return null;
}
}

if (!ADSBLOL_ENABLED) {
console.log('✗ adsb.lol fallback disabled');
throw new Error('Local feed unavailable and fallback disabled');
}
async function getAircraftData() {
// Try local file first (readsb writes to /run/readsb/aircraft.json)
const localData = await readLocalFile();

if (localData && localData.aircraft?.length > 0) {
console.log(`Local file: ${localData.aircraft.length} aircraft`);
return { data: localData, source: 'local' };
}

// Try adsb.lol fallback if enabled
if (ADSBLOL_ENABLED) {
const reason = localData ? '0 aircraft from local' : 'local file not found';
console.log(`Falling back to adsb.lol (${reason})...`);
try {
console.log('Attempting fallback to adsb.lol...');
const adsbLolData = await fetchUrl(ADSBLOL_API);
const convertedData = convertAdsbLolToReadsb(adsbLolData);
console.log(`✓ adsb.lol fallback: ${convertedData.aircraft?.length || 0} aircraft`);
return { data: convertedData, source: 'adsb.lol' };
return await fetchAdsbLol();
} catch (fallbackError) {
console.log(`✗ adsb.lol fallback failed: ${fallbackError.message}`);
throw new Error('Both local and fallback feeds unavailable');
console.log(`adsb.lol fallback failed: ${fallbackError.message}`);
}
}

// Return local data even if empty (if we got a response)
if (localData) {
return { data: localData, source: 'local' };
}

// No data sources available
throw new Error('No data sources available');
}

const server = http.createServer(async (req, res) => {
Expand Down Expand Up @@ -136,7 +156,7 @@ const server = http.createServer(async (req, res) => {

server.listen(PORT, () => {
console.log(`Aircraft data proxy listening on port ${PORT}`);
console.log(`Local feed: ${READSB_URL}`);
console.log(`Local data file: ${LOCAL_DATA_PATH}`);
console.log(`adsb.lol fallback: ${ADSBLOL_ENABLED ? 'enabled' : 'disabled'}`);
if (ADSBLOL_ENABLED) {
console.log(`adsb.lol API: ${ADSBLOL_API}`);
Expand Down