a2eff1a984
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
206 lines
14 KiB
Markdown
206 lines
14 KiB
Markdown
# 🚀 Canlıya Alma (Atlas Homeserver + Dokploy)
|
||
|
||
> Durum: ✅ Yayında — https://kakeibo.ta1sph.website (2026-11-12) · Yedekleme: betikler hazır, sunucuda kurulum bekliyor (§6)
|
||
|
||
## 1. Mimari
|
||
|
||
```
|
||
İnternet ──HTTPS──▶ Traefik (Dokploy) ──▶ kakeibo konteyneri :8080 ──▶ MongoDB (rs0)
|
||
├─ /api/* → Rust (Axum) API
|
||
└─ diğer → derlenmiş arayüz (React)
|
||
```
|
||
|
||
- **Tek imaj, tek servis, tek alan adı** → CORS yok, cookie aynı origin'de.
|
||
- İmaj: çok aşamalı `Dockerfile` (Node → arayüz derlemesi, Rust → release derleme, `debian:trixie-slim` çalışma imajı, root olmayan kullanıcı `kakeibo`, ~175 MB).
|
||
- Sağlık kontrolü: `GET /api/health` → `200 {"status":"ok","db":"ok"}`; veritabanı yoksa `503`.
|
||
- Kapanış: `SIGTERM` gelince süren istekler bitirilir, sonra kapanır.
|
||
|
||
## 2. Ortam değişkenleri
|
||
|
||
| Değişken | Zorunlu | Örnek / varsayılan | Açıklama |
|
||
|---|---|---|---|
|
||
| `MONGODB_URI` | ✅ | `mongodb://kakeibo_app:<şifre>@<host>:27017/kakeibo?authSource=kakeibo&replicaSet=rs0` | Ayrı, yalnızca `kakeibo` DB'sinde `readWrite` yetkili kullanıcı |
|
||
| `JWT_SECRET` | ✅ | `openssl rand -hex 32` çıktısı | En az 32 karakter. **Değişirse tüm oturumlar kapanır** |
|
||
| `APP_URL` | ✅ | `https://kakeibo.<alan-adı>` | `https://` ile başlarsa cookie `Secure` olur |
|
||
| `REGISTRATION` | – | `invite_only` | `invite_only`: ilk hane serbest, sonra yalnızca davet · `open`: herkes |
|
||
| `RUST_LOG` | – | `kakeibo=info,tower_http=info` | Log seviyesi |
|
||
| `RATES_REFRESH_MINUTES` | – | `60` | Otomatik kur yenileme aralığı (dk); `0` → kapalı. Konteynerin internete çıkışı gerekir: `www.tcmb.gov.tr` (döviz), `xaus.com` (altın). Erişilemezse son kur kalır, elle kur girilebilir |
|
||
| `TURNSTILE_SITE_KEY` | önerilir | `0x4AAAA…` | Cloudflare Turnstile **site anahtarı** (herkese açık). Giriş/kayıtta bot doğrulaması; ikisi de boşsa kapalı (§8) |
|
||
| `TURNSTILE_SECRET_KEY` | önerilir | `0x4AAAA…` | Turnstile **gizli anahtarı** — yalnızca sunucuda, kimseyle paylaşmayın. Konteynerin `challenges.cloudflare.com`'a çıkışı gerekir |
|
||
| `TRUST_PROXY` | – | `true` | Dokploy/Traefik arkasında gerçek istemci IP'si `X-Forwarded-For`'dan (giriş kilidi IP'ye göre). Vekil **yoksa** `false` |
|
||
| `PORT` | – | `8080` | İmajda ayarlı, değiştirmeyin |
|
||
| `STATIC_DIR` | – | `/app/static` | İmajda ayarlı, değiştirmeyin |
|
||
|
||
## 3. Yerelde imajı deneme
|
||
|
||
```bash
|
||
docker build -t kakeibo .
|
||
# Yerel MongoDB (27020) ile, geliştirme sunucusuyla çakışmasın diye 8090'da:
|
||
docker run --rm --network host --env-file .env -e PORT=8090 kakeibo
|
||
# → http://localhost:8090
|
||
```
|
||
|
||
Kontrol listesi (Faz 3 – Adım 1'de doğrulandı):
|
||
- [x] `/` ve `/defter` → `index.html` (`Cache-Control: no-cache`)
|
||
- [x] `/assets/*.js` → brotli/gzip, `max-age=31536000, immutable`
|
||
- [x] `/api/health` → `db: ok`; bilinmeyen `/api/...` → JSON 404
|
||
- [x] Docker `HEALTHCHECK` → healthy; süreç `kakeibo` kullanıcısıyla çalışır
|
||
- [x] `docker stop` → düzgün kapanış logu
|
||
|
||
## 4. Canlı MongoDB
|
||
|
||
- Dokploy'da **ayrı** MongoDB servisi (Bütçem'in veritabanı paylaşılmaz): proje `Kakeibo` → Database → MongoDB `kakeibo-mongo`.
|
||
- Servis (iç ağ) adı: **`kakeibo-kakeibomongo-fibzhc`**, imaj `mongo:7`, ağ `dokploy-network`, volume `kakeibo-kakeibomongo-fibzhc-data → /data/db`.
|
||
- Replica set: `rs0`, üye `kakeibo-kakeibomongo-fibzhc:27017` (Dokploy "Replica Sets" açık).
|
||
- Kullanıcılar: `kakeibo_root` (admin, `root`) · `kakeibo_app` (`kakeibo` DB, yalnızca `readWrite`). Şifreler 2026-11-12'de yenilendi (eskileri açığa çıkmıştı).
|
||
- Dış erişim: `docker service inspect … .Endpoint.Ports` → `null` (port yayınlanmıyor) ✅. External Port alanı boş kalmalı.
|
||
- Bağlantı:
|
||
```
|
||
mongodb://kakeibo_app:<APP_ŞİFRESİ>@kakeibo-kakeibomongo-fibzhc:27017/kakeibo?authSource=kakeibo&replicaSet=rs0
|
||
```
|
||
|
||
### ⚠️ Açık konu: MongoDB kimlik doğrulaması kapalı
|
||
Dokploy'un replica set betiği `mongod --replSet rs0 --bind_ip_all` ile başlatıyor (`--keyFile` yok) ve her deploy'da
|
||
Advanced → Command/Args'ı yok sayıp kendi betiğini yazıyor. Sonuç: kullanıcılar tanımlı ama şifre **zorunlu değil**;
|
||
`dokploy-network`'teki herhangi bir konteyner erişebilir. İnternetten erişim yok.
|
||
Çözüm adayları (ileride): Dokploy'un MongoDB'si yerine Compose servisi (kendi komutumuzla `--keyFile`) ya da Dokploy'da keyFile desteği.
|
||
Uygulama zaten kullanıcı/şifreyle bağlandığı için auth açıldığında uygulamada değişiklik gerekmez.
|
||
|
||
### Şifre girerken dikkat
|
||
- `mongosh` içinde `passwordPrompt()`'a **yapıştırmayın** (terminal görünmez karakter ekleyebiliyor → `U_STRINGPREP_PROHIBITED_ERROR`).
|
||
Bunun yerine: `const p = require('crypto').randomBytes(24).toString('hex'); db.changeUserPassword("kakeibo_app", p); print(p)`
|
||
- Şifreyi komuta düz yazmayın; sonra `rm -f /root/.mongodb/mongosh/mongosh_repl_history`.
|
||
- "Rebuild Database" veriyi siler ve Dokploy betiği yeni root şifresini servis ayarına düz metin yazar.
|
||
|
||
## 5. Dokploy kurulumu
|
||
|
||
Proje `Kakeibo` → Create Service → **Application** `kakeibo-app` (iç ad: `kakeibo-kakeiboapp-nd5kek`).
|
||
|
||
| Bölüm | Ayar |
|
||
|---|---|
|
||
| General → Provider | **Gitea** sekmesi (varsayılan GitHub sekmesi seçili kalırsa: `Github Provider not found`) → repo `suphicakir/kakeibo`, branch `main`, Build Path `/` → **Save** |
|
||
| General → Build Type | **Dockerfile**, dosya `Dockerfile`, context `.` |
|
||
| Environment | §2'deki değişkenler (`MONGODB_URI`, `JWT_SECRET`, `APP_URL=https://kakeibo.ta1sph.website`, `REGISTRATION=invite_only`, `RUST_LOG`) |
|
||
| Domains | Host `kakeibo.ta1sph.website`, Path `/`, **Container Port `8080`**, HTTPS açık, Let's Encrypt |
|
||
| Auto Deploy | Açık → Gitea'ya push = otomatik yayın |
|
||
|
||
Notlar:
|
||
- İlk derleme ~6–10 dk (Rust bağımlılıkları); sonrakiler katman önbelleğiyle daha hızlı.
|
||
- Derleme hatası **Deployments** → ilgili deploy logunda görünür; **Logs** sekmesi yalnızca çalışan konteyneri gösterir.
|
||
- Dokploy, Gitea'daki son commit'i derler → değişiklikler önce push edilmeli.
|
||
- Geri dönüş (rollback): Deployments listesinden önceki başarılı deploy seçilerek yeniden yayınlanır.
|
||
- DNS: `kakeibo.ta1sph.website` → ev dış IP'si; modemde 80/443 → Atlas yönlendirmesi Let's Encrypt için gerekli.
|
||
- Sade metin `404 page not found` + "Not secure" = Traefik'in kendi yanıtı (bu host için router yok): domain eklenip **Redeploy** yapılmamış ya da konteyner çalışmıyor demektir.
|
||
|
||
### İlk yayın kontrolü (2026-11-12) ✅
|
||
- `/api/health` → `{"status":"ok","db":"ok"}`, servis `1/1` healthy, migration v1 + indeksler canlı DB'de oluştu.
|
||
- TLS: Let's Encrypt (otomatik yenileme), `/kayit` doğrudan açılıyor (SPA).
|
||
- İlk hesap açıldı → `GET /api/auth/registration` = `new_household_allowed: false` (yalnızca davet).
|
||
|
||
## 6. Yedekleme ve geri yükleme
|
||
|
||
```
|
||
Atlas 03:30 backup.sh ──▶ /var/backups/kakeibo/kakeibo-YYYY-MM-DD_HHMM.archive.gz (son 14)
|
||
│ rsync (SSH)
|
||
Bu bilgisayar 09:00 / açılışta ──▶ ~/Yedekler/kakeibo/ (son 60, 2 günden eskiyse masaüstü uyarısı)
|
||
```
|
||
|
||
| Dosya | Nerede çalışır | İş |
|
||
|---|---|---|
|
||
| `scripts/backup.sh` | Atlas → `/usr/local/lib/kakeibo/` | `mongodump --archive --gzip` → gzip + `mongorestore --dryRun` denetimi → son 14'ü tutar |
|
||
| `scripts/restore.sh` | Atlas (ve yerelde `npm run db:restore`) | yedeği **başka adla** geri yükler, kayıt sayılarını gösterir/karşılaştırır |
|
||
| `scripts/backup-pull.sh` | bu bilgisayar | yeni yedekleri çeker, eskiyse uyarır |
|
||
| `deploy/systemd/*` | ikisi | zamanlayıcılar (`Persistent=true`: kapalıyken kaçan çalışma açılınca yapılır) |
|
||
|
||
Yerel deneme: `npm run db:backup` (kakeibo_dev → `./backups`, git'e girmez) ·
|
||
`COMPARE_DB=kakeibo_dev npm run db:restore -- latest kakeibo_restore_test`.
|
||
|
||
### 6.1 Atlas'ta kurulum (bir kez)
|
||
Bu bilgisayarda, proje klasöründe:
|
||
```bash
|
||
scp scripts/backup.sh scripts/restore.sh deploy/systemd/kakeibo-backup.service deploy/systemd/kakeibo-backup.timer atlas:/tmp/
|
||
ssh atlas
|
||
```
|
||
Atlas'ta:
|
||
```bash
|
||
sudo install -Dm755 /tmp/backup.sh /usr/local/lib/kakeibo/backup.sh
|
||
sudo install -Dm755 /tmp/restore.sh /usr/local/lib/kakeibo/restore.sh
|
||
sudo install -m644 /tmp/kakeibo-backup.service /tmp/kakeibo-backup.timer /etc/systemd/system/
|
||
```
|
||
- SSH'ye **root olmayan** bir kullanıcıyla bağlanıyorsanız: `sudo systemctl edit --full kakeibo-backup.service` →
|
||
`#Environment=BACKUP_GROUP=…` satırının başındaki `#`'i kaldırıp kendi kullanıcı grubunuzu yazın (yoksa yedekleri çekemezsiniz).
|
||
|
||
İlk yedek ve kontrol:
|
||
```bash
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl start kakeibo-backup.service
|
||
journalctl -u kakeibo-backup -n 20 --no-pager # "tamam: …" görülmeli
|
||
```
|
||
|
||
### 6.2 Geri yükleme denemesi (kurulumda bir kez, sonra ayda bir önerilir)
|
||
Canlı veriye dokunmaz; ayrı `kakeibo_restore_test` veritabanına yükler ve sayıları karşılaştırır:
|
||
```bash
|
||
sudo COMPARE_DB=kakeibo /usr/local/lib/kakeibo/restore.sh latest kakeibo_restore_test
|
||
# "✅ kakeibo ile aynı" görülmeli. Sonra deneme veritabanını silin:
|
||
sudo docker exec $(sudo docker ps -q -f name=kakeibo-kakeibomongo-fibzhc) mongosh --quiet kakeibo_restore_test --eval 'db.dropDatabase()'
|
||
```
|
||
Sonra zamanlayıcıyı açın:
|
||
```bash
|
||
sudo systemctl enable --now kakeibo-backup.timer
|
||
systemctl list-timers kakeibo-backup.timer # bir sonraki çalışma saati
|
||
```
|
||
|
||
### 6.3 Bu bilgisayarda (sunucu dışı kopya)
|
||
Önce şifresiz SSH çalışmalı: `ssh -o BatchMode=yes atlas ls /var/backups/kakeibo` yedek dosyasını listelemeli.
|
||
```bash
|
||
scripts/backup-pull.sh # elle bir kez: ~/Yedekler/kakeibo/
|
||
mkdir -p ~/.config/systemd/user
|
||
cp deploy/systemd/kakeibo-backup-pull.service deploy/systemd/kakeibo-backup-pull.timer ~/.config/systemd/user/
|
||
systemctl --user daemon-reload
|
||
systemctl --user enable --now kakeibo-backup-pull.timer
|
||
```
|
||
- SSH'de sunucunun adı `atlas` değilse: `systemctl --user edit --full kakeibo-backup-pull.service` → `KAKEIBO_BACKUP_HOST` satırı.
|
||
- Günlük: `journalctl --user -u kakeibo-backup-pull -n 20`.
|
||
|
||
### 6.4 Felaket sonrası geri yükleme (canlı veritabanının yerine)
|
||
1. Dokploy → `kakeibo-app` → **Stop** (yükleme sırasında yazma olmasın).
|
||
2. Gerekirse yedeği sunucuya kopyalayın: `scp ~/Yedekler/kakeibo/<dosya> atlas:/tmp/`.
|
||
3. Atlas'ta: `sudo /usr/local/lib/kakeibo/restore.sh /tmp/<dosya> kakeibo --force` (önce mevcut `kakeibo` silinir).
|
||
4. `kakeibo-app` → **Start**, `/api/health` ve giriş kontrolü.
|
||
- MongoDB servisi tamamen yeniden kurulduysa önce §4'teki kullanıcıları (`kakeibo_app`) yeniden oluşturun.
|
||
- Yedekte kullanıcı şifre özetleri (argon2) ve oturum bilgisi bulunur → yedek dosyalarını paylaşmayın.
|
||
|
||
### Notlar
|
||
- MongoDB'de kimlik doğrulaması zorunlu olmadığından (§4) `mongodump` konteynerin içinden şifresiz çalışır.
|
||
Auth açıldığında yedek için ayrı bir `backup` rolü kullanıcısı ve betiğe `--config` ile şifre dosyası eklenmeli.
|
||
- Yedek boyutu şu an birkaç KB–MB; 14 + 60 dosya önemsiz yer tutar.
|
||
|
||
## 7. Telefona kurulum (PWA) ve güncellemeler
|
||
|
||
Uygulama **HTTPS** üzerinden açıldığında telefona "uygulama gibi" kurulabilir (service worker yalnız HTTPS'te ve `localhost`'ta çalışır).
|
||
|
||
- **Android (Chrome):** siteyi aç → menü ⋮ → **Ana ekrana ekle / Uygulamayı yükle**.
|
||
- **iPhone (Safari):** siteyi aç → Paylaş ⬆ → **Ana Ekrana Ekle**.
|
||
- Ana ekran ikonuna uzun basınca kısayollar: **Harcama ekle**, **Panel**.
|
||
|
||
Nasıl çalışır:
|
||
- Telefonda yalnızca **uygulama kabuğu** (JS, CSS, ikonlar, fontlar) saklanır → ikinci açılış çok hızlı.
|
||
- **Veri (`/api`) asla saklanmaz**: ekranda her zaman sunucudaki güncel veri vardır; bağlantı yoksa "Bağlantı yok" uyarısı çıkar, kayıt girilemez (çevrimdışı kayıt Faz 11'de, Tauri ile).
|
||
- Yeni sürüm yayınlanınca açık uygulamada **"Yeni sürüm hazır → Yenile"** bildirimi çıkar (saatte bir de kendisi yoklar). Yenile'ye basmadan eski sürüm çalışmaya devam eder; girilen kayıtlar etkilenmez.
|
||
- Sunucu: `sw.js` ve `manifest.webmanifest` hiç önbelleğe alınmaz (`routes/spa.rs`), böylece güncelleme hemen fark edilir.
|
||
|
||
Sorun olursa (eski sürüm takılı kaldı vb.): tarayıcı ayarlarından site verilerini temizlemek service worker'ı da kaldırır.
|
||
|
||
## 8. Giriş güvenliği: bot doğrulaması + deneme kilidi
|
||
|
||
**Deneme kilidi (her zaman açık):** aynı bağlantıdan (IP) üst üste **3 hatalı** deneme → o bağlantıya giriş **ve** kayıt **15 dakika** kapanır. Sayılanlar: yanlış şifre, geçersiz/kullanılmış davet kodu, kapalı kayda deneme, zaten kayıtlı e-posta, başarısız bot doğrulaması. Form hataları (kısa şifre vb.) sayılmaz; başarılı giriş sayacı sıfırlar. Kilit e-postaya değil IP'ye göredir: adresinizi bilen biri sizi dışarıda bırakamaz. Sayaçlar bellektedir (yeni sürüm yayınlanınca sıfırlanır).
|
||
|
||
**Bot doğrulaması (Cloudflare Turnstile) — kurulum:**
|
||
1. Ücretsiz bir Cloudflare hesabı açın (alan adınızın Cloudflare'de olması gerekmez).
|
||
2. Panel → **Turnstile** → **Add widget**: ad `Kakeibo`, **Hostname**: canlı alan adınız (ör. `kakeibo.<alan-adı>`; yerel deneme için `localhost` da eklenebilir), **Widget Mode: Managed**.
|
||
3. Verilen **Site Key** ve **Secret Key**'i Dokploy → uygulama → **Environment**'a `TURNSTILE_SITE_KEY` / `TURNSTILE_SECRET_KEY` olarak girin, yeniden dağıtın.
|
||
4. Doğrulama: loglarda `Bot doğrulaması (Cloudflare Turnstile) açık`; giriş sayfasında "Başarılı ✓" kutusu.
|
||
|
||
Notlar: Gizli anahtarı arayüze, git'e ya da sohbete yazmayın. Cloudflare'e ulaşılamazsa giriş güvenlik için reddedilir (hata hakkından düşmez). Yerelde denemek için Cloudflare'in herkese açık test anahtarları: site `1x00000000000000000000AA`, gizli `1x0000000000000000000000000000000AA` (her zaman geçer; kutuda "yalnızca test" uyarısı görünür).
|
||
|