Refactored and solved bugs

This commit is contained in:
Kevin Veen-Birkenbach 2025-04-08 21:33:43 +02:00
parent 1be413f20d
commit da5962c337
No known key found for this signature in database
GPG Key ID: 44D8F11FD62F878E
31 changed files with 356 additions and 447 deletions

View File

@ -17,6 +17,7 @@ defaults_domains:
gitlab: "gitlab.{{primary_domain}}" gitlab: "gitlab.{{primary_domain}}"
html_server: "html.{{primary_domain}}" html_server: "html.{{primary_domain}}"
keycloak: "auth.{{primary_domain}}" keycloak: "auth.{{primary_domain}}"
lam: "ldap.{{primary_domain}}"
ldap: "ldap.{{primary_domain}}" ldap: "ldap.{{primary_domain}}"
listmonk: "newsletter.{{primary_domain}}" listmonk: "newsletter.{{primary_domain}}"
mailu: "mail.{{primary_domain}}" mailu: "mail.{{primary_domain}}"
@ -35,6 +36,7 @@ defaults_domains:
peertube_alternates: [] peertube_alternates: []
pgadmin: "pgadmin.{{primary_domain}}" pgadmin: "pgadmin.{{primary_domain}}"
phpmyadmin: "phpmyadmin.{{primary_domain}}" phpmyadmin: "phpmyadmin.{{primary_domain}}"
phpmyldap: "phpmyldap.{{primary_domain}}"
pixelfed: "picture.{{primary_domain}}" pixelfed: "picture.{{primary_domain}}"
portfolio: "{{primary_domain}}" portfolio: "{{primary_domain}}"
roulette-wheel: "roulette.{{primary_domain}}" roulette-wheel: "roulette.{{primary_domain}}"
@ -55,7 +57,10 @@ defaults_redirect_domain_mappings:
- { source: "funkwhale.{{primary_domain}}", target: "{{domains.funkwhale}}" } - { source: "funkwhale.{{primary_domain}}", target: "{{domains.funkwhale}}" }
- { source: "gitea.{{primary_domain}}", target: "{{domains.gitea}}" } - { source: "gitea.{{primary_domain}}", target: "{{domains.gitea}}" }
- { source: "keycloak.{{primary_domain}}", target: "{{domains.keycloak}}" } - { source: "keycloak.{{primary_domain}}", target: "{{domains.keycloak}}" }
- { source: "lam.{{primary_domain}}", target: "{{domains.ldap}}" } - {
source: "{{ domains.ldap }}",
target: "{% if 'lam' in group_names %}{{ domains.lam }}{% elif 'phpmyldap' in group_names %}{{ domains.phpmyldap }}{% else %}{{ primary_domain }}{% endif %}"
}
- { source: "listmonk.{{primary_domain}}", target: "{{domains.listmonk}}" } - { source: "listmonk.{{primary_domain}}", target: "{{domains.listmonk}}" }
- { source: "mailu.{{primary_domain}}", target: "{{domains.mailu}}" } - { source: "mailu.{{primary_domain}}", target: "{{domains.mailu}}" }
- { source: "moodle.{{primary_domain}}", target: "{{domains.moodle}}" } - { source: "moodle.{{primary_domain}}", target: "{{domains.moodle}}" }

View File

@ -238,7 +238,7 @@ defaults_applications:
webinterface: "lam" # The webinterface which should be used. Possible: lam and phpldapadmin webinterface: "lam" # The webinterface which should be used. Possible: lam and phpldapadmin
users: users:
administrator: administrator:
username: "{{users.administrator.username}}" username: "{{users.administrator.username}}" # Administrator username
# administrator_password: # CHANGE for security reasons in inventory file # administrator_password: # CHANGE for security reasons in inventory file
# administrator_database_password: # CHANGE for security reasons in inventory file # administrator_database_password: # CHANGE for security reasons in inventory file
force_import: False # Forces the import of the LDIF files force_import: False # Forces the import of the LDIF files
@ -735,7 +735,8 @@ defaults_applications:
# - https://community.taiga.io/t/taiga-and-oidc-plugin/4866 # - https://community.taiga.io/t/taiga-and-oidc-plugin/4866
# #
# Due to this reason this plutin is deactivated atm # Due to this reason this plutin is deactivated atm
enabled: True # De\Activate OIDC for Taiga enabled: False # De\Activate OIDC for Taiga
flavor: 'taigaio' # Potential flavors: robrotheram, taigaio
## YOURLS ## YOURLS

View File

@ -23,6 +23,7 @@ defaults_oidc:
user_info_url: "{{_oidc_client_issuer_url}}/protocol/openid-connect/userinfo" # Endpoint to retrieve user information user_info_url: "{{_oidc_client_issuer_url}}/protocol/openid-connect/userinfo" # Endpoint to retrieve user information
logout_url: "{{_oidc_client_issuer_url}}/protocol/openid-connect/logout" # Endpoint to log out the user logout_url: "{{_oidc_client_issuer_url}}/protocol/openid-connect/logout" # Endpoint to log out the user
change_credentials: "{{_oidc_client_issuer_url}}account/account-security/signing-in" # URL for managing or changing user credentials change_credentials: "{{_oidc_client_issuer_url}}account/account-security/signing-in" # URL for managing or changing user credentials
certs: "{{_oidc_client_issuer_url}}/protocol/openid-connect/certs" # JSON Web Key Set (JWKS)
button_text: "SSO Login({{primary_domain | upper}})" # Default button text button_text: "SSO Login({{primary_domain | upper}})" # Default button text
attributes: attributes:
# Attribut to identify the user # Attribut to identify the user
@ -35,7 +36,7 @@ defaults_oidc:
# Helper Variables: # Helper Variables:
# Keep in mind to mapp this variables if there is ever the possibility for the user to define them in the inventory # Keep in mind to mapp this variables if there is ever the possibility for the user to define them in the inventory
_ldap_dn_base: "dc={{primary_domain_sld}},dc={{primary_domain_tld}}" _ldap_dn_base: "dc={{primary_domain_sld}},dc={{primary_domain_tld}}"
_ldap_server_port: "{% if applications.ldap.openldap.network.local | bool %}{{ ports.localhost.ldap.openldap }}{% else %}{{ ports.localhost.ldaps.openldap }}{% endif %}" _ldap_server_port: "{% if applications.ldap.network.local | bool %}{{ ports.localhost.ldap.openldap }}{% else %}{{ ports.localhost.ldaps.openldap }}{% endif %}"
ldap: ldap:
# Distinguished Names (DN) # Distinguished Names (DN)
@ -56,9 +57,9 @@ ldap:
# Password to access dn.bind # Password to access dn.bind
bind_credential: "{{applications.ldap.administrator_database_password}}" bind_credential: "{{applications.ldap.administrator_database_password}}"
server: server:
domain: "{{applications.ldap.openldap.hostname if applications.ldap.openldap.network.local | bool else domains.ldap}}" # Mapping for public or locale access domain: "{{applications.ldap.hostname if applications.ldap.network.local | bool else domains.ldap}}" # Mapping for public or locale access
port: "{{_ldap_server_port}}" port: "{{_ldap_server_port}}"
uri: "{% if applications.ldap.openldap.network.local | bool %}ldap://{{ applications.ldap.openldap.hostname }}{% else %}ldaps://{{ domains.ldap }}{% endif %}:{{ _ldap_server_port }}" uri: "{% if applications.ldap.network.local | bool %}ldap://{{ applications.ldap.hostname }}{% else %}ldaps://{{ domains.ldap }}{% endif %}:{{ _ldap_server_port }}"
network: network:
local: "{{applications.ldap.openldap.network.local}}" # Uses the application configuration to define if local network should be available or not local: "{{applications.ldap.network.local}}" # Uses the application configuration to define if local network should be available or not

View File

@ -1,4 +1,4 @@
# Docker BigBlueButton 📡 # BigBlueButton 📡
## Description ## Description

View File

@ -64,7 +64,7 @@
coturn: coturn:
freeswitch: freeswitch:
bigbluebutton: bigbluebutton:
mediasoup mediasoup:
marker: "# {mark} ANSIBLE MANAGED BLOCK FOR VOLUMES" marker: "# {mark} ANSIBLE MANAGED BLOCK FOR VOLUMES"
insertbefore: "^services:" insertbefore: "^services:"
listen: setup bigbluebutton listen: setup bigbluebutton

View File

@ -1,4 +1,4 @@
# Docker Compose # Administration Notes
## Delete all containers, networks and volumes ## Delete all containers, networks and volumes

View File

@ -4,9 +4,6 @@ services:
application: application:
container_name: {{ application_id }} container_name: {{ application_id }}
logging:
driver: journald
restart: {{docker_restart_policy}}
image: ghcr.io/ldapaccountmanager/lam:{{applications[application_id].version}} image: ghcr.io/ldapaccountmanager/lam:{{applications[application_id].version}}
ports: ports:
- 127.0.0.1:{{ports.localhost.http[application_id]}}:80 - 127.0.0.1:{{ports.localhost.http[application_id]}}:80

View File

@ -1,12 +1,6 @@
services: services:
{% include 'roles/docker-oauth2-proxy/templates/container.yml.j2' %}
application: application:
container_name: {{ application_id }} container_name: {{ application_id }}
logging:
driver: journald
restart: {{docker_restart_policy}}
image: ghcr.io/ldapaccountmanager/lam:{{applications[application_id].version}} image: ghcr.io/ldapaccountmanager/lam:{{applications[application_id].version}}
ports: ports:
- 127.0.0.1:{{ports.localhost.http[application_id]}}:80 - 127.0.0.1:{{ports.localhost.http[application_id]}}:80

View File

@ -4,7 +4,7 @@
listen: "Import LDIF files" listen: "Import LDIF files"
# @todo Remove the following ignore errors when setting up a new server # @todo Remove the following ignore errors when setting up a new server
# Just here because debugging would take to much time # Just here because debugging would take to much time
# ignore_errors: true ignore_errors: true
- name: Refint Module Activation for OpenLDAP - name: Refint Module Activation for OpenLDAP
shell: > shell: >
@ -14,7 +14,7 @@
failed_when: ldapadd_result.rc not in [0, 68] failed_when: ldapadd_result.rc not in [0, 68]
# @todo Remove the following ignore errors when setting up a new server # @todo Remove the following ignore errors when setting up a new server
# Just here because debugging would take to much time # Just here because debugging would take to much time
# ignore_errors: true ignore_errors: true
- name: Refint Overlay Configuration for OpenLDAP - name: Refint Overlay Configuration for OpenLDAP
shell: > shell: >
@ -24,7 +24,7 @@
failed_when: ldapadd_result.rc not in [0, 68] failed_when: ldapadd_result.rc not in [0, 68]
# @todo Remove the following ignore errors when setting up a new server # @todo Remove the following ignore errors when setting up a new server
# Just here because debugging would take to much time # Just here because debugging would take to much time
# ignore_errors: true ignore_errors: true
- name: "Import users, groups, etc. to LDAP" - name: "Import users, groups, etc. to LDAP"
shell: > shell: >

View File

@ -8,13 +8,13 @@
src: "nginx.stream.conf.j2" src: "nginx.stream.conf.j2"
dest: "{{nginx.directories.streams}}{{domains[application_id]}}.conf" dest: "{{nginx.directories.streams}}{{domains[application_id]}}.conf"
notify: restart nginx notify: restart nginx
when: applications.ldap.openldap.network.public | bool when: applications.ldap.network.public | bool
- name: Remove {{domains[application_id]}}.conf if LDAP is not exposed to internet - name: Remove {{domains[application_id]}}.conf if LDAP is not exposed to internet
file: file:
path: "{{ nginx.directories.streams }}{{ domains[application_id] }}.conf" path: "{{ nginx.directories.streams }}{{ domains[application_id] }}.conf"
state: absent state: absent
when: not applications.ldap.openldap.network.public | bool when: not applications.ldap.network.public | bool
- name: create docker network for LDAP, so that other applications can access it - name: create docker network for LDAP, so that other applications can access it
docker_network: docker_network:

View File

@ -4,13 +4,11 @@ services:
application: application:
container_name: {{ application_id }} container_name: {{ application_id }}
logging:
driver: journald
restart: {{docker_restart_policy}}
image: leenooks/phpldapadmin:{{applications[application_id].version}} image: leenooks/phpldapadmin:{{applications[application_id].version}}
ports: ports:
- 127.0.0.1:{{ports.localhost.http[application_id]}}:8080 - 127.0.0.1:{{ports.localhost.http[application_id]}}:8080
{% include 'roles/docker-compose/templates/services/base.yml.j2' %} {% include 'roles/docker-compose/templates/services/base.yml.j2' %}
{% include 'templates/docker/container/networks.yml.j2' %} {% include 'templates/docker/container/networks.yml.j2' %}
{% include 'templates/docker/compose/volumes.yml.j2' %} {% include 'templates/docker/compose/volumes.yml.j2' %}

View File

@ -0,0 +1,3 @@
# Administrator Notes
It is recommended to don't run this role seperate, because it builds the portfolio page dynamic by checking if applications are in group_names.

View File

@ -1,19 +1,34 @@
# Docker Role: Flask-based Portfolio Setup 🚀 # Portfolio 🚀
This Ansible role facilitates setting up a Flask-based [portfolio application](https://github.com/kevinveenbirkenbach/portfolio) in a Docker container. It allows you to showcase your projects, services, or online presence using a customizable YAML configuration file. ## Description
## Attention This Ansible role deploys and manages a Flask-based [portfolio application](https://github.com/kevinveenbirkenbach/portfolio) in a Docker container. It provides a user-friendly and customizable way to showcase your projects, services, or creative work online. The role leverages Docker, Docker Compose, and integrated web server configurations to ensure a smooth deployment experience.
The default template creates the links based on ``group_names``. If you run this script seperate, may not all necessary menu items are generated. ## Overview
## Features ✨ Tailored for creative professionals and developers, this role streamlines the process of setting up a portfolio site. It automates tasks such as Docker container configuration, dynamic routing via Nginx, and repository integration, so you can concentrate on perfecting your content and design. Enjoy a responsive layout and easy-to-modify YAML files that let you rapidly update your online presence without deep technical intervention.
- **Automated Setup**: Quickly deploy a portfolio using Docker. ## Purpose
- **Customizable Content**: Modify the portfolio using a YAML file.
- **Responsive Design**: Built with Bootstrap for optimal viewing on any device.
- **Dynamic Navigation**: Multi-level menus using nested YAML configurations.
- **Cache Management**: Efficient asset caching for improved performance.
## Author The purpose of the Docker-Portfolio role is to simplify the deployment and management of a personal or professional portfolio. By focusing on usability and a clean presentation, the role helps you:
- Quickly launch a professional-looking website.
- Customize and update your portfolio content effortlessly.
- Integrate seamlessly with complementary roles for Docker Compose and web server management.
- Reduce manual configuration and maintenance tasks.
This role was developed by [Kevin Veen-Birkenbach](https://www.veen.world). ## Features
- **Flask-Based Portfolio App:** Deploy a modern portfolio application designed to highlight your work.
- **Containerized Deployment:** Uses Docker and Docker Compose for easy, isolated, and reproducible deployment.
- **Customizable Configuration:** Adjust the content and appearance through simple YAML configuration files.
- **Responsive Design:** Optimized for devices of any size, ensuring your portfolio always looks great.
- **Integrated Routing:** Works alongside Nginx-domain and repository setup roles to provide reliable domain routing and version control.
- **Automated Updates:** Automatically re-deploys changes when configuration files are updated, keeping your portfolio up-to-date.
## Credits 📝
Developed and maintained by **Kevin Veen-Birkenbach**.
Learn more at [www.veen.world](https://www.veen.world)
Part of the [CyMaIS Project](https://github.com/kevinveenbirkenbach/cymais)
License: [CyMaIS NonCommercial License (CNCL)](https://s.veen.world/cncl)

View File

@ -0,0 +1,90 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import glob
import os
import re
import yaml
from ansible.plugins.lookup import LookupBase
from ansible.errors import AnsibleError
class LookupModule(LookupBase):
def run(self, terms, variables=None, **kwargs):
"""
This lookup iterates over all roles whose folder name starts with 'docker-'
and generates a list of dictionaries (cards). For each role, it:
- Extracts the application_id (everything after "docker-")
- Reads the title from the role's README.md (the first H1 line)
- Retrieves the description from galaxy_info.description in meta/main.yml
- Retrieves the icon class from galaxy_info.logo.class
- Builds the URL using the 'domains' variable (e.g. domains[application_id])
- Sets the iframe flag from applications[application_id].landingpage_iframe_enabled
Only cards whose application_id is included in the variable group_names are returned.
"""
# Default to "roles" if no directory is provided
roles_dir = terms[0] if len(terms) > 0 else "roles"
cards = []
# Get group_names from variables; default to empty list if not set
group_names = variables.get("group_names", [])
# Pattern to match roles starting with "docker-"
pattern = os.path.join(roles_dir, "docker-*")
for role_path in glob.glob(pattern):
role_dir = role_path.rstrip("/")
role_basename = os.path.basename(role_dir)
if not role_basename.startswith("docker-"):
continue
# The application_id is the substring after "docker-"
application_id = role_basename[len("docker-"):]
# Only add card if the application_id is in group_names
if application_id not in group_names:
continue
# Define paths for the README.md and meta/main.yml
readme_path = os.path.join(role_dir, "README.md")
meta_path = os.path.join(role_dir, "meta", "main.yml")
if not os.path.exists(readme_path) or not os.path.exists(meta_path):
continue
try:
with open(readme_path, "r", encoding="utf-8") as f:
readme_content = f.read()
# Extract the first H1 line (title) via regex
title_match = re.search(r'^#\s+(.*)$', readme_content, re.MULTILINE)
title = title_match.group(1).strip() if title_match else application_id
except Exception as e:
raise AnsibleError("Error reading '{}': {}".format(readme_path, str(e)))
try:
with open(meta_path, "r", encoding="utf-8") as f:
meta_data = yaml.safe_load(f)
galaxy_info = meta_data.get("galaxy_info", {})
description = galaxy_info.get("description", "")
logo = galaxy_info.get("logo", {})
icon_class = logo.get("class", "fa-solid fa-cube")
except Exception as e:
raise AnsibleError("Error reading '{}': {}".format(meta_path, str(e)))
# Retrieve variables for domain and application settings.
domains = variables.get("domains", {})
applications = variables.get("applications", {})
domain_url = domains.get(application_id, "")
url = "https://" + domain_url if domain_url else ""
app_data = applications.get(application_id, {})
iframe = app_data.get("landingpage_iframe_enabled", False)
card = {
"icon": {"class": icon_class},
"title": title,
"text": description,
"url": url,
"link_text": "Discover {} Now!".format(title),
"iframe": iframe,
}
cards.append(card)
return [cards]

View File

@ -0,0 +1,24 @@
---
galaxy_info:
author: "Kevin Veen-Birkenbach"
description: "Portfolio to showcase your projects and creative work with a focus on user experience and easy customization. 🚀"
license: "CyMaIS NonCommercial License (CNCL)"
license_url: "https://s.veen.world/cncl"
company: |
Kevin Veen-Birkenbach
Consulting & Coaching Solutions
https://www.veen.world
min_ansible_version: "2.9"
platforms:
- name: Docker
versions:
- latest
galaxy_tags:
- docker
- portfolio
- ansible
- flask
- web
repository: "https://github.com/kevinveenbirkenbach/portfolio"
issue_tracker_url: "https://github.com/kevinveenbirkenbach/portfolio/issues"
documentation: "https://github.com/kevinveenbirkenbach/portfolio#readme"

View File

@ -105,376 +105,7 @@ accounts:
- link: navigation.header.contact - link: navigation.header.contact
cards: cards:
{{ lookup('docker_cards', 'roles') | to_nice_yaml(indent=2) }}
{% if "matomo" in group_names %}
- icon:
class: "fa-solid fa-chart-line"
title: "Matomo Analytics"
text: "Experience the power of Matomo, an innovative open-source analytics platform that delivers real-time insights, robust visitor tracking, and privacy-first features to elevate your website performance. Dive into actionable data with unmatched precision and clarity!"
url: https://{{domains.matomo}}
link_text: "Discover Matomo Now!"
iframe: {{ applications | get_landingpage_iframe_enabled('matomo') }}
{% endif %}
{% if "ldap" in group_names %}
- icon:
class: "fa-solid fa-users"
title: "LDAP Directory"
text: "Unleash the potential of centralized identity management with our vibrant LDAP solution. Enjoy seamless authentication, efficient user management, and enhanced security that empowers your organization to stay connected, agile, and ahead of the curve in digital transformation."
url: https://{{domains.ldap}}
link_text: "Empower Your Network!"
iframe: {{ applications | get_landingpage_iframe_enabled('ldap') }}
{% endif %}
{% if "keycloak" in group_names %}
- icon:
class: "fa-solid fa-lock"
title: "Keycloak Identity"
text: "Step into a secure future with Keycloak! Our dynamic identity and access management solution offers streamlined SSO capabilities, robust security measures, and an intuitive user experience that propels your applications to unprecedented heights of performance and reliability."
url: https://{{domains.keycloak}}
link_text: "Secure Your Future Now!"
iframe: {{ applications | get_landingpage_iframe_enabled('keycloak') }}
{% endif %}
{% if "nextcloud" in group_names %}
- icon:
class: "fa-solid fa-cloud"
title: "Nextcloud"
text: "Elevate your collaboration with Nextcloud, a vibrant self-hosted cloud solution designed for dynamic file sharing, seamless communication, and effortless teamwork. Embrace unparalleled control, flexibility, and a boosted digital workspace that adapts to your every need."
url: https://{{domains.nextcloud}}
link_text: "Experience Nextcloud Today!"
iframe: {{ applications | get_landingpage_iframe_enabled('nextcloud') }}
{% endif %}
{% if "gitea" in group_names %}
- icon:
class: "fa-solid fa-code"
title: "Gitea"
text: "Boost your development journey with Gitea, a lightweight and energetic self-hosted Git service that offers efficient code collaboration, intuitive version control, and an agile environment for your projects. Ignite your coding spirit, innovate faster, and code with confidence!"
url: https://{{domains.gitea}}
link_text: "Ignite Your Code Now!"
iframe: {{ applications | get_landingpage_iframe_enabled('gitea') }}
{% endif %}
{% if "wordpress" in group_names %}
- icon:
class: "fa-solid fa-blog"
title: "WordPress"
text: "Unleash your creative potential with WordPress, a dynamic platform that empowers you to build, manage, and scale stunning websites and blogs effortlessly. Experience an ever-evolving ecosystem that inspires innovation and drives digital success with every click."
url: https://{{domains.wordpress}}
link_text: "Launch Your Site Today!"
iframe: {{ applications | get_landingpage_iframe_enabled('wordpress') }}
{% endif %}
{% if "mediawiki" in group_names %}
- icon:
class: "fa-solid fa-book"
title: "MediaWiki"
text: "Empower your knowledge base with MediaWiki, a versatile and collaborative platform designed to build comprehensive, user-driven documentation. Embrace an energetic community and innovative tools that turn information into a vibrant, living resource."
url: https://{{domains.mediawiki}}
link_text: "Explore MediaWiki Now!"
iframe: {{ applications | get_landingpage_iframe_enabled('mediawiki') }}
{% endif %}
{% if "mybb" in group_names %}
- icon:
class: "fa-solid fa-comments"
title: "MyBB Forum"
text: "Transform your community engagement with MyBB, a feature-rich forum solution that combines modern design with robust functionality. Enjoy dynamic discussions, intuitive moderation, and an energetic user interface that brings people together like never before."
url: https://{{domains.mybb}}
link_text: "Join the Conversation!"
iframe: {{ applications | get_landingpage_iframe_enabled('mybb') }}
{% endif %}
{% if "yourls" in group_names %}
- icon:
class: "fa-solid fa-link"
title: "YOURLS URL Shortener"
text: "Streamline your online presence with YOURLS, a nimble URL shortening solution that makes sharing links faster, easier, and more engaging. Enjoy the benefits of enhanced tracking and a user-friendly interface that energizes your digital strategy."
url: https://{{domains.yourls}}
link_text: "Shorten Links Instantly!"
iframe: {{ applications | get_landingpage_iframe_enabled('yourls') }}
{% endif %}
{% if "mailu" in group_names %}
- icon:
class: "fa-solid fa-envelope"
title: "Mail Server"
text: "Revolutionize your email communications with Mailu, a secure and flexible mail server solution that integrates seamlessly into your workflow. Experience enhanced reliability, robust security, and an energetic approach to managing your digital correspondence."
url: https://{{domains.mailu}}
link_text: "Elevate Your Email Now!"
iframe: {{ applications | get_landingpage_iframe_enabled('mailu') }}
{% endif %}
{% if "mastodon" in group_names %}
- icon:
class: "fa-solid fa-bullhorn"
title: "Mastodon Social"
text: "Dive into a decentralized social experience with Mastodon, a vibrant platform that redefines online communication with its community-driven approach. Enjoy a refreshing burst of innovation, freedom, and energetic interaction every time you connect."
url: https://{{domains.mastodon}}
link_text: "Join the Social Revolution!"
iframe: {{ applications | get_landingpage_iframe_enabled('mastodon') }}
{% endif %}
{% if "pixelfed" in group_names %}
- icon:
class: "fa-solid fa-camera"
title: "Pixelfed"
text: "Showcase your visual story with Pixelfed, an inspiring self-hosted image sharing platform that champions creativity and privacy. Revel in a dynamic, artistic environment where every photo is a window to endless possibilities and vibrant expression."
url: https://{{domains.pixelfed}}
link_text: "Share Your Vision Now!"
iframe: {{ applications | get_landingpage_iframe_enabled('pixelfed') }}
{% endif %}
{% if "peertube" in group_names %}
- icon:
class: "fa-solid fa-video"
title: "PeerTube"
text: "Embrace a new era of video hosting with PeerTube, a decentralized platform that empowers creators with freedom, innovation, and a community-focused approach. Experience seamless streaming and dynamic sharing that fuels your creative ambitions."
url: https://{{domains.peertube}}
link_text: "Stream with Freedom!"
iframe: {{ applications | get_landingpage_iframe_enabled('peertube') }}
{% endif %}
{% if "bigbluebutton" in group_names %}
- icon:
class: "fa-solid fa-chalkboard-teacher"
title: "BigBlueButton"
text: "Transform online learning and collaboration with BigBlueButton, an interactive web conferencing solution designed to energize virtual classrooms and meetings. Enjoy dynamic tools and an engaging environment that makes every session a powerful learning experience."
url: https://{{domains.bigbluebutton}}
link_text: "Start Your Virtual Session!"
iframe: {{ applications | get_landingpage_iframe_enabled('bigbluebutton') }}
{% endif %}
{% if "funkwhale" in group_names %}
- icon:
class: "fa-solid fa-music"
title: "Funkwhale"
text: "Dive into a world of rhythm and sound with Funkwhale, an innovative self-hosted music sharing platform that celebrates creativity and community. Experience an energetic soundscape and seamless music streaming that amplifies your passion for tunes."
url: https://{{domains.funkwhale}}
link_text: "Jam Out Now!"
iframe: {{ applications | get_landingpage_iframe_enabled('funkwhale') }}
{% endif %}
{% if "joomla" in group_names %}
- icon:
class: "fa-solid fa-sitemap"
title: "Joomla CMS"
text: "Elevate your website management with Joomla, a powerful content management system that fuses versatility with dynamic design. Experience a vibrant platform that inspires creativity and drives your digital presence to new, energetic heights."
url: https://{{domains.joomla}}
link_text: "Build with Joomla Today!"
iframe: {{ applications | get_landingpage_iframe_enabled('joomla') }}
{% endif %}
{% if "attendize" in group_names %}
- icon:
class: "fa-solid fa-calendar-check"
title: "Attendize"
text: "Revolutionize your event management with Attendize, an energetic and intuitive platform designed to streamline ticketing and event planning. Enjoy a feature-rich, user-friendly solution that transforms every event into an unforgettable experience."
url: https://{{domains.attendize}}
link_text: "Plan Your Event Now!"
iframe: {{ applications | get_landingpage_iframe_enabled('attendize') }}
{% endif %}
{% if "baserow" in group_names %}
- icon:
class: "fa-solid fa-table"
title: "Baserow"
text: "Empower your data management with Baserow, an innovative platform that makes building and managing databases both fun and efficient. Enjoy a dynamic interface, seamless collaboration, and energetic tools that supercharge your workflow."
url: https://{{domains.baserow}}
link_text: "Manage Data with Ease!"
iframe: {{ applications | get_landingpage_iframe_enabled('baserow') }}
{% endif %}
{% if "listmonk" in group_names %}
- icon:
class: "fa-solid fa-list"
title: "Listmonk"
text: "Elevate your email marketing with Listmonk, a high-energy, self-hosted solution that offers powerful newsletter management and analytics. Enjoy an intuitive design, robust features, and a spirited approach that takes your campaigns to the next level."
url: https://{{domains.listmonk}}
link_text: "Boost Your Campaigns Now!"
iframe: {{ applications | get_landingpage_iframe_enabled('listmonk') }}
{% endif %}
{% if "discourse" in group_names %}
- icon:
class: "fa-solid fa-comment-dots"
title: "Discourse Forum"
text: "Ignite community conversations with Discourse, an innovative forum platform that redefines online discussions with its modern, engaging interface. Experience an energetic, user-friendly environment that brings people together and fuels vibrant exchanges."
url: https://{{domains.discourse}}
link_text: "Join the Discussion!"
iframe: {{ applications | get_landingpage_iframe_enabled('discourse') }}
{% endif %}
{% if "matrix" in group_names %}
- icon:
class: "fa-solid fa-satellite-dish"
title: "Matrix"
text: "Step into the future of communication with Matrix, a dynamic and decentralized platform that delivers secure, real-time messaging and collaboration. Enjoy an innovative ecosystem that energizes your digital interactions and connects you globally."
url: https://{{domains.matrix_synapse}}
link_text: "Connect on Matrix Now!"
iframe: {{ applications | get_landingpage_iframe_enabled('matrix') }}
{% endif %}
{% if "openproject" in group_names %}
- icon:
class: "fa-solid fa-project-diagram"
title: "OpenProject"
text: "Transform your project management with OpenProject, a vibrant and collaborative tool that brings clarity and energy to your planning, tracking, and team communication. Experience streamlined workflows and an innovative platform that propels your projects forward."
url: https://{{domains.openproject}}
link_text: "Manage Projects Dynamically!"
iframe: {{ applications | get_landingpage_iframe_enabled('openproject') }}
{% endif %}
{% if "gitlab" in group_names %}
- icon:
class: "fa-solid fa-code-branch"
title: "GitLab"
text: "Accelerate your software development with GitLab, an energetic, all-in-one platform for source code management and continuous integration. Experience a robust, collaborative environment that empowers teams to innovate and deliver exceptional results."
url: https://{{domains.gitlab}}
link_text: "Revolutionize Your DevOps!"
iframe: {{ applications | get_landingpage_iframe_enabled('gitlab') }}
{% endif %}
{% if "akaunting" in group_names %}
- icon:
class: "fa-solid fa-file-invoice-dollar"
title: "Akaunting"
text: "Empower your financial management with Akaunting, a dynamic and feature-rich accounting platform designed to simplify your bookkeeping and boost your business growth. Enjoy intuitive tools, real-time insights, and an energetic approach to your finances."
url: https://{{domains.akaunting}}
link_text: "Transform Your Finances Today!"
iframe: {{ applications | get_landingpage_iframe_enabled('akaunting') }}
{% endif %}
{% if "moodle" in group_names %}
- icon:
class: "fa-solid fa-graduation-cap"
title: "Moodle"
text: "Ignite the learning experience with Moodle, a powerful and versatile platform for online education that energizes classrooms and fosters interactive learning. Embrace innovative tools, engaging content, and a dynamic community of educators and learners."
url: https://{{domains.moodle}}
link_text: "Start Learning Now!"
iframe: {{ applications | get_landingpage_iframe_enabled('moodle') }}
{% endif %}
{% if "taiga" in group_names %}
- icon:
class: "fa-solid fa-tasks"
title: "Taiga"
text: "Supercharge your project management with Taiga, a dynamic and agile tool designed for teams that thrive on creativity and collaboration. Experience a vibrant interface, robust task tracking, and an energetic platform that drives your projects to success."
url: https://{{domains.taiga}}
link_text: "Boost Your Projects Now!"
iframe: {{ applications | get_landingpage_iframe_enabled('taiga') }}
{% endif %}
{% if "friendica" in group_names %}
- icon:
class: "fa-solid fa-user-friends"
title: "Friendica"
text: "Connect and share like never before with Friendica, an innovative social networking platform that celebrates community, freedom, and dynamic interactions. Enjoy a spirited and open environment where every connection is a step toward a more engaging digital world."
url: https://{{domains.friendica}}
link_text: "Join the Social Movement!"
iframe: {{ applications | get_landingpage_iframe_enabled('friendica') }}
{% endif %}
{% if "portfolio" in group_names %}
- icon:
class: "fa-solid fa-briefcase"
title: "Portfolio"
text: "Showcase your professional journey with Portfolio, a dynamic platform that combines creativity and functionality to highlight your achievements. Experience an energetic design, intuitive features, and a compelling way to present your work to the world."
url: https://{{domains.portfolio}}
link_text: "Elevate Your Profile Now!"
iframe: {{ applications | get_landingpage_iframe_enabled('portfolio') }}
{% endif %}
{% if "bluesky" in group_names %}
- icon:
class: "fa-solid fa-sun"
title: "Bluesky"
text: "Soar to new digital heights with Bluesky, an innovative platform that reimagines social networking with its forward-thinking, community-driven approach. Experience a burst of energy, creativity, and the freedom to connect in a truly inspiring way."
url: https://{{domains.bluesky}}
link_text: "Soar with Bluesky Today!"
iframe: {{ applications | get_landingpage_iframe_enabled('bluesky') }}
{% endif %}
{% if "sphinx" in group_names %}
- icon:
class: "fa-solid fa-book"
title: "Documentation"
text: "Unlock comprehensive insights with our extensive documentation. Explore guides, tutorials, and support resources designed to help you navigate our software effortlessly."
url: https://{{domains.sphinx}}
link_text: "Explore Documentation Now!"
iframe: {{ applications | get_landingpage_iframe_enabled('sphinx') }}
{% endif %}
{% if "phpmyadmin" in group_names %}
- icon:
class: "fa-solid fa-database"
title: "PHPMyAdmin"
text: "Manage your databases with confidence using PHPMyAdmin, a robust and dynamic tool designed to simplify administration and enhance productivity. Enjoy an intuitive interface, powerful features, and an energetic approach that makes database management a breeze."
url: https://{{domains.phpmyadmin}}
link_text: "Optimize Your Database Now!"
iframe: {{ applications | get_landingpage_iframe_enabled('phpmyadmin') }}
{% endif %}
{% if "pgadmin" in group_names %}
- icon:
class: "fa-solid fa-database"
title: "pgAdmin"
text: "Take control of your PostgreSQL databases with pgAdmin — the most advanced and feature-rich administration tool available. Streamline your workflows with an elegant UI and comprehensive capabilities tailored for professionals."
url: https://{{domains.pgadmin}}
link_text: "Launch Your PostgreSQL Dashboard!"
iframe: {{ applications | get_landingpage_iframe_enabled('pgadmin') }}
{% endif %}
{% if "snipe_it" in group_names %}
- icon:
class: "fa-solid fa-box"
title: "SNIPE-IT"
text: "Streamline your asset management with SNIPE-IT, a cutting-edge solution that brings efficiency, clarity, and energy to tracking your hardware and software inventory. Experience a user-friendly design and dynamic features that make asset management simple and engaging."
url: https://{{domains.snipe_it}}
link_text: "Manage Assets Effortlessly!"
iframe: {{ applications | get_landingpage_iframe_enabled('snipe_it') }}
{% endif %}
platform: platform:
titel: {{service_provider.platform.titel}} titel: {{service_provider.platform.titel}}

View File

@ -1,4 +1,4 @@
# Sphinx Documentation Role # Sphinx Documentation
## Description ## Description

View File

@ -14,17 +14,19 @@
include_role: include_role:
name: docker-repository-setup name: docker-repository-setup
- name: "template local.py for taiga-contrib-oidc-auth" - name: "copy templates {{ settings_files }} for taiga-contrib-oidc-auth"
template: template:
src: taiga/local.py.j2 src: "taiga/{{item}}.py.j2"
dest: "{{ docker_compose.directories.config }}taiga-local.py" dest: "{{ docker_compose.directories.config }}taiga-{{item}}.py"
when: applications[application_id].oidc.enabled and applications[application_id].oidc.flavor == 'taigaio' when: applications[application_id].oidc.enabled and applications[application_id].oidc.flavor == 'taigaio'
notify: docker compose project build and setup
loop: "{{ settings_files }}"
- name: "create {{docker_compose_init}}" - name: "create {{docker_compose_init}}"
template: template:
src: "docker-compose-inits.yml.j2" src: "docker-compose-inits.yml.j2"
dest: "{{docker_compose_init}}" dest: "{{docker_compose_init}}"
notify: docker compose project setup notify: docker compose project build and setup
- name: "copy docker-compose.yml and env file" - name: "copy docker-compose.yml and env file"
include_tasks: copy-docker-compose-and-env.yml include_tasks: copy-docker-compose-and-env.yml

View File

@ -44,7 +44,9 @@ services:
{% if applications[application_id].oidc.enabled and applications[application_id].oidc.flavor == 'taigaio' %} {% if applications[application_id].oidc.enabled and applications[application_id].oidc.flavor == 'taigaio' %}
- {{ docker_compose.directories.config }}taiga-local.py:/taiga-back/settings/local.py:ro {% for item in settings_files %}
- {{ docker_compose.directories.config }}taiga-{{ item }}.py:/taiga-back/settings/{{ item }}.py:ro
{% endfor %}
{% endif %} {% endif %}

View File

@ -53,7 +53,7 @@ ENABLE_TELEMETRY = True
# OIDC via taigaio official contrib # OIDC via taigaio official contrib
# @See https://github.com/taigaio/taiga-contrib-oidc-auth # @See https://github.com/taigaio/taiga-contrib-oidc-auth
ENABLE_OIDC=True
OIDC_RP_CLIENT_ID="{{ oidc.client.id }}" OIDC_RP_CLIENT_ID="{{ oidc.client.id }}"
OIDC_RP_CLIENT_SECRET="{{ oidc.client.secret }}" OIDC_RP_CLIENT_SECRET="{{ oidc.client.secret }}"
OIDC_OP_AUTHORIZATION_ENDPOINT="{{ oidc.client.authorize_url }}" OIDC_OP_AUTHORIZATION_ENDPOINT="{{ oidc.client.authorize_url }}"
@ -61,10 +61,7 @@ OIDC_OP_TOKEN_ENDPOINT="{{ oidc.client.token_url }}"
OIDC_OP_USER_ENDPOINT="{{ oidc.client.user_info_url }}" OIDC_OP_USER_ENDPOINT="{{ oidc.client.user_info_url }}"
OIDC_RP_SIGN_ALGO="RS256" OIDC_RP_SIGN_ALGO="RS256"
OIDC_RP_SCOPES="openid profile email" OIDC_RP_SCOPES="openid profile email"
OIDC_USE_STATE=True OIDC_OP_JWKS_ENDPOINT="{{ oidc.client.certs }}"
OIDC_USE_NONCE=True
OIDC_RP_CALLBACK_URL="{{ oidc.client.redirect_uri | default('') }}"
OIDC_OP_JWKS_ENDPOINT="{{ oidc.client.jwks_url | default('') }}"
{% endif %} {% endif %}

View File

@ -9,16 +9,15 @@ AUTHENTICATION_BACKENDS = list(AUTHENTICATION_BACKENDS) + [
ROOT_URLCONF = "settings.urls" ROOT_URLCONF = "settings.urls"
OIDC_CALLBACK_CLASS = "taiga_contrib_oidc_auth.views.TaigaOIDCAuthenticationCallbackView"
OIDC_RP_SCOPES = "openid profile email"
OIDC_RP_SIGN_ALGO = "RS256"
OIDC_BASE_URL = "{{ oidc.base_url }}"
OIDC_OP_JWKS_ENDPOINT = OIDC_BASE_URL + "/Jwks"
OIDC_OP_AUTHORIZATION_ENDPOINT = OIDC_BASE_URL + "/Authorization"
OIDC_OP_TOKEN_ENDPOINT = OIDC_BASE_URL + "/Token"
OIDC_OP_USER_ENDPOINT = OIDC_BASE_URL + "/UserInfo"
import os import os
OIDC_CALLBACK_CLASS = "taiga_contrib_oidc_auth.views.TaigaOIDCAuthenticationCallbackView"
OIDC_RP_SCOPES = os.getenv("OIDC_RP_SCOPES")
OIDC_RP_SIGN_ALGO = os.getenv("OIDC_RP_SIGN_ALGO")
#OIDC_BASE_URL = "" @todo remove if not needed
OIDC_OP_JWKS_ENDPOINT = os.getenv("OIDC_OP_JWKS_ENDPOINT")
OIDC_OP_AUTHORIZATION_ENDPOINT = os.getenv("OIDC_OP_AUTHORIZATION_ENDPOINT")
OIDC_OP_TOKEN_ENDPOINT = os.getenv("OIDC_OP_TOKEN_ENDPOINT")
OIDC_OP_USER_ENDPOINT = os.getenv("OIDC_OP_USER_ENDPOINT")
OIDC_RP_CLIENT_ID = os.getenv("OIDC_RP_CLIENT_ID") OIDC_RP_CLIENT_ID = os.getenv("OIDC_RP_CLIENT_ID")
OIDC_RP_CLIENT_SECRET = os.getenv("OIDC_RP_CLIENT_SECRET") OIDC_RP_CLIENT_SECRET = os.getenv("OIDC_RP_CLIENT_SECRET")

View File

@ -0,0 +1,4 @@
from taiga.urls import *
urlpatterns += [
url(r"^oidc/", include("mozilla_django_oidc.urls")),
]

View File

@ -11,3 +11,7 @@ taiga_image_frontend: >-
{{ 'robrotheram/taiga-front-openid' if applications[application_id].oidc.enabled and applications[application_id].oidc.flavor == 'robrotheram' {{ 'robrotheram/taiga-front-openid' if applications[application_id].oidc.enabled and applications[application_id].oidc.flavor == 'robrotheram'
else 'taigaio/taiga-front' }} else 'taigaio/taiga-front' }}
taiga_frontend_conf_path: "{{docker_compose.directories.config}}conf.json" taiga_frontend_conf_path: "{{docker_compose.directories.config}}conf.json"
settings_files:
- urls
- local

View File

@ -4,7 +4,7 @@ networks:
central_{{ database_type }}: central_{{ database_type }}:
external: true external: true
{% endif %} {% endif %}
{% if applications[application_id].get('ldap', {}).get('enabled', false)|bool and applications.ldap.openldap.network.local|bool %} {% if applications[application_id].get('ldap', {}).get('enabled', false) | bool and applications.ldap.network.local | bool %}
central_ldap: central_ldap:
external: true external: true
{% endif %} {% endif %}

View File

@ -1,5 +1,5 @@
{# This needs to be included in docker-compose.yml which just contain a database volume #} {# This needs to be included in docker-compose.yml which just contain a database volume #}
{% if not applications[application_id].database.central_storage | bool %} {% if not (applications[application_id].database.central_storage | default(false)) | bool %}
volumes: volumes:
database: database:
{% endif %} {% endif %}

View File

@ -1,6 +1,6 @@
{# This template needs to be included in docker-compose.yml which contain a database and additional volumes #} {# This template needs to be included in docker-compose.yml which contain a database and additional volumes #}
volumes: volumes:
{% if not applications[application_id].database.central_storage | bool %} {% if not (applications[application_id].database.central_storage | default(false)) | bool %}
database: database:
{% endif %} {% endif %}
{{ "\n" }} {{ "\n" }}

View File

@ -3,7 +3,7 @@
{% if applications | get_database_central_storage(application_id) | bool and database_type is defined %} {% if applications | get_database_central_storage(application_id) | bool and database_type is defined %}
central_{{ database_type }}: central_{{ database_type }}:
{% endif %} {% endif %}
{% if applications[application_id].get('ldap', {}).get('enabled', false)|bool and applications.ldap.openldap.network.local|bool %} {% if applications[application_id].get('ldap', {}).get('enabled', false)|bool and applications.ldap.network.local|bool %}
central_ldap: central_ldap:
{% endif %} {% endif %}
default: default:

2
tests/README.md Normal file
View File

@ -0,0 +1,2 @@
# Tests
This folder contains the tests for cymais

52
tests/unit/README.md Normal file
View File

@ -0,0 +1,52 @@
# Unit Tests
This directory contains unit tests for various custom components in the project, such as the custom lookup plugin `docker_cards` used in the `docker-portfolio` role.
## Overview
The unit tests are written using Pythons built-in `unittest` framework. They are designed to verify that your custom logic works as expected—such as extracting metadata from role files—without needing to run the entire playbook.
## Running the Tests
You can run the tests using one of the following methods:
1. **Using Unittest Discovery:**
From the project's root directory, run:
```bash
python -m unittest discover -s tests/unit
```
This command will discover and execute all test files within the `tests/unit` directory.
2. **Running a Specific Test File:**
If you want to run only the Docker cards test, execute:
```bash
python tests/unit/test_docker_cards.py
```
## How It Works
- **Setup:**
The test script creates a temporary directory to simulate your roles folder. It then creates a sample role (`docker-portfolio`) with a `README.md` file (containing a header for the title) and a `meta/main.yml` file (with the required metadata).
- **Execution:**
Dummy variable values for `domains` and `applications` are provided (these are the variables the lookup plugin expects). The lookup plugin is then run, which processes the sample role and returns the card information.
- **Verification:**
The test uses assertions to ensure that the output contains the expected title, description, icon information, constructed URL, and the correct iframe flag.
- **Cleanup:**
After the test completes, the temporary directory is removed, ensuring that no test artifacts remain.
## Requirements
- Python 3.6 or newer is recommended.
- All necessary dependencies for your project should be installed.
These tests help ensure that your custom code is reliable and behaves as expected, and they can be easily integrated into a Continuous Integration (CI) pipeline.
Happy testing!

View File

@ -0,0 +1,88 @@
import os
import sys
import tempfile
import shutil
import unittest
# Adjust the PYTHONPATH to include the lookup_plugins folder from the docker-portfolio role.
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../roles/docker-portfolio/lookup_plugins'))
from docker_cards import LookupModule
class TestDockerCardsLookup(unittest.TestCase):
def setUp(self):
# Create a temporary directory to simulate the roles directory.
self.test_roles_dir = tempfile.mkdtemp(prefix="test_roles_")
# Create a sample role "docker-portfolio".
self.role_name = "docker-portfolio"
self.role_dir = os.path.join(self.test_roles_dir, self.role_name)
os.makedirs(os.path.join(self.role_dir, "meta"))
# Create a sample README.md with a H1 line for the title.
readme_path = os.path.join(self.role_dir, "README.md")
with open(readme_path, "w", encoding="utf-8") as f:
f.write("# Portfolio Application\nThis is a sample portfolio role.")
# Create a sample meta/main.yml in the meta folder.
meta_main_path = os.path.join(self.role_dir, "meta", "main.yml")
meta_yaml = """
galaxy_info:
description: "A role for deploying a portfolio application."
logo:
class: fa-solid fa-briefcase
"""
with open(meta_main_path, "w", encoding="utf-8") as f:
f.write(meta_yaml)
def tearDown(self):
# Remove the temporary roles directory after the test.
shutil.rmtree(self.test_roles_dir)
def test_lookup_when_group_includes_application_id(self):
# Instantiate the LookupModule.
lookup_module = LookupModule()
# Define dummy variables including group_names that contain the application_id "portfolio".
fake_variables = {
"domains": {"portfolio": "myportfolio.com"},
"applications": {"portfolio": {"landingpage_iframe_enabled": True}},
"group_names": ["portfolio"]
}
result = lookup_module.run([self.test_roles_dir], variables=fake_variables)
# The result is a list containing one list of card dictionaries.
self.assertIsInstance(result, list)
self.assertEqual(len(result), 1)
cards = result[0]
self.assertIsInstance(cards, list)
# Since "portfolio" is in group_names, one card should be present.
self.assertEqual(len(cards), 1)
card = cards[0]
self.assertEqual(card["title"], "Portfolio Application")
self.assertEqual(card["text"], "A role for deploying a portfolio application.")
self.assertEqual(card["icon"]["class"], "fa-solid fa-briefcase")
self.assertEqual(card["url"], "https://myportfolio.com")
self.assertTrue(card["iframe"])
def test_lookup_when_group_excludes_application_id(self):
# Instantiate the LookupModule.
lookup_module = LookupModule()
# Set fake variables with group_names that do NOT include the application_id "portfolio".
fake_variables = {
"domains": {"portfolio": "myportfolio.com"},
"applications": {"portfolio": {"landingpage_iframe_enabled": True}},
"group_names": [] # Not including "portfolio"
}
result = lookup_module.run([self.test_roles_dir], variables=fake_variables)
# Since the application_id is not in group_names, no card should be added.
self.assertIsInstance(result, list)
self.assertEqual(len(result), 1)
cards = result[0]
self.assertIsInstance(cards, list)
self.assertEqual(len(cards), 0)
if __name__ == "__main__":
unittest.main()