@@ -0,0 +1,83 @@
|
||||
# Back-up en herstel
|
||||
|
||||
## Wat moet worden beschermd
|
||||
|
||||
Prioriteit:
|
||||
|
||||
1. PostgreSQL-database of lokale SQLite-database;
|
||||
2. productie-`.env` en reverse-proxyconfiguratie, versleuteld en apart;
|
||||
3. `media/` met gebruikersdocumenten/snapshots wanneer die functie actief is;
|
||||
4. `config-data/` met niet-geheime bron- en profielconfiguratie;
|
||||
5. immutable image/tag en de bijbehorende repositoryrelease.
|
||||
|
||||
Redis is een werkqueue/cache en hoeft normaal niet te worden hersteld. Ollama-modellen kunnen opnieuw worden gedownload; behoud alleen wanneer bandbreedte of modelbeschikbaarheid dat vereist.
|
||||
|
||||
## Minimumniveau
|
||||
|
||||
- database dagelijks;
|
||||
- configuratie na iedere wijziging;
|
||||
- minstens 7 dagelijkse en 4 wekelijkse herstelpunten;
|
||||
- back-up op een ander fysiek of logisch opslagdoel;
|
||||
- checksums op ieder archief;
|
||||
- minstens per kwartaal een volledige hersteltest;
|
||||
- gevoelige back-ups versleuteld at rest en tijdens transport.
|
||||
|
||||
## Repositoryscripts
|
||||
|
||||
```bash
|
||||
./scripts/backup.sh
|
||||
```
|
||||
|
||||
Het script gebruikt `pg_dump` wanneer `DATABASE_URL` PostgreSQL is en anders een kopie van `local/db.sqlite3`. Het maakt daarnaast een bestandsarchief en SHA-256-lijst. Voor productie moet de uitvoerdirectory naar een beschermd extern volume worden gesynchroniseerd; een back-up naast de live database is geen volwaardige back-up.
|
||||
|
||||
Herstel één databasebestand:
|
||||
|
||||
```bash
|
||||
./scripts/restore.sh /pad/naar/vacatureradar-YYYYMMDDTHHMMSSZ.sql.gz
|
||||
# of
|
||||
./scripts/restore.sh /pad/naar/vacatureradar-YYYYMMDDTHHMMSSZ.sqlite3
|
||||
```
|
||||
|
||||
## PostgreSQL-consistente back-up
|
||||
|
||||
Aanbevolen vanuit een beheercontainer of host met clienttools:
|
||||
|
||||
```bash
|
||||
pg_dump --format=custom --no-owner --no-acl "$DATABASE_URL" \
|
||||
> vacatureradar-$(date -u +%Y%m%dT%H%M%SZ).dump
|
||||
sha256sum vacatureradar-*.dump > SHA256SUMS
|
||||
```
|
||||
|
||||
De huidige scriptvariant gebruikt gecomprimeerde SQL voor brede compatibiliteit. Een custom-formatdump maakt selectiever herstel mogelijk; kies één formaat en test exact dat formaat.
|
||||
|
||||
## Veilige herstelprocedure
|
||||
|
||||
1. Meld onderhoud en blokkeer externe toegang.
|
||||
2. Stop scheduler en worker; stop daarna web zodat geen writes meer gebeuren.
|
||||
3. Maak een forensische kopie van de defecte huidige database/volumes.
|
||||
4. Verifieer checksum, datum, appversie en migratieniveau van de back-up.
|
||||
5. Herstel naar een lege database of aparte testinstantie.
|
||||
6. Gebruik de code/image die bij het back-upmoment hoort.
|
||||
7. Voer migraties alleen vooruit uit nadat de restore op dat oude niveau goed opent.
|
||||
8. Draai health, modelcounts, fixture-import en applicatiesmoke-test.
|
||||
9. Start web, daarna worker en precies één scheduler.
|
||||
10. Documenteer RPO/RTO, verloren wijzigingen en vervolgactie.
|
||||
|
||||
## Hersteltest
|
||||
|
||||
Een hersteltest is pas geslaagd wanneer:
|
||||
|
||||
- checksum is geverifieerd;
|
||||
- database start zonder reparaties;
|
||||
- migratiestatus verklaarbaar is;
|
||||
- aantallen gebruikers, profielen, jobs, bronaliassen, sollicitaties en outboxrecords plausibel zijn;
|
||||
- een bestaande vacaturedetailpagina opent;
|
||||
- een fixture idempotent importeert;
|
||||
- een score en digest kunnen worden aangemaakt;
|
||||
- geen live mailbox of bron onbedoeld wordt gepolld in de testomgeving.
|
||||
|
||||
Gebruik in een hersteltest `IMAP_ENABLED=0`, `CELERY_TASK_ALWAYS_EAGER=1`, consolemail en geen live source polling.
|
||||
|
||||
## Verwijdering en privacy
|
||||
|
||||
Back-ups verlengen feitelijk de bewaartermijn. Documenteer hoe verwijderde persoonsgegevens na de normale rotatie ook uit back-ups verdwijnen. Gebruik korte retentie voor ruwe vacaturemails en bewaar geen mailboxcredentials in databasedumps.
|
||||
@@ -0,0 +1,36 @@
|
||||
# Geodata import voor Belgische locaties
|
||||
|
||||
Deze module gebruikt lokaal beheerde postcode/gemeentedata om locatiecoördinaten te kunnen afleiden.
|
||||
|
||||
## CLI
|
||||
|
||||
```bash
|
||||
python manage.py import_geodata <pad-naar-geodata.csv> \
|
||||
--source-name local \
|
||||
--dataset-version 2026-01-01 \
|
||||
--license-name "Naam van licentie"
|
||||
--license-url "https://..."
|
||||
--replace
|
||||
```
|
||||
|
||||
- Gebruik `--validate-only` om validatie uit te voeren zonder databasewijzigingen.
|
||||
- Gebruik `--replace` wanneer een bron volledig opnieuw opgebouwd moet worden.
|
||||
|
||||
## Verplichte CSV-kolommen
|
||||
|
||||
- `postal_code` (exact 4 cijfers)
|
||||
- `municipality`
|
||||
- `region`
|
||||
- `latitude`
|
||||
- `longitude`
|
||||
|
||||
## Validatie
|
||||
|
||||
- Niet-numerieke coördinaten worden geweigerd.
|
||||
- Latitude en longitude moeten binnen geldige range vallen.
|
||||
- Dubbels op `postal_code + municipality` in de invoer worden afgekeurd.
|
||||
- Bestaande lookupregels van dezelfde `source_name`/`dataset-version` blokkeren import zonder `--replace`.
|
||||
|
||||
## Fallback
|
||||
|
||||
Zonder geïmporteerde geodata blijft afstandsafleiding op "onbekend".
|
||||
@@ -0,0 +1,40 @@
|
||||
# Gitea push-to-deploy (self-hosted, geen handmatige variabelenvulling)
|
||||
|
||||
Push naar `main` of `master` triggert automatisch:
|
||||
|
||||
- checkout op de server runner
|
||||
- `bash scripts/deploy_docker.sh`
|
||||
- deploy met `docker compose up -d --build`
|
||||
|
||||
Deze workflow gebruikt defaults en hoeft niet te wachten op repo-secrets voor host/ssh/credentials.
|
||||
|
||||
## Vereiste eenmalige setup op de server
|
||||
|
||||
1. Start en registreer een self-hosted Gitea-runner voor deze repo.
|
||||
2. Zorg dat die runner Docker/Compose kan draaien in de projectmap waar de runner draait.
|
||||
3. Laat éénmaal dit script draaien op de server:
|
||||
|
||||
```bash
|
||||
bash scripts/deploy_docker.sh
|
||||
```
|
||||
|
||||
Daarna is het gedrag volledig push-only:
|
||||
|
||||
- `git push` naar `main` of `master` → automatische deploy
|
||||
|
||||
## Defaults
|
||||
|
||||
- `APP_HOST`: hostname van de server (fallback: `hostname`)
|
||||
- `APP_SCHEME`: `http`
|
||||
- `DJANGO_DEBUG`: afgeleid uit `APP_SCHEME`
|
||||
- `0` als `APP_SCHEME=https`
|
||||
- `1` als `APP_SCHEME=http`
|
||||
- `DEPLOY_COMPOSE_FILE`: `docker-compose.yml`
|
||||
- `APP_ORIGINS`, `APP_HOSTS`, `APP_PORT`: optioneel en veilig overnemen als op runner gezet
|
||||
|
||||
## Endpoint na deploy
|
||||
|
||||
- `http://<runner-host>:1226/health/ready/`
|
||||
- `http://<runner-host>:1226/health/live/`
|
||||
|
||||
Voor productie: zet later op de runner de gewenste runtime-omgeving (bijv. reverse proxy + TLS) en herstart push.
|
||||
@@ -0,0 +1,101 @@
|
||||
# Lokale ontwikkeling
|
||||
|
||||
## Ondersteunde omgeving
|
||||
|
||||
- Linux, macOS of WSL2;
|
||||
- Python 3.12 of 3.13;
|
||||
- `uv` voor dependency- en lockfilebeheer;
|
||||
- optioneel Docker Compose voor PostgreSQL, Redis en de volledige stack.
|
||||
|
||||
Tests gebruiken standaard SQLite, eager Celery en een in-memory e-mailbackend. Ze hebben geen internet, Redis, mailbox of Ollama nodig.
|
||||
|
||||
## Eerste start
|
||||
|
||||
```bash
|
||||
./scripts/codex_bootstrap.sh
|
||||
uv run python manage.py runserver 0.0.0.0:8080
|
||||
```
|
||||
|
||||
Het script maakt alleen voor lokale ontwikkeling een `.env`, installeert de gelockte dependencies, migreert, laadt veilige demodata en draait alle kwaliteitsgates. De lokaal aangemaakte login is `admin / codex-local-only`. Verwijder of wijzig deze credentials vóór ieder gedeeld of bereikbaar gebruik.
|
||||
|
||||
## Handmatige start
|
||||
|
||||
```bash
|
||||
uv sync --all-groups
|
||||
mkdir -p local media logs backups
|
||||
cp .env.example .env
|
||||
# Laat DATABASE_URL leeg voor SQLite en vul een unieke DJANGO_SECRET_KEY in.
|
||||
uv run python manage.py migrate
|
||||
uv run python manage.py bootstrap_instance --with-demo
|
||||
uv run python manage.py runserver 0.0.0.0:8080
|
||||
```
|
||||
|
||||
## Veelgebruikte commando's
|
||||
|
||||
```bash
|
||||
make help
|
||||
make test
|
||||
make lint
|
||||
make format
|
||||
make verify
|
||||
uv run python manage.py shell
|
||||
uv run python manage.py seed_sources
|
||||
uv run python manage.py import_job_fixture fixtures/pages/sample_jsonld_job.html
|
||||
```
|
||||
|
||||
De volgende achtergrondprocessen zijn alleen nodig wanneer `CELERY_TASK_ALWAYS_EAGER=0`:
|
||||
|
||||
```bash
|
||||
uv run celery -A config worker -l INFO -Q high,default,low
|
||||
uv run celery -A config beat -l INFO
|
||||
```
|
||||
|
||||
## Demo opnieuw opbouwen
|
||||
|
||||
```bash
|
||||
./scripts/reset_demo.sh
|
||||
```
|
||||
|
||||
Dit verwijdert uitsluitend `local/db.sqlite3`. Gebruik het niet tegen een productievolume.
|
||||
|
||||
## Testen per laag
|
||||
|
||||
```bash
|
||||
uv run pytest tests/unit
|
||||
uv run pytest tests/integration
|
||||
uv run pytest tests/security
|
||||
uv run pytest tests/integration/test_pipeline.py -q
|
||||
uv run pytest --cov=apps --cov=config --cov-report=term-missing
|
||||
```
|
||||
|
||||
Voeg bij iedere parserwijziging een bronfixture toe. Tests mogen alleen een echte netwerkcall doen in een expliciet afgescheiden, handmatig gestarte smoke-test die niet in CI draait.
|
||||
|
||||
## Databasewijzigingen
|
||||
|
||||
```bash
|
||||
uv run python manage.py makemigrations
|
||||
uv run python manage.py migrate
|
||||
uv run python manage.py makemigrations --check --dry-run
|
||||
```
|
||||
|
||||
Controleer de gegenereerde migratie. Voor destructieve wijzigingen is een ADR, datamigratie, herstelpad en back-uptest verplicht.
|
||||
|
||||
## Static files
|
||||
|
||||
In development serveert Django static files. Productie gebruikt `collectstatic` in de container en WhiteNoise:
|
||||
|
||||
```bash
|
||||
uv run python manage.py collectstatic --noinput
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**`uv` ontbreekt:** installeer `uv` volgens de officiële instructies van de gekozen werkmachine en heropen de shell.
|
||||
|
||||
**Database lock op SQLite:** stop tweede web/workerprocessen of gebruik lokaal PostgreSQL voor parallel werk.
|
||||
|
||||
**Redis connection refused:** zet voor eenvoudige lokale runs `CELERY_TASK_ALWAYS_EAGER=1`, of start Redis/Compose.
|
||||
|
||||
**Geen digest zichtbaar:** met console-backend staat de mail in de terminal; controleer daarnaast `DigestOutbox` in Django admin.
|
||||
|
||||
**Bron wordt overgeslagen:** controleer `policy=allow`, een toegestane status, `next_run_at`, de domeinpolicy en de platformdenylist. Forceer nooit een fetch om een veiligheidsblokkade te omzeilen.
|
||||
@@ -0,0 +1,118 @@
|
||||
# Operationeel runbook
|
||||
|
||||
## Dagelijkse controle
|
||||
|
||||
Controleer bij voorkeur automatisch en minstens wekelijks handmatig:
|
||||
|
||||
- `/health/ready/` is 200;
|
||||
- web, worker, scheduler, PostgreSQL en Redis zijn actief;
|
||||
- vrije ruimte op database-, media-, log- en back-upvolume;
|
||||
- bronnen in `quarantined`, `paused` of met oplopende `failure_count`;
|
||||
- `DigestOutbox` met status `failed` of langdurig `pending`;
|
||||
- meest recente geslaagde back-up en laatste hersteltest;
|
||||
- onverwachte login- of policyfouten in logs.
|
||||
|
||||
## Incidentprioriteiten
|
||||
|
||||
| Niveau | Voorbeeld | Eerste actie |
|
||||
|---|---|---|
|
||||
| P1 | datalek, secret gepubliceerd, ongeautoriseerde netwerkfetch | stop web/worker/scheduler, isoleer netwerk, roteer secrets |
|
||||
| P2 | database niet beschikbaar, imports corrupt, herhaalde mail | stop schrijvende processen, behoud bewijs, maak snapshot |
|
||||
| P3 | één adapter kapot, digest vertraagd, bron in quarantaine | pauzeer bron/functie, herstel met fixture en test |
|
||||
| P4 | copy/layout/probleem zonder datarisico | registreer backlogtaak en plan normale release |
|
||||
|
||||
## Web niet ready
|
||||
|
||||
1. Controleer liveness. Faalt die ook, inspecteer webcontainer/proces en poortbinding.
|
||||
2. Bij `database=error:*`: controleer PostgreSQL-status, disk, credentials en netwerknaam.
|
||||
3. Test vanuit webcontainer of DNS `postgres` resolveert en poort 5432 bereikbaar is.
|
||||
4. Draai geen migraties herhaald blind; lees eerst de laatste stacktrace.
|
||||
5. Herstart alleen de falende laag. Een volledige stackrestart kan diagnostiek wissen.
|
||||
|
||||
## Worker verwerkt niets
|
||||
|
||||
1. Controleer Redis en `CELERY_BROKER_URL`.
|
||||
2. Controleer workerqueues `high,default,low`.
|
||||
3. Inspecteer actieve en gereserveerde taken:
|
||||
|
||||
```bash
|
||||
celery -A config inspect ping
|
||||
celery -A config inspect active
|
||||
celery -A config inspect reserved
|
||||
```
|
||||
|
||||
4. Kijk naar timeouts, policyblokkades en retryloops.
|
||||
5. Start niet meerdere schedulers; dat kan dubbele planning veroorzaken, ook al zijn imports idempotent.
|
||||
|
||||
## Bron faalt of gaat in quarantaine
|
||||
|
||||
1. Laat quarantaine staan; forceer geen fetch.
|
||||
2. Lees alleen categorie, status, URL-host en beperkte foutmelding in `SourceRun`.
|
||||
3. Controleer of het domein, redirectdoel, robots-/voorwaardenstatus of contenttype veranderde.
|
||||
4. Download geen pagina met een onbeveiligde shellopdracht vanaf de productiehost.
|
||||
5. Reproduceer met een gesaneerde fixture in een ontwikkelomgeving.
|
||||
6. Werk parser/policy en securitytests bij.
|
||||
7. Zet bron pas terug op `trial`, voer één begrensde run uit en promoveer daarna eventueel naar `active`.
|
||||
|
||||
## IMAP-import stopt
|
||||
|
||||
1. Zet `IMAP_ENABLED=0` om lockout of herhaalde fouten te beperken.
|
||||
2. Controleer host, poort, TLS, mailboxnaam en app-password.
|
||||
3. Verifieer dat de mailbox niet is hernoemd en het account niet is vergrendeld.
|
||||
4. Test eerst read-only met `IMAP_MARK_SEEN=0`.
|
||||
5. Een reeds verwerkte mail is idempotent via message identity; verwijder geen `EmailMessageRecord` om opnieuw te proberen zonder analyse.
|
||||
|
||||
## Digest ontbreekt of dubbel
|
||||
|
||||
- Controleer actief profiel, digesttijd/zone en `DIGEST_RECIPIENT`.
|
||||
- Controleer `DigestOutbox` op datum en status.
|
||||
- Bij SMTP-fout: corrigeer configuratie en retry dezelfde outbox gecontroleerd.
|
||||
- Maak geen tweede outboxrecord handmatig; uniciteit per profiel/datum is de duplicaatbarrière.
|
||||
- Een lege digest is toegestaan wanneer geen vacature boven de ingestelde drempel valt.
|
||||
|
||||
## Verdachte of kwaadaardige vacature-inhoud
|
||||
|
||||
1. Open de originele bron niet automatisch in een ingelogde browser.
|
||||
2. Zet bij twijfel het raw document op `quarantined` zodat retentie het bewijs niet wist.
|
||||
3. Controleer opgeslagen gesaniteerde tekst en veldherkomst.
|
||||
4. Voeg de payload als geanonimiseerde fixture toe wanneer een parser/sanitizerfout bestaat.
|
||||
5. Laat AI-output nooit een commando, URL-fetch of configuratiemutatie veroorzaken.
|
||||
|
||||
## Sollicitatiedossier exporteren en opschonen
|
||||
|
||||
1. Open de sollicitatielijst in een ingelogde gebruiker-sessie en ga naar het dossier.
|
||||
2. Controleer dat de timeline alleen mutaties voor dat dossier bevat (status, notities, contact, snapshot).
|
||||
3. Exporteer met het dossier-exportendpoint om een zip met `application.json`, `timeline.csv`, `application_print.html` te controleren.
|
||||
4. Controleer het zipbestand lokaal op:
|
||||
- correcte dossier-ID;
|
||||
- geen raw e-mailbody of bron-payload;
|
||||
- geen externe assets of scriptrequests in HTML.
|
||||
5. Verwijderen mag alleen op gebruikersniveau (`delete` endpoint); herhaalde delete-aanroepen moeten zonder fout uitkomen.
|
||||
|
||||
## Schijfruimte laag
|
||||
|
||||
1. Stop scheduler en worker wanneer de database- of PostgreSQL-volume kritiek vol is.
|
||||
2. Controleer oude back-ups, containerlogs, raw-documentretentie en Ollama-modellen.
|
||||
3. Verwijder nooit willekeurig PostgreSQL-bestanden.
|
||||
4. Laat `cleanup_raw_documents` alleen niet-gequarantaineerde verlopen documenten opruimen.
|
||||
5. Vergroot volume of herstel rotatie, start daarna database, web, worker en scheduler in die volgorde.
|
||||
|
||||
## Secret vermoedelijk gelekt
|
||||
|
||||
1. Stop externe toegang en disable betrokken account/token.
|
||||
2. Roteer Django secret, adminwachtwoord, database-, SMTP-, IMAP- en registrycredentials voor zover betrokken.
|
||||
3. Trek oude sessies in door sessies te verwijderen of het secret te wijzigen.
|
||||
4. Controleer logs en source-runs op misbruik, zonder gevoelige payload verder te kopiëren.
|
||||
5. Maak een incidentnotitie met exacte tijden, scope, rotaties en herstelvalidatie.
|
||||
|
||||
## Herstelvalidatie
|
||||
|
||||
Na ieder incident:
|
||||
|
||||
```bash
|
||||
python manage.py check
|
||||
python manage.py migrate --plan
|
||||
python manage.py shell -c "from apps.core.health import readiness; print(readiness())"
|
||||
```
|
||||
|
||||
Controleer daarna login, één fixture-import, scoreberekening, bronlijst, sollicitatielijst en mailbackend. Start live polling pas nadat deze controles slagen.
|
||||
@@ -0,0 +1,146 @@
|
||||
# Bron onboarden
|
||||
|
||||
Een nieuwe bron wordt nooit direct als onbeperkt actief beschouwd. Gebruik het traject **candidate → trial → active** en ga bij veiligheids- of kwaliteitsproblemen naar **quarantined**.
|
||||
|
||||
## Toegestane broncategorieën
|
||||
|
||||
Voorkeursvolgorde:
|
||||
|
||||
1. openbare carrièrepagina van de feitelijke werkgever;
|
||||
2. openbare, door de werkgever bedoelde ATS-vacaturepagina;
|
||||
3. expliciete RSS/Atom-feed of sitemap met vacatures;
|
||||
4. vacaturemail die de gebruiker zelf heeft geactiveerd;
|
||||
5. handmatige browserimport door de gebruiker.
|
||||
|
||||
Niet onboarden zonder expliciete, aantoonbare toestemming:
|
||||
|
||||
- ingelogde zoekresultaten of profielpagina's;
|
||||
- pagina's achter CAPTCHA, anti-bot challenge of toegangscontrole;
|
||||
- denylistplatformen via directe crawling;
|
||||
- verborgen/private endpoints die alleen door reverse engineering zijn gevonden;
|
||||
- bronnen waarvan robots/voorwaarden of technische signalen automatisering verbieden.
|
||||
|
||||
## Reviewchecklist
|
||||
|
||||
Leg per bron vast:
|
||||
|
||||
- naam, bronsoort, hoofddomein en exacte start-URL;
|
||||
- eigenaar/eindwerkgever en eventuele ATS-provider;
|
||||
- publieke toegankelijkheid zonder login;
|
||||
- datum en samenvatting van robots- en voorwaardenreview;
|
||||
- toegestane paden en eventuele uitgesloten paden;
|
||||
- parserstrategie: JSON-LD, RSS, gespecialiseerd ATS of generieke HTML;
|
||||
- verwachte frequentie en minimale interval;
|
||||
- maximaal documentvolume en paginatie;
|
||||
- welke velden daadwerkelijk aanwezig zijn;
|
||||
- retentiebehoefte en mogelijke persoonsgegevens;
|
||||
- contact-/user-agentinformatie indien passend;
|
||||
- rollback/quarantainecriterium.
|
||||
|
||||
`robots.txt` alleen is geen volledige juridische toestemming, en afwezigheid ervan is geen automatische toestemming. Het technische bronbeleid ondersteunt een conservatieve beslissing; de beheerder blijft verantwoordelijk voor de bronreview.
|
||||
|
||||
## Candidate aanmaken
|
||||
|
||||
Gebruik Django admin of `seed_sources` met een gecontroleerd YAML-record. Begin met:
|
||||
|
||||
```yaml
|
||||
name: Voorbeeld Werkgever
|
||||
source_type: employer
|
||||
base_url: https://careers.example.org/jobs
|
||||
status: candidate
|
||||
policy: review
|
||||
parser_key: auto
|
||||
strict_mode: true
|
||||
honor_robots: true
|
||||
crawl_interval_minutes: 720
|
||||
minimum_interval_seconds: 30
|
||||
max_concurrency: 1
|
||||
```
|
||||
|
||||
Zet `policy: allow` pas na review. Een onbekende bron blijft in strict mode zonder fetch.
|
||||
|
||||
## Fixture vóór live request
|
||||
|
||||
Bewaar een gesaneerd voorbeeld onder `fixtures/pages`, `fixtures/feeds` of `fixtures/emails`. Verwijder trackingtokens, persoonsgegevens en niet-noodzakelijke volledige teksten. Schrijf tests voor:
|
||||
|
||||
- normale vacature;
|
||||
- ontbrekende optionele velden;
|
||||
- nul vacatures;
|
||||
- gewijzigde markup of meerdere jobs;
|
||||
- kwaadaardige HTML en onveilige links;
|
||||
- idempotente replay;
|
||||
- parserconfidence en waarschuwingen.
|
||||
|
||||
## Trialrun
|
||||
|
||||
1. Zet bron op `trial` en `policy=allow`.
|
||||
2. Voer één handmatige run uit via de retryknop of Celerytask.
|
||||
3. Controleer status, final URL, bytes, parser, warnings en tellers.
|
||||
4. Open alleen gesaniteerde jobweergave; vergelijk steekproefsgewijs met de publieke bron.
|
||||
5. Controleer canonieke URL, werkgever, locatie, datum, verloopdatum, taal en duplicaten.
|
||||
6. Verifieer dat redirectdoelen, rate limit en conditional requests correct zijn.
|
||||
7. Laat minimaal twee geplande cycli goed verlopen vóór promotie naar `active`.
|
||||
|
||||
## Automatische quarantainecriteria
|
||||
|
||||
Een bron moet worden gepauzeerd of in quarantaine gezet bij:
|
||||
|
||||
- redirect naar denylist, login, private adresruimte of onverwacht domein;
|
||||
- herhaalde 401/403/429, CAPTCHA of anti-botpagina;
|
||||
- contenttype/grootte buiten beleid;
|
||||
- parseroutput met plotseling nul jobs terwijl de bron zichtbaar jobs bevat;
|
||||
- abnormale volumestijging of duplicaatstorm;
|
||||
- HTML-sanitization/securityfout;
|
||||
- voorwaardenwijziging of verlopen bronreview;
|
||||
- opeenvolgende fouten boven de vastgelegde drempel.
|
||||
|
||||
## Gespecialiseerde ATS-adapter
|
||||
|
||||
Voeg alleen een adapter toe wanneer meerdere bronnen hetzelfde stabiele publieke formaat gebruiken of de generieke adapter onvoldoende bewijs levert. De adapter:
|
||||
|
||||
- krijgt geen credentials;
|
||||
- gebruikt uitsluitend gedocumenteerde publieke jobdata of publieke pagina's;
|
||||
- implementeert het interne adaptercontract;
|
||||
- heeft providerfixtures en contracttests;
|
||||
- valt veilig terug zonder globale pipeline te breken;
|
||||
- documenteert paginatie, sluitingssignalen en rate limits.
|
||||
|
||||
## Bron verwijderen
|
||||
|
||||
Pauzeer eerst. Behoud canonieke vacatures en herkomst zolang productretentie dat vereist; verwijder niet blind clusters die ook andere aliassen hebben. Verwijder raw documents volgens retentie, trek de policy in en noteer de reden en datum.
|
||||
|
||||
## Reviewbeleid bij bronbeoordeling
|
||||
|
||||
- Een bron mag alleen automatisch gefetcht worden wanneer een actuele `SourcePolicyReview` bestaat met geldige reden, scope en vervaldatum.
|
||||
- Terms review blijft menselijk, niet automatisch door AI of heuristiek.
|
||||
- Bij ontbrekende of verlopen review of bij expliciet `pause`/`deny` besluit blokkeert de taakuitvoering.
|
||||
|
||||
## Handmatige import in de interface
|
||||
|
||||
Voor uitzonderlijke vacatures kan de beheerder handmatig importeren via de bronpagina:
|
||||
|
||||
- navigeer naar `Brongezondheid` en gebruik het "Handmatige import" formulier;
|
||||
- gebruik de bookmarklet om de huidige pagina-URL te vullen in `source_url`;
|
||||
- of plak direct relevante vacaturetekst in het tekstveld wanneer fetch niet is toegestaan.
|
||||
|
||||
De bookmarklet stuurt alleen `source_url` naar de import-URL, zonder secret of broninhoud.
|
||||
|
||||
Na import toont de bronlijst:
|
||||
|
||||
- de gekozen modus (`url` of `paste`);
|
||||
- bron-ID/bron-URL;
|
||||
- aantallen herkend, nieuw en duplicaat;
|
||||
- waarschuwingen;
|
||||
- links naar vacaturedetail voor direct vervolg.
|
||||
|
||||
## Providerdetails (VR-106)
|
||||
|
||||
Voor het onboarden van publieke ATS-bronnen moet het bronrecord minimaal een van de volgende providerspecificaties gebruiken:
|
||||
|
||||
- Greenhouse (`*.greenhouse.io` / `*.boards.greenhouse.io`) — max 15 requests/min, alleen publiek toegankelijke jobdata.
|
||||
- Lever (`jobs.lever.co`) — max 60 requests/min, alleen `jobs.lever.co` en officiële publieke endpoints.
|
||||
- Recruitee (`*.recruitee.com`) — max 60 requests/min, geen login/partner endpoint.
|
||||
- SmartRecruiters (`*.smartrecruiters.com`) — max 30 requests/min, alleen publieke vacaturepagina/feeds.
|
||||
- Workable (`apply.workable.com`) — max 30 requests/min, alleen publieke vacaturepagina/feeds.
|
||||
|
||||
Bij twijfel altijd op `review` blijven en eerst via een trialrun met gesloten evaluatiecriteria (gesloten job, lege joblijst, markeringswijziging) valideren.
|
||||
@@ -0,0 +1,163 @@
|
||||
# Unraid-deployment
|
||||
|
||||
Deze handleiding gebruikt `docker-compose.unraid.yml` als declaratieve bron van waarheid. Unraid-installaties verschillen in gebruikte Compose Manager, reverse proxy en shares; behoud de hieronder genoemde volumes, secrets en netwerkgrenzen ook wanneer de UI de services afzonderlijk aanmaakt.
|
||||
|
||||
## Vooraf
|
||||
|
||||
Maak deze directories:
|
||||
|
||||
```text
|
||||
/mnt/user/appdata/vacatureradar/config
|
||||
/mnt/user/appdata/vacatureradar/media
|
||||
/mnt/user/appdata/vacatureradar/logs
|
||||
/mnt/user/appdata/vacatureradar/postgres
|
||||
/mnt/user/appdata/vacatureradar/redis
|
||||
/mnt/user/appdata/vacatureradar/ollama # alleen bij lokale AI
|
||||
/mnt/user/backups/vacatureradar
|
||||
```
|
||||
|
||||
Gebruik bij voorkeur een cache-backed `appdata`-share en neem die afzonderlijk in je back-upregime op. De PostgreSQL-datadir mag niet gelijktijdig door file-syncsoftware worden gemuteerd.
|
||||
|
||||
## Imagekeuze
|
||||
|
||||
De Unraid-compose bevat bewust `ghcr.io/CHANGE_ME/vacatureradar:latest`. Codex kan code, Dockerfile en CI afwerken zonder registrycredentials, maar voor productie moet een van deze paden worden gekozen:
|
||||
|
||||
1. bouw lokaal op de Unraid-host en vervang `image:` door een vaste lokale tag;
|
||||
2. publiceer vanuit CI naar een private registry en pin op een immutable digest;
|
||||
3. gebruik `build:`, mits de gekozen Compose-plugin builds betrouwbaar ondersteunt.
|
||||
|
||||
Gebruik in productie geen zwevende `latest` zonder gecontroleerd rollbackpad.
|
||||
|
||||
## Configuratiebestand
|
||||
|
||||
Kopieer of genereer `.env` naar:
|
||||
|
||||
De deploy-helper `scripts/deploy_docker.sh` vult ontbrekende sleutels op basis van veilige defaults.
|
||||
Zet voor publieke productie vooraf de gewenste variabelen:
|
||||
|
||||
```text
|
||||
/mnt/user/appdata/vacatureradar/config/.env
|
||||
APP_HOST=jobs.example.be
|
||||
APP_SCHEME=https
|
||||
DJANGO_DEBUG=0
|
||||
SESSION_COOKIE_SECURE=1
|
||||
CSRF_COOKIE_SECURE=1
|
||||
SECURE_SSL_REDIRECT=1
|
||||
```
|
||||
|
||||
Gebruik daarna de helper (zie hieronder).
|
||||
|
||||
Beperk de bestandsrechten van `.env` tot de beheerder. Voeg geen secrets toe aan de ZIP, Git, screenshots of supportlogs.
|
||||
|
||||
## Eerste uitrol
|
||||
|
||||
1. Controleer alle `CHANGE_ME`-waarden.
|
||||
2. Gebruik de deploy-helper voor een snelle eerste start:
|
||||
|
||||
```bash
|
||||
cd /app/VacatureRadar
|
||||
APP_HOST=jobs.example.be APP_SCHEME=https DJANGO_DEBUG=0 \
|
||||
SESSION_COOKIE_SECURE=1 CSRF_COOKIE_SECURE=1 SECURE_SSL_REDIRECT=1 \
|
||||
bash scripts/deploy_docker.sh
|
||||
```
|
||||
|
||||
3. Start eerst PostgreSQL en Redis.
|
||||
4. Start web; de entrypoint voert migraties uit.
|
||||
5. Start worker en scheduler.
|
||||
6. Controleer `GET /health/live/` en `GET /health/ready/`.
|
||||
6. Voer eenmalig in de webcontainer uit:
|
||||
|
||||
```bash
|
||||
python manage.py bootstrap_instance
|
||||
python manage.py collectstatic --noinput
|
||||
```
|
||||
|
||||
7. Meld lokaal aan en wijzig het bootstrapwachtwoord.
|
||||
8. Activeer nog geen live bron of mailbox voordat bronbeleid, retentie en back-up zijn gecontroleerd.
|
||||
|
||||
## Releaseverificatie en upgradepad
|
||||
|
||||
VR-117 voegt `./scripts/release_verify.sh` toe als release-check met artifacts voor package, checksum, configdiff, sbom en upgrade/rollback.
|
||||
Het script bewaart:
|
||||
|
||||
- `release-artifacts/<ts>/artifacts/release-smoke.json`
|
||||
- `release-artifacts/<ts>/artifacts/smoke-restored-validate.json`
|
||||
- `release-artifacts/<ts>/artifacts/upgrade-rollback.md`
|
||||
- `release-artifacts/<ts>/artifacts/checksums.txt`
|
||||
- `release-artifacts/<ts>/artifacts/config-diff.txt`
|
||||
|
||||
De restorecontrole vergelijkt primaire en gerestoreerde modelcounts in beide smoke-rapporten.
|
||||
|
||||
## Reverse proxy en TLS
|
||||
|
||||
Expose de applicatie niet rechtstreeks op internet. Plaats Nginx Proxy Manager, Traefik, Caddy of een gelijkwaardig beheerd reverse-proxyprofiel voor poort 1226. Vereisten:
|
||||
|
||||
- geldig TLS-certificaat;
|
||||
- alleen HTTPS extern;
|
||||
- `X-Forwarded-Proto` correct doorgeven;
|
||||
- request-bodylimiet klein houden; er is geen algemene upload-API;
|
||||
- optioneel extra access control/VPN voor persoonlijke installatie;
|
||||
- geen publieke toegang tot PostgreSQL, Redis of Ollama.
|
||||
|
||||
Wanneer een proxy op hetzelfde Docker-netwerk draait, publiceer poort 1226 alleen intern. Wanneer Unraid routing een hostpoort vereist, beperk die via firewall/VLAN tot de proxy of het beheernetwerk.
|
||||
|
||||
## Mailbox
|
||||
|
||||
Gebruik een aparte mailbox of alias, niet de hoofdmailbox. Begin met:
|
||||
|
||||
```dotenv
|
||||
IMAP_ENABLED=0
|
||||
IMAP_MARK_SEEN=0
|
||||
```
|
||||
|
||||
Na een succesvolle fixture- en read-onlytest:
|
||||
|
||||
```dotenv
|
||||
IMAP_ENABLED=1
|
||||
IMAP_HOST=<host>
|
||||
IMAP_USER=<account>
|
||||
IMAP_PASSWORD=<app-password>
|
||||
IMAP_MAILBOX=INBOX
|
||||
IMAP_USE_SSL=1
|
||||
```
|
||||
|
||||
Het account heeft alleen mailboxrechten nodig. Gebruik waar beschikbaar een app-password en schakel interactieve login op die mailbox niet uit zolang herstel nodig is.
|
||||
|
||||
## Ollama
|
||||
|
||||
Ollama is optioneel. Start de composeprofile pas wanneer de deterministische kern goed werkt:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.unraid.yml --profile ai up -d
|
||||
```
|
||||
|
||||
Pin een modelnaam in `OLLAMA_MODEL`; zonder model blijft `OLLAMA_ENABLED=0`. Stel Ollama niet extern bloot. Modeloutput wordt als onbetrouwbare gestructureerde data behandeld en mag geen harde regel of sollicitatieactie uitvoeren.
|
||||
|
||||
## Upgraden
|
||||
|
||||
1. Maak en verifieer een database- en configuratieback-up.
|
||||
2. Lees `CHANGELOG.md` en de releaseartefacten.
|
||||
3. Als de release nog niet geverifieerd is, voer `./scripts/release_verify.sh` lokaal uit.
|
||||
4. Pull/bouw de nieuwe immutable image.
|
||||
5. Stop scheduler en worker, daarna web.
|
||||
6. Start web en laat migraties uitvoeren.
|
||||
7. Controleer health, login en release-smoke-rapport.
|
||||
8. Start worker en scheduler.
|
||||
9. Bewaar de vorige image/digest totdat één volledige schedulercyclus goed verliep.
|
||||
|
||||
## Rollback
|
||||
|
||||
Bij een codefout zonder incompatibele migratie: pin de vorige image en herstart web/worker/scheduler. Bij een incompatibele datamigratie: stop alle schrijvers, herstel de vooraf gemaakte databaseback-up en gebruik de overeenkomende vorige image. Voer nooit een restore uit terwijl de worker of scheduler schrijft.
|
||||
|
||||
## Productiechecklist
|
||||
|
||||
- [ ] alle `CHANGE_ME`-waarden verwijderd;
|
||||
- [ ] debug uit en secure cookies aan;
|
||||
- [ ] TLS en host/origin exact ingesteld;
|
||||
- [ ] PostgreSQL/Redis/Ollama niet publiek;
|
||||
- [ ] dagelijkse back-up plus periodieke hersteltest;
|
||||
- [ ] aparte IMAP-mailbox met minimale rechten;
|
||||
- [ ] bronpolicy per actieve bron op `allow` met reviewdatum;
|
||||
- [ ] adminwachtwoord gewijzigd;
|
||||
- [ ] healthmonitoring en vrije schijfruimtebewaking;
|
||||
- [ ] retentiejob en logrotatie gecontroleerd.
|
||||
Reference in New Issue
Block a user