T ToutadminBelgeler

PHP sürümünü kurmak

PHP sürümü olduğu gibi paylaşımlı bir barındırmaya bırakılır: Composer yok, paket yöneticisi yok, çalışır tutulacak bir servis yok. Bir klasör, bir yapılandırma dosyası, bir zamanlanmış görev. Bu kılavuz boş klasörden üretimdeki kuruluma kadar gider; çoğu kılavuzun atladığı şeyler de içinde: izinler, TLS, zamanlanmış görev, yedekler ve güncelleme.

Gerekenler#

ÖğeSürümNeden
PHP8.1 ya da daha yenisiBirleşim türleri, enum, readonly, never: kod bunları kullanıyor.
pdo_sqliteVeritabanının tamamı. Onsuz hiçbir şey çalışmaz.
mbstringOn altı dilde dizgilerin doğru işlenmesi.
opensslönerilirVeritabanındaki sırların şifrelenmesi (SMTP parolaları, dışa aktarma belirteçleri).
curlönerilirGiden web kancaları ve yedeklerin üçüncü tarafa bırakılması.
intlönerilirTarihler Node sürümündeki gibi yazılır. Yoksa ay adları sözlüklerden gelir.

Kurulum sihirbazı bunların hepsini denetler ve devam etmenize izin vermeden önce ekranda gösterir: engelleyen şey kurulumu durdurur, isteğe bağlı olan ise engellemeden bildirilir.

Hiçbir bağımlılık yok

composer ile kurulacak hiçbir şey yoktur: IMAP okuyucusu, tar.gz arşivi, PDF, CSV, QR kodu ve HTTP istemcisi ürünün içinde yazılmıştır. Komut satırınızın olmadığı bir barındırmaya bırakabilmenizi sağlayan da budur.

1. Dosyaları yüklemek#

PHP sürümünün arşivini alın, sonra yükleyin. Klasör şöyle görünür:

toutadmin/
├── app/            kod (web sunucusu bunu asla sunmaz)
│   ├── Core/       çekirdek: yönlendirme, veritabanı, oturumlar, güvenlik
│   ├── Modules/    iş katmanı: insan kaynakları, mali işler, projeler, kalite…
│   ├── Controllers/
│   ├── views/      şablonlar
│   ├── locales/    on altı sözlük
│   └── schema.sql  şema, Node sürümündekinin aynısı
├── public/         ← web kökü buraya bakar, başka hiçbir yere değil
│   ├── index.php   ön denetleyici
│   ├── css/ js/
├── data/           veritabanı, kasa, imza klasörü, gelen belgeler, yedekler
├── tools/          cron.php, check-keys.php
├── tests/
├── config.sample.php
└── VERSION
Web kökü klasöre değil, public/ dizinine bakar

Bu sayfanın en önemli noktası budur. Sunucu klasörün tamamını sunarsa, data/app.sqlite herkesçe indirilebilir hâle gelir: veritabanının tamamı, parola özetleri ve kasa dahil. data/ klasörü public/ dizininin üstünde, ve bu bilerek böyledir.

Paylaşımlı barındırmada#

İki durum çıkar. Barındırıcınız bir alan adının kökünü seçmenize izin veriyorsa (cPanel, Plesk ve «kök klasör» kipindeki barındırmaların çoğu), klasörü www/ dışına bırakın ve alan adını toutadmin/public dizinine yönlendirin.

Kök dayatılmışsa — çoğunlukla www/ ya da public_html/public/ içeriğini oraya, gerisini ise üstüne bırakın; sonra public/index.php dosyasının başındaki yolu düzeltin:

www/                  ← dayatılan kök
├── index.php
├── css/  js/
toutadmin/            ← gerisi, webin erişemeyeceği yerde
├── app/
├── data/
└── config.php
// www/index.php — düzeltilecek satır
require_once __DIR__ . '/../toutadmin/app/bootstrap.php';

Son çare olarak, kökün üstüne hiçbir şey koyamıyorsanız, data/ içinde gelen .htaccess dosyası erişimi zaten reddeder. Bu bir emniyet kemeridir, çözüm değildir: her zaman kök dışındaki klasörü yeğleyin.

2. Yapılandırmayı yazmak#

cp config.sample.php config.php

Sonra config.php dosyasını açın:

<?php
return [
    // SQLite veritabanı ve veri klasörü: web kökünün dışında.
    'db_path'  => __DIR__ . '/data/app.sqlite',
    'data_dir' => __DIR__ . '/data',

    // Bu kuruluma özgü anahtar. Bir kez üretilir:
    //   php -r "echo bin2hex(random_bytes(32));"
    'session_secret' => '…',

    // Kurulum belirteci: bir sonraki adıma bakın.
    'install_token' => '',

    // Genel adres, e-postayla gönderilen bağlantılar için.
    'base_url' => 'https://intranet.ornek.com.tr',

    // Yalnızca güvenilir bir vekil sunucunun ardında açın.
    'trust_proxy' => false,

    // Tavanlar: adres başına çeyrek saatte oturum açma denemesi,
    // adres başına dakikada istek.
    'login_rate_limit'  => 10,
    'global_rate_limit' => 300,

    // Sona erme: işlemsizlik, ardından hiçbir etkinliğin uzatmadığı mutlak süre.
    'session_idle_minutes' => 60,
    'session_max_hours'    => 12,
];
Kuruluma özgü anahtar

İmza klasöründeki imzaları mühürler ve veritabanındaki sırları şifreler. Onu yitirmek imzaları geçersiz kılar. Örnekteki değeri olduğu gibi bırakırsanız ürün kendi başına data/.instance-key içinde bir tane üretir (izinler 0600) — o zaman bu dosyayı da geri kalanla birlikte yedeklemeyi unutmayın.

Yapılandırmayı başka bir yerde de tutabilirsiniz: TOUTADMIN_CONFIG ortam değişkeni başka bir dosyayı gösterir.

3. Kurulum belirteci koymak#

Dosyaların yüklenmesiyle sihirbaza uğramanız arasında kurulum onu bulanın olur: ilk gelen yönetim hesabını oluşturur. Pencere kısadır, ama yeter. O yüzden yayına almadan önce rastgele bir değer koyun:

php -r "echo bin2hex(random_bytes(16));"
# sonra config.php içine:
'install_token' => 'c3f1…',

Sihirbaz bunu ister, sabit sürede karşılaştırır — sıradan bir karşılaştırma belirteci karakter karakter tahmin ettirirdi — ve her reddi günlüğe yazar. Belirteç olmadan hiçbir şey oluşturulmaz. Kurulum bittiğinde sihirbaz kendiliğinden kapanır: belirteç kalabilir, artık bir işe yaramaz.

4. İzinler#

YolHaklarKim yazar
data/0770Web sunucusu, yalnızca o
data/app.sqlite0660Kurulum tarafından oluşturulur
config.php0640Kimse — sunucu için salt okunur
app/, public/0755Kimse — salt okunur
# PHP'nin www-data olarak çalıştığı bir sunucuda örnek
sudo chown -R siz:www-data /var/www/toutadmin
sudo find /var/www/toutadmin -type d -exec chmod 750 {} \;
sudo find /var/www/toutadmin -type f -exec chmod 640 {} \;
sudo chmod 770 /var/www/toutadmin/data
sudo chmod 750 /var/www/toutadmin/public

SQLite ayrıca komşu dosyalar da yazar (-wal, -shm): yazılabilir olması gereken klasördür, yalnızca veritabanı değil.

5. Web sunucusu#

Nginx#

server {
    listen 443 ssl http2;
    server_name intranet.ornek.com.tr;

    root /var/www/toutadmin/public;   # ve başka hiçbir şey
    index index.php;

    ssl_certificate     /etc/letsencrypt/live/intranet.ornek.com.tr/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/intranet.ornek.com.tr/privkey.pem;

    client_max_body_size 20M;          # kasaya yüklemeler, gelen belgeler

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

    # Yüklenen dosyalar hiçbir zaman doğrudan sunulmaz: oturumu ve özeti
    # denetleyen bir yoldan geçerler.
    location ~ ^/(data|app|tools|tests)/ { deny all; }
}

server {
    listen 80;
    server_name intranet.ornek.com.tr;
    return 301 https://$host$request_uri;
}

Apache#

<VirtualHost *:443>
    ServerName intranet.ornek.com.tr
    DocumentRoot /var/www/toutadmin/public

    SSLEngine on
    SSLCertificateFile    /etc/letsencrypt/live/intranet.ornek.com.tr/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/intranet.ornek.com.tr/privkey.pem

    <Directory /var/www/toutadmin/public>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

Ürünle gelen public/.htaccess dosyası bütün adresleri zaten index.php dosyasına yönlendirir. Apache'li paylaşımlı barındırmada gereken tek şey budur.

TLS#

Ürün, bağlantı şifreli olur olmaz Strict-Transport-Security başlığını koyar ve asla açık metinde koymaz: şifresiz bir sayfadan duyurulsa okunmazdı, üstelik yerel bir denemeyi altı ay boyunca https içine hapsederdi. Kendi yönettiğiniz bir sunucuda:

sudo certbot --nginx -d intranet.ornek.com.tr

TLS'i sonlandıran bir vekil sunucunun ya da yük dağıtıcının ardında 'trust_proxy' => true yapın — ve yalnızca orada: yoksa herkes bir başlıkta dilediği IP adresini ve protokolü duyurabilir.

6. Zamanlanmış görev#

Bir PHP sitesi yalnızca bir istek sırasında çalışır. Node sürümünün saatlik süpürmesinde yaptığı sekiz işlem burada barındırmanın cron'undan yapılır:

* * * * * /usr/bin/php /var/www/toutadmin/tools/cron.php >> /var/log/toutadmin-cron.log 2>&1
Görev ne yaparHangi sıklıkta
Sözleşmesi biten hesapları kapatırher geçişte
Vadesi gelen aboneliklerin faturalarını keserher geçişte, asla iki kez faturalamadan
Vadeleri bildirime çevirirher geçişte, yinelemeden arındırılmış
Okunmuş bildirimleri ve denetim günlüğünü temizlerseçilen saklama süresine göre
Muhasebe posta kutusunu toplar (IMAP)toplama yapılandırılmışsa
Web kancası kuyruğunu, yeniden denemeleriyle boşaltırher geçişte
Otomatik yedeği alır ve dışarı gönderiraralık dolduğunda

Betiği ayarlanan aralıktan daha sık çağırmak daha sık yedek almaz: vadesi gelmeden hiçbir şey tetiklenmez. Cron'u çeyrek saatte bir geçişle sınırlayan bir barındırmada * * * * * yerine */15 * * * * yazın — web kancaları yalnızca bu gecikmeyle yola çıkar.

Cron'u olmayan barındırmada

Kimi giriş seviyesi paketlerde cron yoktur. Üçüncü taraf bir servisten (erişilebilirlik izleyicisi ve benzerleri) gelen HTTP çağrısı yetmez: cron.php komut satırı dışında çalıştırılmayı reddeder — tam da genel bir adres yedeklemeyi tetikleyemesin diye. Bu durumda yedek, Yedekler ekranından elle alınır.

7. Sihirbaz#

Alan adınızı açın. Hiçbir hesap yokken bütün adresler /installation adresine yönlenir. Sihirbaz tek sayfaya sığar:

  1. Önkoşullar, denetlenip gösterilir — engelleyen öyle işaretlenir.
  2. Kurulum belirteci, ayarlanmışsa.
  3. Şirket: her yerde görünen ad ve serbest çalışan olmayan her yeni personele verilen yıllık izin günü sayısı.
  4. Kurulumun varsayılan dili, 16 dil arasından.
  5. Yönetim hesabı: adres ve en az on iki karakterlik bir parola.

Her şey tek bir işlemde yazılır. Bir hesap oluşur oluşmaz /installation giriş sayfasına yönlendirir: sihirbaz kendiliğinden kapanmıştır, elle silinecek hiçbir dosya yoktur.

8. Her şeyin yürüdüğünü doğrulamak#

# testler, makine üzerinde (veritabanınıza yazmazlar)
php tests/run.php

# çeviriler: hiçbir ekran sözlüklerde bulunmayan bir anahtarı kullanmaz
php tools/check-keys.php

Sonra arayüzde: bir üye oluşturun, onunla girin, bir ek yükleyin, elle bir yedek alın ve Yedekler ekranından doğrulayın. Bu dört hareket veritabanına, dosyalara, yetkilere ve arşive dokunur — yani barındırmanın yapabilmesi gereken her şeye.

9. Güncellemek#

# 1. önce bir yedek, her zaman
php tools/cron.php                     # ya da Yedekler ekranı

# 2. kodu değiştirin, veriyi değil
#    app/ public/ tools/ tests/ VERSION  ← değiştirilir
#    data/ config.php                    ← olduğu gibi bırakılır

# 3. veritabanı ilk istekte kendini günceller

Şema eşgüçlü göçlerle ilerler: güncellemeyi yeniden oynatmak hiçbir şeyi bozmaz. Node sürümünün bir kurulumu ile PHP sürümünün bir kurulumu aynı şemayı — 142 tablo, 1311 sütun — ve aynı parolaları paylaşır: bir veritabanı birinden ötekine dönüştürme olmadan geçer.

10. Gerçekten yedeklemek#

tar.gz arşivi veritabanını (VACUUM INTO ile kopyalanır, dolayısıyla yazma sırasında bile tutarlıdır) ve beş dosya klasörünü taşır: profil fotoğrafları, gelen özgeçmişler, kasa, imza klasörü, gelen belgeler. Arşivdeki her dosya kendi SHA-256 özetini taşır ve bu özet geri yüklemede doğrulanır.

Koruduğu sunucuda kalan bir arşiv hiçbir şeyi korumaz: Yedekler ekranından uzak bir hedef (FTPS ya da Google Drive) ayarlayın. Ayrıntısı Yedekleme ve geri yükleme sayfasındadır.

Önce yerelde denemek#

cp config.sample.php config.php
php -S localhost:8000 -t public public/index.php

PHP'nin gömülü sunucusu ürünü tanımaya yeter. Üretime uygun değildir: tek bir süreç, TLS yok, yük sınırı yok.

Toutadmin belgeleri — 2026-09-13 tarihinde derlendi. Yazılımdan bağımsız, kendi başına bir site.