Gehele db production (~15GB) ingeladen
nikitaskliarov
Berichten
-
api.development.thefreighthero.com - update 'dev' database met production database data -
Voicedata Enreach & Shomi assistent installatie1. Licentie aanvragen
Vraag een nieuwe licentie aan bij Voicedata voor een reeds bestaande gebruiker met een toestelnummer (extensie) voor kantoor.
2. Enreach downloaden
Zodra de licentie is geactiveerd, download Enreach via de volgende link:
3. Installeren
Download de juiste versie voor het betreffende besturingssysteem en installeer de applicatie.
4. Inloggen
Log in met het opgegeven e-mailadres en wachtwoord.
Wachtwoord resetten kan op twee manieren:
- Via de Enreach-app: Gebruik de "Wachtwoord vergeten"-knop in het inlogscherm. Er wordt een resetlink naar het e-mailadres gestuurd.
- Via Voicedata (bij fictief e-mailadres): Log in op het Voicedata-portaal β ga naar Gebruikers β selecteer het bestaande account. In de sectie Beveiliging kan een wachtwoord-resetlink worden gegenereerd en het wachtwoord op afstand worden gewijzigd.
5. Gebruikssituaties
Situatie A: Yealink-telefoon + Yealink-headset + PC
Wij gebruiken de Yealink WH64-headset.
Headset koppelen via Bluetooth:
- Gebruik de schakelaar op de headset en zet deze op de Bluetooth-stand (houd 3 seconden ingedrukt).
- Open tegelijkertijd het Bluetooth-scanvenster op de PC.
- De headset wordt nu zichtbaar en kan gekoppeld worden.
Let op: Als de headset al verbonden is met de Yealink-telefoon, blijft de PC-verbinding ook actief.
Werkwijze voor bellen:
- Bellen via Enreach (inkomend/uitgaand): De gebruiker moet de headset gebruiken, aangezien de verbinding via de PC loopt en niet via de telefoon.
- Bellen via Yealink-telefoon (inkomend/uitgaand): Er zijn twee opties: gebruik de headset, of zet het gesprek op luid met de daarvoor bestemde knop op de telefoon.
οΈ Belangrijk: Vergeet niet om na het succesvol koppelen van de headset de audio-instellingen (invoer/uitvoer) te testen in de instellingen van Enreach!
Situatie B: Yealink-telefoon + PC (zonder headset)
Deze gebruikers mogen Enreach niet gebruiken voor het voeren of beantwoorden van gesprekken.
Wel is de belgeschiedenis van de Yealink-telefoon toegankelijk in Enreach via de Shomi-chatfunctie.
-
tfh-pagekit-crm Local Docker Database SyncOverzicht
Dit script automatiseert het synchroniseren van de productiedatabase naar je lokale Docker-omgeving. Het vervangt het handmatige proces van exporteren via phpMyAdmin, downloaden, en importeren via de terminal.
Wat doet het script?
- Controleert of Docker, SSH en MySQL beschikbaar zijn
- Maakt verbinding met de productieserver via SSH
- Draait
mysqldumpop de productieserver (alleen lezen, geen wijzigingen aan productie) - Downloadt het gecomprimeerde bestand naar je lokale machine
- Vraagt om bevestiging voordat lokale tabellen worden verwijderd
- Importeert de export in je lokale Docker database
- Verifieert het resultaat en ruimt tijdelijke bestanden op
Tabellen zonder data
De volgende tabellen worden alleen als structuur geΓ«xporteerd (zonder data), omdat ze grote logtabellen zijn die lokaal niet nodig zijn:
hero_contactmanager_loghero_emailsender_emailloghero_freighthero_calculator_data_audit_log
Configuratie
Inloggegevens instellen
Open
update-local-database.shen vul de volgende variabelen in:# Productieserver (SSH + Database) REMOTE_SSH_HOST="thefreighthero.com" REMOTE_SSH_USER="freighther" REMOTE_DB_USER="<vul in>" # Database gebruikersnaam op productie REMOTE_DB_PASS="<vul in>" # Database wachtwoord op productie # Lokale Docker database LOCAL_HOST="127.0.0.1" LOCAL_PORT="3307" LOCAL_USER="freighther_nl" LOCAL_PASS="freighther_nl" LOCAL_DB="freighther_nl"
οΈ Let op: Dit script bevat wachtwoorden in platte tekst. Deel dit bestand nooit via Git of andere publieke kanalen. Voeg het toe aan .gitignore.SSH-sleutel instellen (aanbevolen)
Om te voorkomen dat je elke keer je SSH-wachtwoord moet invoeren:
ssh-copy-id freighther@thefreighthero.comOptioneel:
pvinstallerenVoor een voortgangsbalk tijdens het importeren:
# macOS brew install pv # Ubuntu/Debian sudo apt install pv
Gebruik
Script uitvoeren
cd ~/Desktop/tfh/helpers bash update-local-database.shVerwachte output
==> Preparing environment β Docker is running β SSH client available β MySQL client available ==> Exporting database from production via SSH Connecting to freighther@thefreighthero.com... [remote] Dumping tables with data... [remote] Dumping structure-only tables... [remote] Compressing... [remote] Done: 45M β Remote export complete ==> Downloading export from production ...progress... β Download complete β Decompressed: freighther_nl_20260212_143022.sql (180MB) ==> Truncating local database Found 47 tables to drop Target: mysql -h 127.0.0.1 -P 3307 -u freighther_nl freighther_nl DROP TABLE `hero_contactmanager_contacts` DROP TABLE `hero_contactmanager_log` DROP TABLE `hero_emailsender_emaillog` ... β οΈ This will DROP all 47 tables in LOCAL database 'freighther_nl' β οΈ Connection: 127.0.0.1:3307 Continue? (yes/no): yes β Dropped 47 tables ==> Importing into local database β Import complete ==> Verifying import β 47 tables imported hero_contactmanager_log: 0 rows (structure only β) hero_emailsender_emaillog: 0 rows (structure only β) hero_freighthero_calculator_data_audit_log: 0 rows (structure only β) ==> Cleaning up β Removed remote dump Local file kept: freighther_nl_20260212_143022.sql ============================================= π³ Database sync complete! =============================================Bevestigingsvraag
Voordat het script tabellen verwijdert, toont het:
- De exacte database-verbinding (
127.0.0.1:3307) - Alle tabellen die verwijderd worden
- Een bevestigingsvraag waar je
yesmoet typen
Typ iets anders dan
yesom te annuleren. Het script ruimt dan het remote bestand op en stopt.
Veiligheid
Productiedatabase wordt NIET gewijzigd
Stap Waar Actie Export Productieserver mysqldumpβ alleen lezenDownload Productie β Lokaal Bestandsoverdracht Drop tabellen Alleen lokale Docker DROP TABLEop127.0.0.1:3307Import Alleen lokale Docker mysql < bestandop127.0.0.1:3307Opruimen Productieserver Verwijdert alleen /tmp/*.sql.gzWachtwoorden beschermen
Het script bevat database-wachtwoorden. Zorg ervoor dat:
- Het bestand niet in versiebeheer (Git) staat
- Voeg toe aan
.gitignore:update-local-database.sh - Stel de juiste bestandsrechten in:
Dit zorgt ervoor dat alleen jij het bestand kunt lezen en uitvoeren.chmod 700 update-local-database.sh
Problemen oplossen
"Access denied" bij remote export
- Controleer
REMOTE_DB_USERenREMOTE_DB_PASS - Zorg dat het wachtwoord geen speciale tekens bevat die verkeerd worden geΓ―nterpreteerd
- Test handmatig:
ssh freighther@thefreighthero.com "mysqldump -u GEBRUIKER -pWACHTWOORD freighther_nl --no-data | head -5"
"Docker is not running"
- Start Docker Desktop of de Docker daemon
Import mislukt
- Controleer of je lokale Docker MySQL-container draait
- Test verbinding:
mysql -h 127.0.0.1 -P 3307 -u freighther_nl -p freighther_nl -e "SELECT 1;"
Geen voortgangsbalk bij import
- Installeer
pv(zie configuratie hierboven)
Structuur-only tabellen aanpassen
Om tabellen toe te voegen of te verwijderen die zonder data worden geΓ«xporteerd, pas het array aan in het script:
STRUCTURE_ONLY_TABLES=( "hero_contactmanager_log" "hero_emailsender_emaillog" "hero_freighthero_calculator_data_audit_log" # Voeg hier extra tabellen toe )Script
#!/bin/bash # ============================================================ # π³ FreightHero Local DB Sync # Exports from production, downloads, and imports locally # ============================================================ # -- Remote (Production) Configuration ---------------------- REMOTE_SSH_HOST="thefreighthero.com" REMOTE_SSH_USER="freighther" REMOTE_DB_NAME="freighther_nl" REMOTE_DB_USER="freighther" REMOTE_DB_PASS="DIRECT ADMIN PASS OF USER" REMOTE_DUMP_PATH="/home/freighther/dump/freighther_nl_export.sql" # -- Local (Docker) Configuration ---------------------------- LOCAL_HOST="127.0.0.1" LOCAL_PORT="3307" LOCAL_DB="LOCAL_DB_NAME" LOCAL_USER="LOCAL_DB_USER" LOCAL_PASS="LOCAL_DB_PASS" # -- Export password for mysql client ------------------------ export MYSQL_PWD="$LOCAL_PASS" # -- Tables to export structure only (no data) --------------- STRUCTURE_ONLY_TABLES=( "hero_contactmanager_log" "hero_emailsender_emaillog" "hero_freighthero_calculator_data_audit_log" ) # -- Local SQL file with timestamp --------------------------- LOCAL_SQL_FILE="freighther_nl_$(date +%Y%m%d_%H%M%S).sql" # -- Helper -------------------------------------------------- log_step() { echo ""; echo "==> $1"; } log_info() { echo " $1"; } log_done() { echo " β $1"; } log_error() { echo " β $1"; exit 1; } # ============================================================ # Step 1: Check prerequisites # ============================================================ log_step "Preparing environment" if ! docker info &>/dev/null; then log_error "Docker is not running. Start Docker first." fi log_done "Docker is running" if ! command -v ssh &>/dev/null; then log_error "SSH client not found." fi log_done "SSH client available" if ! command -v mysql &>/dev/null; then log_error "mysql client not found." fi log_done "MySQL client available" # ============================================================ # Step 2: Run mysqldump on production server via SSH # ============================================================ log_step "Exporting database from production via SSH" log_info "Connecting to ${REMOTE_SSH_USER}@${REMOTE_SSH_HOST}..." # Build --ignore-table flags for structure-only tables IGNORE_FLAGS="" for TABLE in "${STRUCTURE_ONLY_TABLES[@]}"; do IGNORE_FLAGS+=" --ignore-table=${REMOTE_DB_NAME}.${TABLE}" done # Space-separated list for structure-only dump STRUCTURE_TABLES="${STRUCTURE_ONLY_TABLES[*]}" # Write remote script to a temp file to avoid quoting hell REMOTE_SCRIPT_FILE=$(mktemp) cat > "$REMOTE_SCRIPT_FILE" <<EOF #!/bin/bash set -e echo "[remote] Dumping tables with data..." mysqldump -u ${REMOTE_DB_USER} -p${REMOTE_DB_PASS} \ ${REMOTE_DB_NAME} \ ${IGNORE_FLAGS} \ --single-transaction \ --routines \ --triggers \ > ${REMOTE_DUMP_PATH} echo "[remote] Dumping structure-only tables..." mysqldump -u ${REMOTE_DB_USER} -p${REMOTE_DB_PASS} \ ${REMOTE_DB_NAME} \ ${STRUCTURE_TABLES} \ --no-data \ --single-transaction \ >> ${REMOTE_DUMP_PATH} echo "[remote] Compressing..." gzip -f ${REMOTE_DUMP_PATH} FILE_SIZE=\$(ls -lh ${REMOTE_DUMP_PATH}.gz | awk '{print \$5}') echo "[remote] Done: \${FILE_SIZE}" EOF ssh "${REMOTE_SSH_USER}@${REMOTE_SSH_HOST}" bash < "$REMOTE_SCRIPT_FILE" 2>&1 | while IFS= read -r line; do log_info "$line" done if [[ ${PIPESTATUS[0]} -ne 0 ]]; then rm -f "$REMOTE_SCRIPT_FILE" log_error "Remote export failed. Check SSH connection and DB credentials." fi rm -f "$REMOTE_SCRIPT_FILE" log_done "Remote export complete" # ============================================================ # Step 3: Download with progress # ============================================================ log_step "Downloading export from production" if command -v rsync &>/dev/null; then rsync -avz --progress \ "${REMOTE_SSH_USER}@${REMOTE_SSH_HOST}:${REMOTE_DUMP_PATH}.gz" \ "${LOCAL_SQL_FILE}.gz" \ 2>&1 | while IFS= read -r line; do echo " ${line}" done else scp \ "${REMOTE_SSH_USER}@${REMOTE_SSH_HOST}:${REMOTE_DUMP_PATH}.gz" \ "${LOCAL_SQL_FILE}.gz" fi if [[ ! -f "${LOCAL_SQL_FILE}.gz" ]]; then log_error "Download failed." fi log_done "Download complete" log_info "Decompressing..." gunzip -f "${LOCAL_SQL_FILE}.gz" if [[ ! -f "${LOCAL_SQL_FILE}" ]]; then log_error "Decompression failed." fi FILE_SIZE_MB=$(du -m "${LOCAL_SQL_FILE}" | awk '{print $1}') log_done "Decompressed: ${LOCAL_SQL_FILE} (${FILE_SIZE_MB}MB)" # ============================================================ # Step 4: Drop all tables in local database # ============================================================ log_step "Truncating local database" # Re-export password for local commands export MYSQL_PWD="$LOCAL_PASS" TABLES=$(mysql -h "$LOCAL_HOST" -P "$LOCAL_PORT" -u "$LOCAL_USER" "$LOCAL_DB" \ -N -e "SHOW TABLES;" 2>/dev/null) if [[ -z "$TABLES" ]]; then log_info "Local database is empty, skipping." else TABLE_COUNT=$(echo "$TABLES" | wc -l | tr -d ' ') log_info "Found ${TABLE_COUNT} tables to drop" # Disable FK checks, drop all, re-enable DROP_SQL="SET FOREIGN_KEY_CHECKS = 0;" while IFS= read -r TABLE; do DROP_SQL+=" DROP TABLE IF EXISTS \`${TABLE}\`;" done <<< "$TABLES" DROP_SQL+=" SET FOREIGN_KEY_CHECKS = 1;" mysql -h "$LOCAL_HOST" -P "$LOCAL_PORT" -u "$LOCAL_USER" "$LOCAL_DB" \ -e "${DROP_SQL}" 2>/dev/null if [[ $? -ne 0 ]]; then log_error "Failed to drop tables." fi log_done "Dropped ${TABLE_COUNT} tables" fi # ============================================================ # Step 5: Import into local database # ============================================================ log_step "Importing into local database" if command -v pv &>/dev/null; then pv "${LOCAL_SQL_FILE}" | mysql -h "$LOCAL_HOST" -P "$LOCAL_PORT" -u "$LOCAL_USER" "$LOCAL_DB" 2>/dev/null else log_info "(tip: install 'pv' for a progress bar)" mysql -h "$LOCAL_HOST" -P "$LOCAL_PORT" -u "$LOCAL_USER" "$LOCAL_DB" < "$LOCAL_SQL_FILE" fi if [[ $? -ne 0 ]]; then log_error "Import failed." fi log_done "Import complete" # ============================================================ # Step 6: Verify # ============================================================ log_step "Verifying import" TOTAL_TABLES=$(mysql -h "$LOCAL_HOST" -P "$LOCAL_PORT" -u "$LOCAL_USER" "$LOCAL_DB" \ -N -e "SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA='${LOCAL_DB}';" 2>/dev/null) log_done "${TOTAL_TABLES} tables imported" for TABLE in "${STRUCTURE_ONLY_TABLES[@]}"; do ROW_COUNT=$(mysql -h "$LOCAL_HOST" -P "$LOCAL_PORT" -u "$LOCAL_USER" "$LOCAL_DB" \ -N -e "SELECT COUNT(*) FROM \`${TABLE}\`;" 2>/dev/null) log_info "${TABLE}: ${ROW_COUNT:-0} rows (structure only β)" done # ============================================================ # Step 7: Cleanup # ============================================================ log_step "Cleaning up" ssh "${REMOTE_SSH_USER}@${REMOTE_SSH_HOST}" "rm -f ${REMOTE_DUMP_PATH}.gz" 2>/dev/null log_done "Removed remote dump" log_info "Local file kept: ${LOCAL_SQL_FILE}" unset MYSQL_PWD echo "" echo "=============================================" echo " π³ Database sync complete!" echo "=============================================" echo "" -
TFH Dagelijkse Systeemcontrole β Automatisch OpstartscriptOverzicht
Elke ochtend worden er handmatig drie platforms gecontroleerd om te zorgen dat alle systemen van The Freight Hero correct functioneren. Dit script automatiseert deze controles en toont de resultaten direct in de terminal bij het opstarten van de laptop.
De volgende controles worden uitgevoerd:
- Cronitor β Status van alle cronjobs op de server (gezond / falend / gepauzeerd)
- AWS S3 β Overzicht van database-backups in de
tfh-backup-2bucket, inclusief leeftijd van bestanden en maandelijkse kosten. Backups ouder dan 3 dagen worden gemarkeerd als "pending deletion" conform het retentiebeleid. - Google Cloud β Aantal API-verzoeken en foutmeldingen (4xx/5xx) van de Google Maps API's over de afgelopen 24 uur
Vereisten
Tool Installatie jqsudo apt install jqbcsudo apt install bcAWS CLI v2 Installatiehandleiding Google Cloud CLI Installatiehandleiding Configuratie
AWS-inloggegevens
Er is een aparte IAM-gebruiker aangemaakt (
nikita-ubuntu-laptop) met minimale rechten:s3:ListBucketens3:GetBucketLocationoparn:aws:s3:::tfh-backup-2ce:GetCostAndUsagevoor het ophalen van factureringsgegevens
Configureer de AWS CLI eenmalig:
aws configure # Vul Access Key ID, Secret Access Key en regio (bijv. eu-west-1) inGoogle Cloud-inloggegevens
Configureer eenmalig:
gcloud auth login gcloud config set project tfh-maps-477109Omgevingsvariabelen
Bestand:
~/tfh-check/morning-check.env# ============================================================================ # TFH Morning Check β Environment Configuration # Copy this file to morning-check.env and fill in your values # ============================================================================ # --- Cronitor --- # Get your API key from: https://cronitor.io/settings/api CRONITOR_API_KEY=KEY_FROM_TELEMETRY # --- AWS S3 --- # Auth is handled by 'aws configure' (your nikita-ubuntu-laptop IAM user credentials) # No keys needed here β just set bucket and retention policy S3_BUCKET=tfh-backup-2 S3_RETENTION_DAYS=3 # --- Google Cloud --- # Auth is handled by 'gcloud auth login' # Find your project ID at: https://console.cloud.google.com/home/dashboard GCP_PROJECT_ID=tfh-maps-477109 # Comma-separated list of Maps API services to monitor # Find your enabled APIs at: https://console.cloud.google.com/apis/dashboard GCP_MAPS_SERVICES=maps-backend.googleapis.com,geocoding-backend.googleapis.com,places-backend.googleapis.com,directions-backend.googleapis.comLet op: Gebruik SDK API key van cronitor.
Installatie
# Map aanmaken en bestanden plaatsen mkdir -p ~/tfh-check # Kopieer morning-check.sh en morning-check.env naar ~/tfh-check/ chmod +x ~/tfh-check/morning-check.shHet Script
#!/bin/bash # ============================================================================ # TFH Morning Health Check Script # Runs daily checks on: Cronitor, AWS S3 Backups, Google Cloud Maps API # ============================================================================ # --- Colors & Formatting --- RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' BOLD='\033[1m' DIM='\033[2m' NC='\033[0m' # No Color PASS="${GREEN}β${NC}" FAIL="${RED}β${NC}" WARN="${YELLOW}β ${NC}" # --- Configuration (set these or use environment variables) --- CRONITOR_API_KEY="${CRONITOR_API_KEY:-}" AWS_PROFILE="${AWS_PROFILE:-default}" S3_BUCKET="${S3_BUCKET:-tfh-backup-2}" S3_RETENTION_DAYS="${S3_RETENTION_DAYS:-3}" GCP_PROJECT_ID="${GCP_PROJECT_ID:-}" # Comma-separated list of Google Maps API service names to check # Common ones: maps-backend.googleapis.com, places-backend.googleapis.com, # geocoding-backend.googleapis.com, directions-backend.googleapis.com GCP_MAPS_SERVICES="${GCP_MAPS_SERVICES:-maps-backend.googleapis.com,geocoding-backend.googleapis.com,places-backend.googleapis.com,directions-backend.googleapis.com}" # Max backup age in seconds MAX_AGE_SECONDS=$((S3_RETENTION_DAYS * 86400)) # ============================================================================ divider() { echo -e "${DIM}ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ${NC}" } header() { echo "" echo -e "${BOLD}${CYAN}ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ${NC}" echo -e "${BOLD}${CYAN}β TFH Morning Health Check β $(date '+%Y-%m-%d %H:%M:%S') β${NC}" echo -e "${BOLD}${CYAN}ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ${NC}" echo "" } section() { echo -e "${BOLD}${BLUE}βΈ $1${NC}" divider } # ============================================================================ # 1. CRONITOR β Check monitor health # ============================================================================ check_cronitor() { section "CRONITOR β Cron Job Health" if [ -z "$CRONITOR_API_KEY" ]; then echo -e " ${FAIL} CRONITOR_API_KEY not set. Skipping." echo "" return fi # Fetch all monitors (paginated, first page β usually enough) response=$(curl -s -w "\n%{http_code}" \ -u "${CRONITOR_API_KEY}:" \ "https://cronitor.io/api/monitors?page=1") http_code=$(echo "$response" | tail -1) body=$(echo "$response" | sed '$d') if [ "$http_code" != "200" ]; then echo -e " ${FAIL} Cronitor API returned HTTP ${http_code}" echo "" return fi # Parse monitors using jq # Cronitor status values: up, down, grace, new, paused # The status may also be in .passing or latest_event fields depending on API version total=$(echo "$body" | jq '.monitors | length') echo "$body" | jq -r '.monitors[] | "\(.status // .passing // "unknown")|\(.name // .key)|\(.key)"' | \ while IFS='|' read -r status name key; do case "$status" in up|true) echo -e " ${PASS} ${name}" ;; down|false) echo -e " ${FAIL} ${name} ${RED}(DOWN)${NC}" ;; grace) echo -e " ${WARN} ${name} ${YELLOW}(grace period)${NC}" ;; paused) echo -e " ${WARN} ${name} ${YELLOW}(paused)${NC}" ;; new) echo -e " ${WARN} ${name} ${YELLOW}(new β no data yet)${NC}" ;; *) echo -e " ${WARN} ${name} ${DIM}(status: ${status})${NC}" ;; esac done # Summary counts ok_count=$(echo "$body" | jq '[.monitors[] | select(.status == "up" or .passing == true)] | length') fail_count=$(echo "$body" | jq '[.monitors[] | select(.status == "down" or .passing == false)] | length') other_count=$((total - ok_count - fail_count)) echo "" echo -e " ${DIM}Total: ${total} | OK: ${ok_count} | Failing: ${fail_count} | Other: ${other_count}${NC}" echo "" } # ============================================================================ # 2. AWS S3 β Backup bucket check # ============================================================================ check_s3_backups() { section "AWS S3 β Backup Bucket (${S3_BUCKET})" # Check if AWS CLI is available if ! command -v aws &> /dev/null; then echo -e " ${FAIL} AWS CLI not installed. Skipping." echo "" return fi # --- 2a. Check remaining credits (AWS billing / cost explorer) --- echo -e " ${BOLD}Account Balance / Credits:${NC}" # Try to get credit balance from Cost Explorer # Note: This requires ce:GetCostAndUsage permission month_start=$(date '+%Y-%m-01') today=$(date '+%Y-%m-%d') credit_info=$(aws ce get-cost-and-usage \ --time-period "Start=${month_start},End=${today}" \ --granularity MONTHLY \ --metrics "UnblendedCost" \ --output json 2>&1) if echo "$credit_info" | jq -e '.ResultsByTime' &>/dev/null 2>&1; then raw_cost=$(echo "$credit_info" | jq -r '.ResultsByTime[0].Total.UnblendedCost.Amount // "0"') currency=$(echo "$credit_info" | jq -r '.ResultsByTime[0].Total.UnblendedCost.Unit // "USD"') # Format nicely β show $0.00 instead of -0.0000000006 formatted_cost=$(printf "%.2f" "$raw_cost") echo -e " ${DIM}Current month spend: ${currency} ${formatted_cost}${NC}" else echo -e " ${WARN} Could not fetch billing data (need ce:GetCostAndUsage permission)" echo -e " ${DIM}Tip: Check manually at https://console.aws.amazon.com/billing/${NC}" fi echo "" # --- 2b. List backups and check ages --- echo -e " ${BOLD}Backup Objects (max ${S3_RETENTION_DAYS} days old):${NC}" # List all objects, sorted by date (newest first) objects=$(aws s3api list-objects-v2 \ --bucket "$S3_BUCKET" \ --query 'Contents[].{Key: Key, LastModified: LastModified, Size: Size}' \ --output json 2>&1) if echo "$objects" | jq -e '.' &>/dev/null 2>&1 && [ "$objects" != "null" ]; then now_epoch=$(date +%s) total_objects=$(echo "$objects" | jq 'length') fresh_count=0 stale_count=0 echo "$objects" | jq -r '.[] | "\(.LastModified)|\(.Key)|\(.Size)"' | sort -r | \ while IFS='|' read -r modified key size; do # Strip timezone suffix (+00:00 or Z) for date parsing clean_date=$(echo "$modified" | sed 's/+00:00$//' | sed 's/Z$//') mod_epoch=$(date -d "$clean_date" +%s 2>/dev/null || echo 0) age_seconds=$((now_epoch - mod_epoch)) age_days=$((age_seconds / 86400)) age_hours=$(( (age_seconds % 86400) / 3600 )) # Human-readable size if [ "$size" -gt 1073741824 ] 2>/dev/null; then h_size="$(printf "%.2f GB" "$(echo "scale=2; $size/1073741824" | bc)")" elif [ "$size" -gt 1048576 ] 2>/dev/null; then h_size="$(printf "%.1f MB" "$(echo "scale=1; $size/1048576" | bc)")" elif [ "$size" -gt 1024 ] 2>/dev/null; then h_size="$(printf "%.1f KB" "$(echo "scale=1; $size/1024" | bc)")" else h_size="${size} B" fi # Truncate filename: show just the filename part, max 50 chars short_name=$(basename "$key") if [ ${#short_name} -gt 50 ]; then short_name="${short_name:0:47}..." fi # Check freshness if [ "$age_seconds" -gt "$MAX_AGE_SECONDS" ]; then echo -e " ${DIM}β ${short_name} ${age_days}d ${age_hours}h (pending deletion)${NC}" else echo -e " ${PASS} ${short_name} ${DIM}${age_days}d ${age_hours}h | ${h_size}${NC}" fi done # Summary β use shell-based counting to avoid jq date issues stale=0 fresh=0 echo "$objects" | jq -r '.[].LastModified' | while read -r mod; do clean=$(echo "$mod" | sed 's/+00:00$//' | sed 's/Z$//') ep=$(date -d "$clean" +%s 2>/dev/null || echo 0) if [ $((now_epoch - ep)) -gt "$MAX_AGE_SECONDS" ]; then echo "stale" else echo "fresh" fi done | sort | uniq -c | while read -r count label; do if [ "$label" = "stale" ]; then stale=$count; fi if [ "$label" = "fresh" ]; then fresh=$count; fi done # Recount for display (subshell workaround) stale=$(echo "$objects" | jq -r '.[].LastModified' | while read -r mod; do clean=$(echo "$mod" | sed 's/+00:00$//' | sed 's/Z$//') ep=$(date -d "$clean" +%s 2>/dev/null || echo 0) [ $((now_epoch - ep)) -gt "$MAX_AGE_SECONDS" ] && echo 1 done | wc -l) fresh=$((total_objects - stale)) echo "" echo -e " ${DIM}Total: ${total_objects} | Fresh: ${fresh} | Pending deletion: ${stale}${NC}" if [ "$fresh" -gt 0 ]; then echo -e " ${PASS} Latest backup: $(echo "$objects" | jq -r 'sort_by(.LastModified) | last | .LastModified' | sed 's/+00:00$//')" fi else echo -e " ${FAIL} Could not list objects in s3://${S3_BUCKET}" echo -e " ${DIM}Error: ${objects}${NC}" fi echo "" } # ============================================================================ # 3. GOOGLE CLOUD β Maps API usage & errors # ============================================================================ check_google_maps() { section "GOOGLE CLOUD β Maps API Usage & Errors" # Check if gcloud is available if ! command -v gcloud &> /dev/null; then echo -e " ${FAIL} gcloud CLI not installed. Skipping." echo "" return fi # Auto-detect project if not set if [ -z "$GCP_PROJECT_ID" ]; then GCP_PROJECT_ID=$(gcloud config get-value project 2>/dev/null) fi if [ -z "$GCP_PROJECT_ID" ]; then echo -e " ${FAIL} GCP_PROJECT_ID not set and no default project configured." echo "" return fi echo -e " ${DIM}Project: ${GCP_PROJECT_ID}${NC}" echo "" # --- 3a. Billing cost for current month --- echo -e " ${BOLD}Current Month Billing:${NC}" # Use gcloud billing to get cost info (if available) # Alternative: use Cloud Billing API billing_account=$(gcloud billing projects describe "$GCP_PROJECT_ID" \ --format="value(billingAccountName)" 2>/dev/null) if [ -n "$billing_account" ]; then echo -e " ${DIM}Billing account: ${billing_account}${NC}" echo -e " ${DIM}Tip: Detailed costs at https://console.cloud.google.com/billing/${NC}" else echo -e " ${WARN} Could not fetch billing info" fi echo "" # --- 3b. API Usage (request count) last 24 hours --- echo -e " ${BOLD}API Request Counts (last 24h):${NC}" # Calculate time window end_time=$(date -u '+%Y-%m-%dT%H:%M:%SZ') start_time=$(date -u -d '24 hours ago' '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null || \ date -u -v-24H '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null) IFS=',' read -ra SERVICES <<< "$GCP_MAPS_SERVICES" for service in "${SERVICES[@]}"; do service=$(echo "$service" | xargs) # trim whitespace # Query the monitoring API for request count # Metric: serviceruntime.googleapis.com/api/request_count result=$(gcloud monitoring time-series list \ --project="$GCP_PROJECT_ID" \ --filter="metric.type=\"serviceruntime.googleapis.com/api/request_count\" AND resource.labels.service=\"${service}\"" \ --interval-start-time="$start_time" \ --interval-end-time="$end_time" \ --format=json 2>/dev/null) if [ $? -eq 0 ] && echo "$result" | jq -e '.' &>/dev/null 2>&1; then # Sum all data points total_requests=$(echo "$result" | jq '[.[].points[].value.int64Value // 0 | tonumber] | add // 0') # Count errors (4xx + 5xx) error_requests=$(echo "$result" | jq ' [.[] | select(.metric.labels.response_code_class == "4xx" or .metric.labels.response_code_class == "5xx") | .points[].value.int64Value // 0 | tonumber ] | add // 0') if [ "$error_requests" -gt 0 ]; then echo -e " ${WARN} ${service}" echo -e " Requests: ${total_requests} | ${RED}Errors: ${error_requests}${NC}" elif [ "$total_requests" -gt 0 ]; then echo -e " ${PASS} ${service}" echo -e " ${DIM}Requests: ${total_requests} | Errors: 0${NC}" else echo -e " ${DIM} β ${service} β no requests in last 24h${NC}" fi else # Fallback: try using curl directly with the monitoring API token=$(gcloud auth print-access-token 2>/dev/null) if [ -n "$token" ]; then api_result=$(curl -s \ -H "Authorization: Bearer $token" \ "https://monitoring.googleapis.com/v3/projects/${GCP_PROJECT_ID}/timeSeries?filter=metric.type%3D%22serviceruntime.googleapis.com%2Fapi%2Frequest_count%22%20AND%20resource.labels.service%3D%22${service}%22&interval.endTime=${end_time}&interval.startTime=${start_time}&aggregation.alignmentPeriod=86400s&aggregation.perSeriesAligner=ALIGN_SUM") if echo "$api_result" | jq -e '.timeSeries' &>/dev/null 2>&1; then total=$(echo "$api_result" | jq '[.timeSeries[].points[].value.int64Value // "0" | tonumber] | add // 0') echo -e " ${PASS} ${service} β ${total} requests" else echo -e " ${DIM} β ${service} β no data or insufficient permissions${NC}" fi else echo -e " ${FAIL} ${service} β could not authenticate" fi fi done # --- 3c. Check for API errors specifically --- echo "" echo -e " ${BOLD}Error Summary (4xx/5xx, last 24h):${NC}" token=$(gcloud auth print-access-token 2>/dev/null) if [ -n "$token" ]; then # Query for error responses across all Maps APIs error_result=$(curl -s \ -H "Authorization: Bearer $token" \ "https://monitoring.googleapis.com/v3/projects/${GCP_PROJECT_ID}/timeSeries?filter=metric.type%3D%22serviceruntime.googleapis.com%2Fapi%2Frequest_count%22%20AND%20metric.labels.response_code_class%3Done_of(%224xx%22%2C%225xx%22)&interval.endTime=${end_time}&interval.startTime=${start_time}&aggregation.alignmentPeriod=86400s&aggregation.perSeriesAligner=ALIGN_SUM") if echo "$error_result" | jq -e '.timeSeries[0]' &>/dev/null 2>&1; then echo "$error_result" | jq -r '.timeSeries[] | "\(.resource.labels.service)|\(.metric.labels.response_code_class)|\(.points[0].value.int64Value // 0)"' | \ while IFS='|' read -r svc code count; do echo -e " ${FAIL} ${svc}: ${count} ${code} errors" done else echo -e " ${PASS} No API errors in the last 24 hours." fi else echo -e " ${WARN} Could not authenticate to check errors" fi echo "" } # ============================================================================ # MAIN # ============================================================================ header check_cronitor check_s3_backups check_google_maps echo -e "${BOLD}${CYAN}ββββββββββββββββββββββββββββββββββββββββββββββββββββββ${NC}" echo -e "${DIM}Check complete. $(date '+%H:%M:%S')${NC}" echo ""Gebruik
Handmatig uitvoeren
morningDit alias is toegevoegd aan
~/.bashrc:alias morning="set -a && source ~/tfh-check/morning-check.env && set +a && ~/tfh-check/morning-check.sh"Automatisch bij opstarten
Het script draait automatisch bij het opstarten van de laptop via Opstartapplicaties (Startup Applications) met het volgende commando:
gnome-terminal -- bash -c "set -a && source ~/tfh-check/morning-check.env && set +a && ~/tfh-check/morning-check.sh; echo; read -p 'Press Enter to close...'"Daarnaast draait het script eenmalig per dag bij het openen van de eerste terminal via een markerbestand in
/tmp.Voorbeelduitvoer
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β TFH Morning Health Check β 2026-02-12 11:14:06 β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ βΈ CRONITOR β Cron Job Health ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β root /usr/sbin/csf --lfd restart > 2 β systemctl restart redis.service β /usr/local/bin/server-health-check.sh β sh /home/freighther/tfh-backup-database.sh β sh /home/freighther/...-database.sh > ... Total: 5 | OK: 5 | Failing: 0 | Other: 0 βΈ AWS S3 β Backup Bucket (tfh-backup-2) ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ Account Balance / Credits: Current month spend: USD 0.00 Backup Objects (max 3 days old): β ...DATABASE-20260212_110001.tar.gz 0d 0h | 1.39 GB β ...DATABASE-20260212_090001.tar.gz 0d 2h | 1.39 GB β ...DATABASE-20260212_060002.tar.gz 0d 5h | 1.39 GB ... β ...DATABASE-20260209_110001.tar.gz 3d 0h (pending deletion) Total: 31 | Fresh: 27 | Pending deletion: 4 βΈ GOOGLE CLOUD β Maps API Usage & Errors ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ Project: tfh-maps-477109 API Request Counts (last 24h): β maps-backend.googleapis.com β 1155 requests β places-backend.googleapis.com β 426 requests β directions-backend.googleapis.com β 105 requests Error Summary (4xx/5xx, last 24h): β No API errors in the last 24 hours. -
Gebruiker Toevoegen aan Chrome-extensieGebruiker Toevoegen aan Chrome-extensie
Deze handleiding beschrijft hoe een nieuwe gebruiker correct wordt geconfigureerd zodat de Realtime-notificaties (pop-ups) verschijnen in de browser.
Stap 1: Verbinding maken met de Interne Server
Maak verbinding via SSH β dit kan alleen vanuit hetzelfde netwerk, anders is de PC extern niet te vinden:
ssh root@192.168.1.25Als je SSH-key nog niet in de authorized keys lijst staat, wordt er om een wachtwoord gevraagd. Dit kun je aanvragen bij Nikita Skliarov.
Stap 2: Gebruiker Toevoegen aan
extensions.jsonBewerk het bestand:
nano /home/internal/internal-server/data/extensions.jsonVoeg een nieuw object toe in het volgende formaat:
{ "extension": "", "name": "", "user": "", "uri": "{{user}}@mv.voicedata.nl", "ip": "" }Veld Uitleg extensionHet gewenste toestelnummer, toegewezen door Voicedata. nameWeergavenaam in de Chrome-extensie. userZoek op het Voicedata Managed Voice panel in de tab Kanalen β de gebruikersnaam naast het extensienummer is je user.uriBegint met de user-waarde (hetzelfde als vorige veld) en eindigt met@mv.voicedata.nl.ipIP-adres van het toestel. Dit vind je op het toestel zelf: druk op OK en zoek het IPv4 IP-adres of in een dezelfde lijst van Kanalen.
Stap 3: Herstart de Interne Service
systemctl restart internal && systemctl status internalMocht de service failed zijn:
- Controleer of de JSON geldig is. De meest voorkomende fouten zijn een komma aan het einde van de lijst, aan het einde van values, of aan het einde van een item.
- Gebruik
cat /home/internal/internal-server/data/extensions.jsonom de inhoud te bekijken en online te valideren via een JSON-validator. - Gebruik
journalctlom verder te onderzoeken wat het probleem is.
Stap 4: Toestel Instellingen Openen
Open de volgende URL in je browser (vervang
{{TOESTEL IP ADDRESS}}door het IP-adres van het toestel):https://{{TOESTEL IP ADDRESS}}/servlet?m=mod_data&p=features-remotecontrl&q=loadInloggegevens:
- Gebruikersnaam:
admin - Wachtwoord:
0+ ons Voicedata-klantnummer (te vinden linksboven als je Managed Voice opent)
Stap 5: Remote Control Configureren
Ga naar Features (hoofdtab) β Remote Control (subtab).
Stel de volgende velden in op het server IP-adres:
Veld Waarde Push XML Server IP Address 192.168.1.25Action URI Allow IP List 192.168.1.25
οΈ Vergeet niet op Confirm te drukken!
Stap 6: Action URL's Configureren
Blijf in de Features tab en ga naar de subtab Action URL (links).
Stel de volgende URL's in. Vervang
ipdoor het IP-adres van het toestel!Incoming Call:
ip:3000/phone/incoming?active_host=$active_host&active_user=$active_user&ip=$ip&remote=$remote&local=$local&display_remote=$display_remote&display_local=$display_local&call_id=$call_id&called_number=$calledNumberOutgoing Call:
ip:3000/phone/outgoing?active_host=$active_host&active_user=$active_user&ip=$ip&remote=$remote&local=$local&display_remote=$display_remote&display_local=$display_local&call_id=$call_id&called_number=$calledNumberMissed Call:
ip:3000/phone/missed?active_user=$active_user&call_id=$call_idEstablished:
ip:3000/phone/established?active_user=$active_user&call_id=$call_id
οΈ Vergeet niet ipte vervangen door het daadwerkelijke IP-adres van het toestel!
οΈ Vergeet niet op Confirm te drukken!
Stap 7: Chrome-extensie Installeren bij de Medewerker
Installeer de Chrome-extensie op de PC van de medewerker. Volg hiervoor de installatie-instructies:
Installatie Telefoon Extensie β TFH Docs
Als alles goed is verlopen, zou je nu een nieuwe gebruiker moeten zien verschijnen in de Chrome-extensie.
-
Internal-server (HP pc in het kantoor en hoe deze samewerkt met live.thefreighthero.nl en chrome extensie)Internal Server Documentation The Freight Hero
1. Algemene Informatie
De Internal-Server is een fysieke HP-server op de kantoorlocatie. Deze dient als de cruciale schakel (proxy) tussen de kantoor-PBX (VoIP-telefooncentrale) en het cloudplatform Realtime.
Hoofdfunctie: Het monitoren van de PBX via webhooks/events, het verwerken van deze data en het doorsturen van notificaties naar de cloud-API (
live.thefreighthero.nl). Dit zorgt ervoor dat inkomende en uitgaande gesprekken zichtbaar worden in de browsers van medewerkers.2. Toegang en Beheer
- Fysieke Toegang: Indien SSH niet beschikbaar is, kan beheer plaatsvinden via een directe monitor- en toetsenbordverbinding.
- Wachtwoordherstel: Kan worden uitgevoerd door de GRUB-bootloader te onderbreken tijdens het opstarten en de
single user mode(ofinit=/bin/bash) te gebruiken. - SSH-verbinding: Toegang via het lokale netwerk:
ssh root@192.168.1.25.
3. Services en Configuratie
Het systeem maakt gebruik van systemd om de applicatie te beheren.
- Service-naam:
internal.service - Configuratiebestand:
/etc/systemd/system/internal.service- Hierin staat de cruciale
INTERNAL_API_KEY. Zonder de juiste key zal de cloudserver alle verzoeken weigeren met een 403 Forbidden error.
- Hierin staat de cruciale
- Applicatie-locatie:
/home/internal/internal-server/bin/www(Node.js).
4. Veelvoorkomende Commando's
- Logs inzien:
journalctl -u internal -f(essentieel voor het debuggen van gesprekken). - Service herstarten:
systemctl restart internal. - Configuratie herladen:
systemctl daemon-reload(nodig na wijzigingen in het servicebestand).
Cruciaal: De
INTERNAL_API_KEYmoet exact overeenkomen met de sleutel die door de cloudserver wordt verwacht. Deze is te verifiΓ«ren in de omgeving van de draaiende Node-processen op de VPS server (root@thefreighthero.com) viacat /proc/[PID]/environwaar PID is van realtime service (gebruik ps aux hiervoor) -
TFH Server Backup & Cron Dashboard DocumentatieTFH Server Backup & Cron Dashboard Documentatie
Laatst bijgewerkt: 4 februari 2026
Auteur: Nikita Skliarov
1. Database Backup naar Amazon S3
Overzicht
De database (
freighther_nl, ~16 GB) wordt gedumpt metmysqldump, gecomprimeerd metgzip -9en geΓΌpload naar Amazon S3. Gecomprimeerde backups zijn ongeveer 1,4 GB per stuk.Configuratie
Instelling Waarde Script locatie /home/freighther/tfh-backup-database.shS3 bucket tfh-backup-2S3 regio eu-north-1(Stockholm)AWS IAM gebruiker tfh-backup-userAWS credentials Opgeslagen via aws configure(root)Logbestand /home/freighther/backup.logFoutenlog /home/freighther/backup_errors.logAWS IAM Beleid
De
tfh-backup-userheeft een minimaal inline beleid (tfh-backup-s3-access) met alleen deze rechten op detfh-backup-2bucket:s3:PutObjects3:GetObjects3:ListBucket
Cron Schema
Schema Omschrijving 0 9-17/2 * * *Elke 2 uur, 9:00 β 17:00 0 18,22,2,6 * * *Elke 4 uur, 18:00 β 8:00 Handmatige Commando's
# Backup handmatig uitvoeren /home/freighther/tfh-backup-database.sh # Backup log bekijken tail -f /home/freighther/backup.log # Backups in S3 bekijken aws s3 ls s3://tfh-backup-2/thefreighthero.nl/ # Backup downloaden vanuit S3 aws s3 cp s3://tfh-backup-2/thefreighthero.nl/BESTANDSNAAM.tar.gz /tmp/ # Database herstellen vanuit backup gunzip < /tmp/BESTANDSNAAM.tar.gz | mysql -u freighther_nl -p freighther_nlAWS CLI Opnieuw Configureren
Als de credentials geroteerd moeten worden:
aws configure # Voer nieuwe Access Key ID, Secret Access Key in, regio: eu-north-1, output: jsonNieuwe access key aanmaken: AWS Console β IAM β Users β tfh-backup-user β Security credentials β Create access key
2. Crontab Guru Dashboard
Overzicht
Een zelf-gehost webdashboard voor het monitoren en beheren van cron jobs. GeΓ―nstalleerd via Cronitor CLI. De webinterface op
cronitor.iotoont logs en jobstatus, maar kan geen jobs verwijderen of bewerken β alleencrontab -eop de server kan jobs aanpassen.Service
Instelling Waarde Service naam crontab-guruService bestand /etc/systemd/system/crontab-guru.servicePoort 9000(alleen localhost)Binary /usr/bin/cronitorTelemetrie Uitgeschakeld Service Beheren
# Status controleren systemctl status crontab-guru # Herstarten systemctl restart crontab-guru # Stoppen systemctl stop crontab-guru # Logs bekijken journalctl -u crontab-guru -fDashboard Openen
Het dashboard is alleen bereikbaar via SSH tunnel β het is niet toegankelijk via het internet.
Vanaf je lokale machine (laptop):
ssh -L 9000:localhost:9000 root@thefreighthero.comOpen daarna in je browser: http://localhost:9000
Log in met het dashboard gebruikersnaam en wachtwoord.
Dashboard Wachtwoord Wijzigen
cronitor configure --dash-username NIEUWE_GEBRUIKERSNAAM --dash-password NIEUW_WACHTWOORD systemctl restart crontab-guruCronitor.io Cloud Interface
Het dashboard is ook gekoppeld aan cronitor.io β een online interface waar je logs en jobstatus kunt bekijken. Dit is alleen-lezen: je kunt geen jobs verwijderen of bewerken via de website.
Voordelen:
- Meldingen ontvangen als een backup of cron job mislukt
- Overzicht van alle jobs zonder SSH tunnel nodig
Wat Cronitor ziet:
- Job namen, schema's, uitvoertijden, succes/faal status
Wat Cronitor NIET ziet:
- Geen database inhoud, geen bestanden, geen wachtwoorden
- Geen SSH toegang, geen inkomend verkeer (server stuurt alleen data naar Cronitor, niet andersom)
Inloggen: cronitor.io met je Cronitor account
Meldingen instellen: Cronitor.io β Alerts β configureer e-mail of SlackBeveiliging
- Dashboard luistert alleen op
localhost:9000β niet bereikbaar vanaf het internet - Toegang vereist een SSH tunnel (= SSH inloggegevens nodig)
- Dashboard login vereist een apart gebruikersnaam/wachtwoord
- De online Cronitor webinterface is alleen-lezen (logs bekijken, geen jobs bewerken)
- Cronitor account beveiligen met een sterk wachtwoord en bij voorkeur 2FA
3. S3 Kosten & Retentiebeleid
Retentiebeleid
Er is een S3 lifecycle rule ingesteld die backups automatisch verwijdert na 2 dagen. Hierdoor zijn altijd de backups van vandaag en gisteren beschikbaar, en alles ouder wordt automatisch opgeruimd.
Lifecycle rule configuratie:
- Naam:
delete-old-backups - Scope: Alle objecten in de bucket
- Actie: Expire current versions of objects
- Na: 2 dagen
Kostenberekening (S3 Standard, eu-north-1 Stockholm)
Onderdeel Prijs Opslag $0,023 per GB per maand PUT verzoeken (uploads) $0,005 per 1.000 verzoeken DELETE verzoeken (verwijdering) Gratis Lifecycle expiratie acties Gratis Geschatte maandelijkse kosten:
Onderdeel Berekening Kosten/maand Opslag ~9 backups/dag Γ 2 dagen Γ 1,5 GB = ~27 GB ~$0,62 PUT verzoeken ~270 uploads/maand ~$0,001 Totaal ~$0,62/maand De kosten stijgen evenredig als de database groeit. Bij een verdubbeling naar 3 GB per backup worden de kosten ~$1,10/maand.
4. Overzicht Alle Cron Jobs
# Database backup: elke 2 uur tijdens kantooruren 0 9-17/2 * * * /home/freighther/tfh-backup-database.sh >> /home/freighther/backup.log 2>&1 # Database backup: elke 4 uur buiten kantooruren 0 18,22,2,6 * * * /home/freighther/tfh-backup-database.sh >> /home/freighther/backup.log 2>&1 # Server health check: dagelijks om 6:00 0 6 * * * /usr/local/bin/server-health-check.sh # Redis herstarten: elk uur 0 * * * * systemctl restart redis.serviceCron Jobs Bewerken
crontab -e # Cron jobs bewerken crontab -l # Huidige cron jobs bekijken
-
Ubuntu Pro Setup HandleidingUbuntu Pro Setup & Documentatie
Huidige Status
- Machine: HP Laptop (Nikita's werk laptop)
- Subscription: Ubuntu Pro - free personal subscription
- Account: helpdesk@thefreighthero.com
- Machines gebruikt: 1/5
- Kosten: β¬0,00
- Verloopt: Nooit
Wat doet Ubuntu Pro?
Automatische bescherming:
ESM Apps - Extended Security Maintenance voor applicaties
ESM Infra - Extended Security Maintenance voor infrastructuur
Livepatch - Kernel updates zonder reboot
Voordelen:
- 10 jaar beveiligingsupdates (i.p.v. 5 jaar)
- 23.000+ extra packages krijgen security patches
- Kernel updates zonder systeem te rebooten
- Minder downtime tijdens werk
Dagelijkse Routine
Niets verandert!
Blijf gewoon je normale update routine doen:
sudo apt update && sudo apt upgradeESM updates zijn automatisch inbegrepen. Geen extra stappen nodig.
Optioneel: Maandelijkse check
Als je wilt verifiΓ«ren dat alles werkt:
pro status canonical-livepatch statusLet op: Dit is niet verplicht - het systeem werkt automatisch op de achtergrond.
Setup Instructies voor Nieuwe IT Laptop
Stap 1: Ubuntu installeren
Installeer Ubuntu 24.04 LTS (of nieuwste LTS versie)
Stap 2: Ubuntu Pro koppelen
sudo pro attach <token-uit-password-manager>Het systeem zal automatisch ESM en Livepatch enablen.
Stap 3: Verifieer installatie
pro statusVerwachte output:
SERVICE ENTITLED STATUS DESCRIPTION esm-apps yes enabled Expanded Security Maintenance for Applications esm-infra yes enabled Expanded Security Maintenance for Infrastructure livepatch yes enabled Canonical Livepatch serviceStap 4: Eerste update
sudo apt update && sudo apt upgrade
Credential Opslag
Bewaar dit in password manager:
Service: Ubuntu Pro Free Personal Account: helpdesk@thefreighthero.com Password: [wachtwoord voor Ubuntu One account] Token: [de attach token - zie dashboard] Dashboard URL: https://ubuntu.com/pro/dashboard Machines limiet: 5 Huidige machines: HP Laptop
Troubleshooting
Pro status checken
pro statusLivepatch status checken
canonical-livepatch statusPro opnieuw koppelen (bij problemen)
sudo pro detach sudo pro attach <token>Welke packages worden beschermd?
apt list --installed | grep esm
Disaster Recovery
Als laptop kapot gaat of gestolen wordt:
-
Nieuwe Ubuntu installatie
- Installeer Ubuntu 24.04 LTS
- Herstel backup van oude laptop
-
Pro opnieuw activeren
sudo pro attach <token> -
Verifieer
pro status
Belangrijk: Je kunt dezelfde token gebruiken op een nieuwe machine. De oude machine wordt automatisch losgekoppeld na een tijdje inactiviteit, of je kunt deze handmatig loskoppelen via het dashboard.
Dashboard beheer
- Ga naar: https://ubuntu.com/pro/dashboard
- Login met: helpdesk@thefreighthero.com
- Hier zie je alle gekoppelde machines
- Je kunt machines handmatig loskoppelen indien nodig
Belangrijke Notities
Wat Ubuntu Pro NIET verandert:
Geen nieuwe cron jobs nodig
Geen monitoring scripts nodig
Geen extra onderhoudstaken
Geen veranderingen in dagelijkse workflow
Wat Ubuntu Pro WEL doet:
Automatisch kernel patches toepassen (zonder reboot)
Meer security updates in normale apt upgrade
Langere support periode (10 jaar i.p.v. 5)
Bescherming voor development packages (PHP, Composer, FFmpeg, etc.)
Gratis tier limieten:
- Maximum 5 machines
- Voor persoonlijk of klein zakelijk gebruik
- Alle enterprise features inbegrepen
- Geen support contract (community support only)
Voor Volgende IT Medewerker
Onboarding checklist:
- Ubuntu Pro token locatie in password manager
- Dashboard toegang (helpdesk@thefreighthero.com)
- Verificeer dat HP Laptop nog steeds attached is
- Test
pro statuscommand - Review deze documentatie
Belangrijker dan Ubuntu Pro:
Zorg dat je ook documentatie hebt voor:
SSH keys voor server toegang
Database credentials
API keys (Brevo, Retell AI, Google Maps, etc.)
GitHub/GitLab toegang
Hosting/server provider accounts
Domain registrar toegang
Ubuntu Pro is nice-to-have, maar bovenstaande zijn mission-critical!
Updates & Onderhoud
Normale updates (wanneer systeem vraagt):
sudo apt update && sudo apt upgradeKernel updates:
- Worden automatisch toegepast door Livepatch
- Meestal geen reboot nodig
- Als reboot WEL nodig: je krijgt een notificatie
Distributie upgrades (bijv. 24.04 β 26.04):
sudo do-release-upgradeUbuntu Pro blijft actief na distributie upgrade.
Contact & Support
Ubuntu Pro Dashboard
https://ubuntu.com/pro/dashboard
Canonical Support
- Community forum: https://discourse.ubuntu.com/
- Documentatie: https://ubuntu.com/pro/docs
Interne vragen
Contact Nikita of check deze documentatie eerst.
Changelog
Datum Wijziging Door 22-01-2026 InitiΓ«le setup HP Laptop Nikita 22-01-2026 Documentatie aangemaakt Nikita
Snelle Referentie
# Status checken pro status # Livepatch status canonical-livepatch status # Normale updates (inclusief ESM) sudo apt update && sudo apt upgrade # Machine koppelen sudo pro attach <token> # Machine loskoppelen sudo pro detach # Alle Pro services bekijken pro status --all # Specifieke service enablen (normaal niet nodig) sudo pro enable <service-name> -
Factuur / quote maken code workflowInvoice System - Zendingspagina
Overzicht
Stack: PHP 7.3, Pagekit, Vue 1, Webpack 1
Functionaliteiten
- Factuur aanmaken
- Voortschrijdende factuur aanmaken
- Knop voor PDF regenereren
- Creditfactuur aanmaken via wissen knop
Architectuur
InvoiceApiController.php
Doel: API-endpoints voor invoice operaties
Routes:/invoice,/advanceinvoice
Verantwoordelijkheid: Request handling, render/rerender functiesTwee if-else opties voor render:
- Optie 1: Direct render
- Optie 2: Render met cache
InvoiceFactory.php
Doel: Business logic en invoice-object creatie
InvoiceMakerModule.php
Doel: Module initialisatie
Invoice Generatie
Invoice.php
Locatie:
packages/bixie/freighthero/invoice_templates/Stappen:
Invoice::create()- DOMPDF generate html
- Render
- Output
Templates & Vertalingen
Locatie:
packages/bixie/freighthero/invoice_templates/Drie templates (taal-gebaseerd):
- 'en' β
TFH-Invoice - 'de' β
thefreighthero_de - 'nl' β
thefreighthero
Vertaalde tekst:
invoice_templates/languages/(3 talen)Vertaling gebeurt op de plek in template met vertaalde calculatie lijnen (zie ShipmentApiController)
Shipment & Invoice Koppeling
ShipmentApiController.php
Doel: Return van vertaalde calculatie lijnen
Property:
$shipment->language- Gebruikt om template te definiΓ«ren van komende factuur
PDF Opslag
Locatie:
root/../invoices/INVOICENUMMER - COMPANY NAME- facturen
storage/pdf/quotes/zendingsnummer/Quote-S{{ZENDINGSNUMMER}}-{{QUOTE ID VAN hero_freighthero_quote db tabel}}-{{BEDRIJFSNAAM}}-.pdf- offertesProces:
- Invoices aanmaken/regenereren
- PDF genereren via DOMPDF
- Opslaan in gestructureerde directory
Quote System
QuoteApiController.php
Doel: API-endpoints voor quote operaties
Routes:/quote
Verantwoordelijkheid: Request handling
Quote.php
Locatie:
packages/bixie/freighthero/quote_templates/Functie: Bevat alle render logica
Workflow: Identiek aan invoice workflow
Templates & Vertalingen - Quote
Locatie: Frontend
invoicemaker -> settings -> offertesTekstinstellingen:
- Drie textvelden (per taal)
- Achtergrondafbeelding uploader
Opslaan: Deze instellingen worden opgeslagen in settings, niet in
invoice_templates/languages/(behalve sommige text in quote, zie quote.php bestand
Versie: 1.0
Status: Documentatie van huidige implementatie -
api.development.thefreighthero.com - update 'dev' database met production database data- Export db van production, drop .sql file ergens in /home/dev
- Run
mysql -u dev_portal -p dev_portal < freighther_nl.sqlwaar dev_portal is gebruikersnaam en db naam
wachtwoord vind je in /home/dev/domains/api.development.thefreighthero.nl/public_html/config.phpWachtwoord vergeten / niet kan vinden?
DirectAdmin -> users -> dev -> login as dev -> mysql -> dev_portal -> generate nieuwe wachtwoord en sla deze op
Vergeet ook niet config.php wachtwoord aan te passen.Niet live db aanraken aub, dit mag alleen in dev gedaan worden.
-
Live TFH - Gebruikers lijst - gebruiker toevoegenssh root@thefreighthero.comnano /home/realtime/domains/live.thefreighthero.nl/tfhapp data/users.jsonGebruik voor makkelijkheid een bestaande gebruikers id van pagekit, voer een gebruikersnaam en wacthwoord (MD5 hashing). TFHID is niet verplicht om in te voeren.
echo -n "testpassword" | md5sumniet vergeten:
systemctl restart realtimedan checken of het actief is
systemctl status realtime -
Installatie telefoon extensieInstallatie nieuwe extensie
-
Download de laatste versie via link hierboven
-
Je hebt nu een zip bestand gedownload. Open deze en pak deze uit.
-
Open Chrome en vul het volgende in de adresbak in: "chrome://extensions/"
-
Zet een vinkje aan recht bovenin "Developer mode".
Kies linksboven voor "Uitgepakte extensie laden" en klik de uitgepakte map extension aan. -
Onder de Freight Hero Intern extensie klik je op Details en scroll naar onderen. Kies voor "Opties voor extensies"
-
Je ziet de geopende TFH VOIP instellingen. Zet Server IP op "192.168.1.25"
-
Link rechtsboven op het TFH icoon en klik op CHeck verbinding
-
Klik naast de popup, zodat deze sluit, en klik daarna weer op het TFH icoon.
-
Kies de juiste gebruiker. De telefonie zou het nu moeten doen.
Mocht bellen niet automatisch werken na het uitvoeren van stap 9 dan kan het zijn dat ik nog een instelling voor je account / telefoon moet goed zetten.
-
-
Mollie data flowBevat nog steeds een issue met shipment betaald status. De reden zijn onbekend.
-
Mollie data flow