chore: lots of changes
This commit is contained in:
8
.dockerignore
Normal file
8
.dockerignore
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
.git
|
||||||
|
vendor
|
||||||
|
storage/logs/*
|
||||||
|
storage/app/*
|
||||||
|
storage/framework/cache/*
|
||||||
|
storage/framework/sessions/*
|
||||||
|
storage/framework/views/*
|
||||||
|
*.log
|
||||||
15
.editorconfig
Normal file
15
.editorconfig
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.{yml,yaml,json}]
|
||||||
|
indent_size = 2
|
||||||
18
.gitignore
vendored
18
.gitignore
vendored
@@ -1,7 +1,23 @@
|
|||||||
|
# Editor / IDE
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
# System
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Build / Runtime
|
||||||
vendor/
|
vendor/
|
||||||
node_modules/
|
|
||||||
.env
|
.env
|
||||||
*.log
|
*.log
|
||||||
*.retry
|
*.retry
|
||||||
ansible/.vault_pass
|
ansible/.vault_pass
|
||||||
|
*.Zone.Identifier
|
||||||
|
|
||||||
|
# Backup Dateien
|
||||||
|
*~
|
||||||
|
|
||||||
|
|
||||||
|
.php-cs-fixer.php
|
||||||
|
|
||||||
|
|
||||||
|
#ssl/*.pem
|
||||||
|
|||||||
111
Makefile
111
Makefile
@@ -1,16 +1,107 @@
|
|||||||
.PHONY: deploy setup stop build restart
|
# ----------------------------------
|
||||||
|
# Projekt: michaelschiemer.de
|
||||||
|
# Docker & Ansible Makefile
|
||||||
|
# ----------------------------------
|
||||||
|
|
||||||
setup:
|
PROJECT_NAME = michaelschiemer
|
||||||
ansible-playbook -i ansible/inventory.ini ansible/setup.yml
|
ENV ?= dev
|
||||||
|
|
||||||
deploy:
|
# Standart Docker Compose Befehle
|
||||||
ansible-playbook -i ansible/inventory.ini ansible/deploy.yml
|
|
||||||
|
|
||||||
stop:
|
up: ## Startet alle Docker-Container
|
||||||
docker compose down
|
./bin/up
|
||||||
|
|
||||||
|
down: ## Stoppt alle Container
|
||||||
|
./bin/down
|
||||||
|
|
||||||
build:
|
build:
|
||||||
docker compose build --no-cache
|
docker compose build
|
||||||
|
|
||||||
restart: stop build
|
restart: ## Neustart aller Container
|
||||||
docker compose up -d
|
./bin/restart
|
||||||
|
|
||||||
|
logs: ## Zeigt Logs aus Docker
|
||||||
|
docker compose logs -f
|
||||||
|
|
||||||
|
ps: ## Docker PS
|
||||||
|
docker compose ps
|
||||||
|
|
||||||
|
reload: ## Dump Autoload & Restart PHP
|
||||||
|
docker-compose exec php composer dump-autoload -o
|
||||||
|
docker-compose restart php
|
||||||
|
|
||||||
|
|
||||||
|
# Wähle dev- oder prod-PHP-Konfig je nach ENV
|
||||||
|
phpinfo:
|
||||||
|
@echo "Aktive PHP-Konfiguration: php.$(ENV).ini"
|
||||||
|
|
||||||
|
# Ansible Deployment
|
||||||
|
|
||||||
|
setup: ## Führt Ansible Setup aus
|
||||||
|
./bin/setup
|
||||||
|
|
||||||
|
deploy: ## Führt Ansible Deploy aus
|
||||||
|
./bin/deploy
|
||||||
|
|
||||||
|
test: ## Führt Tests aus (Platzhalter)
|
||||||
|
./bin/test
|
||||||
|
|
||||||
|
# Cleanup temporärer/metadaten-Dateien
|
||||||
|
clean: ## Entfernt temporäre Dateien
|
||||||
|
find . -type f -name "*Zone.Identifier" -delete
|
||||||
|
find . -type f -name "*.retry" -delete
|
||||||
|
|
||||||
|
# Projektstatus
|
||||||
|
status: ## Zeigt Container-Status
|
||||||
|
@echo "Aktuelles Projekt: $(PROJECT_NAME)"
|
||||||
|
@echo "Umgebung: $(ENV)"
|
||||||
|
|
||||||
|
doctor: ## Prüft ob Komponenten installiert sind
|
||||||
|
@echo "🔍 Prüfe Voraussetzungen..."
|
||||||
|
@which docker > /dev/null || echo "❌ Docker fehlt"
|
||||||
|
@which ansible-playbook > /dev/null || echo "❌ Ansible fehlt"
|
||||||
|
@test -f .env || echo "⚠️ .env-Datei fehlt"
|
||||||
|
|
||||||
|
# Helfer: Automatische Zielübersicht
|
||||||
|
help: ## Zeigt diese Hilfe an
|
||||||
|
@echo ""
|
||||||
|
@echo "🛠 Verfügbare Make-Befehle:"
|
||||||
|
@grep -E '^[a-zA-Z_-]+:.*?## ' Makefile | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-16s\033[0m %s\n", $$1, $$2}'
|
||||||
|
@echo ""
|
||||||
|
|
||||||
|
|
||||||
|
composer: ## Use Composer
|
||||||
|
docker compose exec php composer $(ARGS)
|
||||||
|
|
||||||
|
fix-perms: ## Fix permissions
|
||||||
|
sudo chown -R $(USER):$(USER) .
|
||||||
|
|
||||||
|
cs:
|
||||||
|
@$(MAKE) composer ARGS="cs"
|
||||||
|
|
||||||
|
cs-fix-file: ## Fix code style for a specific file
|
||||||
|
docker compose exec -e PHP_CS_FIXER_IGNORE_ENV=1 php ./vendor/bin/php-cs-fixer fix $(subst \,/,$(FILE))
|
||||||
|
|
||||||
|
cs-fix: ## Fix code style for all PHP files
|
||||||
|
docker compose exec -e PHP_CS_FIXER_IGNORE_ENV=1 php ./vendor/bin/php-cs-fixer fix
|
||||||
|
|
||||||
|
health:
|
||||||
|
ansible-playbook ansible/check.yml
|
||||||
|
|
||||||
|
|
||||||
|
# Konfiguration
|
||||||
|
ANSIBLE_INVENTORY=ansible/inventory/hosts.ini
|
||||||
|
PLAYBOOK_DIR=ansible/playbooks/deploy
|
||||||
|
|
||||||
|
.PHONY: dev staging production
|
||||||
|
|
||||||
|
dev:
|
||||||
|
ansible-playbook -i $(ANSIBLE_INVENTORY) $(PLAYBOOK_DIR)/dev.yml #--ask-become-pass
|
||||||
|
|
||||||
|
staging:
|
||||||
|
ansible-playbook -i $(ANSIBLE_INVENTORY) $(PLAYBOOK_DIR)/staging.yml
|
||||||
|
|
||||||
|
production:
|
||||||
|
ansible-playbook -i $(ANSIBLE_INVENTORY) $(PLAYBOOK_DIR)/production.yml
|
||||||
|
|
||||||
|
.PHONY: up down build restart logs ps phpinfo deploy setup clean status
|
||||||
|
|||||||
14
README.md
Normal file
14
README.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
## 🚀 Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Starten
|
||||||
|
make up
|
||||||
|
|
||||||
|
# Logs anzeigen
|
||||||
|
make logs
|
||||||
|
|
||||||
|
# Setup-Playbook (Server einmalig vorbereiten)
|
||||||
|
make setup
|
||||||
|
|
||||||
|
# Deployment (Code + Compose auf Server bringen)
|
||||||
|
make deploy
|
||||||
26
ansible.cfg
Normal file
26
ansible.cfg
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
[defaults]
|
||||||
|
#inventory = ./ansible/inventory.ini
|
||||||
|
inventory = ./ansible/inventory/hosts.ini
|
||||||
|
roles_path = ./ansible/roles
|
||||||
|
remote_tmp = ~/.ansible/tmp
|
||||||
|
forks = 5
|
||||||
|
timeout = 10
|
||||||
|
retry_files_enabled = False
|
||||||
|
deprecation_warnings = False
|
||||||
|
interpreter_python = auto_silent
|
||||||
|
#stdout_callback = json
|
||||||
|
host_key_checking = False
|
||||||
|
command_warnings = False
|
||||||
|
gathering = smart
|
||||||
|
fact_caching = jsonfile
|
||||||
|
fact_caching_connection = .ansible/cache
|
||||||
|
fact_caching_timeout = 3600
|
||||||
|
|
||||||
|
[privilege_escalation]
|
||||||
|
become = true
|
||||||
|
become_method = sudo
|
||||||
|
become_user = root
|
||||||
|
|
||||||
|
[ssh_connection]
|
||||||
|
pipelining = true
|
||||||
|
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
|
||||||
10
ansible/client-configs/michael.conf
Normal file
10
ansible/client-configs/michael.conf
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[Interface]
|
||||||
|
PrivateKey = +DcT11ipmMwPXpzEqmCPGwy7cSmseG1YzZWk+tTtM30=
|
||||||
|
Address = 10.8.0.2/32
|
||||||
|
DNS = 1.1.1.1
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
PublicKey = 3qFEUREx6VfqrKoGVtzHt2ojgaly7LvwxjPQPNsFyxM=
|
||||||
|
Endpoint = 94.16.110.151:51820
|
||||||
|
AllowedIPs = 10.8.0.0/24, 94.16.110.151/32
|
||||||
|
PersistentKeepalive = 25
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
- hosts: web
|
|
||||||
become: false
|
|
||||||
roles:
|
|
||||||
- deploy
|
|
||||||
39
ansible/group_vars/all.yml
Normal file
39
ansible/group_vars/all.yml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Basis-Konfiguration
|
||||||
|
app_name: michaelschiemer
|
||||||
|
app_domain: test.michaelschiemer.de
|
||||||
|
app_email: kontakt@michaelschiemer.de
|
||||||
|
|
||||||
|
# Verzeichnisse
|
||||||
|
project_root: "{{ playbook_dir | dirname }}"
|
||||||
|
app_root: /var/www/{{ app_name }}
|
||||||
|
app_public: "{{ app_root }}/public"
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
docker_version: "20.10"
|
||||||
|
docker_compose_version: "2.24.5"
|
||||||
|
|
||||||
|
# Benutzer
|
||||||
|
deploy_user: deploy
|
||||||
|
|
||||||
|
# Let's Encrypt
|
||||||
|
letsencrypt_enabled: true
|
||||||
|
letsencrypt_certbot_method: webroot # oder standalone oder nginx
|
||||||
|
|
||||||
|
|
||||||
|
#netcup_customer_id: "218722"
|
||||||
|
#netcup_api_key: "dmJINUMyNjRmOG1aNDViajZHN2JkOTFRUjU3ckE5ZjJ1Zm1vUz"
|
||||||
|
#netcup_api_password: "iGWL8Hl4m93DgESsP/MPXmtDd0hEVkZ3480Na0psTlXRALnopl"
|
||||||
|
#netcup_vserver_id: "v2202309206672239295"
|
||||||
|
|
||||||
|
|
||||||
|
# fallback_ip:
|
||||||
|
|
||||||
|
wg_all_clients_private_keys:
|
||||||
|
michael: "PITbFZ3UfY5vD5dYUCELO37Qo2W8I4R8+r6D9CeMrm4="
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
wireguard_clients:
|
||||||
|
- name: michael
|
||||||
|
address: 10.8.0.2
|
||||||
|
public_key: DEIN_PUBLIC_KEY
|
||||||
4
ansible/group_vars/vpn.yml
Normal file
4
ansible/group_vars/vpn.yml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
wg_privkey: "HIER_DEIN_PRIVATER_KEY_ODER_DATEIPFAD"
|
||||||
|
|
||||||
|
wg_all_clients_private_keys:
|
||||||
|
michael: "PITbFZ3UfY5vD5dYUCELO37Qo2W8I4R8+r6D9CeMrm4="
|
||||||
0
ansible/group_vars/web.yml
Normal file
0
ansible/group_vars/web.yml
Normal file
@@ -1,2 +1,8 @@
|
|||||||
|
#[web]
|
||||||
|
#localhost ansible_connection=local
|
||||||
|
|
||||||
[web]
|
[web]
|
||||||
localhost ansible_connection=local
|
94.16.110.151 ansible_user=deploy ansible_ssh_private_key_file=/mnt/c/Users/Mike/.ssh/test.michaelschiemer.de
|
||||||
|
|
||||||
|
[vpn]
|
||||||
|
94.16.110.151 ansible_user=deploy
|
||||||
|
|||||||
12
ansible/inventory/hosts.ini
Normal file
12
ansible/inventory/hosts.ini
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[localhost]
|
||||||
|
127.0.0.1 ansible_connection=local
|
||||||
|
|
||||||
|
[staging]
|
||||||
|
94.16.110.151 ansible_user=deploy ansible_ssh_private_key_file=/mnt/c/Users/Mike/.ssh/test.michaelschiemer.de
|
||||||
|
|
||||||
|
[production]
|
||||||
|
|
||||||
|
|
||||||
|
[vpn]
|
||||||
|
94.16.110.151 ansible_user=deploy
|
||||||
|
|
||||||
40
ansible/playbooks/check.yml
Normal file
40
ansible/playbooks/check.yml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#- name: Check ob /ping erreichbar ist
|
||||||
|
# uri:
|
||||||
|
# url: "http://localhost/ping"
|
||||||
|
# status_code: 200
|
||||||
|
# return_content: yes
|
||||||
|
# register: ping_response
|
||||||
|
#
|
||||||
|
#- debug:
|
||||||
|
# var: ping_response.content
|
||||||
|
|
||||||
|
- name: Healthcheck nach dem Deployment
|
||||||
|
hosts: localhost
|
||||||
|
connection: local
|
||||||
|
gather_facts: false
|
||||||
|
become: false
|
||||||
|
|
||||||
|
vars:
|
||||||
|
healthcheck_url: "http://127.0.0.1:8080/ping"
|
||||||
|
max_retries: 10
|
||||||
|
delay_between_retries: 3
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Warte, bis der Webserver erreichbar ist
|
||||||
|
uri:
|
||||||
|
url: "{{ healthcheck_url }}"
|
||||||
|
status_code: 200
|
||||||
|
return_content: yes
|
||||||
|
register: healthcheck_response
|
||||||
|
retries: "{{ max_retries }}"
|
||||||
|
delay: "{{ delay_between_retries }}"
|
||||||
|
until: >
|
||||||
|
healthcheck_response is defined and
|
||||||
|
healthcheck_response.status is defined and
|
||||||
|
healthcheck_response.status == 200
|
||||||
|
failed_when: healthcheck_response.status != 200
|
||||||
|
ignore_errors: false
|
||||||
|
|
||||||
|
- name: Ausgabe des Healthcheck-Resultats
|
||||||
|
debug:
|
||||||
|
msg: "Healthcheck erfolgreich: {{ healthcheck_response.content }}"
|
||||||
24
ansible/playbooks/deploy.yml
Normal file
24
ansible/playbooks/deploy.yml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
- name: Deployment in jeweilige Umgebung
|
||||||
|
hosts: all
|
||||||
|
become: true
|
||||||
|
gather_facts: false
|
||||||
|
|
||||||
|
vars:
|
||||||
|
docker_compose_project_path: "/var/www/michaelschiemer/"
|
||||||
|
env_file_path: "/var/www/michaelschiemer/.env"
|
||||||
|
|
||||||
|
deploy_root: /var/www/michaelschiemer
|
||||||
|
deploy_public: "{{ deploy_root }}/public"
|
||||||
|
deploy_user: deploy
|
||||||
|
|
||||||
|
|
||||||
|
app_domain: "example.com" # Passe ggf. an
|
||||||
|
project_root: "{{ playbook_dir }}/../.."
|
||||||
|
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- app
|
||||||
|
- nginx
|
||||||
|
- php
|
||||||
|
- redis
|
||||||
|
|
||||||
26
ansible/playbooks/deploy/dev.yml
Normal file
26
ansible/playbooks/deploy/dev.yml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
- name: Deployment für DEV (localhost)
|
||||||
|
hosts: localhost
|
||||||
|
become: true
|
||||||
|
gather_facts: false
|
||||||
|
|
||||||
|
vars:
|
||||||
|
docker_compose_project_path: "/home/michael/dev/michaelschiemer"
|
||||||
|
env_file_path: "/var/www/michaelschiemer/.env"
|
||||||
|
|
||||||
|
deploy_root: /var/www/michaelschiemer
|
||||||
|
deploy_public: "{{ deploy_root }}/public"
|
||||||
|
deploy_user: deploy
|
||||||
|
|
||||||
|
app_domain: "localhost" # Passe ggf. an
|
||||||
|
project_root: "/home/michael/dev/michaelschiemer"
|
||||||
|
|
||||||
|
|
||||||
|
roles:
|
||||||
|
#- app
|
||||||
|
- nginx
|
||||||
|
- php
|
||||||
|
- redis
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Common Deployment Tasks
|
||||||
|
import_tasks: ../deploy/includes/deploy_common.yml
|
||||||
29
ansible/playbooks/deploy/includes/deploy_common.yml
Normal file
29
ansible/playbooks/deploy/includes/deploy_common.yml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
- name: Docker Compose Files & Konfigurationen synchronisieren
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: "{{ item.src }}"
|
||||||
|
dest: "{{ docker_compose_project_path }}/{{ item.dest }}"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: 0644
|
||||||
|
loop:
|
||||||
|
- { src: '{{ project_root }}/docker-compose.yml', dest: 'docker-compose.yml' }
|
||||||
|
- { src: '{{ project_root }}/.env', dest: '.env' }
|
||||||
|
# Weitere Konfigdateien nach Bedarf (z.B. nginx.conf, redis.conf, ...)
|
||||||
|
- { src: '{{ project_root }}/docker/nginx/nginx.conf', dest: 'nginx.conf' }
|
||||||
|
|
||||||
|
- name: "Docker Compose: Container hochfahren (Build & Start)"
|
||||||
|
ansible.builtin.command: |
|
||||||
|
docker-compose -f {{ docker_compose_project_path }}/docker-compose.yml up -d --build
|
||||||
|
args:
|
||||||
|
chdir: "{{ docker_compose_project_path }}"
|
||||||
|
|
||||||
|
- name: Status prüfen
|
||||||
|
ansible.builtin.command: |
|
||||||
|
docker-compose -f {{ docker_compose_project_path }}/docker-compose.yml ps
|
||||||
|
args:
|
||||||
|
chdir: "{{ docker_compose_project_path }}"
|
||||||
|
register: compose_ps
|
||||||
|
|
||||||
|
- name: Ergebnis anzeigen
|
||||||
|
ansible.builtin.debug:
|
||||||
|
var: compose_ps.stdout_lines
|
||||||
23
ansible/playbooks/deploy/production.yml
Normal file
23
ansible/playbooks/deploy/production.yml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
- name: Deployment für PRODUCTION
|
||||||
|
hosts: production
|
||||||
|
become: true
|
||||||
|
gather_facts: false
|
||||||
|
|
||||||
|
vars:
|
||||||
|
docker_compose_project_path: "/var/www/www.michaelschiemer.de/"
|
||||||
|
env_file_path: "/var/www/www.michaelschiemer.de/.env"
|
||||||
|
deploy_root: /var/www/www.michaelschiemer.de
|
||||||
|
deploy_public: "{{ deploy_root }}/public"
|
||||||
|
deploy_user: deploy
|
||||||
|
app_domain: "michaelschiemer.de"
|
||||||
|
project_root: "{{ playbook_dir }}/../.."
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- app
|
||||||
|
- nginx
|
||||||
|
- php
|
||||||
|
- redis
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Common Deployment Tasks
|
||||||
|
import_tasks: ../deploy/includes/deploy_common.yml
|
||||||
23
ansible/playbooks/deploy/staging.yml
Normal file
23
ansible/playbooks/deploy/staging.yml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
- name: Deployment für STAGING
|
||||||
|
hosts: staging
|
||||||
|
become: true
|
||||||
|
gather_facts: false
|
||||||
|
|
||||||
|
vars:
|
||||||
|
docker_compose_project_path: "/var/www/stage.michaelschiemer/"
|
||||||
|
env_file_path: "/var/www/stage.michaelschiemer/.env"
|
||||||
|
deploy_root: /var/www/stage.michaelschiemer
|
||||||
|
deploy_public: "{{ deploy_root }}/public"
|
||||||
|
deploy_user: deploy
|
||||||
|
app_domain: "stage.example.com"
|
||||||
|
project_root: "{{ playbook_dir }}/../.."
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- app
|
||||||
|
- nginx
|
||||||
|
- php
|
||||||
|
- redis
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Common Deployment Tasks
|
||||||
|
import_tasks: ../deploy/includes/deploy_common.yml
|
||||||
11
ansible/playbooks/setup.yml
Normal file
11
ansible/playbooks/setup.yml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
- name: Basis Setup für alle Zielsysteme
|
||||||
|
hosts: all
|
||||||
|
become: true
|
||||||
|
#gather_facts: true
|
||||||
|
|
||||||
|
roles:
|
||||||
|
#- common
|
||||||
|
- docker
|
||||||
|
#- webserver
|
||||||
|
#- app
|
||||||
6
ansible/playbooks/test.yml
Normal file
6
ansible/playbooks/test.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
- hosts: web
|
||||||
|
become: true
|
||||||
|
gather_facts: true
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- console
|
||||||
7
ansible/playbooks/wireguard.yml
Normal file
7
ansible/playbooks/wireguard.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# ansible/wireguard.yml
|
||||||
|
- hosts: vpn
|
||||||
|
become: false
|
||||||
|
gather_facts: false
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- wireguard
|
||||||
134
ansible/roles/app/tasks/main.yml
Normal file
134
ansible/roles/app/tasks/main.yml
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
- name: Zielverzeichnis erstellen
|
||||||
|
file:
|
||||||
|
path: "{{ deploy_root }}"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ deploy_user }}"
|
||||||
|
group: "{{ deploy_user }}"
|
||||||
|
mode: '0755'
|
||||||
|
|
||||||
|
- name: SSL-Verzeichnis sicherstellen
|
||||||
|
file:
|
||||||
|
path: "{{ deploy_root }}/ssl"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ deploy_user }}"
|
||||||
|
group: "{{ deploy_user }}"
|
||||||
|
mode: '0755'
|
||||||
|
|
||||||
|
- name: SSL-Zertifikate prüfen
|
||||||
|
stat:
|
||||||
|
path: "/etc/letsencrypt/live/{{ app_domain }}/fullchain.pem"
|
||||||
|
register: ssl_certs
|
||||||
|
|
||||||
|
- name: SSL-Zertifikate kopieren (falls vorhanden)
|
||||||
|
copy:
|
||||||
|
src: "{{ item.src }}"
|
||||||
|
dest: "{{ item.dest }}"
|
||||||
|
remote_src: yes
|
||||||
|
owner: "{{ deploy_user }}"
|
||||||
|
group: "{{ deploy_user }}"
|
||||||
|
mode: '0644'
|
||||||
|
loop:
|
||||||
|
- { src: "/etc/letsencrypt/live/{{ app_domain }}/fullchain.pem", dest: "{{ deploy_root }}/ssl/fullchain.pem" }
|
||||||
|
- { src: "/etc/letsencrypt/live/{{ app_domain }}/privkey.pem", dest: "{{ deploy_root }}/ssl/privkey.pem" }
|
||||||
|
when: ssl_certs.stat.exists
|
||||||
|
|
||||||
|
- name: public-Verzeichnis synchronisieren
|
||||||
|
synchronize:
|
||||||
|
src: "{{ playbook_dir }}/../../public/"
|
||||||
|
dest: "{{ deploy_public }}/"
|
||||||
|
delete: yes
|
||||||
|
recursive: yes
|
||||||
|
|
||||||
|
- name: Projekt-Stammdaten kopieren
|
||||||
|
copy:
|
||||||
|
src: "{{ playbook_dir }}/../../docker-compose.yml"
|
||||||
|
dest: "{{ deploy_root }}/docker-compose.yml"
|
||||||
|
owner: "{{ deploy_user }}"
|
||||||
|
group: "{{ deploy_user }}"
|
||||||
|
mode: '0644'
|
||||||
|
|
||||||
|
- name: .env-Datei prüfen
|
||||||
|
stat:
|
||||||
|
path: "{{ project_root }}/.env"
|
||||||
|
register: env_file
|
||||||
|
|
||||||
|
- name: .env kopieren (falls vorhanden)
|
||||||
|
copy:
|
||||||
|
src: "{{ project_root }}/.env"
|
||||||
|
dest: "{{ deploy_root }}/.env"
|
||||||
|
mode: '0644'
|
||||||
|
when: env_file.stat.exists
|
||||||
|
|
||||||
|
- name: Quellcode synchronisieren
|
||||||
|
synchronize:
|
||||||
|
src: "{{ playbook_dir }}/../../src/"
|
||||||
|
dest: "{{ deploy_root }}/src/"
|
||||||
|
delete: yes
|
||||||
|
recursive: yes
|
||||||
|
|
||||||
|
- name: Docker-Verzeichnis prüfen
|
||||||
|
stat:
|
||||||
|
path: "{{ project_root }}/docker"
|
||||||
|
register: docker_dir
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
|
||||||
|
- name: Docker-Configs synchronisieren (falls vorhanden)
|
||||||
|
synchronize:
|
||||||
|
src: "{{ project_root }}/docker/"
|
||||||
|
dest: "{{ deploy_root }}/docker/"
|
||||||
|
delete: yes
|
||||||
|
recursive: yes
|
||||||
|
when: docker_dir.stat.exists
|
||||||
|
|
||||||
|
- name: Rechte im Zielverzeichnis korrigieren
|
||||||
|
file:
|
||||||
|
path: "{{ deploy_root }}"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ deploy_user }}"
|
||||||
|
group: "{{ deploy_user }}"
|
||||||
|
mode: '0755'
|
||||||
|
recurse: yes
|
||||||
|
|
||||||
|
# Cache-Verzeichnis für UID/GID 1000 (z.B. appuser im Container)
|
||||||
|
- name: Stelle Schreibrechte für Cache-Verzeichnis her
|
||||||
|
file:
|
||||||
|
path: "{{ deploy_root }}/cache"
|
||||||
|
state: directory
|
||||||
|
owner: 1000
|
||||||
|
group: 1000
|
||||||
|
mode: '0775'
|
||||||
|
recurse: yes
|
||||||
|
|
||||||
|
- name: Docker Compose neu bauen und starten
|
||||||
|
shell: |
|
||||||
|
docker compose down
|
||||||
|
docker compose up -d --build
|
||||||
|
args:
|
||||||
|
chdir: "{{ deploy_root }}"
|
||||||
|
|
||||||
|
- name: PHP-Container für Composer starten
|
||||||
|
shell: docker compose up -d php
|
||||||
|
args:
|
||||||
|
chdir: "{{ deploy_root }}"
|
||||||
|
|
||||||
|
- name: Kurze Wartezeit bis PHP-Container bereit
|
||||||
|
wait_for:
|
||||||
|
timeout: 5
|
||||||
|
|
||||||
|
- name: Composer Abhängigkeiten installieren
|
||||||
|
shell: docker compose exec -T php composer install --no-interaction
|
||||||
|
args:
|
||||||
|
chdir: "{{ deploy_root }}"
|
||||||
|
register: composer_result
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
|
- name: Composer-Ergebnis anzeigen
|
||||||
|
debug:
|
||||||
|
var: composer_result.stdout_lines
|
||||||
|
when: composer_result.stdout is defined
|
||||||
|
|
||||||
|
- name: Composer-Fehler anzeigen
|
||||||
|
debug:
|
||||||
|
var: composer_result.stderr_lines
|
||||||
|
when: composer_result.stderr is defined
|
||||||
26
ansible/roles/common/tasks/main.yml
Normal file
26
ansible/roles/common/tasks/main.yml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Grundlegende Systemkonfiguration
|
||||||
|
- name: Basis-Pakete aktualisieren und installieren
|
||||||
|
apt:
|
||||||
|
name:
|
||||||
|
- sudo
|
||||||
|
- vim
|
||||||
|
- htop
|
||||||
|
- git
|
||||||
|
- zip
|
||||||
|
- unzip
|
||||||
|
- curl
|
||||||
|
- wget
|
||||||
|
state: present
|
||||||
|
update_cache: yes
|
||||||
|
become: true
|
||||||
|
|
||||||
|
# Passwordless sudo für den deploy-Benutzer einrichten
|
||||||
|
- name: Konfiguriere passwordless sudo für deploy-Benutzer
|
||||||
|
lineinfile:
|
||||||
|
path: "/etc/sudoers.d/{{ deploy_user }}"
|
||||||
|
line: "{{ deploy_user }} ALL=(ALL) NOPASSWD: ALL"
|
||||||
|
state: present
|
||||||
|
create: yes
|
||||||
|
mode: '0440'
|
||||||
|
validate: 'visudo -cf %s'
|
||||||
|
become: true
|
||||||
9
ansible/roles/console/tasks/main.yml
Normal file
9
ansible/roles/console/tasks/main.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
- name: Füge Funktion für ms (mit Argumenten) hinzu
|
||||||
|
blockinfile:
|
||||||
|
path: "/home/{{ ansible_user }}/.bashrc"
|
||||||
|
marker: "# {mark} ms docker alias"
|
||||||
|
block: |
|
||||||
|
ms() {
|
||||||
|
docker compose exec php php ms "$@"
|
||||||
|
}
|
||||||
|
become: false
|
||||||
9
ansible/roles/docker/README.md
Normal file
9
ansible/roles/docker/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Rolle: Docker
|
||||||
|
|
||||||
|
Diese Rolle installiert Docker Engine, CLI, Compose-Plugin sowie (optional) Docker Compose V1 als Fallback.
|
||||||
|
- Fügt den gewünschten User zur Docker-Gruppe hinzu.
|
||||||
|
- Startet und aktiviert den Docker-Dienst.
|
||||||
|
|
||||||
|
## Variablen
|
||||||
|
- `docker_compose_version`: Version von Docker Compose V1 für Fallback (Standard: 1.29.2).
|
||||||
|
- `docker_user`: Benutzer, der in die Gruppe `docker` aufgenommen werden soll (Standard: aktueller Ansible-User).
|
||||||
3
ansible/roles/docker/defaults/main.yml
Normal file
3
ansible/roles/docker/defaults/main.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
docker_compose_version: "v2.29.2"
|
||||||
|
docker_install_compose: true
|
||||||
|
docker_user: "{{ ansible_user || default('michael' }}"
|
||||||
4
ansible/roles/docker/handlers/main.yml
Normal file
4
ansible/roles/docker/handlers/main.yml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
- name: restart docker
|
||||||
|
ansible.builtin.service:
|
||||||
|
name: docker
|
||||||
|
state: restarted
|
||||||
58
ansible/roles/docker/tasks/main.yml
Normal file
58
ansible/roles/docker/tasks/main.yml
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
- name: Docker-Abhängigkeiten installieren
|
||||||
|
apt:
|
||||||
|
name:
|
||||||
|
- apt-transport-https
|
||||||
|
- ca-certificates
|
||||||
|
- curl
|
||||||
|
- gnupg
|
||||||
|
- lsb-release
|
||||||
|
state: present
|
||||||
|
update_cache: yes
|
||||||
|
|
||||||
|
- name: Docker GPG-Schlüssel hinzufügen
|
||||||
|
apt_key:
|
||||||
|
url: https://download.docker.com/linux/{{ ansible_distribution | lower }}/gpg
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Docker Repository hinzufügen
|
||||||
|
apt_repository:
|
||||||
|
repo: "deb [arch=amd64] https://download.docker.com/linux/{{ ansible_distribution | lower }} {{ ansible_distribution_release }} stable"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Docker Engine installieren
|
||||||
|
apt:
|
||||||
|
name:
|
||||||
|
- docker-ce
|
||||||
|
- docker-ce-cli
|
||||||
|
- containerd.io
|
||||||
|
- docker-compose-plugin
|
||||||
|
state: present
|
||||||
|
update_cache: yes
|
||||||
|
|
||||||
|
- name: Docker Compose installieren (V1 als Fallback)
|
||||||
|
get_url:
|
||||||
|
url: "https://github.com/docker/compose/releases/download/v{{ docker_compose_version }}/docker-compose-linux-x86_64"
|
||||||
|
dest: /usr/local/bin/docker-compose
|
||||||
|
mode: '0755'
|
||||||
|
|
||||||
|
- name: Benutzer zur Docker-Gruppe hinzufügen
|
||||||
|
user:
|
||||||
|
name: "{{ ansible_user }}"
|
||||||
|
groups: docker
|
||||||
|
append: yes
|
||||||
|
|
||||||
|
- name: Docker-Service starten und aktivieren
|
||||||
|
service:
|
||||||
|
name: docker
|
||||||
|
state: started
|
||||||
|
enabled: yes
|
||||||
|
notify: restart docker
|
||||||
|
|
||||||
|
|
||||||
|
- name: Starte Docker-Container via Compose
|
||||||
|
community.docker.docker_compose_v2:
|
||||||
|
#project_src: "{{ playbook_dir | dirname }}/../" # ggf. anpassen auf deinen Compose-Pfad!
|
||||||
|
project_src: "{{ app_root }}"
|
||||||
|
build: always
|
||||||
|
recreate: always
|
||||||
|
|
||||||
5
ansible/roles/nginx/defaults/main.yml
Normal file
5
ansible/roles/nginx/defaults/main.yml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
nginx_conf_template: nginx.conf.j2
|
||||||
|
nginx_default_site_template: default.conf.j2
|
||||||
|
nginx_ssl_src_dir: "{{ app_root }}/ssl"
|
||||||
|
nginx_ssl_dest_dir: "/var/www/michaelschiemer/ssl"
|
||||||
|
nginx_target_dir: "/var/www/michaelschiemer/docker/nginx"
|
||||||
0
ansible/roles/nginx/files/docker-entrypoint.sh
Normal file
0
ansible/roles/nginx/files/docker-entrypoint.sh
Normal file
2
ansible/roles/nginx/handlers/main.yml
Normal file
2
ansible/roles/nginx/handlers/main.yml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
- name: reload nginx
|
||||||
|
ansible.builtin.command: docker exec <nginx_container_name> nginx -s reload
|
||||||
37
ansible/roles/nginx/tasks/main.yml
Normal file
37
ansible/roles/nginx/tasks/main.yml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
- name: Stelle das nginx-Verzeichnis sicher
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ nginx_target_dir }}"
|
||||||
|
state: directory
|
||||||
|
recurse: yes
|
||||||
|
mode: '0755'
|
||||||
|
|
||||||
|
- name: Kopiere nginx-Konfiguration (nginx.conf)
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "{{ nginx_conf_template }}"
|
||||||
|
dest: "{{ nginx_target_dir }}/nginx.conf"
|
||||||
|
mode: '0644'
|
||||||
|
|
||||||
|
- name: Kopiere default site conf
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "{{ nginx_default_site_template }}"
|
||||||
|
dest: "{{ nginx_target_dir }}/default.conf"
|
||||||
|
mode: '0644'
|
||||||
|
|
||||||
|
- name: Kopiere docker-entrypoint Skript
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: docker-entrypoint.sh
|
||||||
|
dest: "{{ nginx_target_dir }}/docker-entrypoint.sh"
|
||||||
|
mode: '0755'
|
||||||
|
|
||||||
|
- name: Baue und starte Nginx-Container (optional, wenn Compose separat genutzt wird, dann hier nicht nötig)
|
||||||
|
ansible.builtin.command: docker-compose up -d --build web
|
||||||
|
args:
|
||||||
|
chdir: "{{ docker_compose_project_path }}"
|
||||||
|
when: nginx_target_dir is defined
|
||||||
|
register: nginx_compose_result
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: Zeige Compose-Resultat
|
||||||
|
ansible.builtin.debug:
|
||||||
|
var: nginx_compose_result.stdout_lines
|
||||||
|
when: nginx_compose_result is defined
|
||||||
0
ansible/roles/nginx/templates/default.conf.j2
Normal file
0
ansible/roles/nginx/templates/default.conf.j2
Normal file
0
ansible/roles/nginx/templates/nginx.conf.j2
Normal file
0
ansible/roles/nginx/templates/nginx.conf.j2
Normal file
4
ansible/roles/setup/handlers/main.yml
Normal file
4
ansible/roles/setup/handlers/main.yml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
- name: Reload nginx
|
||||||
|
service:
|
||||||
|
name: nginx
|
||||||
|
state: reloaded
|
||||||
@@ -1,3 +1,47 @@
|
|||||||
- name: Test-Task Setup-Rolle lokal
|
- name: Docker installieren
|
||||||
debug:
|
apt:
|
||||||
msg: "Setup-Rolle ist vorbereitet – echte Installation folgt auf Server."
|
name:
|
||||||
|
- docker.io
|
||||||
|
- docker-compose
|
||||||
|
state: present
|
||||||
|
update_cache: yes
|
||||||
|
|
||||||
|
- name: Certbot + Plugin installieren
|
||||||
|
apt:
|
||||||
|
name:
|
||||||
|
- certbot
|
||||||
|
- python3-certbot-nginx
|
||||||
|
state: present
|
||||||
|
update_cache: yes
|
||||||
|
|
||||||
|
- name: Challenge-Verzeichnis für Let's Encrypt anlegen
|
||||||
|
file:
|
||||||
|
path: /var/www/html/.well-known/acme-challenge
|
||||||
|
state: directory
|
||||||
|
owner: www-data
|
||||||
|
group: www-data
|
||||||
|
mode: '0755'
|
||||||
|
recurse: yes
|
||||||
|
|
||||||
|
- name: Füge Let's Encrypt Challenge-Pfad in den Nginx-Vhost ein
|
||||||
|
blockinfile:
|
||||||
|
path: /etc/nginx/sites-available/default
|
||||||
|
marker: "# {mark} ANSIBLE LETSENCRYPT"
|
||||||
|
insertafter: "^\\s*server\\s*{"
|
||||||
|
block: |
|
||||||
|
location ^~ /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/html;
|
||||||
|
allow all;
|
||||||
|
default_type "text/plain";
|
||||||
|
}
|
||||||
|
notify: Reload nginx
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- name: Let's Encrypt Zertifikat anfordern
|
||||||
|
command: >
|
||||||
|
certbot --nginx -n --agree-tos --redirect
|
||||||
|
-m kontakt@michaelschiemer.de
|
||||||
|
-d test.michaelschiemer.de
|
||||||
|
args:
|
||||||
|
creates: /etc/letsencrypt/live/test.michaelschiemer.de/fullchain.pem
|
||||||
|
|||||||
18
ansible/roles/system_update/tasks/main.yml
Normal file
18
ansible/roles/system_update/tasks/main.yml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
- name: Systempakete aktualisieren
|
||||||
|
apt:
|
||||||
|
update_cache: yes
|
||||||
|
upgrade: safe
|
||||||
|
autoremove: yes
|
||||||
|
autoclean: yes
|
||||||
|
register: upgrade_result
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Zeige ggf. Anzahl aktualisierter Pakete
|
||||||
|
debug:
|
||||||
|
msg: "Anzahl aktualisierter Pakete: {{ upgrade_result.stdout_lines | default([]) | length }}"
|
||||||
|
|
||||||
|
- name: Reboot durchführen, wenn notwendig
|
||||||
|
reboot:
|
||||||
|
msg: "Reboot wegen Kernel-/System-Update erforderlich"
|
||||||
|
pre_reboot_delay: 30
|
||||||
|
when: upgrade_result.changed
|
||||||
50
ansible/roles/webserver/tasks/main.yml
Normal file
50
ansible/roles/webserver/tasks/main.yml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
- name: Certbot + Plugin installieren
|
||||||
|
apt:
|
||||||
|
name:
|
||||||
|
- certbot
|
||||||
|
- python3-certbot-nginx
|
||||||
|
state: present
|
||||||
|
update_cache: yes
|
||||||
|
when: letsencrypt_enabled
|
||||||
|
|
||||||
|
- name: Challenge-Verzeichnis für Let's Encrypt anlegen
|
||||||
|
file:
|
||||||
|
path: "{{ app_public }}/.well-known/acme-challenge"
|
||||||
|
state: directory
|
||||||
|
owner: www-data
|
||||||
|
group: www-data
|
||||||
|
mode: '0755'
|
||||||
|
recurse: yes
|
||||||
|
when: letsencrypt_enabled and letsencrypt_certbot_method == 'webroot'
|
||||||
|
|
||||||
|
- name: Stoppe Nginx für Standalone-Methode
|
||||||
|
service:
|
||||||
|
name: nginx
|
||||||
|
state: stopped
|
||||||
|
when: letsencrypt_enabled and letsencrypt_certbot_method == 'standalone'
|
||||||
|
|
||||||
|
- name: Let's Encrypt Zertifikat anfordern (Standalone)
|
||||||
|
command: >
|
||||||
|
certbot certonly --standalone -n --agree-tos
|
||||||
|
-m {{ app_email }}
|
||||||
|
-d {{ app_domain }}
|
||||||
|
args:
|
||||||
|
creates: /etc/letsencrypt/live/{{ app_domain }}/fullchain.pem
|
||||||
|
when: letsencrypt_enabled and letsencrypt_certbot_method == 'standalone'
|
||||||
|
|
||||||
|
- name: Let's Encrypt Zertifikat anfordern (Webroot)
|
||||||
|
command: >
|
||||||
|
certbot certonly --webroot -w {{ app_public }} -n --agree-tos
|
||||||
|
-m {{ app_email }}
|
||||||
|
-d {{ app_domain }}
|
||||||
|
args:
|
||||||
|
creates: /etc/letsencrypt/live/{{ app_domain }}/fullchain.pem
|
||||||
|
when: letsencrypt_enabled and letsencrypt_certbot_method == 'webroot'
|
||||||
|
|
||||||
|
- name: Kopiere SSL-Zertifikate für Docker
|
||||||
|
copy:
|
||||||
|
src: "/etc/letsencrypt/live/{{ app_domain }}/"
|
||||||
|
dest: "{{ app_root }}/ssl/"
|
||||||
|
remote_src: yes
|
||||||
|
mode: '0644'
|
||||||
|
when: letsencrypt_enabled
|
||||||
6
ansible/roles/wireguard/defaults/main.yml
Normal file
6
ansible/roles/wireguard/defaults/main.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
wireguard_interface: wg0
|
||||||
|
wireguard_port: 51820
|
||||||
|
wireguard_address: 10.8.0.1/24
|
||||||
|
wireguard_server_ip: 94.16.110.151 # oder deine Domain
|
||||||
|
|
||||||
|
wireguard_network: "10.8.0.0/24"
|
||||||
133
ansible/roles/wireguard/tasks/configure.yml
Normal file
133
ansible/roles/wireguard/tasks/configure.yml
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
# --------------------------------------------------------
|
||||||
|
# WireGuard installieren
|
||||||
|
# --------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Stelle sicher, dass WireGuard installiert ist
|
||||||
|
apt:
|
||||||
|
name: wireguard
|
||||||
|
state: present
|
||||||
|
update_cache: yes
|
||||||
|
become: true
|
||||||
|
when: ansible_connection != "local"
|
||||||
|
|
||||||
|
# --------------------------------------------------------
|
||||||
|
# Server-Schlüssel erzeugen und speichern
|
||||||
|
# --------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Prüfe ob privater Server-Schlüssel existiert
|
||||||
|
stat:
|
||||||
|
path: /etc/wireguard/privatekey
|
||||||
|
register: privkey_file
|
||||||
|
become: true
|
||||||
|
when: ansible_connection != "local"
|
||||||
|
|
||||||
|
- name: Erstelle Schlüsselpaar für Server (wenn nicht vorhanden)
|
||||||
|
command: wg genkey
|
||||||
|
register: server_private_key
|
||||||
|
when: ansible_connection != "local" and (not privkey_file.stat.exists | default(true))
|
||||||
|
|
||||||
|
- name: Speichere privaten Schlüssel
|
||||||
|
copy:
|
||||||
|
content: "{{ server_private_key.stdout }}"
|
||||||
|
dest: /etc/wireguard/privatekey
|
||||||
|
mode: "0600"
|
||||||
|
when: server_private_key.stdout is defined and server_private_key.stdout is defined
|
||||||
|
|
||||||
|
- name: Lies privaten Schlüssel ein
|
||||||
|
slurp:
|
||||||
|
src: /etc/wireguard/privatekey
|
||||||
|
become: true
|
||||||
|
when: ansible_connection != "local"
|
||||||
|
|
||||||
|
- name: Erzeuge öffentlichen Server-Schlüssel
|
||||||
|
command: "echo '{{ wg_privkey }}' | wg pubkey"
|
||||||
|
register: wg_pubkey
|
||||||
|
when: ansible_connection != "local"
|
||||||
|
|
||||||
|
- name: Privaten Server-Schlüssel anzeigen
|
||||||
|
debug:
|
||||||
|
msg: "{{ server_private_key }}"
|
||||||
|
when: ansible_connection != "local"
|
||||||
|
|
||||||
|
# --------------------------------------------------------
|
||||||
|
# Client-Key-Erzeugung lokal (einmalig pro Client)
|
||||||
|
# --------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Generiere privaten Schlüssel für Clients (auf dem Server)
|
||||||
|
command: wg genkey
|
||||||
|
args:
|
||||||
|
creates: "/etc/wireguard/client-{{ item.name }}.key"
|
||||||
|
loop: "{{ wireguard_clients }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.name }}"
|
||||||
|
register: client_private_keys
|
||||||
|
when: ansible_connection != "local"
|
||||||
|
|
||||||
|
|
||||||
|
- name: Erzeuge öffentlichen Schlüssel für Clients
|
||||||
|
command: "echo '{{ client_privkey_result.stdout }}' | wg pubkey"
|
||||||
|
register: client_pubkey_result
|
||||||
|
when:
|
||||||
|
- ansible_connection != "local"
|
||||||
|
- client_privkey_result is defined
|
||||||
|
- client_privkey_result.stdout is defined
|
||||||
|
|
||||||
|
- name: wireguard_clients mit public_key anreichern
|
||||||
|
set_fact:
|
||||||
|
wireguard_clients: "{{ wireguard_clients_with_pubkey | default([]) + [ item.0 | combine({'public_key': item.1.stdout|trim }) ] }}"
|
||||||
|
loop: "{{ wireguard_clients | zip(client_public_keys.results) | list }}"
|
||||||
|
when: client_public_keys is defined
|
||||||
|
|
||||||
|
- name: Aktuelles wireguard_clients-Set überschreiben
|
||||||
|
set_fact:
|
||||||
|
wireguard_clients: "{{ wireguard_clients_with_pubkey }}"
|
||||||
|
when: wireguard_clients_with_pubkey is defined
|
||||||
|
|
||||||
|
# --------------------------------------------------------
|
||||||
|
# Konfigurationsdatei erzeugen
|
||||||
|
# --------------------------------------------------------
|
||||||
|
|
||||||
|
#- debug:
|
||||||
|
# var: wireguard_clients
|
||||||
|
|
||||||
|
- name: Render wg0.conf
|
||||||
|
template:
|
||||||
|
src: wg0.conf.j2
|
||||||
|
dest: /etc/wireguard/wg0.conf
|
||||||
|
when: wg_privkey is defined and wg_privkey != ""
|
||||||
|
|
||||||
|
# --------------------------------------------------------
|
||||||
|
# IP Forwarding & WireGuard aktivieren
|
||||||
|
# --------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Aktiviere IP-Forwarding
|
||||||
|
sysctl:
|
||||||
|
name: net.ipv4.ip_forward
|
||||||
|
value: 1
|
||||||
|
state: present
|
||||||
|
sysctl_set: yes
|
||||||
|
reload: yes
|
||||||
|
become: true
|
||||||
|
when: ansible_connection != "local"
|
||||||
|
|
||||||
|
- name: Starte und aktiviere WireGuard
|
||||||
|
systemd:
|
||||||
|
name: wg-quick@wg0
|
||||||
|
enabled: true
|
||||||
|
state: started
|
||||||
|
daemon_reload: yes
|
||||||
|
become: true
|
||||||
|
when: ansible_connection != "local"
|
||||||
|
|
||||||
|
- name: Verteilt für jeden Client die Client-Config
|
||||||
|
template:
|
||||||
|
src: client.conf.j2
|
||||||
|
dest: "/etc/wireguard/clients/{{ item.name }}.conf"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: 0600
|
||||||
|
loop: "{{ wireguard_clients }}"
|
||||||
|
#delegate_to: localhost
|
||||||
|
run_once: true
|
||||||
|
become: true
|
||||||
|
when: ansible_connection != "local"
|
||||||
54
ansible/roles/wireguard/tasks/failsafe.yml
Normal file
54
ansible/roles/wireguard/tasks/failsafe.yml
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
# roles/wireguard/tasks/failsafe.yml
|
||||||
|
# Sicherstellt, dass SSH über VPN funktioniert und ein Fallback vorhanden ist
|
||||||
|
|
||||||
|
- name: Stelle sicher, dass wireguard_network gesetzt ist
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- wireguard_network is defined
|
||||||
|
fail_msg: "wireguard_network muss gesetzt sein (z. B. 10.8.0.0/24)"
|
||||||
|
|
||||||
|
- name: Automatisch externe IP als fallback_ip setzen (nur wenn nicht gesetzt)
|
||||||
|
shell: curl -s ifconfig.me
|
||||||
|
register: detected_fallback_ip
|
||||||
|
when: fallback_ip is not defined
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Setze fallback_ip dynamisch als Ansible-Fact (wenn nicht gesetzt)
|
||||||
|
set_fact:
|
||||||
|
fallback_ip: "{{ detected_fallback_ip.stdout }}"
|
||||||
|
when: fallback_ip is not defined
|
||||||
|
|
||||||
|
- name: (Optional) Erlaube temporär Fallback-SSH von aktueller IP
|
||||||
|
ufw:
|
||||||
|
rule: allow
|
||||||
|
port: 22
|
||||||
|
proto: tcp
|
||||||
|
from_ip: "{{ fallback_ip }}"
|
||||||
|
|
||||||
|
- name: Erlaube SSH-Zugriff über VPN
|
||||||
|
ufw:
|
||||||
|
rule: allow
|
||||||
|
port: 22
|
||||||
|
proto: tcp
|
||||||
|
from_ip: "{{ wireguard_network }}"
|
||||||
|
|
||||||
|
- name: (Warnung) Prüfe ob VPN-Interface aktiv ist
|
||||||
|
shell: ip a show dev wg0
|
||||||
|
register: vpn_interface_check
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Hinweis, wenn VPN-Interface nicht aktiv ist
|
||||||
|
debug:
|
||||||
|
msg: "⚠️ VPN-Interface wg0 scheint nicht aktiv zu sein. SSH über VPN wird nicht funktionieren."
|
||||||
|
when: vpn_interface_check.rc != 0
|
||||||
|
|
||||||
|
- name: (Optional) SSH von überall blockieren – nur wenn VPN aktiv
|
||||||
|
when:
|
||||||
|
- ssh_lockdown | default(false)
|
||||||
|
- vpn_interface_check.rc == 0
|
||||||
|
ufw:
|
||||||
|
rule: deny
|
||||||
|
port: 22
|
||||||
|
proto: tcp
|
||||||
|
from_ip: 0.0.0.0/0
|
||||||
83
ansible/roles/wireguard/tasks/firewall.yml
Normal file
83
ansible/roles/wireguard/tasks/firewall.yml
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
# Beispiel: Passe jeden Task in dieser Datei so an:
|
||||||
|
- name: Aktiviere Firewall-Regeln für WireGuard
|
||||||
|
ufw:
|
||||||
|
rule: allow
|
||||||
|
port: "{{ wireguard_port }}"
|
||||||
|
proto: udp
|
||||||
|
become: true
|
||||||
|
when: ansible_connection != "local"
|
||||||
|
- name: Prüfe, ob UFW installiert ist
|
||||||
|
command: which ufw
|
||||||
|
register: ufw_installed
|
||||||
|
ignore_errors: true
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Installiere UFW (falls nicht vorhanden)
|
||||||
|
apt:
|
||||||
|
name: ufw
|
||||||
|
state: present
|
||||||
|
update_cache: yes
|
||||||
|
when: ufw_installed.rc != 0
|
||||||
|
|
||||||
|
# Setze Standardrichtlinien (erst Konfiguration, dann am Ende aktivieren)
|
||||||
|
- name: Setze Policy für eingehenden Traffic auf "deny"
|
||||||
|
ufw:
|
||||||
|
direction: incoming
|
||||||
|
policy: deny
|
||||||
|
|
||||||
|
- name: Setze Policy für ausgehenden Traffic auf "allow"
|
||||||
|
ufw:
|
||||||
|
direction: outgoing
|
||||||
|
policy: allow
|
||||||
|
|
||||||
|
# WireGuard-Port freigeben (UDP)
|
||||||
|
- name: WireGuard-Port erlauben
|
||||||
|
ufw:
|
||||||
|
rule: allow
|
||||||
|
port: "{{ wireguard_port | default(51820) }}"
|
||||||
|
proto: udp
|
||||||
|
|
||||||
|
# SSH von bestimmter IP erlauben
|
||||||
|
- name: SSH von deiner IP erlauben (empfohlen)
|
||||||
|
ufw:
|
||||||
|
rule: allow
|
||||||
|
port: 22
|
||||||
|
proto: tcp
|
||||||
|
from_ip: "{{ fallback_ip }}"
|
||||||
|
when: fallback_ip is defined and fallback_ip | length > 0
|
||||||
|
|
||||||
|
# Temporär für Tests: SSH für alle erlauben (nur bei Bedarf!)
|
||||||
|
- name: SSH von überall erlauben (fail-safe, NUR während Setup/Test)
|
||||||
|
ufw:
|
||||||
|
rule: allow
|
||||||
|
port: 22
|
||||||
|
proto: tcp
|
||||||
|
when: (not (fallback_ip is defined and fallback_ip | length > 0)) or (enable_ssh_from_anywhere | default(false))
|
||||||
|
|
||||||
|
# Masquerading für WireGuard
|
||||||
|
- name: NAT für WireGuard aktivieren
|
||||||
|
iptables:
|
||||||
|
table: nat
|
||||||
|
chain: POSTROUTING
|
||||||
|
out_interface: "{{ wireguard_exit_interface | default('eth0') }}"
|
||||||
|
source: "{{ wireguard_network }}"
|
||||||
|
jump: MASQUERADE
|
||||||
|
|
||||||
|
- name: WireGuard Kernel-Modul laden
|
||||||
|
modprobe:
|
||||||
|
name: wireguard
|
||||||
|
state: present
|
||||||
|
|
||||||
|
# UFW ganz am Schluss aktivieren
|
||||||
|
- name: UFW aktivieren
|
||||||
|
ufw:
|
||||||
|
state: enabled
|
||||||
|
|
||||||
|
- name: Aktive UFW-Regeln anzeigen (zum Debuggen)
|
||||||
|
command: ufw status verbose
|
||||||
|
register: ufw_status
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Zeige UFW-Regeln im Ansible-Output
|
||||||
|
debug:
|
||||||
|
var: ufw_status.stdout
|
||||||
60
ansible/roles/wireguard/tasks/generate_client_single.yml
Normal file
60
ansible/roles/wireguard/tasks/generate_client_single.yml
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
- name: Key-Verzeichnis für Client anlegen
|
||||||
|
file:
|
||||||
|
path: "{{ role_path }}/client-keys/{{ client.name }}"
|
||||||
|
state: directory
|
||||||
|
mode: "0700"
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Existenz des privaten Schlüssels prüfen
|
||||||
|
stat:
|
||||||
|
path: "{{ role_path }}/client-keys/{{ client.name }}/private.key"
|
||||||
|
register: client_private_key_stat
|
||||||
|
|
||||||
|
- name: Privaten Schlüssel generieren (nur falls nicht vorhanden)
|
||||||
|
command: wg genkey
|
||||||
|
register: genpriv
|
||||||
|
args:
|
||||||
|
chdir: "{{ role_path }}/client-keys/{{ client.name }}"
|
||||||
|
when: not client_private_key_stat.stat.exists
|
||||||
|
|
||||||
|
- name: Privaten Schlüssel speichern (nur falls nicht vorhanden)
|
||||||
|
copy:
|
||||||
|
content: "{{ genpriv.stdout }}"
|
||||||
|
dest: "{{ role_path }}/client-keys/{{ client.name }}/private.key"
|
||||||
|
mode: "0600"
|
||||||
|
when: not client_private_key_stat.stat.exists
|
||||||
|
|
||||||
|
- name: Public Key aus privaten Schlüssel generieren (bei Neuerstellung)
|
||||||
|
command: wg pubkey
|
||||||
|
args:
|
||||||
|
stdin: "{{ genpriv.stdout }}"
|
||||||
|
chdir: "{{ role_path }}/client-keys/{{ client.name }}"
|
||||||
|
register: genpub
|
||||||
|
when: not client_private_key_stat.stat.exists
|
||||||
|
|
||||||
|
- name: Bestehenden privaten Schlüssel laden (falls vorhanden)
|
||||||
|
slurp:
|
||||||
|
src: "{{ role_path }}/client-keys/{{ client.name }}/private.key"
|
||||||
|
register: loaded_private
|
||||||
|
when: client_private_key_stat.stat.exists
|
||||||
|
|
||||||
|
- name: Public Key aus gespeichertem Private Key erzeugen (falls vorhanden)
|
||||||
|
command: wg pubkey
|
||||||
|
args:
|
||||||
|
stdin: "{{ loaded_private.content | b64decode }}"
|
||||||
|
chdir: "{{ role_path }}/client-keys/{{ client.name }}"
|
||||||
|
register: genpub_existing
|
||||||
|
when: client_private_key_stat.stat.exists
|
||||||
|
|
||||||
|
- name: Public Key für Client in Datei schreiben
|
||||||
|
copy:
|
||||||
|
content: >
|
||||||
|
{{ (genpub.stdout if not client_private_key_stat.stat.exists else genpub_existing.stdout) }}
|
||||||
|
dest: "{{ role_path }}/client-keys/{{ client.name }}/public.key"
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: Variablen für Client setzen (private/public key, Adresse)
|
||||||
|
set_fact:
|
||||||
|
"wg_{{ client.name }}_private_key": "{{ (genpriv.stdout if not client_private_key_stat.stat.exists else loaded_private.content | b64decode) }}"
|
||||||
|
"wg_{{ client.name }}_public_key": "{{ (genpub.stdout if not client_private_key_stat.stat.exists else genpub_existing.stdout) }}"
|
||||||
|
"wg_{{ client.name }}_address": "{{ client.address }}"
|
||||||
39
ansible/roles/wireguard/tasks/generate_clients.yml
Normal file
39
ansible/roles/wireguard/tasks/generate_clients.yml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
- name: Schleife über alle WireGuard-Clients
|
||||||
|
include_tasks: generate_client_single.yml
|
||||||
|
loop: "{{ wireguard_clients }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: client
|
||||||
|
|
||||||
|
- name: Generiere privaten Schlüssel für jeden Client
|
||||||
|
shell: "wg genkey"
|
||||||
|
register: wg_client_private_keys
|
||||||
|
loop: "{{ wireguard_clients }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.name }}"
|
||||||
|
# kein delegate_to mehr!
|
||||||
|
run_once: true # ggf. auch entfernen, siehe Anmerkung unten
|
||||||
|
|
||||||
|
- name: Setze globale Client-Key-Facts für alle Clients
|
||||||
|
set_fact:
|
||||||
|
wg_all_clients_private_keys: >-
|
||||||
|
{{
|
||||||
|
wg_all_clients_private_keys | default({}) | combine({
|
||||||
|
item.1.name: item.0.stdout
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
loop: "{{ wg_client_private_keys.results | zip(wireguard_clients) | list }}"
|
||||||
|
delegate_to: localhost
|
||||||
|
run_once: true
|
||||||
|
|
||||||
|
|
||||||
|
- name: Generiere Private Keys für Clients
|
||||||
|
command: "wg genkey"
|
||||||
|
register: client_keys_raw
|
||||||
|
loop: "{{ wireguard_clients }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: client
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Mappe Keys nach Namen
|
||||||
|
set_fact:
|
||||||
|
wg_all_clients_private_keys: "{{ dict(wireguard_clients | map(attribute='name') | list | zip(client_keys_raw.results | map(attribute='stdout') | list)) }}"
|
||||||
7
ansible/roles/wireguard/tasks/install.yml
Normal file
7
ansible/roles/wireguard/tasks/install.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
- name: Stelle sicher, dass WireGuard installiert ist
|
||||||
|
apt:
|
||||||
|
name: wireguard
|
||||||
|
state: present
|
||||||
|
update_cache: yes
|
||||||
|
become: true
|
||||||
|
when: ansible_connection != "local"
|
||||||
22
ansible/roles/wireguard/tasks/main.yml
Normal file
22
ansible/roles/wireguard/tasks/main.yml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#- include_tasks: install.yml
|
||||||
|
#- include_tasks: configure.yml
|
||||||
|
#- include_tasks: generate_clients.yml
|
||||||
|
#- include_tasks: firewall.yml
|
||||||
|
|
||||||
|
|
||||||
|
- name: Installiere WireGuard
|
||||||
|
import_tasks: install.yml
|
||||||
|
when: ansible_connection != "local"
|
||||||
|
|
||||||
|
- name: Konfiguriere WireGuard
|
||||||
|
import_tasks: configure.yml
|
||||||
|
|
||||||
|
- name: Generiert .conf Dateien
|
||||||
|
import_tasks: generate_clients.yml
|
||||||
|
|
||||||
|
- name: Setze Firewall-Regeln
|
||||||
|
import_tasks: firewall.yml
|
||||||
|
when: ansible_connection != "local"
|
||||||
|
|
||||||
|
- name: Wende VPN-Failsafe-Regeln an
|
||||||
|
import_tasks: failsafe.yml
|
||||||
10
ansible/roles/wireguard/templates/client.conf.j2
Normal file
10
ansible/roles/wireguard/templates/client.conf.j2
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[Interface]
|
||||||
|
PrivateKey = {{ wg_all_clients_private_keys[item.name] }}
|
||||||
|
Address = {{ item.address }}/32
|
||||||
|
DNS = 1.1.1.1
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
PublicKey = {{ item.public_key }}
|
||||||
|
Endpoint = {{ wireguard_server_ip }}:{{ wireguard_port }}
|
||||||
|
AllowedIPs = {{ wireguard_network }}, {{ wireguard_server_ip }}/32
|
||||||
|
PersistentKeepalive = 25
|
||||||
12
ansible/roles/wireguard/templates/wg0.conf.j2
Normal file
12
ansible/roles/wireguard/templates/wg0.conf.j2
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[Interface]
|
||||||
|
Address = {{ wireguard_address }}
|
||||||
|
PrivateKey = {{ wg_privkey | b64decode | trim }}
|
||||||
|
ListenPort = {{ wireguard_port }}
|
||||||
|
PostUp = iptables -A FORWARD -i {{ wireguard_interface }} -j ACCEPT; iptables -A FORWARD -o {{ wireguard_interface }} -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
|
||||||
|
PostDown = iptables -D FORWARD -i {{ wireguard_interface }} -j ACCEPT; iptables -D FORWARD -o {{ wireguard_interface }} -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
|
||||||
|
|
||||||
|
{% for client in wireguard_clients %}
|
||||||
|
[Peer]
|
||||||
|
PublicKey = {{ client.public_key }}
|
||||||
|
AllowedIPs = {{ client.address }}/32
|
||||||
|
{% endfor %}
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
- hosts: web
|
|
||||||
become: false
|
|
||||||
roles:
|
|
||||||
- setup
|
|
||||||
7
ansible/wireguard-create-config.yml
Normal file
7
ansible/wireguard-create-config.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# ansible/wireguard-create-config.yml
|
||||||
|
- hosts: vpn
|
||||||
|
gather_facts: false
|
||||||
|
roles:
|
||||||
|
- role: wireguard
|
||||||
|
tasks_from: generate_clients # Zum Beispiel, je nach Task
|
||||||
|
# tasks_from: generate_client_single # Oder für einzelne Clients
|
||||||
9
ansible/wireguard-install-server.yml
Normal file
9
ansible/wireguard-install-server.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# ansible/wireguard-install-server.yml
|
||||||
|
- hosts: vpn
|
||||||
|
become: true
|
||||||
|
gather_facts: true
|
||||||
|
roles:
|
||||||
|
- role: wireguard
|
||||||
|
tasks_from: install # z.B., je nach Namensschema deiner Rolle
|
||||||
|
- role: wireguard
|
||||||
|
tasks_from: configure # Für Config/Firewall usw.
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
FROM nginx:stable-alpine
|
|
||||||
|
|
||||||
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<?php
|
|
||||||
phpinfo();
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name localhost;
|
|
||||||
|
|
||||||
root /var/www/html;
|
|
||||||
index index.php index.html;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
try_files $uri $uri/ =404;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~ \.php$ {
|
|
||||||
include fastcgi_params;
|
|
||||||
fastcgi_pass php:9000;
|
|
||||||
fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
11
bin/check-env
Normal file
11
bin/check-env
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
if [ ! -f .env ]; then
|
||||||
|
echo "❌ .env fehlt!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! grep -q "APP_PORT=" .env; then
|
||||||
|
echo "⚠️ APP_PORT nicht gesetzt"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# TODO In make up oder make deploy einbauen.
|
||||||
4
bin/deploy
Executable file
4
bin/deploy
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh -l
|
||||||
|
# Führt das Ansible-Deploy-Playbook aus
|
||||||
|
|
||||||
|
/home/michael/.local/bin/ansible-playbook -i ansible/inventory.ini ansible/playbooks/deploy.yml
|
||||||
3
bin/down
Executable file
3
bin/down
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Stoppt alle laufenden Container
|
||||||
|
docker compose down
|
||||||
3
bin/logs
Executable file
3
bin/logs
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Zeigt die Live-Logs aller Container
|
||||||
|
docker compose logs -f
|
||||||
3
bin/restart
Executable file
3
bin/restart
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Stoppt und startet alle Container neu
|
||||||
|
docker compose down && docker compose up -d
|
||||||
3
bin/setup
Executable file
3
bin/setup
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Führt das Ansible-Deploy-Playbook aus
|
||||||
|
/home/michael/.local/bin/ansible-playbook -i ansible/inventory.ini ansible/playbooks/setup.yml
|
||||||
3
bin/test
Executable file
3
bin/test
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Platzhalter für Tests – kann später durch phpunit, etc. ersetzt werden
|
||||||
|
docker compose exec php ./vendor/bin/pest # --coverage
|
||||||
3
bin/up
Executable file
3
bin/up
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Startet Docker-Container im Hintergrund
|
||||||
|
docker compose up -d
|
||||||
39
composer.json
Normal file
39
composer.json
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"name": "michaelschiemer/website",
|
||||||
|
"description": "michaelschiemer.de website",
|
||||||
|
"type": "project",
|
||||||
|
"license": "proprietary",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"App\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Michael Schiemer",
|
||||||
|
"email": "kontakt@michaelschiemer.de"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"require-dev": {
|
||||||
|
"pestphp/pest": "^3.8",
|
||||||
|
"friendsofphp/php-cs-fixer": "^3.75"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"allow-plugins": {
|
||||||
|
"pestphp/pest-plugin": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"predis/predis": "^3.0",
|
||||||
|
"ext-dom": "*",
|
||||||
|
"ext-libxml": "*",
|
||||||
|
"ext-curl": "*"
|
||||||
|
},
|
||||||
|
|
||||||
|
"scripts": {
|
||||||
|
"cs": "php-cs-fixer fix --dry-run --diff",
|
||||||
|
"cs-fix": "php-cs-fixer fix --allow-risky=yes || true",
|
||||||
|
"reload": "composer dump-autoload -o"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
config/app.php
Normal file
1
config/app.php
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?php
|
||||||
@@ -1,16 +1,99 @@
|
|||||||
version: "3.9"
|
x-docker-settings: &docker-settings
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
args:
|
||||||
|
- BUILDKIT_INLINE_CACHE=1
|
||||||
|
|
||||||
services:
|
services:
|
||||||
web:
|
web:
|
||||||
build: ./app
|
build:
|
||||||
|
context: ./docker/nginx
|
||||||
|
dockerfile: Dockerfile
|
||||||
ports:
|
ports:
|
||||||
- "${APP_PORT}:80"
|
- "${APP_PORT:-8000}:80"
|
||||||
|
- "127.0.0.1:8080:80"
|
||||||
|
- "${APP_SSL_PORT:-443}:443"
|
||||||
|
environment:
|
||||||
|
- APP_ENV=${APP_ENV:-development}
|
||||||
volumes:
|
volumes:
|
||||||
- ./app/html:/var/www/html
|
- ./:/var/www/html:cached
|
||||||
|
- ./ssl:/etc/nginx/ssl:ro # SSL-Zertifikate mounten
|
||||||
depends_on:
|
depends_on:
|
||||||
- php
|
php:
|
||||||
|
condition: service_started
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- frontend
|
||||||
|
- backend
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
|
||||||
php:
|
php:
|
||||||
image: php:${PHP_VERSION}-fpm
|
container_name: php
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: docker/php/Dockerfile
|
||||||
|
args:
|
||||||
|
- ENV=${APP_ENV:-dev}
|
||||||
|
- COMPOSER_INSTALL_FLAGS=${COMPOSER_INSTALL_FLAGS:---no-scripts --no-autoloader}
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./app/html:/var/www/html
|
# Shared Volume für Composer-Cache über Container-Neustarts hinweg
|
||||||
|
- composer-cache:/root/.composer/cache
|
||||||
|
# Bindet das Projektverzeichnis für Live-Änderungen ein
|
||||||
|
- ./:/var/www/html:cached
|
||||||
|
# Verhindert Überschreiben der Vendor-Verzeichnisse
|
||||||
|
#- /var/www/html/vendor
|
||||||
|
|
||||||
|
#- cache-volume:/var/www/html/cache:rw
|
||||||
|
environment:
|
||||||
|
PHP_IDE_CONFIG: "serverName=docker"
|
||||||
|
APP_ENV: ${APP_ENV:-development}
|
||||||
|
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD", "php", "-v" ]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- backend
|
||||||
|
#- cache
|
||||||
|
# backend:
|
||||||
|
# aliases:
|
||||||
|
# - php
|
||||||
|
# cache:
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:8-alpine
|
||||||
|
volumes:
|
||||||
|
- ./docker/redis/redis.conf:/usr/local/etc/redis/redis.conf
|
||||||
|
- redis_data:/data
|
||||||
|
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "ping"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- cache
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
|
||||||
|
networks:
|
||||||
|
frontend:
|
||||||
|
driver: bridge
|
||||||
|
backend:
|
||||||
|
driver: bridge
|
||||||
|
cache:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
redis_data:
|
||||||
|
composer-cache:
|
||||||
|
#cache-volume:
|
||||||
|
|||||||
33
docker/nginx/Dockerfile
Normal file
33
docker/nginx/Dockerfile
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
FROM nginx:alpine
|
||||||
|
|
||||||
|
# Standard-Konfiguration entfernen
|
||||||
|
RUN rm /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
# Verzeichnisse erstellen mit korrekten Berechtigungen
|
||||||
|
RUN mkdir -p /var/cache/nginx /var/log/nginx /etc/nginx/template && \
|
||||||
|
chmod -R 777 /var/cache/nginx /var/log/nginx
|
||||||
|
|
||||||
|
# Kopiere die Template-Konfiguration
|
||||||
|
COPY ./nginx.conf /etc/nginx/nginx.conf
|
||||||
|
COPY ./default.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
# Kopiere die SSL-Zertifikate
|
||||||
|
COPY ./ssl/ /etc/nginx/ssl/
|
||||||
|
|
||||||
|
# Startup-Skript zum Ersetzen der Variablen
|
||||||
|
COPY ./docker-entrypoint.sh /
|
||||||
|
RUN chmod +x /docker-entrypoint.sh
|
||||||
|
|
||||||
|
#Install Netcat
|
||||||
|
RUN apk add --no-cache netcat-openbsd
|
||||||
|
|
||||||
|
|
||||||
|
# Als user www-data laufen lassen
|
||||||
|
RUN addgroup -g 1000 www && adduser -D -G www -u 1000 www-data \
|
||||||
|
&& chown -R www-data:www /var/cache/nginx /var/log/nginx /etc/nginx
|
||||||
|
USER www-data
|
||||||
|
|
||||||
|
EXPOSE 80 443
|
||||||
|
|
||||||
|
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
228
docker/nginx/default.conf
Normal file
228
docker/nginx/default.conf
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
# FastCGI-Cache-Einstellungen
|
||||||
|
fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=PHPCACHE:100m inactive=60m;
|
||||||
|
fastcgi_cache_key "$scheme$request_method$host$request_uri";
|
||||||
|
fastcgi_cache_use_stale error timeout invalid_header http_500;
|
||||||
|
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
|
||||||
|
|
||||||
|
# Hardcoded Umgebungsmodus basierend auf Template-Ersetzung
|
||||||
|
map $http_host $env_mode {
|
||||||
|
default "${APP_ENV}";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Dynamische Cache-Kontrolle basierend auf Umgebungsvariable
|
||||||
|
map $env_mode $should_skip_cache {
|
||||||
|
default 0; # Standard (Produktion): Cache aktivieren
|
||||||
|
development 1; # Entwicklung: Cache deaktivieren
|
||||||
|
testing 1; # Testing: Cache deaktivieren
|
||||||
|
}
|
||||||
|
|
||||||
|
# Skip-Cache für Sessions und basierend auf Umgebung
|
||||||
|
map $http_cookie$should_skip_cache $skip_cache {
|
||||||
|
"~PHPSESSID" 1; # Sessions nie cachen
|
||||||
|
"1" 1; # Cache überspringen, wenn should_skip_cache = 1
|
||||||
|
default 0; # Ansonsten cachen
|
||||||
|
}
|
||||||
|
|
||||||
|
map $host $block_health {
|
||||||
|
default 1; # Blockiere alles
|
||||||
|
localhost 0; # Erlaube nur Host "localhost"
|
||||||
|
}
|
||||||
|
|
||||||
|
upstream php-upstream {
|
||||||
|
server php:9000; # „php“ ist durch Network-Alias immer erreichbar
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name localhost;
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
# Korrigierte HTTP/2 Syntax
|
||||||
|
listen 443 ssl;
|
||||||
|
http2 on; # Neue Syntax für HTTP/2
|
||||||
|
server_name localhost;
|
||||||
|
|
||||||
|
#ssl_certificate /etc/nginx/ssl/localhost+2.pem;
|
||||||
|
#ssl_certificate_key /etc/nginx/ssl/localhost+2-key.pem;
|
||||||
|
ssl_certificate /etc/nginx/ssl/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
|
||||||
|
|
||||||
|
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_prefer_server_ciphers on;
|
||||||
|
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
|
||||||
|
|
||||||
|
# Verbesserte SSL-Konfiguration
|
||||||
|
ssl_session_timeout 1d;
|
||||||
|
ssl_session_cache shared:SSL:10m;
|
||||||
|
ssl_session_tickets off;
|
||||||
|
|
||||||
|
# OCSP Stapling (auskommentiert, wenn Zertifikate fehlen)
|
||||||
|
# ssl_stapling on;
|
||||||
|
# ssl_stapling_verify on;
|
||||||
|
resolver 1.1.1.1 1.0.0.1 valid=300s;
|
||||||
|
resolver_timeout 5s;
|
||||||
|
|
||||||
|
root /var/www/html/public;
|
||||||
|
index index.php index.html;
|
||||||
|
|
||||||
|
# Debug-Header für die Entwicklung
|
||||||
|
add_header X-Environment $env_mode always;
|
||||||
|
|
||||||
|
# Sicherheits-Header
|
||||||
|
add_header X-Content-Type-Options nosniff;
|
||||||
|
add_header X-Frame-Options DENY always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||||
|
add_header Permissions-Policy "geolocation=(), microphone=()" always;
|
||||||
|
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
|
||||||
|
|
||||||
|
# CSP Header
|
||||||
|
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'" always;
|
||||||
|
|
||||||
|
# Buffer-Größen anpassen
|
||||||
|
client_body_buffer_size 10K;
|
||||||
|
client_header_buffer_size 1k;
|
||||||
|
client_max_body_size 10m;
|
||||||
|
large_client_header_buffers 2 1k;
|
||||||
|
|
||||||
|
# Verbesserte Gzip-Kompression
|
||||||
|
gzip on;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_proxied any;
|
||||||
|
gzip_comp_level 6;
|
||||||
|
gzip_buffers 16 8k;
|
||||||
|
gzip_http_version 1.1;
|
||||||
|
gzip_min_length 256;
|
||||||
|
gzip_types
|
||||||
|
application/atom+xml
|
||||||
|
application/javascript
|
||||||
|
application/json
|
||||||
|
application/ld+json
|
||||||
|
application/manifest+json
|
||||||
|
application/rss+xml
|
||||||
|
application/vnd.geo+json
|
||||||
|
application/vnd.ms-fontobject
|
||||||
|
application/x-font-ttf
|
||||||
|
application/x-web-app-manifest+json
|
||||||
|
application/xhtml+xml
|
||||||
|
application/xml
|
||||||
|
font/opentype
|
||||||
|
image/bmp
|
||||||
|
image/svg+xml
|
||||||
|
image/x-icon
|
||||||
|
text/cache-manifest
|
||||||
|
text/css
|
||||||
|
text/plain
|
||||||
|
text/vcard
|
||||||
|
text/vnd.rim.location.xloc
|
||||||
|
text/vtt
|
||||||
|
text/x-component
|
||||||
|
text/x-cross-domain-policy;
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
access_log /var/log/nginx/access.log combined;
|
||||||
|
error_log /var/log/nginx/error.log error;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.php?$query_string;
|
||||||
|
autoindex off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Caching Header für statische Dateien
|
||||||
|
location ~* \.(jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable, max-age=31536000";
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~* \.(css|js)$ {
|
||||||
|
expires 1w;
|
||||||
|
add_header Cache-Control "public, max-age=604800";
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~* \.(json|xml)$ {
|
||||||
|
expires 1d;
|
||||||
|
add_header Cache-Control "public, max-age=86400";
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ \.php$ {
|
||||||
|
try_files $uri =404;
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_pass php-upstream;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
|
||||||
|
# Wichtig: APP_ENV an PHP weitergeben
|
||||||
|
fastcgi_param APP_ENV $env_mode;
|
||||||
|
|
||||||
|
# Timeout-Einstellungen
|
||||||
|
fastcgi_read_timeout 60s;
|
||||||
|
fastcgi_connect_timeout 60s;
|
||||||
|
fastcgi_send_timeout 60s;
|
||||||
|
|
||||||
|
# Caching-Einstellungen
|
||||||
|
fastcgi_buffer_size 128k;
|
||||||
|
fastcgi_buffers 4 256k;
|
||||||
|
fastcgi_busy_buffers_size 256k;
|
||||||
|
|
||||||
|
# Cache FastCGI-Antworten
|
||||||
|
fastcgi_cache_bypass $skip_cache;
|
||||||
|
fastcgi_no_cache $skip_cache;
|
||||||
|
|
||||||
|
fastcgi_cache PHPCACHE;
|
||||||
|
fastcgi_cache_valid 200 60m;
|
||||||
|
|
||||||
|
# Debug-Header hinzufügen
|
||||||
|
add_header X-Cache-Status $upstream_cache_status;
|
||||||
|
add_header X-Cache-Environment $env_mode;
|
||||||
|
add_header X-Cache-Skip $skip_cache;
|
||||||
|
|
||||||
|
# Für bessere Performance
|
||||||
|
fastcgi_keep_conn on;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Sicherheitseinstellungen
|
||||||
|
location ~ /\.(?!well-known).* {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
|
||||||
|
server_tokens off;
|
||||||
|
limit_req zone=mylimit burst=20 nodelay;
|
||||||
|
|
||||||
|
location ~* /(?:uploads|files)/.*\.php$ {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Healthcheck-Endpunkt
|
||||||
|
location = /ping {
|
||||||
|
access_log off;
|
||||||
|
add_header Content-Type text/plain;
|
||||||
|
return 200 'pong';
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /health {
|
||||||
|
if ($block_health) {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
try_files /health.php =404;
|
||||||
|
|
||||||
|
allow 127.0.0.1; # Lokal erlaubt (Ansible, Docker, Monitoring intern)
|
||||||
|
allow ::1;
|
||||||
|
allow 192.168.0.0/16; # Optional: internes Netz (z.B. für internen Loadbalancer)
|
||||||
|
deny all;
|
||||||
|
error_page 403 =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
error_page 404 /errors/404.html;
|
||||||
|
error_page 403 /errors/403.html;
|
||||||
|
error_page 500 502 503 504 /errors/50x.html;
|
||||||
|
|
||||||
|
location /errors/ {
|
||||||
|
internal; # Verhindert direkten Zugriff
|
||||||
|
}
|
||||||
|
}
|
||||||
20
docker/nginx/docker-entrypoint.sh
Normal file
20
docker/nginx/docker-entrypoint.sh
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
until nc -z -w 2 php 9000; do
|
||||||
|
echo "Warte auf PHP-FPM..."
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
# Optional: eigene Umgebungsvariable mit Default setzen
|
||||||
|
export APP_ENV="${APP_ENV:-production}"
|
||||||
|
|
||||||
|
echo "Starte Nginx mit APP_ENV=$APP_ENV"
|
||||||
|
|
||||||
|
# Ersetze Umgebungsvariablen wie ${APP_ENV} in der Nginx-Config per envsubst
|
||||||
|
envsubst '${APP_ENV}' < /etc/nginx/conf.d/default.conf > /etc/nginx/conf.d/default.conf.tmp
|
||||||
|
mv /etc/nginx/conf.d/default.conf.tmp /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
# Starte Nginx (Foreground)
|
||||||
|
exec nginx -g 'daemon off;'
|
||||||
37
docker/nginx/nginx.conf
Normal file
37
docker/nginx/nginx.conf
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
worker_processes auto;
|
||||||
|
pid /tmp/nginx.pid;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
server_tokens off;
|
||||||
|
|
||||||
|
# Rate-Limiting für besseren DDoS-Schutz
|
||||||
|
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
|
||||||
|
|
||||||
|
# Logging-Einstellungen
|
||||||
|
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||||
|
'$status $body_bytes_sent "$http_referer" '
|
||||||
|
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||||
|
|
||||||
|
access_log /var/log/nginx/access.log main;
|
||||||
|
error_log /var/log/nginx/error.log warn;
|
||||||
|
|
||||||
|
sendfile on;
|
||||||
|
tcp_nopush on;
|
||||||
|
tcp_nodelay on;
|
||||||
|
keepalive_timeout 65;
|
||||||
|
types_hash_max_size 2048;
|
||||||
|
|
||||||
|
# TLS-Einstellungen
|
||||||
|
ssl_session_cache shared:SSL:10m;
|
||||||
|
ssl_session_timeout 10m;
|
||||||
|
|
||||||
|
# Include server configs
|
||||||
|
include /etc/nginx/conf.d/*.conf;
|
||||||
|
}
|
||||||
28
docker/nginx/ssl/localhost+2-key.pem
Normal file
28
docker/nginx/ssl/localhost+2-key.pem
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDAzwS8FGSCDwDg
|
||||||
|
7QX8OpGkX1SbSwbUyzXNjEta319BvAH2OfcFFCj6u/iqfL7gKOM83t8u71VBFsCx
|
||||||
|
ZlxX2Ilyu2+r72sCdGBXcK6riTHrkjTs4uV6YV98eJuYhvAzSijpsRQjwnwQ587c
|
||||||
|
axtCXZhOzee3Tnbtzq4plqmOKR10D+cvrOZxuoKI914blXpGe8ds3vWEixewrex0
|
||||||
|
CYhzPj/zEF3yfCoSXeTmFBUbmmH/JwcCK8uO5t6XR1Dyo3M4GOMrmGtO7U4nuL6e
|
||||||
|
7JsbZfPaEW9wKtDjEwFDJSLy0ALEpiNWvbW4OaZWNkJk0jfKYwyBunNSs62B4307
|
||||||
|
oF8lqVo1AgMBAAECggEAbPlU0ryv5fZ256nvlRTBVmbvGep4zPKh0TA3MwBHBY8u
|
||||||
|
iK1QWVWAp95v+GQTOfzCGphZCl0JEYW7mUiibqAbZ3Za8pGaKMP/48vzXU5ooZ18
|
||||||
|
PlsrmlTItEAyqS2zOznyD8se9+snViK+f0QmHwdpWzjze15kx5nmQ+k8ofXJCNwq
|
||||||
|
q3dJIMI/WNuc0e/mMHYjZBsIwuoUi6YJHCE6RkWhGcnvlyXdKUV73/n8Loy6DUtW
|
||||||
|
VmshXag7+GfbVZIesMCjfnJ0gr9OG+XrFl6AcggzFA1ZHRoQliraVYGB2duQlIpW
|
||||||
|
o1wJMhFSGFPZxvl67hwXHJeo7ghHHfqNYXS1OuhV7QKBgQDBrvyzLtav51LzqOUY
|
||||||
|
2HPvaH86arbARc4Fy6ZJ0TaSlmKQ5GzRG0lG2CR03oZz+OcMV/BU8xUMM7CX0zUq
|
||||||
|
9RAmbE7rvXYOvqTe8pcdHeKKflzsr5p0HNROaeZdpMu8xoK1KLelAo6UCEBUGEny
|
||||||
|
oMtQWapuYvmdlHR2el2ICRGNzwKBgQD+1/iM1LcF9CYvEc8Sly9XuoRsdUCxavQa
|
||||||
|
sssv7eG5kkL8HroNs1pGZU8lNuZaT1V0ekWVOFk+X3+dGgCXg5/e/CluK9K7qOHX
|
||||||
|
3IkyUnZLEH5sDXGMGBzYA9AQTaB1PMTQYku6GNWYab6LFQTvpvvLcIILaFHokq8p
|
||||||
|
D/dGVJH8uwKBgQCBOxDBPe9hTye6DGdQPJyekUrS34EwqWLd2xQJDN8sz8rUgpVY
|
||||||
|
sKwj6PPqRs/PcbQ4ODTTeZ4BljuuEe7XyswL1xiRksjC7dF0MMlDVD1jywyVoFWe
|
||||||
|
Q94ks+RRdzO5sXplBdYC88HOY/MIKWytxzvhUPK21LNYwUU0CFGAAw0DYQKBgQD4
|
||||||
|
mT/qSdscoLXa9tl0fiz9vIJPtvXb3MSxgra5U6n9t9NGVMcUdGBdCZjyaaK+eGOZ
|
||||||
|
U2mrjiNouAop++KV6x26jWvxACj7TVy6kXT4tP6WbUmWKGsaya7hfp6qOL+NfjFU
|
||||||
|
Qn8y0+URYB4zWNbO3asFIwSJEkPMx8K9IMkMP5WF3wKBgCYiqAhPDF4WxA3fAqP7
|
||||||
|
95px8Clrety0mwOtE/rMQRf1nKJ78oA4pr+/VXRbyghAxtD4psbmBQofX3iwnn3B
|
||||||
|
o1DV3FLpNw004mvcKGScUcNwHQtWAtWX2nVDcxes5R2DgN+lpmWmf5Tq47p0r5ZP
|
||||||
|
nRb92drrnf8FoBv78CxLjIu+
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
25
docker/nginx/ssl/localhost+2.pem
Normal file
25
docker/nginx/ssl/localhost+2.pem
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEHjCCAoagAwIBAgIQLqhFNHvvWJKUpuypArU2CjANBgkqhkiG9w0BAQsFADBb
|
||||||
|
MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExGDAWBgNVBAsMD21pY2hh
|
||||||
|
ZWxATWlrZS1QQzEfMB0GA1UEAwwWbWtjZXJ0IG1pY2hhZWxATWlrZS1QQzAeFw0y
|
||||||
|
NTA1MTgxOTUyMDlaFw0yNzA4MTgxOTUyMDlaMEMxJzAlBgNVBAoTHm1rY2VydCBk
|
||||||
|
ZXZlbG9wbWVudCBjZXJ0aWZpY2F0ZTEYMBYGA1UECwwPbWljaGFlbEBNaWtlLVBD
|
||||||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwM8EvBRkgg8A4O0F/DqR
|
||||||
|
pF9Um0sG1Ms1zYxLWt9fQbwB9jn3BRQo+rv4qny+4CjjPN7fLu9VQRbAsWZcV9iJ
|
||||||
|
crtvq+9rAnRgV3Cuq4kx65I07OLlemFffHibmIbwM0oo6bEUI8J8EOfO3GsbQl2Y
|
||||||
|
Ts3nt0527c6uKZapjikddA/nL6zmcbqCiPdeG5V6RnvHbN71hIsXsK3sdAmIcz4/
|
||||||
|
8xBd8nwqEl3k5hQVG5ph/ycHAivLjubel0dQ8qNzOBjjK5hrTu1OJ7i+nuybG2Xz
|
||||||
|
2hFvcCrQ4xMBQyUi8tACxKYjVr21uDmmVjZCZNI3ymMMgbpzUrOtgeN9O6BfJala
|
||||||
|
NQIDAQABo3YwdDAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEw
|
||||||
|
HwYDVR0jBBgwFoAUhhzxUvThIGRX4MSoX91Vzm1zZ9AwLAYDVR0RBCUwI4IJbG9j
|
||||||
|
YWxob3N0hwR/AAABhxAAAAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4IB
|
||||||
|
gQDUFLYZPo8RrfZh/vwT15LcIce8brdVegms6DvPK9lMZX6C4sGf4+rTJCwPuqHW
|
||||||
|
dqVZAhHdvcsyGI15xvVPT4qSh89RN1JB9uIHCk+weIzp+Rn06MMrB49m4abAvWp2
|
||||||
|
hB8bCo80hMVIsCb3Wr9sHg7CsJItsdGz8jHYCvHpvPLR7gWhYjm1g0meglT3tZqd
|
||||||
|
TsKDMb3Vj/vsivEueM6Oj/of8xbamVSSkqljWbRls7Ti7xqXMbmf7nl0WvG9IXg3
|
||||||
|
5Ucv1AWJIFEeLnMM5V0nEbO3sAhbNMLXieGPBWHXOgHuvVnQyu1mBESjgc5bjwfN
|
||||||
|
UjYBHluFkF9aYw3mGcFqAlb1FpGoMtHwTw0uGZzHzj5FY8oZix5edq/upriV6cU2
|
||||||
|
t0tidlfhvkJNSSO4zjAPjU1wd+/QRZwY2PcB5kBxs5MzSmiMlEjTkGgHWqMWMBf1
|
||||||
|
NPbyaxtjL69xBVonxpqD6BLJ2qLatgCs6fkZZF7AT38OFXr8Cv5vxt1rR5fs1P6X
|
||||||
|
mI0=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
69
docker/php/Dockerfile
Normal file
69
docker/php/Dockerfile
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# Dockerfile für PHP-FPM
|
||||||
|
FROM php:8.4-fpm AS base
|
||||||
|
|
||||||
|
# System-Abhängigkeiten: Werden selten geändert, daher ein eigener Layer
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
git \
|
||||||
|
unzip \
|
||||||
|
libzip-dev \
|
||||||
|
zip \
|
||||||
|
&& docker-php-ext-install zip pdo pdo_mysql \
|
||||||
|
&& docker-php-ext-install opcache \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Composer installieren
|
||||||
|
RUN curl -sS https://getcomposer.org/installer | php \
|
||||||
|
&& mv composer.phar /usr/local/bin/composer
|
||||||
|
|
||||||
|
# Installiere Xdebug nur im Entwicklungsmodus
|
||||||
|
ARG ENV=prod
|
||||||
|
RUN if [ "$ENV" = "dev" ]; then \
|
||||||
|
pecl install xdebug \
|
||||||
|
&& docker-php-ext-enable xdebug; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
WORKDIR /var/www/html
|
||||||
|
|
||||||
|
# Kopiere zuerst nur composer.json/lock für besseres Layer-Caching
|
||||||
|
COPY composer.json composer.lock ./
|
||||||
|
|
||||||
|
# Installiere Abhängigkeiten - variiert je nach Umgebung
|
||||||
|
RUN if [ "$ENV" = "prod" ]; then \
|
||||||
|
composer install --no-dev --no-scripts --no-autoloader --optimize-autoloader; \
|
||||||
|
else \
|
||||||
|
composer install --no-scripts --no-autoloader; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Kopiere PHP-Konfigurationen
|
||||||
|
COPY docker/php/php.common.ini /usr/local/etc/php/php.common.ini
|
||||||
|
COPY docker/php/php.${ENV}.ini /usr/local/etc/php/php.ini
|
||||||
|
|
||||||
|
# Wenn dev, kopiere auch xdebug-Konfiguration
|
||||||
|
RUN if [ "$ENV" = "dev" ]; then \
|
||||||
|
mkdir -p /usr/local/etc/php/conf.d/; \
|
||||||
|
fi
|
||||||
|
COPY docker/php/xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
|
||||||
|
|
||||||
|
# Kopiere den Rest des Projekts
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Optimiere Autoloader
|
||||||
|
RUN composer dump-autoload --optimize
|
||||||
|
|
||||||
|
# <<--- ALLE zusätzlichen System-Dateien und chmod noch als root!
|
||||||
|
COPY docker/php/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
||||||
|
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||||
|
|
||||||
|
# Danach erst den Nutzer wechseln!
|
||||||
|
RUN groupadd -g 1000 appuser && useradd -u 1000 -g appuser -m appuser
|
||||||
|
RUN chown -R appuser:appuser /var/www/html
|
||||||
|
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
RUN mkdir -p /var/www/html/cache && \
|
||||||
|
chown -R 1000:1000 /var/www/html/cache && \
|
||||||
|
chmod -R 775 /var/www/html/cache
|
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
|
||||||
|
CMD ["php-fpm"]
|
||||||
4
docker/php/docker-entrypoint.sh
Normal file
4
docker/php/docker-entrypoint.sh
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
chown -R www-data:www-data /var/www/html/cache
|
||||||
|
chmod -R 775 /var/www/html/cache
|
||||||
|
exec "$@"
|
||||||
8
docker/php/php.common.ini
Normal file
8
docker/php/php.common.ini
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
expose_php = Off
|
||||||
|
|
||||||
|
session.cookie_secure = 1
|
||||||
|
session.cookie_httponly = 1
|
||||||
|
session.cookie_samesite = Lax
|
||||||
|
|
||||||
|
|
||||||
|
date.timezone = Europe/Berlin
|
||||||
29
docker/php/php.development.ini
Normal file
29
docker/php/php.development.ini
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
; php.ini für Entwicklung
|
||||||
|
include = php.common.ini
|
||||||
|
|
||||||
|
|
||||||
|
[opcache]
|
||||||
|
opcache.enable=1
|
||||||
|
opcache.enable_cli=0
|
||||||
|
opcache.memory_consumption=128
|
||||||
|
opcache.max_accelerated_files=10000
|
||||||
|
; Häufigere Validierung im Dev-Modus
|
||||||
|
opcache.revalidate_freq=0
|
||||||
|
; Timestamps-Validierung einschalten für Entwicklung
|
||||||
|
opcache.validate_timestamps=1
|
||||||
|
|
||||||
|
opcache.file_cache=
|
||||||
|
realpath_cache_ttl=0
|
||||||
|
|
||||||
|
opcache.interned_strings_buffer=16
|
||||||
|
|
||||||
|
|
||||||
|
display_errors = On
|
||||||
|
display_startup_errors = On
|
||||||
|
error_reporting = E_ALL
|
||||||
|
memory_limit = 512M
|
||||||
|
upload_max_filesize = 20M
|
||||||
|
post_max_size = 25M
|
||||||
|
max_execution_time = 60
|
||||||
|
|
||||||
|
; Xdebug-Einstellungen können auch hier hinzugefügt werden, falls gewünscht
|
||||||
31
docker/php/php.prod.ini
Normal file
31
docker/php/php.prod.ini
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
; php.ini für Produktion
|
||||||
|
include = php.common.ini
|
||||||
|
|
||||||
|
[opcache]
|
||||||
|
; Aktiviere OPcache
|
||||||
|
opcache.enable=1
|
||||||
|
; Aktiviere OPcache für CLI-Anwendungen (optional)
|
||||||
|
opcache.enable_cli=0
|
||||||
|
; Maximale Speichernutzung für Cache in MB
|
||||||
|
opcache.memory_consumption=128
|
||||||
|
; Maximale Anzahl an gecachten Dateien
|
||||||
|
opcache.max_accelerated_files=10000
|
||||||
|
; Wie oft wird der Cache validiert (0 = bei jedem Request, empfohlen für Entwicklung)
|
||||||
|
; In Produktion höher setzen für bessere Performance
|
||||||
|
opcache.revalidate_freq=60
|
||||||
|
; Cache-Zeitstempel prüfen (0 für Produktionsumgebungen)
|
||||||
|
opcache.validate_timestamps=0
|
||||||
|
; Performance-Optimierungen
|
||||||
|
opcache.interned_strings_buffer=16
|
||||||
|
; JIT (Just-In-Time Compilation) - Optional für PHP 8.0+
|
||||||
|
opcache.jit_buffer_size=100M
|
||||||
|
opcache.jit=1255
|
||||||
|
|
||||||
|
|
||||||
|
display_errors = Off
|
||||||
|
display_startup_errors = Off
|
||||||
|
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
|
||||||
|
memory_limit = 256M
|
||||||
|
upload_max_filesize = 10M
|
||||||
|
post_max_size = 12M
|
||||||
|
max_execution_time = 30
|
||||||
7
docker/php/xdebug.ini
Normal file
7
docker/php/xdebug.ini
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
; Xdebug 3 Konfiguration
|
||||||
|
xdebug.mode=${XDEBUG_MODE:-off}
|
||||||
|
xdebug.client_host=host.docker.internal
|
||||||
|
xdebug.client_port=9003
|
||||||
|
xdebug.start_with_request=yes
|
||||||
|
xdebug.log=/var/log/xdebug.log
|
||||||
|
xdebug.idekey=PHPSTORM
|
||||||
7
docker/redis/redis.conf
Normal file
7
docker/redis/redis.conf
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
bind 0.0.0.0
|
||||||
|
#protected-mode yes
|
||||||
|
dir /data
|
||||||
|
save 900 1
|
||||||
|
save 300 10
|
||||||
|
save 60 10000
|
||||||
|
appendonly yes
|
||||||
25
docs/ARCHITECURE.md
Normal file
25
docs/ARCHITECURE.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Architektur-Prinzipien
|
||||||
|
|
||||||
|
Dieses Dokument beschreibt die grundlegenden Architekturprinzipien unseres Frameworks.
|
||||||
|
|
||||||
|
## 1. Immutabilität und Unveränderlichkeit
|
||||||
|
|
||||||
|
Wo immer möglich, sollten Objekte unveränderlich (immutable) sein. Dies verbessert die Voraussagbarkeit und Testbarkeit.
|
||||||
|
|
||||||
|
## 2. Final by Default
|
||||||
|
|
||||||
|
Alle Klassen sollten standardmäßig als `final` deklariert werden, es sei denn, es gibt einen konkreten Grund für Vererbung.
|
||||||
|
Begründung:
|
||||||
|
- Vermeidet unbeabsichtigte Vererbungshierarchien
|
||||||
|
- Verbessert die Kapselung
|
||||||
|
- Ermöglicht interne Änderungen, ohne Kinderklassen zu beeinflussen
|
||||||
|
|
||||||
|
## 3. Explizite über Implizite
|
||||||
|
|
||||||
|
- Alle Abhängigkeiten sollten explizit injiziert werden
|
||||||
|
- Keine globalen Zustände oder Singletons
|
||||||
|
- Typen immer explizit deklarieren
|
||||||
|
|
||||||
|
## 4. Modularität
|
||||||
|
|
||||||
|
Jedes Modul sollte in sich geschlossen sein und minimale Abhängigkeiten nach außen haben.
|
||||||
@@ -8,7 +8,7 @@ Dieses Projekt verwendet `.env`-Dateien zur Konfiguration von Docker Compose und
|
|||||||
|
|
||||||
```env
|
```env
|
||||||
COMPOSE_PROJECT_NAME=michaelschiemer
|
COMPOSE_PROJECT_NAME=michaelschiemer
|
||||||
APP_PORT=8080
|
APP_PORT=8000
|
||||||
PHP_VERSION=8.2
|
PHP_VERSION=8.2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
42
docs/features.md
Normal file
42
docs/features.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Framework Features
|
||||||
|
|
||||||
|
## Core Features
|
||||||
|
- [ ] Routing mit Unterstützung für PHP-Attribute
|
||||||
|
- [ ] Dependency Injection Container
|
||||||
|
- [ ] Request/Response Abstraktion
|
||||||
|
- [ ] Template-Engine
|
||||||
|
- [ ] Error/Exception Handling
|
||||||
|
- [ ] Konfigurationssystem
|
||||||
|
|
||||||
|
## Database Features
|
||||||
|
- [ ] PDO-Wrapper
|
||||||
|
- [ ] Query Builder
|
||||||
|
- [ ] Migrations-System
|
||||||
|
- [ ] Schema Manager
|
||||||
|
- [ ] Entity-Mapping (optional)
|
||||||
|
|
||||||
|
## Security Features
|
||||||
|
- [ ] CSRF-Schutz
|
||||||
|
- [ ] XSS-Filtierung
|
||||||
|
- [ ] Input-Validierung
|
||||||
|
- [ ] Authentifizierung
|
||||||
|
- [ ] Autorisierung/Rechtemanagement
|
||||||
|
|
||||||
|
## Module: Music
|
||||||
|
- [ ] Album-Verwaltung
|
||||||
|
- [ ] Track-Management
|
||||||
|
- [ ] Playlists
|
||||||
|
- [ ] Integrationsmöglichkeit mit Spotify/SoundCloud
|
||||||
|
|
||||||
|
## Module: Content
|
||||||
|
- [ ] Blog-System
|
||||||
|
- [ ] Markdown-Support
|
||||||
|
- [ ] Medienbibliothek
|
||||||
|
- [ ] SEO-Optimierung
|
||||||
|
- [ ] Kommentarsystem
|
||||||
|
|
||||||
|
## Admin Interface
|
||||||
|
- [ ] Dashboard
|
||||||
|
- [ ] Content-Editor
|
||||||
|
- [ ] Benutzer-/Rechteverwaltung
|
||||||
|
- [ ] Statistiken
|
||||||
13
package.json
Normal file
13
package.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/jest": "^29.2.5",
|
||||||
|
"jest": "^29.3.1",
|
||||||
|
"vite": "^6.3.5"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "jest",
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
}
|
||||||
|
}
|
||||||
18
phpunit.xml
Normal file
18
phpunit.xml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||||
|
bootstrap="vendor/autoload.php"
|
||||||
|
colors="true"
|
||||||
|
>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Test Suite">
|
||||||
|
<directory suffix="Test.php">./tests</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
<source>
|
||||||
|
<include>
|
||||||
|
<directory>app</directory>
|
||||||
|
<directory>src</directory>
|
||||||
|
</include>
|
||||||
|
</source>
|
||||||
|
</phpunit>
|
||||||
1
public/assets/css-CKd28aW2.js
Normal file
1
public/assets/css-CKd28aW2.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
1
public/assets/css-CLfz37Tz.css
Normal file
1
public/assets/css-CLfz37Tz.css
Normal file
@@ -0,0 +1 @@
|
|||||||
|
p{color:red}html{background:#00f}
|
||||||
12
public/assets/css/styles.css
Normal file
12
public/assets/css/styles.css
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
* {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
footer > nav {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer > nav > li {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
1
public/assets/js-DjO_n7Y6.js
Normal file
1
public/assets/js-DjO_n7Y6.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import"./css-CKd28aW2.js";
|
||||||
0
public/favico.ico
Normal file
0
public/favico.ico
Normal file
7
public/health.php
Normal file
7
public/health.php
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
$status = ['status' => 'ok'];
|
||||||
|
|
||||||
|
echo json_encode($status);
|
||||||
84
public/index.php
Normal file
84
public/index.php
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use App\Framework\Core\Discovery;
|
||||||
|
use App\Framework\Core\DynamicRoute;
|
||||||
|
use App\Framework\Core\PhpObjectExporter;
|
||||||
|
use App\Framework\Core\RouteCache;
|
||||||
|
use App\Framework\Core\RouteMapper;
|
||||||
|
use App\Framework\Core\StaticRoute;
|
||||||
|
use App\Framework\ErrorHandling\ErrorHandler;
|
||||||
|
use App\Framework\Http\HttpMethod;
|
||||||
|
use App\Framework\Router\RouteCollection;
|
||||||
|
|
||||||
|
require __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
|
// Fehleranzeige für die Entwicklung aktivieren
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
|
||||||
|
$rfl = new ReflectionClass(Discovery::class);;
|
||||||
|
$ghost = $rfl->newLazyGhost(function (Discovery $object) {
|
||||||
|
// Initialize object in-place
|
||||||
|
$object->__construct();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/*$clientrequest = new \App\Framework\HttpClient\ClientRequest(HttpMethod::GET, 'https://jsonplaceholder.typicode.com/posts');
|
||||||
|
|
||||||
|
$client = new \App\Framework\HttpClient\CurlHttpClient();
|
||||||
|
|
||||||
|
var_dump($client->send($clientrequest));*/
|
||||||
|
|
||||||
|
$emitter = new \App\Framework\Http\ResponseEmitter();
|
||||||
|
ErrorHandler::register($emitter);
|
||||||
|
|
||||||
|
#echo dirname(__DIR__) . '/cache/routes.cache.php';
|
||||||
|
|
||||||
|
$discovery = new Discovery(new \App\Framework\Core\RouteMapper());
|
||||||
|
|
||||||
|
$results = $discovery->discover(__DIR__ . '/../src/Application/');
|
||||||
|
|
||||||
|
|
||||||
|
$rc = new \App\Framework\Core\RouteCompiler();
|
||||||
|
|
||||||
|
$routes = $rc->compile($results[\App\Framework\Attributes\Route::class]);
|
||||||
|
|
||||||
|
$cacheFile = dirname(__DIR__) . '/cache/routes.cache.php';
|
||||||
|
|
||||||
|
$routeCache = new \App\Framework\Core\RouteCache($cacheFile);
|
||||||
|
|
||||||
|
$routeCache->save($routes);
|
||||||
|
|
||||||
|
$request = new \App\Framework\Http\HttpRequest(
|
||||||
|
method: \App\Framework\Http\HttpMethod::tryFrom($_SERVER['REQUEST_METHOD'] ?? 'GET'),
|
||||||
|
path:parse_url( $_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH),
|
||||||
|
);
|
||||||
|
|
||||||
|
#var_dump("<pre>", $routeCache->load());
|
||||||
|
|
||||||
|
$router = new \App\Framework\Router\HttpRouter(new RouteCollection($routeCache->load()));
|
||||||
|
|
||||||
|
$match = $router->match($request->method->value, $request->path);
|
||||||
|
|
||||||
|
|
||||||
|
$dispatcher = new \App\Framework\Router\RouteDispatcher();
|
||||||
|
$return = $dispatcher->dispatch($match);
|
||||||
|
|
||||||
|
$responder = new \App\Framework\Router\RouteResponder();
|
||||||
|
$response = $responder->respond($return);
|
||||||
|
|
||||||
|
$emitter = new \App\Framework\Http\ResponseEmitter();
|
||||||
|
$emitter->emit($response);
|
||||||
|
|
||||||
|
/*$redis = new Predis\Client([
|
||||||
|
'scheme' => 'tcp',
|
||||||
|
'host' => 'redis', // Service-Name aus docker-compose
|
||||||
|
'port' => 6379,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$redis->set('hello', 'world');
|
||||||
|
echo $redis->get('hello'); // Gibt: world aus*/
|
||||||
|
|
||||||
|
exit;
|
||||||
7
resources/css/styles.css
Normal file
7
resources/css/styles.css
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
p {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
background: blue;
|
||||||
|
}
|
||||||
1
resources/js/main.js
Normal file
1
resources/js/main.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import '../css/styles.css';
|
||||||
21
src/Application/Api/IrkEndpoint.php
Normal file
21
src/Application/Api/IrkEndpoint.php
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Application\Api;
|
||||||
|
|
||||||
|
use App\Framework\Attributes\Route;
|
||||||
|
use App\Framework\Router\ActionResult;
|
||||||
|
|
||||||
|
class IrkEndpoint
|
||||||
|
{
|
||||||
|
#[Route(method: 'GET', path: '/irk-impressum')]
|
||||||
|
public function impressum()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route(method: 'GET', path: '/irk-datenschutz')]
|
||||||
|
public function datenschutz()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/Application/EPK/ShowEpk.php
Normal file
16
src/Application/EPK/ShowEpk.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Application\EPK;
|
||||||
|
|
||||||
|
use App\Framework\Attributes\Route;
|
||||||
|
use App\Framework\Router\ActionResult;
|
||||||
|
use App\Framework\Router\ResultType;
|
||||||
|
|
||||||
|
class ShowEpk
|
||||||
|
{
|
||||||
|
#[Route(path: '/epk')]
|
||||||
|
public function epk(): ActionResult
|
||||||
|
{
|
||||||
|
return new ActionResult(ResultType::Html, 'epk', ['text' => 'EPK!']);
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/Application/Website/ShowHome.php
Normal file
33
src/Application/Website/ShowHome.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Application\Website;
|
||||||
|
|
||||||
|
use App\Framework\Attributes\Route;
|
||||||
|
use App\Framework\Http\Request;
|
||||||
|
use App\Framework\Router\ActionResult;
|
||||||
|
use App\Framework\Router\ResultType;
|
||||||
|
|
||||||
|
class ShowHome
|
||||||
|
{
|
||||||
|
#[Route(method: 'GET', path: '/')]
|
||||||
|
public function __invoke(): ActionResult
|
||||||
|
{
|
||||||
|
return new ActionResult(
|
||||||
|
ResultType::Html,
|
||||||
|
'test',
|
||||||
|
['name' => 'Michael','title' => 'HalloWeltTitel'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route(method: 'GET', path: '/epk')]
|
||||||
|
public function impressum(string $test = 'hallo'): ActionResult
|
||||||
|
{
|
||||||
|
return new ActionResult(
|
||||||
|
ResultType::Plain,
|
||||||
|
'test',
|
||||||
|
['text' => 'EPK!'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/Application/Website/ShowImpressum.php
Normal file
32
src/Application/Website/ShowImpressum.php
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Application\Website;
|
||||||
|
|
||||||
|
use App\Framework\Attributes\Route;
|
||||||
|
use App\Framework\Router\ActionResult;
|
||||||
|
use App\Framework\Router\ResultType;
|
||||||
|
|
||||||
|
class ShowImpressum
|
||||||
|
{
|
||||||
|
#[Route(method: 'GET', path: '/impressum')]
|
||||||
|
public function impressum(string $test = 'hallo'): ActionResult
|
||||||
|
{
|
||||||
|
return new ActionResult(
|
||||||
|
ResultType::Html,
|
||||||
|
'impressum',
|
||||||
|
['text' => 'Hallo Welt!'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route(method: 'GET', path: '/datenschutz')]
|
||||||
|
public function datenschutz(string $test = 'hallo'): ActionResult
|
||||||
|
{
|
||||||
|
return new ActionResult(
|
||||||
|
ResultType::Html,
|
||||||
|
'impressum',
|
||||||
|
['title' => 'Datenschutz!'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/Framework/Attributes/Route.php
Normal file
18
src/Framework/Attributes/Route.php
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Framework\Attributes;
|
||||||
|
|
||||||
|
use Attribute;
|
||||||
|
|
||||||
|
#[Attribute(\Attribute::TARGET_METHOD, \Attribute::IS_REPEATABLE)]
|
||||||
|
|
||||||
|
class Route
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public string $path,
|
||||||
|
public string $method = 'GET',
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/Framework/Attributes/Singleton.php
Normal file
9
src/Framework/Attributes/Singleton.php
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Framework\Attributes;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||||
|
final class Singleton
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
17
src/Framework/Core/AttributeMapper.php
Normal file
17
src/Framework/Core/AttributeMapper.php
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Framework\Core;
|
||||||
|
|
||||||
|
interface AttributeMapper
|
||||||
|
{
|
||||||
|
public function getAttributeClass(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param object $reflectionTarget ReflectionClass|ReflectionMethod
|
||||||
|
* @param object $attributeInstance
|
||||||
|
* @return array|null
|
||||||
|
*/
|
||||||
|
public function map(object $reflectionTarget, object $attributeInstance): ?array;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user