# Makefile — wowa-edge (Webserver auf http_lib-Basis)

PBC      = pbcompilerc
PBFLAGS  = --thread --console

# ── Reproduzierbarer Build ───────────────────────────────────────────────────
# Das Binary darf nicht an der Uhrzeit haengen (#PB_Compiler_Date). Stattdessen
# wird eine Build-Kennung injiziert: per Default der Zeitstempel des HEAD-Commits,
# ueberschreibbar via SOURCE_DATE_EPOCH (reproducible-builds.org-Konvention).
# Ergebnis: zwei Builds derselben Quellen sind byte-identisch (siehe `make repro`).
# Quelle in dieser Reihenfolge: explizit gesetzt → .source_date_epoch (liegt im
# dist-Tarball, damit auch ein Build ausserhalb von Git reproduzierbar bleibt)
# → HEAD-Commit → 0 (= "dev").
SOURCE_DATE_EPOCH ?= $(shell cat .source_date_epoch 2>/dev/null || git log -1 --format=%ct 2>/dev/null || echo 0)
BUILDFLAGS = --constant WOWA_EDGE_BUILD=$(SOURCE_DATE_EPOCH)

# Alle Quellen, aus denen das Binary entsteht — inkl. der Include-Module unter
# inc/ UND der Submodule. WICHTIG: fehlt hier eine Datei, baut `make` nach einer
# Aenderung daran NICHT neu und man testet stillschweigend ein altes Binary.
# Die Submodule standen frueher NICHT drin: eine Aenderung in http_lib/net_lib/
# lhs_lib fuehrte dann zu einem Binary ohne diese Aenderung, ohne jeden Hinweis.
LIB_SRC  = $(wildcard http_lib/*.pbi) $(wildcard net_lib/*.pbi) \
           $(wildcard lhs_lib/*.pbi) $(wildcard lhs_lib/*/*.pbi)
EDGE_SRC = wowa-edge.pb $(wildcard inc/*.pbi) $(LIB_SRC)
PREFIX  ?= /usr/local
SBINDIR  = $(PREFIX)/sbin
MANDIR   = $(PREFIX)/share/man
ETCDIR   = /etc/wowa-edge
LOGDIR   = /var/log/wowa-edge
CACHEDIR = /var/cache/wowa-edge
SYSTEMD  = /etc/systemd/system
LOGROTATE = /etc/logrotate.d
# Dienstkonto: wwwrun:www statt eines eigenen Benutzers. wowa-edge liefert als
# PHP-Origin die statischen Dateien der Anwendung aus, waehrend php-fpm darin
# laufend NEUE Dateien als wwwrun:www anlegt — ein eigenes Konto verliert den
# Zugriff bei jeder App-Installation (Nextcloud setzt apps/ auf 0750 wwwrun:www
# → alles darunter 404). Auf openSUSE kommt das Konto aus system-user-wwwrun.
SERVICE_USER = wwwrun
SERVICE_GROUP = www

# ── Versionierung ────────────────────────────────────────────────────────────
# Single Source of Truth ist die Konstante #WOWA_EDGE_VERSION in inc/edge_types.pbi.
# Schema (SemVer, Pre-1.0): MAJOR.MINOR.PATCH
#   bump-patch → 0.1.0 -> 0.1.1   Bug fixes (keine API/Config-Aenderung)
#   bump-minor → 0.1.5 -> 0.2.0   Funktions-Changes (neue/geaenderte Direktiven)
#   bump-major → 0.9.0 -> 1.0.0   inkompatibler Major-Sprung (selten)
VERSION_FILE = inc/edge_types.pbi
VERSION      = $(shell grep -oP '\#WOWA_EDGE_VERSION\s*=\s*"\K[0-9]+\.[0-9]+\.[0-9]+' $(VERSION_FILE))

# Gemeinsame Bump-Logik: $(call do_bump,<part>) — editiert die Konstante in-place
# und baut das Binary neu, damit `wowa-edge -v` sofort die neue Version meldet.
define do_bump
	@cur="$(VERSION)"; \
	if [ -z "$$cur" ]; then echo "FEHLER: #WOWA_EDGE_VERSION nicht in $(VERSION_FILE) gefunden"; exit 1; fi; \
	maj=$${cur%%.*}; rest=$${cur#*.}; min=$${rest%%.*}; pat=$${rest##*.}; \
	case "$(1)" in \
	  major) maj=$$((maj+1)); min=0; pat=0;; \
	  minor) min=$$((min+1)); pat=0;; \
	  patch) pat=$$((pat+1));; \
	  *) echo "unbekannter Bump-Typ: $(1)"; exit 1;; \
	esac; \
	new="$$maj.$$min.$$pat"; \
	sed -i 's/\(#WOWA_EDGE_VERSION[[:space:]]*=[[:space:]]*"\)[0-9.]*"/\1'"$$new"'"/' $(VERSION_FILE); \
	echo "Version: $$cur -> $$new ($(1))"; \
	$(MAKE) --no-print-directory wowa-edge
endef

.PHONY: all check test test-quick soak fuzz repro dist dist-bin dist-inst migrator diag clean install install-user \
        install-config install-systemd install-logrotate uninstall installer version \
        bump-patch bump-minor bump-major openssl-check

all: wowa-edge

wowa-edge: $(EDGE_SRC)
	$(PBC) wowa-edge.pb $(PBFLAGS) $(BUILDFLAGS) --executable wowa-edge

# Self-Deploy-Installer bauen (interaktives CLI: Install/Upgrade/Uninstall,
# Config-Generierung, systemd-Unit, Firewall). Liegt neben der Binary in inst/.
installer: wowa-edge
	$(PBC) inst/install-wowa-edge.pb $(PBFLAGS) --executable inst/install-wowa-edge
	@echo "  -> sudo ./inst/install-wowa-edge             (Install/Upgrade automatisch erkannt)"
	@echo "  -> sudo ./inst/install-wowa-edge --uninstall"

# Apache-Migrator bauen (uebersetzt /etc/apache2 nach wowa-edge + Bericht)
# Diagnosebau: wie `make`, zusaetzlich mit Symbolen UND dem Bestandsbericht
# (NETLIB-218). Der Bericht nimmt reihum jede Registersperre — deshalb ist er im
# Auslieferungsbau gar nicht erst einkompiliert, nicht nur abgeschaltet.
# Aktiviert wird er dann zur Laufzeit mit `MemoryCensusInterval SEKUNDEN`.
diag:
	$(PBC) wowa-edge.pb $(PBFLAGS) $(BUILDFLAGS) --debugsymbols --constant WOWA_CENSUS=1 --executable wowa-edge-diag
	@echo "  -> wowa-edge-diag  (Symbole + Bestandsbericht; Config: MemoryCensusInterval)"

migrator:
	$(PBC) tools/apache-migrate.pb $(PBFLAGS) --executable tools/apache-migrate
	@echo "  -> ./tools/apache-migrate --help"

# Syntax-Check der Beispiel-Config (ohne wirklich zu starten)
check: wowa-edge
	./wowa-edge -c wowa-edge.conf.example -t

# ── Paketpflege ──────────────────────────────────────────────────────────────
# Meldet, wenn die gebuendelte OpenSSL-LTS-Reihe einen neueren Punktstand hat.
# Ende-Code 1 = es gibt einen — damit auch aus einem Timer/CI heraus brauchbar.
openssl-check:
	@./packaging/openssl-check

# ── Tests ────────────────────────────────────────────────────────────────────
# Volle Regressionssuite: startet wowa-edge auf unprivilegierten Ports gegen
# 127.0.0.1, prueft Verhalten (nicht nur Syntax) und raeumt hinter sich auf.
#   make test                 alles
#   make test T=tls           nur Faelle, deren Dateiname 'tls' enthaelt
test: wowa-edge
	@./test/run.sh $(T)

# Nur die schnellen Faelle (ohne TLS/OCSP-Zertifikatserzeugung)
test-quick: wowa-edge
	@./test/run.sh static auth rewrite proxy waf cgi

# Dauerlast mit Bewertung: gemischte Last in Zyklen, Ruhephase vor jeder Messung,
# am Ende werden RSS-/Deskriptor-/Thread-Steigung gegen Schwellwerte geprueft.
# Parameter ueber die Umgebung, s. TESTING.md:  CYCLES= REQS= CONNS= QUIET=
soak: wowa-edge
	@./test/soak.sh

fuzz: wowa-edge
	@test/fuzz.sh

# Reproduzierbarkeit nachweisen: zweimal bauen, Pruefsummen vergleichen.
# Muss identisch sein — sonst haengt das Binary an Build-Metadaten.
repro:
	@echo "SOURCE_DATE_EPOCH=$(SOURCE_DATE_EPOCH)"
	@$(PBC) wowa-edge.pb $(PBFLAGS) $(BUILDFLAGS) --executable .repro-a >/dev/null
	@$(PBC) wowa-edge.pb $(PBFLAGS) $(BUILDFLAGS) --executable .repro-b >/dev/null
	@a=$$(sha256sum .repro-a | cut -d" " -f1); b=$$(sha256sum .repro-b | cut -d" " -f1); \
	 rm -f .repro-a .repro-b; \
	 echo "  A: $$a"; echo "  B: $$b"; \
	 if [ "$$a" = "$$b" ]; then echo "  ✓ byte-identisch — reproduzierbar"; \
	 else echo "  ✗ NICHT reproduzierbar"; exit 1; fi

# ── Distribution ─────────────────────────────────────────────────────────────
DISTNAME = wowa-edge-$(VERSION)
TARFLAGS = --sort=name --owner=0 --group=0 --numeric-owner \
           --mtime=@$(SOURCE_DATE_EPOCH) --format=gnu

# Quell-Tarball (self-contained inkl. Submodul-Arbeitsbaeume, ohne .git).
# Deterministisch: sortiert, feste mtime/Owner, gzip ohne Zeitstempel.
dist:
	@rm -rf .dist && mkdir -p .dist/$(DISTNAME)
	@echo $(SOURCE_DATE_EPOCH) > .source_date_epoch
	@tar -c $(TARFLAGS) \
	    --exclude-vcs --exclude=.dist --exclude=wowa-edge --exclude=inst/install-wowa-edge \
	    --exclude='*.tar.gz' --exclude='.repro-*' \
	    --exclude='testing/*/var' --exclude='testing/*/htdocs' --exclude='testing/*/*.log' \
	    wowa-edge.pb inc test testing tools docs man inst Makefile LICENSE README.md CHANGELOG.md \
	    CONTRIBUTING.md SECURITY.md TESTING.md CLAUDE.md wowa-edge.conf.example wowa-edge.service \
	    conf.d loadtest.sh .source_date_epoch http_lib net_lib lhs_lib 2>/dev/null \
	  | tar -x -C .dist/$(DISTNAME)
	@tar -c $(TARFLAGS) -C .dist $(DISTNAME) | gzip -n -9 > $(DISTNAME).tar.gz
	@rm -rf .dist .source_date_epoch
	@echo "  → $(DISTNAME).tar.gz  ($$(du -h $(DISTNAME).tar.gz | cut -f1), sha256 $$(sha256sum $(DISTNAME).tar.gz | cut -c1-16)…)"

# Installations-Paket: NUR die Binaries plus Kurzanleitung und Beispiel-Configs.
# Das ist das Paket zum Ausliefern/Deployen — keine Quellen, keine Handbuecher.
dist-inst: wowa-edge installer migrator
	@rm -rf .dist && mkdir -p .dist/$(DISTNAME)/conf.d
	@cp wowa-edge inst/install-wowa-edge tools/apache-migrate .dist/$(DISTNAME)/
	@cp inst/INSTALL.md LICENSE wowa-edge.conf.example wowa-edge.service .dist/$(DISTNAME)/
	@cp conf.d/*.example .dist/$(DISTNAME)/conf.d/ 2>/dev/null || true
	@chmod 0755 .dist/$(DISTNAME)/wowa-edge .dist/$(DISTNAME)/install-wowa-edge .dist/$(DISTNAME)/apache-migrate
	@tar -c $(TARFLAGS) -C .dist $(DISTNAME) | gzip -n -9 > $(DISTNAME)-linux-x86_64-install.tar.gz
	@rm -rf .dist
	@echo "  -> $(DISTNAME)-linux-x86_64-install.tar.gz  ($$(du -h $(DISTNAME)-linux-x86_64-install.tar.gz | cut -f1))"
	@echo "     entpacken und  sudo ./install-wowa-edge  ausfuehren"

# Binaer-Tarball: fertiges Binary + Configs + Unit + Doku
dist-bin: wowa-edge
	@rm -rf .dist && mkdir -p .dist/$(DISTNAME)/conf.d
	@cp wowa-edge .dist/$(DISTNAME)/
	@cp LICENSE README.md CHANGELOG.md SECURITY.md wowa-edge.conf.example \
	    wowa-edge.service .dist/$(DISTNAME)/
	@cp -r docs .dist/$(DISTNAME)/
	@cp conf.d/*.example .dist/$(DISTNAME)/conf.d/ 2>/dev/null || true
	@tar -c $(TARFLAGS) -C .dist $(DISTNAME) | gzip -n -9 > $(DISTNAME)-linux-x86_64.tar.gz
	@rm -rf .dist
	@echo "  → $(DISTNAME)-linux-x86_64.tar.gz"

clean:
	rm -f wowa-edge inst/install-wowa-edge tools/apache-migrate .repro-a .repro-b .source_date_epoch
	rm -rf .dist
	rm -f wowa-edge-*.tar.gz
	rm -f wowa-edge-diag

# Binary installieren
install: wowa-edge install-man
	install -d $(DESTDIR)$(SBINDIR)
	install -m 0755 wowa-edge $(DESTDIR)$(SBINDIR)/wowa-edge

# Man-Pages (Version aus $(VERSION) substituiert). RPM/OBS brp-compress gzippt sie.
install-man:
	install -d $(DESTDIR)$(MANDIR)/man8 $(DESTDIR)$(MANDIR)/man5
	sed 's/@VERSION@/$(VERSION)/g' man/wowa-edge.8      > $(DESTDIR)$(MANDIR)/man8/wowa-edge.8
	sed 's/@VERSION@/$(VERSION)/g' man/wowa-edge.conf.5 > $(DESTDIR)$(MANDIR)/man5/wowa-edge.conf.5
	chmod 0644 $(DESTDIR)$(MANDIR)/man8/wowa-edge.8 $(DESTDIR)$(MANDIR)/man5/wowa-edge.conf.5

# Default-Config-Skelett installieren (ohne bestehende zu ueberschreiben)
# Legt wwwrun:www nur an, falls nicht vorhanden (z.B. frisches System ohne
# system-user-wwwrun/apache2). Auf openSUSE ist "zypper in system-user-wwwrun"
# der sauberere Weg — dann passiert hier nichts.
install-user:
	@if [ -n "$(DESTDIR)" ]; then :; \
	elif ! getent group $(SERVICE_GROUP) >/dev/null; then groupadd --system $(SERVICE_GROUP); fi
	@if [ -n "$(DESTDIR)" ]; then :; \
	elif ! getent group $(SERVICE_USER) >/dev/null; then groupadd --system $(SERVICE_USER); fi
	@if [ -n "$(DESTDIR)" ]; then :; \
	elif ! getent passwd $(SERVICE_USER) >/dev/null; then useradd --system --gid $(SERVICE_USER) --groups $(SERVICE_GROUP) --home-dir /var/lib/$(SERVICE_USER) --shell /sbin/nologin --comment "WWW daemon apache" $(SERVICE_USER); fi

install-config: install-user
	install -d $(DESTDIR)$(ETCDIR) $(DESTDIR)$(ETCDIR)/conf.d \
	            $(DESTDIR)$(LOGDIR) $(DESTDIR)$(CACHEDIR)/acme-webroot \
	            $(DESTDIR)$(ETCDIR)/certs
	@if [ ! -f $(DESTDIR)$(ETCDIR)/wowa-edge.conf ]; then \
	    install -m 0644 wowa-edge.conf.example $(DESTDIR)$(ETCDIR)/wowa-edge.conf; \
	    echo "  → $(DESTDIR)$(ETCDIR)/wowa-edge.conf"; \
	else \
	    echo "  ⤳ $(DESTDIR)$(ETCDIR)/wowa-edge.conf existiert — uebersprungen"; \
	fi
	@if [ ! -f $(DESTDIR)$(ETCDIR)/conf.d/example.com.conf ]; then \
	    install -m 0644 conf.d/example.com.conf.example \
	            $(DESTDIR)$(ETCDIR)/conf.d/example.com.conf.example; \
	    echo "  → $(DESTDIR)$(ETCDIR)/conf.d/example.com.conf.example  (umbenennen+anpassen)"; \
	fi
	@if [ -z "$(DESTDIR)" ]; then \
	    chown -R $(SERVICE_USER):$(SERVICE_GROUP) $(LOGDIR) $(CACHEDIR) $(ETCDIR)/certs $(ETCDIR)/ca; \
	fi

install-systemd: install-user
	install -m 0644 wowa-edge.service $(DESTDIR)$(SYSTEMD)/wowa-edge.service
	@echo "  → systemctl daemon-reload && systemctl enable --now wowa-edge"

# Protokoll-Rotation. Ohne die waechst /var/log/wowa-edge unbegrenzt: der Dienst
# rotiert zwar selbst nach Groesse, raeumt danach aber nichts weg. Auf 1000lb1
# waren das 1,1 GB auf einer 18-GB-Platte — ein Webserver, dem die Platte
# volllaeuft, faellt aus, ohne dass an ihm etwas kaputt waere.
install-logrotate:
	install -d $(DESTDIR)$(LOGROTATE)
	install -m 0644 inst/wowa-edge.logrotate $(DESTDIR)$(LOGROTATE)/wowa-edge
	@echo "  → $(DESTDIR)$(LOGROTATE)/wowa-edge  (Test: logrotate -d /etc/logrotate.d/wowa-edge)"

uninstall:
	rm -f $(DESTDIR)$(SBINDIR)/wowa-edge $(DESTDIR)$(SYSTEMD)/wowa-edge.service

# ── Versions-Targets ─────────────────────────────────────────────────────────
# Aktuelle Version anzeigen
version:
	@echo "wowa-edge $(VERSION)"

# Bug fix: PATCH erhoehen (0.1.0 -> 0.1.1) + Rebuild
bump-patch:
	$(call do_bump,patch)

# Funktions-Change: MINOR erhoehen, PATCH zuruecksetzen (0.1.5 -> 0.2.0) + Rebuild
bump-minor:
	$(call do_bump,minor)

# Major-Sprung: MAJOR erhoehen, Rest zuruecksetzen (0.9.0 -> 1.0.0) + Rebuild
bump-major:
	$(call do_bump,major)
