diff --git a/group_vars/all/09_networks.yml b/group_vars/all/09_networks.yml index f6f06dbd..9fd88a0c 100644 --- a/group_vars/all/09_networks.yml +++ b/group_vars/all/09_networks.yml @@ -98,6 +98,8 @@ defaults_networks: subnet: 192.168.103.176/28 web-app-chess: subnet: 192.168.103.192/28 + web-app-magento: + subnet: 192.168.103.208/28 # /24 Networks / 254 Usable Clients web-app-bigbluebutton: diff --git a/group_vars/all/10_ports.yml b/group_vars/all/10_ports.yml index 9d4d2173..fdf3672d 100644 --- a/group_vars/all/10_ports.yml +++ b/group_vars/all/10_ports.yml @@ -73,6 +73,7 @@ ports: web-app-bookwyrm: 8049 web-app-chess: 8050 web-app-bluesky_view: 8051 + web-app-magento: 8052 web-app-bigbluebutton: 48087 # This port is predefined by bbb. @todo Try to change this to a 8XXX port public: # The following ports should be changed to 22 on the subdomain via stream mapping diff --git a/roles/web-app-magento/Administration.md b/roles/web-app-magento/Administration.md new file mode 100644 index 00000000..13fcceec --- /dev/null +++ b/roles/web-app-magento/Administration.md @@ -0,0 +1,55 @@ +# Administration + +## 🗑️ Cleanup (Remove Instance & Volumes) +```bash +cd {{ PATH_DOCKER_COMPOSE_INSTANCES }}magento/ +docker compose down +docker volume rm magento_data +cd {{ PATH_DOCKER_COMPOSE_INSTANCES }} && rm -vR {{ PATH_DOCKER_COMPOSE_INSTANCES }}magento +``` + +## 🔍 Access Container Shell +```bash +docker compose exec -it application /bin/bash +``` + +## 🧰 Common Magento CLI Tasks +```bash +# Reindex +docker compose exec -it application bin/magento indexer:reindex + +# Flush caches +docker compose exec -it application bin/magento cache:flush + +# Enable maintenance mode +docker compose exec -it application bin/magento maintenance:enable + +# Disable maintenance mode +docker compose exec -it application bin/magento maintenance:disable + +# Recompile DI (when switching modes) +docker compose exec -it application bin/magento setup:di:compile + +# Deploy static content (example for English/German) +docker compose exec -it application bin/magento setup:static-content:deploy en_US de_DE -f +``` + +## 🚀 Performance +```bash +# Production mode +docker compose exec -it application bin/magento deploy:mode:set production + +# Developer mode +docker compose exec -it application bin/magento deploy:mode:set developer +``` + +## 🔐 Admin User +```bash +# Create another admin (example) +docker compose exec -it application bin/magento admin:user:create \ + --admin-user="admin2" \ + --admin-password="ChangeMe_12345" \ + --admin-email="{{ users.administrator.email }}" \ + --admin-firstname="Admin" \ + --admin-lastname="User" +``` diff --git a/roles/web-app-magento/Installation.md b/roles/web-app-magento/Installation.md new file mode 100644 index 00000000..dd1e582e --- /dev/null +++ b/roles/web-app-magento/Installation.md @@ -0,0 +1,30 @@ +# ⚙️ Installation & First Run + +## 1) Prepare DNS & Ports +Ensure a canonical domain is mapped (e.g. `shop.{{ PRIMARY_DOMAIN }}`) and a free localhost port in `group_vars/all/10_ports.yml`: +``` +web-app-magento: 80xx +``` + +## 2) Seed Credentials +Provide (at minimum) an admin password in your inventory (vault recommended): +```yaml +applications: + web-app-magento: + credentials: + admin_password: "use-a-strong-secret" +``` +The admin username/email are taken from `users.administrator.*`. + +## 3) Deploy +Run the Infinito.Nexus playbook for your host(s). The role will: +- Start OpenSearch (single node) +- Start MariaDB (if `central_database` is disabled, the app-local DB is used instead) +- Start Magento application container +- Wire environment via `templates/env.j2` + +## 4) Verify +Open your domain (e.g. `https://shop.{{ PRIMARY_DOMAIN }}`) and complete any remaining onboarding steps in the admin panel. + +**Admin Panel:** `{{ domains | get_url('web-app-magento', WEB_PROTOCOL) }}/admin` +(Default path can vary; set a custom `ADMINURI` later via `bin/magento setup:config:set` if desired.) diff --git a/roles/web-app-magento/README.md b/roles/web-app-magento/README.md new file mode 100644 index 00000000..fa09f7aa --- /dev/null +++ b/roles/web-app-magento/README.md @@ -0,0 +1,43 @@ +# Magento + +## Description + +**Magento (Adobe Commerce Open Source)** is a powerful, extensible e-commerce platform built with PHP. It supports multi-store setups, advanced catalog management, promotions, checkout flows, and a rich extension ecosystem. + +## Overview + +This role deploys **Magento 2** via Docker Compose. It is aligned with the Infinito.Nexus stack patterns: +- Reverse-proxy integration (front proxy handled by platform roles) +- Optional **central database** (MariaDB) or app-local DB +- **OpenSearch** for catalog search (required by Magento 2.4+) +- Optional **Redis** cache/session (can be toggled) +- Health checks, volumes, and environment templating +- SMTP wired via platform's `SYSTEM_EMAIL` settings + +For setup & operations, see: +- [Installation.md](./Installation.md) +- [Administration.md](./Administration.md) +- [Upgrade.md](./Upgrade.md) +- [User_Administration.md](./User_Administration.md) + +## Features + +- **Modern search:** OpenSearch out of the box (single-node). +- **Flexible DB:** Use platform's central MariaDB or app-local DB. +- **Optional Redis:** Toggle cache/session backend. +- **Proxy-aware:** Exposes HTTP on localhost, picked up by front proxy role. +- **Automation-friendly:** Admin user seeded from inventory variables. + +## Further Resources + +- Magento Open Source: https://magento.com/ +- DevDocs: https://developer.adobe.com/commerce/ +- OpenSearch: https://opensearch.org/ + +## License / Credits + +Developed and maintained by **Kevin Veen-Birkenbach**. +Learn more at [veen.world](https://www.veen.world). + +Part of the [Infinito.Nexus Project](https://s.infinito.nexus/code) +Licensed under [Infinito.Nexus NonCommercial License](https://s.infinito.nexus/license). diff --git a/roles/web-app-magento/Upgrade.md b/roles/web-app-magento/Upgrade.md new file mode 100644 index 00000000..92a72dcb --- /dev/null +++ b/roles/web-app-magento/Upgrade.md @@ -0,0 +1,19 @@ +# 🔼 Upgrade + +> Always back up the database and the `magento_data` volume before upgrades. + +1. Update images/versions in the application config (`roles/web-app-magento/config/main.yml` or inventory overrides). +2. Recreate containers: + ```bash + cd {{ PATH_DOCKER_COMPOSE_INSTANCES }}magento/ + docker compose pull + docker compose up -d --remove-orphans + ``` +3. Run upgrade routines: + ```bash + docker compose exec -it application bin/magento maintenance:enable + docker compose exec -it application bin/magento setup:upgrade + docker compose exec -it application bin/magento setup:di:compile + docker compose exec -it application bin/magento cache:flush + docker compose exec -it application bin/magento maintenance:disable + ``` diff --git a/roles/web-app-magento/User_Administration.md b/roles/web-app-magento/User_Administration.md new file mode 100644 index 00000000..5b17ff04 --- /dev/null +++ b/roles/web-app-magento/User_Administration.md @@ -0,0 +1,15 @@ +# 👤 User Administration + +- Access the admin panel at: + `{{ domains | get_url('web-app-magento', WEB_PROTOCOL) }}/admin` + *(or your custom admin path if configured)* + +- New admin accounts can be created via the web UI or CLI: + ```bash + docker compose exec -it application bin/magento admin:user:create \ + --admin-user="john" \ + --admin-password="SuperSecret_12345" \ + --admin-email="john@example.com" \ + --admin-firstname="John" \ + --admin-lastname="Doe" + ``` diff --git a/roles/web-app-magento/config/main.yml b/roles/web-app-magento/config/main.yml new file mode 100644 index 00000000..e306b5fd --- /dev/null +++ b/roles/web-app-magento/config/main.yml @@ -0,0 +1,37 @@ +features: + matomo: true + css: true + desktop: true + central_database: true # Use platform DB (recommended). If false, app-local DB container is enabled. + oidc: false # Magento SSO via OIDC requires extensions; not wired by default + logout: true + ldap: false + +server: + csp: + whitelist: {} + domains: + canonical: + - "shop.{{ PRIMARY_DOMAIN }}" + aliases: + - "magento.{{ PRIMARY_DOMAIN }}" + +docker: + services: + application: + image: "bitnami/magento" + version: "latest" + name: "magento" + backup: + no_stop_required: true + database: + enabled: true # Central DB recommended; if disabled, app-local DB is created + redis: + enabled: true + search: + enabled: true + image: "opensearchproject/opensearch" + version: "latest" + name: "magento-opensearch" + volumes: + data: "magento_data" diff --git a/roles/web-app-magento/meta/main.yml b/roles/web-app-magento/meta/main.yml new file mode 100644 index 00000000..55ad848d --- /dev/null +++ b/roles/web-app-magento/meta/main.yml @@ -0,0 +1,25 @@ +--- +galaxy_info: + author: "Kevin Veen-Birchenbach" + description: "Deploy Magento (Adobe Commerce Open Source) via Docker Compose with OpenSearch, MariaDB, optional Redis, and proxy integration for Infinito.Nexus." + license: "Infinito.Nexus NonCommercial License" + license_url: "https://s.infinito.nexus/license" + company: | + Kevin Veen-Birchenbach + Consulting & Coaching Solutions + https://www.veen.world + galaxy_tags: + - magento + - ecommerce + - php + - docker + - opensearch + - mariadb + repository: "https://s.infinito.nexus/code" + issue_tracker_url: "https://s.infinito.nexus/issues" + documentation: "https://docs.infinito.nexus" + logo: + class: "fa-solid fa-cart-shopping" + run_after: + - web-app-keycloak +dependencies: [] diff --git a/roles/web-app-magento/schema/main.yml b/roles/web-app-magento/schema/main.yml new file mode 100644 index 00000000..f7b3e748 --- /dev/null +++ b/roles/web-app-magento/schema/main.yml @@ -0,0 +1,6 @@ +credentials: + admin_password: + description: "Password for the initial Magento admin account" + algorithm: "alphanumeric" + validation: + min_length: 12 diff --git a/roles/web-app-magento/tasks/01_core.yml b/roles/web-app-magento/tasks/01_core.yml new file mode 100644 index 00000000..91a7fc3a --- /dev/null +++ b/roles/web-app-magento/tasks/01_core.yml @@ -0,0 +1,6 @@ +- name: "load docker, db/redis/proxy for {{ application_id }}" + include_role: + name: sys-stk-full-stateful + +- name: flush docker service + meta: flush_handlers diff --git a/roles/web-app-magento/tasks/main.yml b/roles/web-app-magento/tasks/main.yml new file mode 100644 index 00000000..5a67c6c1 --- /dev/null +++ b/roles/web-app-magento/tasks/main.yml @@ -0,0 +1,9 @@ +--- +- name: "construct {{ role_name }}" + include_tasks: 01_core.yml + when: run_once_web_app_magento is not defined + +- name: run the docker magento tasks once + set_fact: + run_once_web_app_magento: true + when: run_once_web_app_magento is not defined diff --git a/roles/web-app-magento/templates/docker-compose.yml.j2 b/roles/web-app-magento/templates/docker-compose.yml.j2 new file mode 100644 index 00000000..d8c63a74 --- /dev/null +++ b/roles/web-app-magento/templates/docker-compose.yml.j2 @@ -0,0 +1,34 @@ +{% include 'roles/docker-compose/templates/base.yml.j2' %} + + application: +{% set container_port = 8080 %} +{% include 'roles/docker-container/templates/base.yml.j2' %} + image: "{{ magento_image }}:{{ magento_version }}" + container_name: "{{ magento_name }}" + ports: + - "127.0.0.1:{{ ports.localhost.http[application_id] }}:{{ container_port }}" + volumes: + - "data:/bitnami/magento" +{% include 'roles/docker-container/templates/depends_on/dmbs_excl.yml.j2' %} + depends_on: + - search +{% include 'roles/docker-container/templates/healthcheck/tcp.yml.j2' %} +{% include 'roles/docker-container/templates/networks.yml.j2' %} + + search: +{% set container_port = 9200 %} + image: "{{ magento_search_image }}:{{ magento_search_version }}" + container_name: "{{ magento_search_name }}" +{% include 'roles/docker-container/templates/base.yml.j2' %} + environment: + - discovery.type=single-node + - plugins.security.disabled=true + - OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m +{% include 'roles/docker-container/templates/healthcheck/tcp.yml.j2' %} +{% include 'roles/docker-container/templates/networks.yml.j2' %} + +{% include 'roles/docker-compose/templates/volumes.yml.j2' %} + data: + name: {{ magento_data }} + +{% include 'roles/docker-compose/templates/networks.yml.j2' %} diff --git a/roles/web-app-magento/templates/env.j2 b/roles/web-app-magento/templates/env.j2 new file mode 100644 index 00000000..15ea9bd3 --- /dev/null +++ b/roles/web-app-magento/templates/env.j2 @@ -0,0 +1,39 @@ +# Magento environment +# Base references: +# - https://github.com/bitnami/containers/tree/main/bitnami/magento + +# Host & URLs +MAGENTO_HOST="{{ domains | get_domain(application_id) }}" +MAGENTO_BASE_URL="{{ domains | get_url(application_id, WEB_PROTOCOL) }}/" +MAGENTO_ENABLE_HTTPS={{ (WEB_PORT == 443) | string | lower }} + +# Admin (seed from global administrator) +MAGENTO_USERNAME="{{ users.administrator.username }}" +MAGENTO_PASSWORD="{{ applications | get_app_conf(application_id, 'credentials.admin_password') }}" +MAGENTO_EMAIL="{{ users.administrator.email }}" +MAGENTO_FIRST_NAME="{{ users.administrator.firstname | default('Admin') }}" +MAGENTO_LAST_NAME="{{ users.administrator.lastname | default('User') }}" + +# Database (central DB preferred) +MARIADB_HOST="{{ database_host }}" +MARIADB_PORT_NUMBER="{{ database_port }}" +MAGENTO_DATABASE_USER="{{ database_username }}" +MAGENTO_DATABASE_PASSWORD="{{ database_password }}" +MAGENTO_DATABASE_NAME="{{ database_name }}" + +# Search (Magento 2.4+) +MAGENTO_SEARCH_ENGINE="opensearch" +OPENSEARCH_HOST="search" +OPENSEARCH_PORT_NUMBER="9200" + +# SMTP +SMTP_HOST="{{ SYSTEM_EMAIL.HOST }}" +SMTP_PORT="{{ SYSTEM_EMAIL.PORT }}" +SMTP_USER="{{ users['no-reply'].email }}" +SMTP_PASSWORD="{{ users['no-reply'].mailu_token }}" +SMTP_PROTOCOL={{ SYSTEM_EMAIL.TLS | ternary('tls','ssl') }} + +# Misc +ALLOW_EMPTY_PASSWORD="no" +BITNAMI_DEBUG="false" +PHP_MEMORY_LIMIT="1024M" diff --git a/roles/web-app-magento/vars/main.yml b/roles/web-app-magento/vars/main.yml new file mode 100644 index 00000000..c374b67b --- /dev/null +++ b/roles/web-app-magento/vars/main.yml @@ -0,0 +1,17 @@ +# General +application_id: "web-app-magento" +database_type: "mariadb" + +# Magento (application container) +magento_version: "{{ applications | get_app_conf(application_id, 'docker.services.application.version', True) | default('latest') }}" +magento_image: "{{ applications | get_app_conf(application_id, 'docker.services.application.image', True) | default('bitnami/magento') }}" +magento_name: "{{ applications | get_app_conf(application_id, 'docker.services.application.name', True) | default('magento') }}" +magento_data: "{{ applications | get_app_conf(application_id, 'docker.volumes.data', True) | default('magento_data') }}" + +# Search (OpenSearch) +magento_search_version: "{{ applications | get_app_conf(application_id, 'docker.services.search.version', True) | default('latest') }}" +magento_search_image: "{{ applications | get_app_conf(application_id, 'docker.services.search.image', True) | default('opensearchproject/opensearch') }}" +magento_search_name: "{{ applications | get_app_conf(application_id, 'docker.services.search.name', True) | default('magento-opensearch') }}" + +# Docker helpers +docker_compose_flush_handlers: true