# Namestitev na Webicom cPanel

Navodila so pripravljena za cPanel **Setup Python App / Application Manager**. Najvarneje je najprej uporabiti začasno poddomeno, po potrditvi pa aplikacijo povezati z glavno domeno.

## 1. Varnostna kopija stare strani

Pred spremembo naredite dve ločeni varnostni kopiji:

1. v **File Managerju** stisnite trenutno mapo `public_html` v ZIP;
2. v **phpMyAdminu** izvozite trenutno MySQL-bazo v obliki SQL.

Kopiji prenesite tudi na lokalni računalnik. Starih datotek in baze med preizkusom ne brišite.

## 2. Nova MySQL-baza

V **MySQL Databases** ustvarite:

- novo bazo;
- novega uporabnika z močnim geslom;
- uporabniku dodelite vse pravice za novo bazo.

Imena v cPanelu običajno dobijo predpono uporabniškega računa, na primer `uporabnik_weindorfer`.

## 3. Python-aplikacija

1. ZIP naložite v ločeno mapo, na primer `/home/UPORABNIK/weindorfer` in ga razširite.
2. Odprite **Setup Python App** in izberite Python **3.12** (če ni na voljo, 3.11).
3. Kot *Application root* nastavite mapo, v kateri sta `manage.py` in `passenger_wsgi.py`.
4. Kot *Application startup file* nastavite `passenger_wsgi.py`.
5. Kot *Application entry point* nastavite `application`.
6. Najprej izberite začasno poddomeno; glavno domeno povežite šele po preizkusu.

Terminal ni potreben. Po ustvarjeni aplikaciji v razdelku **Configuration files**
dodajte `requirements.txt`, nato izberite **Run Pip Install → requirements.txt**.
Počakajte na uspešen zaključek namestitve paketov.

## 4. Okoljske spremenljivke

V cPanelu odprite **Setup Python App**, izberite aplikacijo Weindorfer in poiščite
razdelek **Environment variables**. Z gumbom **Add Variable** dodajte vsako
spremenljivko posebej, spremembe shranite in nato pritisnite **Restart**.

Prepišite vrednosti iz `.env.example`. Datoteke `.env.example` ne preimenujte v
javno dostopno datoteko in je ne polnite z dejanskimi gesli.

Obvezne vrednosti so:

- `DJANGO_SETTINGS_MODULE=weindorfer_cms.settings.production`
- `DJANGO_SECRET_KEY` – dolg naključen ključ;
- `ALLOWED_HOSTS=odvetnik-weindorfer.si,www.odvetnik-weindorfer.si`
- `CSRF_TRUSTED_ORIGINS=https://odvetnik-weindorfer.si,https://www.odvetnik-weindorfer.si`
- `SITE_URL=https://odvetnik-weindorfer.si`
- `DB_NAME`, `DB_USER`, `DB_PASSWORD`, `DB_HOST=localhost`, `DB_PORT=3306`
- nastavitve `EMAIL_*`, `DEFAULT_FROM_EMAIL` in `CONTACT_RECIPIENT`;
- `TURNSTILE_REQUIRED=1`, `TURNSTILE_SITE_KEY`, `TURNSTILE_SECRET_KEY` in
  `TURNSTILE_EXPECTED_HOSTNAMES`;
- `REQUIRE_ADMIN_2FA=1` in `ENABLE_DJANGO_ADMIN=0`.

Naključni Django ključ in začetno skrbniško geslo ustvarite brez terminala:

1. v isti aplikaciji poiščite **Execute Python Script**;
2. vnesite `generate_setup_secrets.py` in pritisnite **Run Script**;
3. obe prikazani vrednosti takoj shranite v upravljalnik gesel;
4. ključ vnesite kot `DJANGO_SECRET_KEY`, geslo pa kot
   `INITIAL_ADMIN_PASSWORD`.

Za prvi zagon dodajte še:

- `CPANEL_SITE_DOMAIN=odvetnik-weindorfer.si`
- `INITIAL_ADMIN_USERNAME` – izbrano skrbniško uporabniško ime;
- `INITIAL_ADMIN_EMAIL` – skrbniški e-poštni naslov;
- `INITIAL_ADMIN_PASSWORD` – prikazano začetno geslo.

Za SMTP uporabite e-poštni predal domene. Gesla ne vpisujte v izvorne datoteke.
Kontaktni obrazec ne odpira e-poštnega programa obiskovalca: sporočilo sprejme
Django in ga prek teh SMTP-nastavitev pošlje na `CONTACT_RECIPIENT`.

## 5. Priprava CMS-a in vsebine brez terminala

V **Setup Python App → Execute Python Script** vnesite:

```text
cpanel_setup.py
```

in pritisnite **Run Script**. Skripta sama izvede migracije, pripravi začetno
vsebino, ustvari prvi skrbniški račun, zaščiti dokumente, zbere statične
datoteke in izvede produkcijsko preverjanje.

Skripta je varna tudi za poznejše posodobitve: če CMS že vsebuje kontaktno stran
in nastavitve pisarne, začetnih vsebin ne uvozi ponovno in ne prepiše sprememb,
narejenih v CMS-u. Prav tako nikoli ne spreminja gesla obstoječega skrbnika.

Po uspešnem izpisu:

1. iz **Environment variables** izbrišite `INITIAL_ADMIN_PASSWORD`;
2. shranite spremembe;
3. pritisnite **Restart**.

Mapi `media` in `staticfiles` morata biti za aplikacijo berljivi; `media` mora
biti tudi zapisljiva.

Ob prvi prijavi na `/cms/` bo sistem po vnosu gesla prikazal QR-kodo. Poskenirajte
jo z Google Authenticatorjem, Microsoft Authenticatorjem, 2FAS ali drugo TOTP
aplikacijo. Nato vnesite prikazano šestmestno kodo in varno shranite deset
enkratnih obnovitvenih kod. Brez nastavljenega drugega faktorja produkcijski CMS
ne dovoli dostopa.

Če izgubite telefon in vse obnovitvene kode, v **Environment variables** začasno
dodajte `RESET_2FA_USERNAME` in `RESET_2FA_CONFIRM=1`. Nato v
**Execute Python Script** zaženite `cpanel_reset_2fa.py`. Takoj po uspehu obe
začasni spremenljivki odstranite in pritisnite **Restart**.

Pred preizkusom obrazca lahko povezavo z e-poštnim strežnikom preverite brez
terminala: v **Execute Python Script** zaženite `cpanel_test_email.py`. Testno
sporočilo se pošlje na nastavljeni `CONTACT_RECIPIENT`.

## 6. Preizkus pred objavo

Preverite najmanj:

- vse elemente glavnega menija in mobilni meni;
- obe fotografiji odvetnikov;
- telefon in e-poštne povezave;
- gumb **Naloži Google zemljevid** in **Navodila za pot**;
- dejansko prejeto testno sporočilo iz kontaktnega obrazca;
- prijavo na `/cms/` in urejanje posamezne strani;
- dodajanje in razvrščanje **Naslovnih fotografij** brez ročnega obrezovanja;
- nastavitve večje pisave in visokega kontrasta na računalniku ter telefonu;
- Nastavitve → **Podatki odvetniške pisarne** → **Vzdrževalni način**;
- stran zasebnosti, ki jo je treba pred objavo uskladiti z dejanskimi internimi roki hrambe pisarne.

## 7. Zaščita obrazca in pošte

V produkciji obrazec namenoma ne pošilja brez Cloudflare Turnstile. Ustvarite
ključ za vse domene, na katerih boste stran preizkušali, in dodajte:

- `TURNSTILE_SITE_KEY`
- `TURNSTILE_SECRET_KEY`

V `TURNSTILE_EXPECTED_HOSTNAMES` naštejte iste domene. Sistem žeton preveri na
strežniku ter potrdi tudi gostiteljsko ime in dejanje `contact`. Poleg tega
uporablja skrito polje, podpisan čas začetka izpolnjevanja in skupne kratke ter
dnevne omejitve v MySQL. Surovih IP-naslovov in vsebine sporočil ne shranjuje.

V cPanelovem **Email Deliverability** preverite veljavna zapisa SPF in DKIM. Za
domeno nastavite tudi DMARC. Poštni predal zaščitite z MFA, SMTP geslo pa naj bo
ločeno in dolgo. Prenos med spletno stranjo in poštnim strežnikom uporablja TLS;
e-pošta kljub temu ni šifriran odvetniški portal, zato obrazec ne sme vabiti k
pošiljanju zaupnih dokumentov.

Za osnovni zemljevid z žebljičkom in zunanji gumb za navodila API-ključ ni potreben. Zemljevid se naloži šele po kliku obiskovalca.

Če želite še izračun poti **znotraj strani**, v Google Cloud vključite *Maps Embed API*, ustvarite ključ, ga omejite na domeni `odvetnik-weindorfer.si` in `www.odvetnik-weindorfer.si` ter na ta API, nato v cPanel dodajte:

- `GOOGLE_MAPS_EMBED_API_KEY`

API-ključ je po zasnovi viden v naslovu vgrajenega zemljevida, zato so omejitve domene in API-ja obvezne. V CMS-u lahko pri podatkih pisarne dodate tudi uradni Google Place ID, da žebljiček nedvoumno označi pravi vhod oziroma stavbo.

## 8. Preklop na glavno domeno in povrnitev

Ko je preizkus uspešen, v cPanelu aplikacijo povežite z glavno domeno in preverite veljaven SSL-certifikat. Če bi bilo treba povrniti staro stran, odklopite Python-aplikacijo z domene ter obnovite arhiv `public_html` in stari SQL-izvoz.

Skrbniški naslov po objavi: `https://odvetnik-weindorfer.si/cms/`.

## 9. Posodobitve brez shell dostopa

Izvorna koda je lahko shranjena v zasebnem Git repozitoriju, vendar zaradi
Webicomovega izključenega shell dostopa cPanel ne more neposredno izvajati
`Update from Remote`. To ne vpliva na zasebnost kode.

Za vsako novo izdajo:

1. iz zasebnega repozitorija pripravite namestitveni ZIP;
2. v **File Managerju** predhodno naredite varnostno kopijo;
3. ZIP razširite čez mapo Python-aplikacije;
4. če se je spremenil `requirements.txt`, ponovno uporabite **Run Pip Install**;
5. v **Execute Python Script** zaženite `cpanel_setup.py`;
6. pritisnite **Restart** in preverite stran.

`cpanel_setup.py` pri posodobitvah izvede samo potrebne migracije in pripravo
datotek; obstoječe vsebine CMS-a in skrbniška gesla ostanejo nespremenjeni.
Datoteke z gesli, baza, naložene slike, dnevniki in varnostne kopije ne sodijo v
Git ali namestitveni ZIP.

## 10. Redno varnostno vzdrževanje

- V cPanelu vključite vsakodnevno varnostno kopijo MySQL-baze in mape `media`.
  Vsaj ena kopija naj bo zunaj istega uporabniškega računa.
- Mesečno preverite in namestite varnostne posodobitve paketov Django, Wagtail
  in drugih odvisnosti; po posodobitvi vedno zaženite teste in `check --deploy`.
- V **Cron Jobs** dodajte dnevno čiščenje zastarelih anonimiziranih števcev:

```bash
cd /home/UPORABNIK/weindorfer && /POT/DO/VENV/bin/python manage.py cleanup_security_limits --days 30
```

- Mape in datoteke naj imajo najmanj potrebne pravice. Izvorna koda in okoljske
  nastavitve ne smejo biti javno dostopne prek `public_html`. Nikoli ne uporabite
  pravic `777`.
- Če vključite `TRUST_CLOUDFLARE_IP_HEADER=1`, mora biti domena dejansko za
  Cloudflarejem, neposreden dostop do izvornega strežnika pa blokiran. Sicer
  pustite vrednost `0`.
- Dokumenti so omejeni na PDF. URL-mapa `media/documents` mora ostati blokirana;
  dokumente vedno streže Wagtail prek poti `/documents/`.
