feat: improve WireGuard client management and framework initialization
- Improve WireGuard client IP calculation logic (find next available IP) - Add local wireguard-clients directory for storing client configs - Integrate Redis pool into CacheInitializer - Improve ContainerBootstrapper with better imports and Redis pool - Add monitoring role tags for better task organization - Update WireGuard documentation - Store generated WireGuard client configs locally
This commit is contained in:
@@ -9,6 +9,7 @@
|
|||||||
wireguard_config_path: "/etc/wireguard"
|
wireguard_config_path: "/etc/wireguard"
|
||||||
wireguard_config_file: "{{ wireguard_config_path }}/{{ wireguard_interface }}.conf"
|
wireguard_config_file: "{{ wireguard_config_path }}/{{ wireguard_interface }}.conf"
|
||||||
wireguard_client_configs_path: "/etc/wireguard/clients"
|
wireguard_client_configs_path: "/etc/wireguard/clients"
|
||||||
|
wireguard_local_client_configs_dir: "{{ playbook_dir }}/../wireguard-clients"
|
||||||
|
|
||||||
pre_tasks:
|
pre_tasks:
|
||||||
- name: Set WireGuard network
|
- name: Set WireGuard network
|
||||||
@@ -60,14 +61,28 @@
|
|||||||
set_fact:
|
set_fact:
|
||||||
server_vpn_ip: "{{ (wireguard_server_config_read.content | b64decode | regex_search('Address = ([0-9.]+)', '\\1')) | first | default('10.8.0.1') }}"
|
server_vpn_ip: "{{ (wireguard_server_config_read.content | b64decode | regex_search('Address = ([0-9.]+)', '\\1')) | first | default('10.8.0.1') }}"
|
||||||
|
|
||||||
- name: Count existing clients in config
|
- name: Extract WireGuard server IP octets
|
||||||
set_fact:
|
set_fact:
|
||||||
existing_clients_count: "{{ (wireguard_server_config_read.content | b64decode | regex_findall('\\[Peer\\]') | length) }}"
|
wireguard_server_ip_octets: "{{ server_vpn_ip.split('.') }}"
|
||||||
|
when: client_ip == ""
|
||||||
|
|
||||||
|
- name: Gather existing client addresses
|
||||||
|
set_fact:
|
||||||
|
existing_client_ips: "{{ (wireguard_server_config_read.content | b64decode | regex_findall('AllowedIPs = ([0-9A-Za-z.]+)/32', '\\1')) }}"
|
||||||
when: client_ip == ""
|
when: client_ip == ""
|
||||||
|
|
||||||
- name: Calculate client IP if not provided
|
- name: Calculate client IP if not provided
|
||||||
|
vars:
|
||||||
|
existing_last_octets: "{{ (existing_client_ips | default([])) | map('regex_replace', '^(?:\\d+\\.\\d+\\.\\d+\\.)', '') | select('match', '^[0-9]+$') | map('int') | list }}"
|
||||||
|
server_last_octet: "{{ wireguard_server_ip_octets[3] | int }}"
|
||||||
|
next_octet_candidate: "{{ (existing_last_octets + [server_last_octet]) | map('int') | list | max + 1 if (existing_last_octets + [server_last_octet]) else server_last_octet + 1 }}"
|
||||||
set_fact:
|
set_fact:
|
||||||
client_ip: "{{ server_vpn_ip | regex_replace('^(\\d+\\.\\d+\\.\\d+\\.)\\d+$', '\\1') }}{{ (server_vpn_ip | regex_replace('^(\\d+\\.\\d+\\.\\d+\\.)(\\d+)', '\\2') | int) + (existing_clients_count | default(1)) | int }}"
|
client_ip: "{{ [
|
||||||
|
wireguard_server_ip_octets[0],
|
||||||
|
wireguard_server_ip_octets[1],
|
||||||
|
wireguard_server_ip_octets[2],
|
||||||
|
next_octet_candidate
|
||||||
|
] | join('.') }}"
|
||||||
when: client_ip == ""
|
when: client_ip == ""
|
||||||
|
|
||||||
- name: Generate client private key
|
- name: Generate client private key
|
||||||
@@ -84,12 +99,6 @@
|
|||||||
changed_when: false
|
changed_when: false
|
||||||
no_log: yes
|
no_log: yes
|
||||||
|
|
||||||
- name: Check if client already exists in config
|
|
||||||
shell: "grep -q '{{ client_name }}' {{ wireguard_config_file }} || echo 'not found'"
|
|
||||||
register: client_exists_check
|
|
||||||
changed_when: false
|
|
||||||
failed_when: false
|
|
||||||
|
|
||||||
- name: Add client to WireGuard server config
|
- name: Add client to WireGuard server config
|
||||||
blockinfile:
|
blockinfile:
|
||||||
path: "{{ wireguard_config_file }}"
|
path: "{{ wireguard_config_file }}"
|
||||||
@@ -99,7 +108,7 @@
|
|||||||
PublicKey = {{ client_public_key.stdout }}
|
PublicKey = {{ client_public_key.stdout }}
|
||||||
AllowedIPs = {{ client_ip }}/32
|
AllowedIPs = {{ client_ip }}/32
|
||||||
marker: "# {mark} ANSIBLE MANAGED BLOCK - Client: {{ client_name }}"
|
marker: "# {mark} ANSIBLE MANAGED BLOCK - Client: {{ client_name }}"
|
||||||
when: "'not found' in client_exists_check.stdout"
|
register: wireguard_client_block
|
||||||
|
|
||||||
- name: Ensure client configs directory exists
|
- name: Ensure client configs directory exists
|
||||||
file:
|
file:
|
||||||
@@ -109,6 +118,15 @@
|
|||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
|
|
||||||
|
- name: Ensure local client configs directory exists
|
||||||
|
file:
|
||||||
|
path: "{{ wireguard_local_client_configs_dir }}"
|
||||||
|
state: directory
|
||||||
|
mode: '0700'
|
||||||
|
delegate_to: localhost
|
||||||
|
become: no
|
||||||
|
run_once: true
|
||||||
|
|
||||||
- name: Get server public key
|
- name: Get server public key
|
||||||
shell: "cat {{ wireguard_config_path }}/{{ wireguard_interface }}_private.key | wg pubkey"
|
shell: "cat {{ wireguard_config_path }}/{{ wireguard_interface }}_private.key | wg pubkey"
|
||||||
register: server_public_key_cmd
|
register: server_public_key_cmd
|
||||||
@@ -124,6 +142,20 @@
|
|||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
|
|
||||||
|
- name: Download client configuration to control machine
|
||||||
|
fetch:
|
||||||
|
src: "{{ wireguard_client_configs_path }}/{{ client_name }}.conf"
|
||||||
|
dest: "{{ wireguard_local_client_configs_dir }}/{{ client_name }}.conf"
|
||||||
|
flat: yes
|
||||||
|
mode: '0600'
|
||||||
|
|
||||||
|
- name: Ensure local client configuration has strict permissions
|
||||||
|
file:
|
||||||
|
path: "{{ wireguard_local_client_configs_dir }}/{{ client_name }}.conf"
|
||||||
|
mode: '0600'
|
||||||
|
delegate_to: localhost
|
||||||
|
become: no
|
||||||
|
|
||||||
- name: Read WireGuard server config to find server IP
|
- name: Read WireGuard server config to find server IP
|
||||||
slurp:
|
slurp:
|
||||||
src: "{{ wireguard_config_file }}"
|
src: "{{ wireguard_config_file }}"
|
||||||
@@ -133,7 +165,7 @@
|
|||||||
systemd:
|
systemd:
|
||||||
name: "wg-quick@{{ wireguard_interface }}"
|
name: "wg-quick@{{ wireguard_interface }}"
|
||||||
state: restarted
|
state: restarted
|
||||||
when: "'not found' in client_exists_check.stdout"
|
when: wireguard_client_block.changed
|
||||||
|
|
||||||
- name: Display client configuration
|
- name: Display client configuration
|
||||||
debug:
|
debug:
|
||||||
@@ -144,6 +176,9 @@
|
|||||||
|
|
||||||
Client Configuration File:
|
Client Configuration File:
|
||||||
{{ wireguard_client_configs_path }}/{{ client_name }}.conf
|
{{ wireguard_client_configs_path }}/{{ client_name }}.conf
|
||||||
|
|
||||||
|
Local Copy:
|
||||||
|
{{ wireguard_local_client_configs_dir }}/{{ client_name }}.conf
|
||||||
|
|
||||||
Client IP: {{ client_ip }}
|
Client IP: {{ client_ip }}
|
||||||
Server Endpoint: {{ server_external_ip_content }}:{{ wireguard_port }}
|
Server Endpoint: {{ server_external_ip_content }}:{{ wireguard_port }}
|
||||||
@@ -166,4 +201,4 @@
|
|||||||
- name: Display QR code
|
- name: Display QR code
|
||||||
debug:
|
debug:
|
||||||
msg: "{{ qr_code.stdout }}"
|
msg: "{{ qr_code.stdout }}"
|
||||||
when: qr_code.rc == 0
|
when: qr_code.rc == 0
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ monitoring_stack_path: "{{ stacks_base_path }}/monitoring"
|
|||||||
monitoring_wait_timeout: "{{ wait_timeout | default(60) }}"
|
monitoring_wait_timeout: "{{ wait_timeout | default(60) }}"
|
||||||
monitoring_env_template: "{{ role_path }}/../../templates/monitoring.env.j2"
|
monitoring_env_template: "{{ role_path }}/../../templates/monitoring.env.j2"
|
||||||
monitoring_vault_file: "{{ role_path }}/../../secrets/production.vault.yml"
|
monitoring_vault_file: "{{ role_path }}/../../secrets/production.vault.yml"
|
||||||
|
monitoring_vpn_ip_whitelist: "{{ wireguard_network_default | default('10.8.0.0/24') }}"
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
register: monitoring_vault_stat
|
register: monitoring_vault_stat
|
||||||
become: no
|
become: no
|
||||||
|
tags:
|
||||||
|
- monitoring
|
||||||
|
|
||||||
- name: Optionally load monitoring secrets from vault
|
- name: Optionally load monitoring secrets from vault
|
||||||
include_vars:
|
include_vars:
|
||||||
@@ -13,16 +15,22 @@
|
|||||||
no_log: yes
|
no_log: yes
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
become: no
|
become: no
|
||||||
|
tags:
|
||||||
|
- monitoring
|
||||||
|
|
||||||
- name: Set Grafana admin password from vault or generate
|
- name: Set Grafana admin password from vault or generate
|
||||||
set_fact:
|
set_fact:
|
||||||
grafana_admin_password: "{{ vault_grafana_admin_password | default(lookup('password', '/dev/null length=25 chars=ascii_letters,digits')) }}"
|
grafana_admin_password: "{{ vault_grafana_admin_password | default(lookup('password', '/dev/null length=25 chars=ascii_letters,digits')) }}"
|
||||||
no_log: yes
|
no_log: yes
|
||||||
|
tags:
|
||||||
|
- monitoring
|
||||||
|
|
||||||
- name: Set Prometheus password from vault or generate
|
- name: Set Prometheus password from vault or generate
|
||||||
set_fact:
|
set_fact:
|
||||||
prometheus_password: "{{ vault_prometheus_password | default(lookup('password', '/dev/null length=25 chars=ascii_letters,digits')) }}"
|
prometheus_password: "{{ vault_prometheus_password | default(lookup('password', '/dev/null length=25 chars=ascii_letters,digits')) }}"
|
||||||
no_log: yes
|
no_log: yes
|
||||||
|
tags:
|
||||||
|
- monitoring
|
||||||
|
|
||||||
- name: Generate Prometheus BasicAuth hash
|
- name: Generate Prometheus BasicAuth hash
|
||||||
shell: |
|
shell: |
|
||||||
@@ -30,17 +38,23 @@
|
|||||||
register: prometheus_auth_hash
|
register: prometheus_auth_hash
|
||||||
changed_when: false
|
changed_when: false
|
||||||
no_log: yes
|
no_log: yes
|
||||||
|
tags:
|
||||||
|
- monitoring
|
||||||
|
|
||||||
- name: Set Prometheus BasicAuth string
|
- name: Set Prometheus BasicAuth string
|
||||||
set_fact:
|
set_fact:
|
||||||
prometheus_auth: "admin:{{ prometheus_auth_hash.stdout }}"
|
prometheus_auth: "admin:{{ prometheus_auth_hash.stdout }}"
|
||||||
no_log: yes
|
no_log: yes
|
||||||
|
tags:
|
||||||
|
- monitoring
|
||||||
|
|
||||||
- name: Ensure monitoring stack directory exists
|
- name: Ensure monitoring stack directory exists
|
||||||
file:
|
file:
|
||||||
path: "{{ monitoring_stack_path }}"
|
path: "{{ monitoring_stack_path }}"
|
||||||
state: directory
|
state: directory
|
||||||
mode: '0755'
|
mode: '0755'
|
||||||
|
tags:
|
||||||
|
- monitoring
|
||||||
|
|
||||||
- name: Create monitoring stack .env file
|
- name: Create monitoring stack .env file
|
||||||
template:
|
template:
|
||||||
@@ -50,6 +64,8 @@
|
|||||||
group: "{{ ansible_user }}"
|
group: "{{ ansible_user }}"
|
||||||
mode: '0600'
|
mode: '0600'
|
||||||
no_log: yes
|
no_log: yes
|
||||||
|
tags:
|
||||||
|
- monitoring
|
||||||
|
|
||||||
- name: Deploy Monitoring stack
|
- name: Deploy Monitoring stack
|
||||||
community.docker.docker_compose_v2:
|
community.docker.docker_compose_v2:
|
||||||
@@ -57,12 +73,18 @@
|
|||||||
state: present
|
state: present
|
||||||
pull: always
|
pull: always
|
||||||
register: monitoring_compose_result
|
register: monitoring_compose_result
|
||||||
|
tags:
|
||||||
|
- monitoring
|
||||||
|
|
||||||
- name: Wait for Monitoring to be ready
|
- name: Wait for Monitoring to be ready
|
||||||
wait_for:
|
wait_for:
|
||||||
timeout: "{{ monitoring_wait_timeout }}"
|
timeout: "{{ monitoring_wait_timeout }}"
|
||||||
when: monitoring_compose_result.changed
|
when: monitoring_compose_result.changed
|
||||||
|
tags:
|
||||||
|
- monitoring
|
||||||
|
|
||||||
- name: Record monitoring deployment facts
|
- name: Record monitoring deployment facts
|
||||||
set_fact:
|
set_fact:
|
||||||
monitoring_stack_changed: "{{ monitoring_compose_result.changed | default(false) }}"
|
monitoring_stack_changed: "{{ monitoring_compose_result.changed | default(false) }}"
|
||||||
|
tags:
|
||||||
|
- monitoring
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
# Domain Configuration
|
# Domain Configuration
|
||||||
DOMAIN={{ app_domain }}
|
DOMAIN={{ app_domain }}
|
||||||
|
|
||||||
|
# VPN Access Control
|
||||||
|
MONITORING_VPN_IP_WHITELIST={{ monitoring_vpn_ip_whitelist }}
|
||||||
|
|
||||||
# Grafana Configuration
|
# Grafana Configuration
|
||||||
GRAFANA_ADMIN_USER={{ grafana_admin_user | default('admin') }}
|
GRAFANA_ADMIN_USER={{ grafana_admin_user | default('admin') }}
|
||||||
GRAFANA_ADMIN_PASSWORD={{ grafana_admin_password }}
|
GRAFANA_ADMIN_PASSWORD={{ grafana_admin_password }}
|
||||||
@@ -18,4 +21,4 @@ GRAFANA_PLUGINS={{ grafana_plugins | default('') }}
|
|||||||
# Prometheus BasicAuth
|
# Prometheus BasicAuth
|
||||||
# Format: username:hashed_password
|
# Format: username:hashed_password
|
||||||
# Note: Dollar signs are escaped for Docker Compose ($$ becomes $)
|
# Note: Dollar signs are escaped for Docker Compose ($$ becomes $)
|
||||||
PROMETHEUS_AUTH={{ prometheus_auth | replace('$', '$$') }}
|
PROMETHEUS_AUTH={{ prometheus_auth | replace('$', '$$') }}
|
||||||
|
|||||||
27
deployment/ansible/wireguard-clients/grafana-test.conf
Normal file
27
deployment/ansible/wireguard-clients/grafana-test.conf
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# WireGuard Client Configuration for grafana-test
|
||||||
|
# Generated by Ansible - DO NOT EDIT MANUALLY
|
||||||
|
|
||||||
|
[Interface]
|
||||||
|
# Client private key
|
||||||
|
PrivateKey = uMhNKh+Wi0aykTnazfSJD6l7Wc2V1Pe+7rFtFcnfynw=
|
||||||
|
|
||||||
|
# Client IP address in VPN network
|
||||||
|
Address = 10.8.0.4/24
|
||||||
|
|
||||||
|
# DNS server (optional)
|
||||||
|
DNS = 1.1.1.1, 8.8.8.8
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
# Server public key
|
||||||
|
PublicKey = hT3OCWZ6ElX79YdAdexSsZnbWLzRM/5szk+XNEBUaS8=
|
||||||
|
|
||||||
|
# Server endpoint
|
||||||
|
Endpoint = 94.16.110.151:51820
|
||||||
|
|
||||||
|
# Allowed IPs (routes through VPN)
|
||||||
|
# IMPORTANT: Only VPN network is routed through VPN by default
|
||||||
|
# SSH access via normal IP (94.16.110.151) remains available
|
||||||
|
AllowedIPs = 10.8.0.0/24
|
||||||
|
|
||||||
|
# Keep connection alive
|
||||||
|
PersistentKeepalive = 25
|
||||||
27
deployment/ansible/wireguard-clients/mikepc.conf
Normal file
27
deployment/ansible/wireguard-clients/mikepc.conf
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# WireGuard Client Configuration for mikepc
|
||||||
|
# Generated by Ansible - DO NOT EDIT MANUALLY
|
||||||
|
|
||||||
|
[Interface]
|
||||||
|
# Client private key
|
||||||
|
PrivateKey = wFxqFHe4R8IVzkAQSHaAwVfwQ2rfm5vCySZMpvPsVUQ=
|
||||||
|
|
||||||
|
# Client IP address in VPN network
|
||||||
|
Address = 10.8.0.3/24
|
||||||
|
|
||||||
|
# DNS server (optional)
|
||||||
|
DNS = 1.1.1.1, 8.8.8.8
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
# Server public key
|
||||||
|
PublicKey = hT3OCWZ6ElX79YdAdexSsZnbWLzRM/5szk+XNEBUaS8=
|
||||||
|
|
||||||
|
# Server endpoint
|
||||||
|
Endpoint = 94.16.110.151:51820
|
||||||
|
|
||||||
|
# Allowed IPs (routes through VPN)
|
||||||
|
# IMPORTANT: Only VPN network is routed through VPN by default
|
||||||
|
# SSH access via normal IP (94.16.110.151) remains available
|
||||||
|
AllowedIPs = 10.8.0.0/24
|
||||||
|
|
||||||
|
# Keep connection alive
|
||||||
|
PersistentKeepalive = 25
|
||||||
@@ -75,6 +75,8 @@ services:
|
|||||||
- "traefik.http.routers.grafana.entrypoints=websecure"
|
- "traefik.http.routers.grafana.entrypoints=websecure"
|
||||||
- "traefik.http.routers.grafana.tls=true"
|
- "traefik.http.routers.grafana.tls=true"
|
||||||
- "traefik.http.routers.grafana.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.grafana.tls.certresolver=letsencrypt"
|
||||||
|
- "traefik.http.middlewares.grafana-vpn-only.ipwhitelist.sourcerange=${MONITORING_VPN_IP_WHITELIST:-10.8.0.0/24}"
|
||||||
|
- "traefik.http.routers.grafana.middlewares=grafana-vpn-only"
|
||||||
- "traefik.http.services.grafana.loadbalancer.server.port=3000"
|
- "traefik.http.services.grafana.loadbalancer.server.port=3000"
|
||||||
depends_on:
|
depends_on:
|
||||||
prometheus:
|
prometheus:
|
||||||
|
|||||||
@@ -38,6 +38,24 @@ Folgende Dienste sollen später nur noch über VPN erreichbar sein:
|
|||||||
- Aktuell: `https://portainer.michaelschiemer.de` (öffentlich)
|
- Aktuell: `https://portainer.michaelschiemer.de` (öffentlich)
|
||||||
- Zukünftig: Nur `https://10.8.0.1:9443` über VPN
|
- Zukünftig: Nur `https://10.8.0.1:9443` über VPN
|
||||||
|
|
||||||
|
## Aktueller Test: Grafana nur per VPN
|
||||||
|
|
||||||
|
- Realisiert über Traefik-IP-Whitelist (`grafana-vpn-only` Middleware)
|
||||||
|
- Whitelist-Wert: `MONITORING_VPN_IP_WHITELIST` in `deployment/stacks/monitoring/.env` (Ansible default `monitoring_vpn_ip_whitelist`)
|
||||||
|
- Ziel: Erst Grafana absichern, später Prometheus/Portainer nachziehen
|
||||||
|
|
||||||
|
### Rollout-Schritte
|
||||||
|
1. Neue Stacks auf den Server syncen: \
|
||||||
|
`ansible-playbook -i inventory/production.yml playbooks/sync-stacks.yml`
|
||||||
|
2. Monitoring-Rolle komplett laufen lassen (die `.env` muss generiert werden): \
|
||||||
|
`ansible-playbook -i inventory/production.yml playbooks/setup-infrastructure.yml --tags monitoring`
|
||||||
|
3. Prüfen, dass `monitoring_stack_changed` in der Zusammenfassung `true` ist oder der Grafana-Container neu gestartet wurde
|
||||||
|
|
||||||
|
### Verifikation
|
||||||
|
- Ohne VPN (öffentliche IP): `curl -I https://grafana.michaelschiemer.de` → `403 Forbidden`
|
||||||
|
- Mit WireGuard (`10.8.0.x` Client): Grafana im Browser laden, Login funktioniert
|
||||||
|
- Optional: Traefik-Logs prüfen (`docker compose -f ~/deployment/stacks/traefik/docker-compose.yml logs -f traefik`) für geblockte IPs
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Geplante Implementierungsschritte
|
## Geplante Implementierungsschritte
|
||||||
@@ -210,4 +228,4 @@ Nach der Umsetzung sollte aktualisiert werden:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Hinweis**: Diese Härtung wird erst durchgeführt, wenn das VPN stabil läuft und alle notwendigen Clients konfiguriert sind.
|
**Hinweis**: Diese Härtung wird erst durchgeführt, wenn das VPN stabil läuft und alle notwendigen Clients konfiguriert sind.
|
||||||
|
|||||||
@@ -488,6 +488,10 @@ ansible-playbook -i inventory/production.yml playbooks/add-wireguard-client.yml
|
|||||||
|
|
||||||
**Dokumentation**: Siehe `deployment/ansible/playbooks/README-WIREGUARD.md`
|
**Dokumentation**: Siehe `deployment/ansible/playbooks/README-WIREGUARD.md`
|
||||||
|
|
||||||
|
> Nach dem Lauf liegt die Client-Konfiguration zusätzlich lokal im Repository unter
|
||||||
|
> `deployment/ansible/wireguard-clients/<client_name>.conf` – ideal zum direkten
|
||||||
|
> Import auf deinem Admin-Rechner (Datei bleibt mit `chmod 600` geschützt).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Verzeichnisstruktur
|
## Verzeichnisstruktur
|
||||||
@@ -555,4 +559,4 @@ Bei Problemen:
|
|||||||
---
|
---
|
||||||
|
|
||||||
**Zuletzt aktualisiert**: 2025-10-31
|
**Zuletzt aktualisiert**: 2025-10-31
|
||||||
**Version**: 1.0
|
**Version**: 1.0
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use App\Framework\DI\Initializer;
|
|||||||
use App\Framework\Performance\Contracts\PerformanceCollectorInterface;
|
use App\Framework\Performance\Contracts\PerformanceCollectorInterface;
|
||||||
use App\Framework\Redis\RedisConfig;
|
use App\Framework\Redis\RedisConfig;
|
||||||
use App\Framework\Redis\RedisConnection;
|
use App\Framework\Redis\RedisConnection;
|
||||||
|
use App\Framework\Redis\RedisConnectionPool;
|
||||||
use App\Framework\Serializer\Json\JsonSerializer;
|
use App\Framework\Serializer\Json\JsonSerializer;
|
||||||
use App\Framework\Serializer\Php\PhpSerializer;
|
use App\Framework\Serializer\Php\PhpSerializer;
|
||||||
|
|
||||||
@@ -27,10 +28,9 @@ final readonly class CacheInitializer
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
private PerformanceCollectorInterface $performanceCollector,
|
private PerformanceCollectorInterface $performanceCollector,
|
||||||
private Container $container,
|
private Container $container,
|
||||||
private ?AsyncService $asyncService = null,
|
|
||||||
#private CacheMetricsInterface $cacheMetrics,
|
#private CacheMetricsInterface $cacheMetrics,
|
||||||
private string $redisHost = 'redis',
|
private RedisConnectionPool $redisConnectionPool,
|
||||||
private int $redisPort = 6379,
|
private ?AsyncService $asyncService = null,
|
||||||
private int $compressionLevel = -1,
|
private int $compressionLevel = -1,
|
||||||
private int $minCompressionLength = 1024,
|
private int $minCompressionLength = 1024,
|
||||||
private bool $enableAsync = true
|
private bool $enableAsync = true
|
||||||
@@ -61,12 +61,8 @@ final readonly class CacheInitializer
|
|||||||
throw new \RuntimeException('Redis extension is not loaded. Please install php-redis extension or use alternative cache drivers.');
|
throw new \RuntimeException('Redis extension is not loaded. Please install php-redis extension or use alternative cache drivers.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$redisConfig = new RedisConfig(
|
$redisConnection = $this->redisConnectionPool->getCacheConnection();
|
||||||
host: $this->redisHost,
|
#$redisConnection = new RedisConnection($redisConfig, 'cache');
|
||||||
port: $this->redisPort,
|
|
||||||
database: 1 // Use DB 1 for cache
|
|
||||||
);
|
|
||||||
$redisConnection = new RedisConnection($redisConfig, 'cache');
|
|
||||||
$redisCache = new GeneralCache(new RedisCache($redisConnection), $serializer, $compression);
|
$redisCache = new GeneralCache(new RedisCache($redisConnection), $serializer, $compression);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
// Fallback to file cache if Redis is not available
|
// Fallback to file cache if Redis is not available
|
||||||
|
|||||||
@@ -13,12 +13,21 @@ use App\Framework\DI\ContainerCompiler;
|
|||||||
use App\Framework\DI\DefaultContainer;
|
use App\Framework\DI\DefaultContainer;
|
||||||
use App\Framework\DI\DependencyResolver;
|
use App\Framework\DI\DependencyResolver;
|
||||||
use App\Framework\Discovery\DiscoveryServiceBootstrapper;
|
use App\Framework\Discovery\DiscoveryServiceBootstrapper;
|
||||||
|
use App\Framework\Http\Parser\HttpRequestParser;
|
||||||
|
use App\Framework\Http\Parser\ParserCache;
|
||||||
|
use App\Framework\Http\Request;
|
||||||
|
use App\Framework\Http\RequestFactory;
|
||||||
use App\Framework\Http\ResponseEmitter;
|
use App\Framework\Http\ResponseEmitter;
|
||||||
use App\Framework\Logging\DefaultLogger;
|
use App\Framework\Logging\DefaultLogger;
|
||||||
|
use App\Framework\Logging\Formatter\DevelopmentFormatter;
|
||||||
|
use App\Framework\Logging\Handlers\ConsoleHandler;
|
||||||
use App\Framework\Logging\Logger;
|
use App\Framework\Logging\Logger;
|
||||||
use App\Framework\Logging\LoggerInitializer;
|
use App\Framework\Logging\LoggerInitializer;
|
||||||
|
use App\Framework\Logging\LogLevel;
|
||||||
use App\Framework\Performance\Contracts\PerformanceCollectorInterface;
|
use App\Framework\Performance\Contracts\PerformanceCollectorInterface;
|
||||||
use App\Framework\Reflection\CachedReflectionProvider;
|
use App\Framework\Reflection\CachedReflectionProvider;
|
||||||
|
use App\Framework\Redis\RedisPoolInitializer;
|
||||||
|
use App\Framework\Config\Environment;
|
||||||
|
|
||||||
final readonly class ContainerBootstrapper
|
final readonly class ContainerBootstrapper
|
||||||
{
|
{
|
||||||
@@ -149,19 +158,20 @@ final readonly class ContainerBootstrapper
|
|||||||
|
|
||||||
$container->instance(Logger::class, $logger);
|
$container->instance(Logger::class, $logger);
|
||||||
$container->instance(PerformanceCollectorInterface::class, $collector);
|
$container->instance(PerformanceCollectorInterface::class, $collector);
|
||||||
$container->instance(Cache::class, new CacheInitializer($collector, $container)());
|
$pool = new RedisPoolInitializer($container, $container->get(Environment::class))->initialize();
|
||||||
|
$container->instance(Cache::class, new CacheInitializer($collector, $container, $pool)());
|
||||||
|
|
||||||
$container->instance(ResponseEmitter::class, new ResponseEmitter());
|
$container->instance(ResponseEmitter::class, new ResponseEmitter());
|
||||||
|
|
||||||
// TEMPORARY FIX: Manual RequestFactory binding until Discovery issue is resolved
|
// TEMPORARY FIX: Manual RequestFactory binding until Discovery issue is resolved
|
||||||
$container->singleton(\App\Framework\Http\Request::class, function ($container) {
|
$container->singleton(Request::class, function ($container) {
|
||||||
error_log("ContainerBootstrapper: Creating Request singleton");
|
error_log("ContainerBootstrapper: Creating Request singleton");
|
||||||
|
|
||||||
// Get Cache from container (it was just registered above)
|
// Get Cache from container (it was just registered above)
|
||||||
$frameworkCache = $container->get(\App\Framework\Cache\Cache::class);
|
$frameworkCache = $container->get(Cache::class);
|
||||||
$parserCache = new \App\Framework\Http\Parser\ParserCache($frameworkCache);
|
$parserCache = new ParserCache($frameworkCache);
|
||||||
$parser = new \App\Framework\Http\Parser\HttpRequestParser($parserCache);
|
$parser = new HttpRequestParser($parserCache);
|
||||||
$factory = new \App\Framework\Http\RequestFactory($parser);
|
$factory = new RequestFactory($parser);
|
||||||
|
|
||||||
error_log("ContainerBootstrapper: About to call factory->createFromGlobals()");
|
error_log("ContainerBootstrapper: About to call factory->createFromGlobals()");
|
||||||
$request = $factory->createFromGlobals();
|
$request = $factory->createFromGlobals();
|
||||||
@@ -232,15 +242,15 @@ final readonly class ContainerBootstrapper
|
|||||||
$handlers = $isMcpMode
|
$handlers = $isMcpMode
|
||||||
? [new \App\Framework\Logging\Handlers\NullHandler()]
|
? [new \App\Framework\Logging\Handlers\NullHandler()]
|
||||||
: [
|
: [
|
||||||
new \App\Framework\Logging\Handlers\ConsoleHandler(
|
new ConsoleHandler(
|
||||||
new \App\Framework\Logging\Formatter\DevelopmentFormatter(),
|
new DevelopmentFormatter(),
|
||||||
\App\Framework\Logging\LogLevel::DEBUG
|
LogLevel::DEBUG
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
$logger = new \App\Framework\Logging\DefaultLogger(
|
$logger = new \App\Framework\Logging\DefaultLogger(
|
||||||
clock: $clock,
|
clock: $clock,
|
||||||
minLevel: \App\Framework\Logging\LogLevel::DEBUG,
|
minLevel: LogLevel::DEBUG,
|
||||||
handlers: $handlers,
|
handlers: $handlers,
|
||||||
processorManager: new \App\Framework\Logging\ProcessorManager(),
|
processorManager: new \App\Framework\Logging\ProcessorManager(),
|
||||||
contextManager: new \App\Framework\Logging\LogContextManager()
|
contextManager: new \App\Framework\Logging\LogContextManager()
|
||||||
|
|||||||
@@ -15,12 +15,14 @@ use App\Framework\Random\RandomGenerator;
|
|||||||
use App\Framework\Random\SecureRandomGenerator;
|
use App\Framework\Random\SecureRandomGenerator;
|
||||||
use App\Framework\Redis\RedisConfig;
|
use App\Framework\Redis\RedisConfig;
|
||||||
use App\Framework\Redis\RedisConnection;
|
use App\Framework\Redis\RedisConnection;
|
||||||
|
use App\Framework\Redis\RedisConnectionPool;
|
||||||
use App\Framework\Security\CsrfTokenGenerator;
|
use App\Framework\Security\CsrfTokenGenerator;
|
||||||
|
|
||||||
final readonly class SessionInitializer
|
final readonly class SessionInitializer
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private Container $container
|
private Container $container,
|
||||||
|
private RedisConnectionPool $redisConnectionPool,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,8 +49,7 @@ final readonly class SessionInitializer
|
|||||||
throw new \RuntimeException('Redis extension not loaded');
|
throw new \RuntimeException('Redis extension not loaded');
|
||||||
}
|
}
|
||||||
|
|
||||||
$redisConfig = new RedisConfig(host: 'redis', database: 3);
|
$redisConnection = $this->redisConnectionPool->getSessionConnection();
|
||||||
$redisConnection = new RedisConnection($redisConfig, 'session');
|
|
||||||
$storage = new RedisSessionStorage($redisConnection);
|
$storage = new RedisSessionStorage($redisConnection);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
// Fallback to file-based storage if Redis is not available
|
// Fallback to file-based storage if Redis is not available
|
||||||
|
|||||||
Reference in New Issue
Block a user