Skip to content
Open
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
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ sudo ./scripts/download-content.sh

**7. Use it.** Join the Wi-Fi network you created and open **http://box.lan/home**.

### Take it with you: phone downloads

The Wi-Fi reaches only a few dozen metres. **http://box.lan/home/telefon.html** ("Telefona İndir" on the homepage) lets people install the Kiwix Android app from the box and download the ZIM files on it, so they can keep reading after they leave. The ZIM files are served straight from `/library/zims/content` (no copies, no extra space), Turkish content first, each download limited to 2 MB/s so one phone can't choke the hotspot. Fetch the app once while the Pi has internet:
```bash
sudo ./scripts/download-app.sh
```
It fetches two builds from Kiwix's GitHub releases, about 70 MB for current phones and 64 MB for older ones, each checked against the SHA-256 GitHub publishes; re-run it later to get a newer version. iPhones can't install the app from the box, since Apple only allows the App Store; the page links to Kiwix on the App Store for phones with mobile data, and the downloaded files open in Kiwix once it is installed.

### Optional: internet over a USB Wi-Fi adapter

The Pi's built-in Wi-Fi is dedicated to the hotspot. If the Pi needs internet temporarily (updates, new content) and there is no Ethernet cable, add a cheap USB Wi-Fi adapter as a second card. Adapters with the MediaTek MT7601U chip work out of the box.
Expand Down Expand Up @@ -94,12 +102,16 @@ install.sh main installer
scripts/setup-ssd.sh mounts the SSD at /library
scripts/download-content.sh downloads/updates ZIM files from Kiwix
scripts/install-homepage.sh installs the custom homepage
scripts/install-telefon.sh sets up phone downloads
scripts/download-app.sh downloads the Kiwix Android app for phones
scripts/status.sh health check: disk, services, hotspot, content, power
scripts/disk-health.sh disk and power health check (saglik/: its systemd timer)
config/iiab-overrides.yml Internet-in-a-Box settings
config/content.conf list of content to download
www/home/index.html homepage
www/home/acil.html emergency page (Turkish, sourced from AFAD and EPA)
www/home/telefon.html phone downloads page
telefon/ nginx config for phone downloads
```

### Troubleshooting
Expand Down Expand Up @@ -199,6 +211,14 @@ sudo ./scripts/download-content.sh

**7. Kullan.** Oluşturduğun Wi-Fi ağına bağlan ve **http://box.lan/home** adresini aç.

### Bilgiyi yanında götür: telefona indirme

Wi-Fi yalnızca birkaç on metre çeker. **http://box.lan/home/telefon.html** (ana sayfada "Telefona İndir") sayfasından insanlar Kiwix'in Android uygulamasını kutudan kurar ve kutudaki ZIM dosyalarını indirir; kutudan uzaklaşınca da okumaya devam eder. ZIM dosyaları doğrudan `/library/zims/content` klasöründen sunulur (kopya yok, ek yer tutmaz), Türkçe içerikler en üstte, her indirme 2 MB/sn ile sınırlı ki tek bir telefon hotspot'u tıkamasın. Uygulamayı Pi internete bağlıyken bir kez indir:
```bash
sudo ./scripts/download-app.sh
```
Kiwix'in GitHub sürümlerinden iki paket indirir, güncel telefonlar için yaklaşık 70 MB, eskiler için 64 MB; her biri GitHub'ın yayımladığı SHA-256 ile doğrulanır. Yeni sürüm için betiği tekrar çalıştır. Apple yalnızca App Store'dan kuruluma izin verdiği için iPhone'a uygulama kutudan kurulamaz; sayfa, mobil verisi olan telefonlar için App Store'daki Kiwix'e bağlantı verir, indirilen dosyalar Kiwix kurulunca açılır.

### İsteğe bağlı: USB Wi-Fi adaptörüyle internet

Pi'nin dahili Wi-Fi kartı sadece hotspot için kullanılıyor. Pi'nin geçici olarak internete çıkması gerekirse (güncelleme, yeni içerik) ve Ethernet kablosu yoksa ikinci kart olarak ucuz bir USB Wi-Fi adaptörü tak. MediaTek MT7601U yongalı adaptörler kurulumsuz çalışıyor.
Expand Down Expand Up @@ -232,12 +252,16 @@ install.sh ana kurulum betiği
scripts/setup-ssd.sh SSD'yi /library klasörüne bağlar
scripts/download-content.sh Kiwix'ten ZIM dosyalarını indirir/günceller
scripts/install-homepage.sh özel ana sayfayı kurar
scripts/install-telefon.sh telefona indirmeyi kurar
scripts/download-app.sh telefonlar için Kiwix Android uygulamasını indirir
scripts/status.sh sağlık kontrolü: disk, servisler, hotspot, içerik, güç
scripts/disk-health.sh disk ve güç sağlığı denetimi (saglik/: systemd zamanlayıcısı)
config/iiab-overrides.yml Internet-in-a-Box ayarları
config/content.conf indirilecek içerik listesi
www/home/index.html ana sayfa
www/home/acil.html acil durum sayfası (AFAD ve EPA kaynaklı)
www/home/telefon.html telefona indirme sayfası
telefon/ telefona indirme için nginx ayarı
```

### Sorun giderme
Expand Down
4 changes: 3 additions & 1 deletion install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,15 @@ fi
say "3/3 Ana sayfa / homepage"
"$REPO/scripts/install-homepage.sh"
"$REPO/scripts/install-disk-health.sh"
"$REPO/scripts/install-telefon.sh"

IP=$(hostname -I | awk '{print $1}')
say "Bitti / done"
cat <<EOF
Ana sayfa / homepage: http://$IP/home (hotspot üzerinden / via hotspot: http://box.lan/home)
Yönetim / admin console: http://$IP/admin (kullanıcı / user: iiab-admin)

Sıradaki adım / next step: içerikleri indir / download content
Sıradaki adım / next step: içerikleri ve telefon uygulamasını indir / download content and the phone app
sudo ./scripts/download-content.sh
sudo ./scripts/download-app.sh
EOF
61 changes: 61 additions & 0 deletions scripts/download-app.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/env bash
# Kiwix'in Android uygulamasını kutuya indirir; telefonlar onu internetsiz, ana sayfadan kurar.
# Downloads the Kiwix Android app so phones can install it from the homepage without internet.
#
# Kullanım / usage: sudo ./scripts/download-app.sh
set -euo pipefail

API=https://api.github.com/repos/kiwix/kiwix-android/releases/latest
# Güncel telefonlar arm64-v8a (~70 MB), eskiler armeabi-v7a (~64 MB); hepsi bir arada paket 236 MB.
# Current phones are arm64-v8a, older ones armeabi-v7a; the all-in-one package is 236 MB.
ABIS="arm64-v8a armeabi-v7a"
DIR=/library/www/html/telefon

die(){ echo "HATA / ERROR: $*" >&2; exit 1; }
[ "$EUID" -eq 0 ] || die "sudo ile çalıştır / run with sudo"
[ -d /library/www/html ] || die "/library/www/html yok. Önce / install first: sudo ./install.sh"

# GitHub her dosyanın SHA-256 özetini verir; Kiwix sunucusundaki .md5 dosyaları güncel değil.
# GitHub publishes a SHA-256 for every file; the .md5 files on Kiwix's own server are stale.
RELEASE=$(curl -fsSL "$API") || die "GitHub'a ulaşılamadı / cannot reach GitHub"
LIST=$(printf '%s' "$RELEASE" | python3 -c '
import json, sys
r = json.load(sys.stdin)
for abi in sys.argv[1].split():
for a in r["assets"]:
if a["name"].endswith(f"-{abi}-standalone.apk") and (a.get("digest") or "").startswith("sha256:"):
print(abi, a["name"], a["digest"][7:], a["browser_download_url"])
' "$ABIS")
[ -n "$LIST" ] || die "sürümde uygulama dosyası yok / no app files in the release"

install -d -m 755 "$DIR"
keep=()
while read -r _ name sha url; do
keep+=("$name")
if [ -f "$DIR/$name" ]; then
echo "Güncel / up to date: $name"
continue
fi
echo "İndiriliyor / downloading $name"
curl -fL --retry 3 -C - -o "$DIR/$name.part" "$url" || true
if ! echo "$sha $DIR/$name.part" | sha256sum -c --status 2>/dev/null; then
rm -f "$DIR/$name.part"
die "SHA-256 tutmadı, dosya silindi / checksum mismatch, file removed: $name"
fi
mv "$DIR/$name.part" "$DIR/$name"
chmod 644 "$DIR/$name"
done <<< "$LIST"

for f in "$DIR"/*.apk; do
[ -e "$f" ] || continue
printf '%s\n' "${keep[@]}" | grep -qxF "$(basename "$f")" || rm -f "$f"
done

python3 -c '
import json, os, sys
print(json.dumps([{"abi": abi, "file": name, "size": os.path.getsize(os.path.join(sys.argv[1], name))}
for abi, name, *_ in (line.split() for line in sys.stdin if line.strip())]))
' "$DIR" <<< "$LIST" > "$DIR/app.json.tmp"
chmod 644 "$DIR/app.json.tmp"
mv "$DIR/app.json.tmp" "$DIR/app.json"
echo "Telefonlar buradan kurar / phones install it from: http://box.lan/home/telefon.html"
2 changes: 1 addition & 1 deletion scripts/install-homepage.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ DST=/library/www/html/home
if [ -f "$DST/index.html" ] && [ ! -f "$DST/index.html.iiab-original" ]; then
cp "$DST/index.html" "$DST/index.html.iiab-original"
fi
cp "$SRC/index.html" "$SRC/acil.html" "$DST/"
cp "$SRC"/*.html "$DST/"
echo "Ana sayfa kuruldu / homepage installed: http://box.lan/home"
echo "Geri almak için / to restore: sudo cp $DST/index.html.iiab-original $DST/index.html"
29 changes: 29 additions & 0 deletions scripts/install-telefon.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
# "Telefona indir" sayfasını kurar: telefonlar Kiwix uygulamasını ve içerikleri kutudan indirir.
# Tekrar çalıştırmak güvenlidir.
# Installs the "Telefona indir" page: phones download the Kiwix app and content from the box. Safe to re-run.
#
# Kullanım / usage: sudo ./scripts/install-telefon.sh
set -euo pipefail

REPO="$(cd "$(dirname "$0")/.." && pwd)"
NGINX_CONF=/etc/nginx/conf.d/has-telefon-nginx.conf

die(){ echo "HATA / ERROR: $*" >&2; exit 1; }
[ "$EUID" -eq 0 ] || die "sudo ile çalıştır / run with sudo: sudo ./scripts/install-telefon.sh"
[ -d /etc/nginx/conf.d ] || die "nginx bulunamadı. Önce / install first: sudo ./install.sh"

install -d -m 755 /library/www/html/telefon
install -m 644 "$REPO/telefon/telefon-nginx.conf" "$NGINX_CONF"
if nginx -t 2>/dev/null; then
systemctl reload nginx
else
rm -f "$NGINX_CONF"
nginx -t || true
die "nginx ayarı hatalı, geri alındı / nginx config failed and was removed"
fi

cat <<EOT
Telefona indir sayfası kuruldu / page installed: http://box.lan/home/telefon.html
Kiwix uygulamasını indir / download the Kiwix app: sudo ./scripts/download-app.sh
EOT
14 changes: 14 additions & 0 deletions telefon/telefon-nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# HAS_PrepperDisk "Telefona indir": ZIM dosyaları ve listeleri, Kiwix Android uygulaması.
# Internet-in-a-Box bu klasördeki dosyaları server bloğunun içine ekler.
# ZIM files and their JSON list plus the Kiwix Android app, for phones.
# Internet-in-a-Box includes files from /etc/nginx/conf.d inside its server block.
location ^~ /telefon/zim/ {
alias /library/zims/content/;
autoindex on;
autoindex_format json;
# Tek bir indirme hotspot'u tıkamasın / one download must not choke the hotspot
limit_rate 2m;
}
location ^~ /telefon/ {
limit_rate 2m;
}
4 changes: 2 additions & 2 deletions www/home/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
body{font-size:14px}
.wrap{padding:4px}
.navlinks{font-size:15px;line-height:2.2}
.navlinks a{margin:0 14px 0 0}
.navlinks a{margin:0 14px 0 0;display:inline-block}
.crumb{font-size:14px}
.small{font-size:12px}
.forumline th{font-size:13px}
Expand All @@ -96,7 +96,7 @@
<div class="slogan">Çevrimdışı bilgi arşivi &mdash; internet olmasa da açık</div>
</div>
<div class="navlinks">
<a href="/home/acil.html" class="acil">Acil Durum</a><a href="/kiwix/">Kütüphane</a><a href="/maps">Haritalar</a><a href="/admin">Yönetim</a>
<a href="/home/acil.html" class="acil">Acil Durum</a><a href="/kiwix/">Kütüphane</a><a href="/home/telefon.html">Telefona İndir</a><a href="/maps">Haritalar</a><a href="/admin">Yönetim</a>
</div>
</div>

Expand Down
156 changes: 156 additions & 0 deletions www/home/telefon.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
<!doctype html>
<html lang="tr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>HAS_PrepperDisk :: Telefona İndir</title>
<style>
body{margin:0;background:#E5E5E5;color:#000;font-family:Verdana,Tahoma,Arial,sans-serif;font-size:15px}
a{color:#006699}
.wrap{max-width:760px;margin:0 auto;padding:8px}
.bodyline{background:#fff;border:1px solid #98AAB1;padding:8px}
.logo{font-family:"Trebuchet MS",Verdana,sans-serif;font-size:24px;font-weight:bold;color:#006699;letter-spacing:-1px;padding:4px}
.logo span{color:#DD6900}
.crumb{font-weight:bold;margin:6px 2px 10px}
h2{font-size:16px;margin:0;padding:8px;background:linear-gradient(#3F7FAD,#006699);color:#FFA34F}
.box{border:1px solid #006699;margin-bottom:12px}
.in{padding:10px;background:#EFEFEF}
.in p{margin:0 0 8px}
.in ol{margin:8px 0;padding-left:22px}
.in li{margin-bottom:4px}
.btn{display:inline-block;font-weight:bold;background:#FAFAFA;border:1px solid #000;padding:8px 16px;color:#000;text-decoration:none}
.note{font-size:13px;color:#444;margin-top:8px}
.entry{padding:10px;border-top:1px solid #C7D0D7;background:#fff;display:flex;align-items:center;gap:10px}
.entry .ad{font-weight:bold;flex:1}
.entry .boy{font-size:13px;color:#555;white-space:nowrap}
.tag{display:inline-block;font-size:11px;font-weight:bold;padding:1px 5px;color:#fff;background:#006699;margin-left:6px;vertical-align:middle}
.tag.tr{background:#B00000}
.warn{color:#B00000;font-weight:bold}
</style>
</head>
<body>
<div class="wrap"><div class="bodyline">
<div class="logo">HAS_<span>Prepper</span>Disk</div>
<div class="crumb"><a href="/home">Ana Sayfa</a> &raquo; Telefona İndir</div>

<div class="box">
<div class="in">
<p>Kutunun Wi-Fi'ı yalnızca birkaç on metre çeker. Uygulamayı ve içerikleri telefonuna indirirsen kutudan uzaklaştığında da internetsiz okuyabilirsin.</p>
<p class="note">Wi-Fi'a bağlanınca kendiliğinden açılan pencerede indirme çalışmayabilir. Bu sayfayı Chrome ya da Safari'de aç: <b>http://box.lan/home/telefon.html</b></p>
</div>
</div>

<div class="box">
<h2>1. Kiwix uygulaması (Android)</h2>
<div class="in">
<div id="app">Yükleniyor...</div>
<ol>
<li>İndirme bitince dosyaya dokun.</li>
<li>Telefon izin isterse "Bu kaynaktan yüklemeye izin ver"i aç.</li>
<li>"Yükle"ye dokun.</li>
</ol>
<div class="note"><b>iPhone:</b> Apple, uygulamaların App Store dışından kurulmasına izin vermiyor. Telefonunda internet varsa (mobil veri de olur) Kiwix'i buradan kur: <a class="btn" href="https://apps.apple.com/app/id997079563">App Store'da Kiwix</a><br>İnternet yoksa, aşağıdaki dosyaları şimdi indir, Kiwix'i ilk fırsatta kurup açarsın.</div>
</div>
</div>

<div class="box">
<h2>2. İçerikler</h2>
<div class="in">
<p>Dosyayı indir, sonra Kiwix'i aç. Dosya kütüphanede görünmezse Kiwix içinden İndirilenler klasöründeki dosyayı aç. Büyük dosyalar için telefonunda yer olduğundan emin ol.</p>
</div>
<div id="liste"></div>
</div>

<div class="box" id="acil" hidden>
<h2>3. Acil durum bilgileri</h2>
<div class="in">
<p>Acil durum sayfasını telefonuna kaydet; uygulama gerektirmez, tarayıcıda internetsiz açılır.</p>
<a class="btn" href="/home/acil.html" download="acil-durum.html">Acil durum sayfasını indir</a>
</div>
</div>
</div></div>

<script>
const esc = s => String(s).replace(/[&<>"]/g, c => ({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"}[c]));
const $ = id => document.getElementById(id);

const NAMES = {
"wikipedia_tr_all_maxi": "Vikipedi (Türkçe)",
"wiktionary_tr_all_nopic": "Vikisözlük (Türkçe)",
"wikibooks_tr_all_maxi": "Vikikitap (Türkçe)",
"wikisource_tr_all_maxi": "Vikikaynak (Türkçe)",
"wikiquote_tr_all_maxi": "Vikisöz (Türkçe)",
"ifixit_tr_all": "iFixit (Türkçe)",
"phet_tr_all": "PhET Simülasyonları (Türkçe)",
"wikipedia_en_all_maxi": "Wikipedia (English)",
"zimgit-post-disaster_en": "Afet Sonrası Kitaplığı",
"zimgit-water_en": "Su Arıtma",
"zimgit-food-preparation_en": "Gıda Hazırlama ve Saklama",
"zimgit-knots_en": "Düğümler Kitaplığı",
"ready.gov_en_all": "Ready.gov",
"solar.lowtechmagazine.com_mul_all": "Low-tech Magazine",
"appropedia_en_all_maxi": "Appropedia",
"wikipedia_en_medicine_maxi": "WikiMed",
"mdwiki_en_all_maxi": "MDWiki",
"medlineplus.gov_en_all": "MedlinePlus",
"zimgit-medicine_en": "Tıp Kitaplığı",
"irp.fas.org_en_military-medicine": "Askeri Sahra Tıbbı",
"fas-military-medicine_en": "Askeri Sahra Tıbbı",
"nhs.uk_en_medicines": "NHS İlaç Rehberi",
"ifixit_en_all": "iFixit (English)",
"diy.stackexchange.com_en_all": "Ev Tamiri (DIY)",
"gardening.stackexchange.com_en_all": "Bahçecilik",
"cooking.stackexchange.com_en_all": "Yemek Pişirme",
"ham.stackexchange.com_en_all": "Amatör Telsiz",
"mechanics.stackexchange.com_en_all": "Araç Bakımı",
"outdoors.stackexchange.com_en_all": "Doğa ve Kamp",
"sustainability.stackexchange.com_en_all": "Sürdürülebilirlik"
};

function fmtSize(b){
if (b >= 1e9) return (b / 1e9).toFixed(1).replace(".", ",") + " GB";
return Math.max(1, Math.round(b / 1e6)) + " MB";
}

async function getJSON(url){
try { const r = await fetch(url, {cache:"no-store"}); return r.ok ? await r.json() : null; }
catch(e) { return null; }
}

async function loadApp(){
const apps = await getJSON("/telefon/app.json") || [];
const yeni = apps.find(a => a.abi === "arm64-v8a"), eski = apps.find(a => a.abi === "armeabi-v7a");
const link = (a, text) => `<a class="btn" href="/telefon/${encodeURIComponent(a.file)}">${text} (${esc(fmtSize(a.size))})</a>`;
$("app").innerHTML = !apps.length
? `<span class="warn">Uygulama henüz kutuya yüklenmemiş.</span> <span class="note">Kutu sahibi: sudo ./scripts/download-app.sh</span>`
: (yeni ? link(yeni, "Kiwix'i indir") : "")
+ (eski ? `<div class="note">Telefon "uygulama yüklenmedi" derse eski telefonlar için olanı dene:<br>${link(eski, "Eski telefonlar için")}</div>` : "");
}

async function loadZims(){
const files = await getJSON("/telefon/zim/");
if (!files) { $("liste").innerHTML = `<div class="entry"><span class="warn">İçerik listesi okunamadı.</span></div>`; return; }
/* Aynı içeriğin yalnızca en yeni sürümü: ad_YYYY-MM.zim */
const latest = {};
for (const f of files) {
if (f.type !== "file" || !f.name.endsWith(".zim")) continue;
const m = f.name.match(/^(.+?)_(\d{4}-\d{2}[a-z]?)\.zim$/);
const key = m ? m[1] : f.name.slice(0, -4), ver = m ? m[2] : "";
if (!latest[key] || latest[key].ver < ver) latest[key] = {key, ver, file: f.name, size: f.size};
}
const items = Object.values(latest).sort((a, b) =>
(b.key.includes("_tr_") - a.key.includes("_tr_")) || a.size - b.size);
$("liste").innerHTML = items.length ? items.map(i => {
const tr = i.key.includes("_tr_");
return `<div class="entry"><span class="ad">${esc(NAMES[i.key] || i.key)}<span class="tag${tr ? " tr" : ""}">${tr ? "TR" : "EN"}</span></span>
<span class="boy">${esc(fmtSize(i.size))}</span>
<a class="btn" href="/telefon/zim/${encodeURIComponent(i.file)}" download>İndir</a></div>`;
}).join("") : `<div class="entry">Kutuda henüz içerik yok.</div>`;
}

fetch("/home/acil.html", {method:"HEAD"}).then(r => { $("acil").hidden = !r.ok; }).catch(() => {});
loadApp();
loadZims();
</script>
</body>
</html>
Loading