diff --git a/cli/fix/move_unnecessary_dependencies.py b/cli/fix/move_unnecessary_dependencies.py index cbfaefcb..93d8843c 100644 --- a/cli/fix/move_unnecessary_dependencies.py +++ b/cli/fix/move_unnecessary_dependencies.py @@ -228,7 +228,7 @@ def parse_meta_dependencies(role_dir: str) -> List[str]: def sanitize_run_once_var(role_name: str) -> str: """ Generate run_once variable name from role name. - Example: 'sys-srv-web-inj-logout' -> 'run_once_sys_srv_web_inj_logout' + Example: 'sys-front-inj-logout' -> 'run_once_sys_front_inj_logout' """ return "run_once_" + role_name.replace("-", "_") diff --git a/lookup_plugins/local_mtime_qs.py b/lookup_plugins/local_mtime_qs.py new file mode 100644 index 00000000..6e8f4aa2 --- /dev/null +++ b/lookup_plugins/local_mtime_qs.py @@ -0,0 +1,53 @@ +from __future__ import annotations +from ansible.plugins.lookup import LookupBase +from ansible.errors import AnsibleError +import os + +class LookupModule(LookupBase): + """ + Return a cache-busting string based on the LOCAL file's mtime. + + Usage (single path → string via Jinja): + {{ lookup('local_mtime_qs', '/path/to/file.css') }} + -> "?version=1712323456" + + Options: + param (str): query parameter name (default: "version") + mode (str): "qs" (default) → returns "?=" + "epoch" → returns "" + + Multiple paths (returns list, one result per term): + {{ lookup('local_mtime_qs', '/a.js', '/b.js', param='v') }} + """ + + def run(self, terms, variables=None, **kwargs): + if not terms: + return [] + + param = kwargs.get('param', 'version') + mode = kwargs.get('mode', 'qs') + + if mode not in ('qs', 'epoch'): + raise AnsibleError("local_mtime_qs: 'mode' must be 'qs' or 'epoch'") + + results = [] + for term in terms: + path = os.path.abspath(os.path.expanduser(str(term))) + + # Fail fast if path is missing or not a regular file + if not os.path.exists(path): + raise AnsibleError(f"local_mtime_qs: file does not exist: {path}") + if not os.path.isfile(path): + raise AnsibleError(f"local_mtime_qs: not a regular file: {path}") + + try: + mtime = int(os.stat(path).st_mtime) + except OSError as e: + raise AnsibleError(f"local_mtime_qs: cannot stat '{path}': {e}") + + if mode == 'qs': + results.append(f"?{param}={mtime}") + else: # mode == 'epoch' + results.append(str(mtime)) + + return results diff --git a/roles/categories.yml b/roles/categories.yml index 2f8cba1a..ce4c5ecf 100644 --- a/roles/categories.yml +++ b/roles/categories.yml @@ -56,6 +56,16 @@ roles: description: "Stack levels to setup the server" icon: "fas fa-bars-staggered" invokable: false + front: + title: "System Frontend Helpers" + description: "Frontend helpers for reverse-proxied apps (injection, shared assets, CDN plumbing)." + icon: "fas fa-wand-magic-sparkles" + invokable: false + inj: + title: "Injection" + description: "Composable HTML injection roles (CSS, JS, logout interceptor, analytics, desktop iframe) for Nginx/OpenResty via sub_filter/Lua with CDN-backed assets." + icon: "fas fa-filter" + invokable: false update: title: "Updates & Package Management" description: "OS & package updates" @@ -106,11 +116,6 @@ roles: description: "General server roles for provisioning and managing server infrastructure—covering web servers, proxy servers, network services, and other backend components." icon: "fas fa-server" invokable: false - web: - title: "Webserver" - description: "Web-server roles for installing and configuring Nginx (core, TLS, injection filters, composer modules)." - icon: "fas fa-server" - invokable: false proxy: title: "Proxy Server" description: "Proxy-server roles for virtual-host orchestration and reverse-proxy setups." diff --git a/roles/dev-locales/tasks/main.yml b/roles/dev-locales/tasks/main.yml index 0c6ffc10..09c90c56 100644 --- a/roles/dev-locales/tasks/main.yml +++ b/roles/dev-locales/tasks/main.yml @@ -1,8 +1,14 @@ --- - name: Setup locale.gen - template: src=locale.gen dest=/etc/locale.gen + template: + src: locale.gen.j2 + dest: /etc/locale.gen + - name: Setup locale.conf - template: src=locale.conf dest=/etc/locale.conf + template: + src: locale.conf.j2 + dest: /etc/locale.conf + - name: Generate locales shell: locale-gen become: true diff --git a/roles/dev-locales/templates/locale.conf b/roles/dev-locales/templates/locale.conf deleted file mode 100644 index 456b20a7..00000000 --- a/roles/dev-locales/templates/locale.conf +++ /dev/null @@ -1,2 +0,0 @@ -LANG=en_US.UTF-8 -LANGUAGE=en_US.UTF-8 diff --git a/roles/dev-locales/templates/locale.conf.j2 b/roles/dev-locales/templates/locale.conf.j2 new file mode 100644 index 00000000..d5f9520d --- /dev/null +++ b/roles/dev-locales/templates/locale.conf.j2 @@ -0,0 +1,2 @@ +LANG={{ HOST_LL_CC }}.UTF-8 +LANGUAGE={{ HOST_LL_CC }}.UTF-8 diff --git a/roles/dev-locales/templates/locale.gen b/roles/dev-locales/templates/locale.gen.j2 similarity index 100% rename from roles/dev-locales/templates/locale.gen rename to roles/dev-locales/templates/locale.gen.j2 diff --git a/roles/srv-composer/README.md b/roles/srv-composer/README.md index 637c7557..6cc85e03 100644 --- a/roles/srv-composer/README.md +++ b/roles/srv-composer/README.md @@ -2,7 +2,7 @@ This Ansible role composes and orchestrates all necessary HTTPS-layer tasks and HTML-content injections for your webserver domains. It integrates two key sub-roles into a unified workflow: -1. **`sys-srv-web-inj-compose`** +1. **`sys-front-inj-all`** Injects global HTML snippets (CSS, Matomo tracking, iFrame notifier, custom JavaScript) into responses using Nginx `sub_filter`. 2. **`sys-svc-certs`** Handles issuing, renewing, and managing TLS certificates via ACME/Certbot. diff --git a/roles/srv-composer/tasks/main.yml b/roles/srv-composer/tasks/main.yml index c5447e78..9bd3e06f 100644 --- a/roles/srv-composer/tasks/main.yml +++ b/roles/srv-composer/tasks/main.yml @@ -1,8 +1,8 @@ # run_once_srv_composer: deactivated -- name: "include role sys-srv-web-inj-compose for '{{ domain }}'" +- name: "include role sys-front-inj-all for '{{ domain }}'" include_role: - name: sys-srv-web-inj-compose + name: sys-front-inj-all - name: "include role sys-svc-certs for '{{ domain }}'" include_role: diff --git a/roles/srv-proxy-core/templates/location/html.conf.j2 b/roles/srv-proxy-core/templates/location/html.conf.j2 index 73e96fec..1b02f204 100644 --- a/roles/srv-proxy-core/templates/location/html.conf.j2 +++ b/roles/srv-proxy-core/templates/location/html.conf.j2 @@ -35,6 +35,6 @@ location {{location}} {% if proxy_lua_enabled %} proxy_set_header Accept-Encoding ""; - {% include 'roles/sys-srv-web-inj-compose/templates/location.lua.j2'%} + {% include 'roles/sys-front-inj-all/templates/location.lua.j2'%} {% endif %} } \ No newline at end of file diff --git a/roles/srv-proxy-core/templates/vhost/basic.conf.j2 b/roles/srv-proxy-core/templates/vhost/basic.conf.j2 index 2d4841c2..56372e97 100644 --- a/roles/srv-proxy-core/templates/vhost/basic.conf.j2 +++ b/roles/srv-proxy-core/templates/vhost/basic.conf.j2 @@ -7,7 +7,7 @@ server {% include 'roles/web-app-oauth2-proxy/templates/endpoint.conf.j2'%} {% endif %} - {% include 'roles/sys-srv-web-inj-compose/templates/server.conf.j2'%} + {% include 'roles/sys-front-inj-all/templates/server.conf.j2'%} {% if proxy_extra_configuration is defined %} {# Additional Domain Specific Configuration #} diff --git a/roles/srv-proxy-core/templates/vhost/ws_generic.conf.j2 b/roles/srv-proxy-core/templates/vhost/ws_generic.conf.j2 index 5d819bf8..82b5ba86 100644 --- a/roles/srv-proxy-core/templates/vhost/ws_generic.conf.j2 +++ b/roles/srv-proxy-core/templates/vhost/ws_generic.conf.j2 @@ -8,7 +8,7 @@ server { {% include 'roles/srv-letsencrypt/templates/ssl_header.j2' %} - {% include 'roles/sys-srv-web-inj-compose/templates/server.conf.j2' %} + {% include 'roles/sys-front-inj-all/templates/server.conf.j2' %} client_max_body_size {{ client_max_body_size | default('100m') }}; keepalive_timeout 70; diff --git a/roles/sys-srv-web-inj-compose/README.md b/roles/sys-front-inj-all/README.md similarity index 100% rename from roles/sys-srv-web-inj-compose/README.md rename to roles/sys-front-inj-all/README.md diff --git a/roles/sys-srv-web-inj-compose/__init__.py b/roles/sys-front-inj-all/__init__.py similarity index 100% rename from roles/sys-srv-web-inj-compose/__init__.py rename to roles/sys-front-inj-all/__init__.py diff --git a/roles/sys-srv-web-inj-compose/filter_plugins/__init__.py b/roles/sys-front-inj-all/filter_plugins/__init__.py similarity index 100% rename from roles/sys-srv-web-inj-compose/filter_plugins/__init__.py rename to roles/sys-front-inj-all/filter_plugins/__init__.py diff --git a/roles/sys-srv-web-inj-compose/filter_plugins/inj_enabled.py b/roles/sys-front-inj-all/filter_plugins/inj_enabled.py similarity index 94% rename from roles/sys-srv-web-inj-compose/filter_plugins/inj_enabled.py rename to roles/sys-front-inj-all/filter_plugins/inj_enabled.py index 5b1b031a..7e3c87ff 100644 --- a/roles/sys-srv-web-inj-compose/filter_plugins/inj_enabled.py +++ b/roles/sys-front-inj-all/filter_plugins/inj_enabled.py @@ -1,4 +1,3 @@ -# roles/sys-srv-web-inj-compose/filter_plugins/inj_enabled.py # # Usage in tasks: # - set_fact: diff --git a/roles/sys-srv-web-inj-compose/filter_plugins/inj_snippets.py b/roles/sys-front-inj-all/filter_plugins/inj_snippets.py similarity index 79% rename from roles/sys-srv-web-inj-compose/filter_plugins/inj_snippets.py rename to roles/sys-front-inj-all/filter_plugins/inj_snippets.py index 424cdf8f..285cc769 100644 --- a/roles/sys-srv-web-inj-compose/filter_plugins/inj_snippets.py +++ b/roles/sys-front-inj-all/filter_plugins/inj_snippets.py @@ -2,10 +2,10 @@ Jinja filter: `inj_features(kind)` filters a list of features to only those that actually provide the corresponding snippet template file. -- kind='head' -> roles/sys-srv-web-inj-/templates/head_sub.j2 -- kind='body' -> roles/sys-srv-web-inj-/templates/body_sub.j2 +- kind='head' -> roles/sys-front-inj-/templates/head_sub.j2 +- kind='body' -> roles/sys-front-inj-/templates/body_sub.j2 -If the feature's role directory (roles/sys-srv-web-inj-) does not +If the feature's role directory (roles/sys-front-inj-) does not exist, this filter raises FileNotFoundError. Usage in a template: @@ -15,13 +15,13 @@ Usage in a template: import os -# This file lives at: roles/sys-srv-web-inj-compose/filter_plugins/inj_snippets.py +# This file lives at: roles/sys-front-inj-all/filter_plugins/inj_snippets.py _THIS_DIR = os.path.dirname(__file__) -_ROLE_DIR = os.path.abspath(os.path.join(_THIS_DIR, "..")) # roles/sys-srv-web-inj-compose +_ROLE_DIR = os.path.abspath(os.path.join(_THIS_DIR, "..")) # roles/sys-front-inj-all _ROLES_DIR = os.path.abspath(os.path.join(_ROLE_DIR, "..")) # roles def _feature_role_dir(feature: str) -> str: - return os.path.join(_ROLES_DIR, f"sys-srv-web-inj-{feature}") + return os.path.join(_ROLES_DIR, f"sys-front-inj-{feature}") def _has_snippet(feature: str, kind: str) -> bool: if kind not in ("head", "body"): diff --git a/roles/sys-srv-web-inj-compose/meta/main.yml b/roles/sys-front-inj-all/meta/main.yml similarity index 96% rename from roles/sys-srv-web-inj-compose/meta/main.yml rename to roles/sys-front-inj-all/meta/main.yml index 3839faea..18f3f843 100644 --- a/roles/sys-srv-web-inj-compose/meta/main.yml +++ b/roles/sys-front-inj-all/meta/main.yml @@ -14,7 +14,7 @@ galaxy_info: - theming repository: "https://s.infinito.nexus/code" issue_tracker_url: "https://s.infinito.nexus/issues" - documentation: "https://s.infinito.nexus/code/tree/main/roles/sys-srv-web-inj-compose" + documentation: "https://s.infinito.nexus/code/tree/main/roles/sys-front-inj-all" min_ansible_version: "2.9" platforms: - name: Any diff --git a/roles/sys-srv-web-inj-compose/tasks/main.yml b/roles/sys-front-inj-all/tasks/main.yml similarity index 58% rename from roles/sys-srv-web-inj-compose/tasks/main.yml rename to roles/sys-front-inj-all/tasks/main.yml index 91518243..72e2df03 100644 --- a/roles/sys-srv-web-inj-compose/tasks/main.yml +++ b/roles/sys-front-inj-all/tasks/main.yml @@ -2,39 +2,10 @@ set_fact: inj_enabled: "{{ applications | inj_enabled(application_id, SRV_WEB_INJ_COMP_FEATURES_ALL) }}" -- block: - - name: Include dependency 'srv-core' - include_role: - name: srv-core - when: run_once_srv_core is not defined - - include_tasks: utils/run_once.yml - when: run_once_sys_srv_web_inj_compose is not defined - -- name: "Activate Portfolio iFrame notifier for '{{ domain }}'" +- name: "Load CDN Service for '{{ domain }}'" include_role: - name: sys-srv-web-inj-desktop - public: true # Vars used in templates - when: inj_enabled.desktop - -- name: "Load CDN for '{{ domain }}'" - include_role: - name: web-svc-cdn - public: false - when: - - inj_enabled.logout - - inj_enabled.desktop - - application_id != 'web-svc-cdn' - - run_once_web_svc_cdn is not defined - -- name: Overwritte CDN handlers with neutral handlers - ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/utils/load_handlers.yml" - loop: - - svc-prx-openresty - - docker-compose - loop_control: - label: "{{ item }}" - vars: - handler_role_name: "{{ item }}" + name: sys-svc-cdn + public: true # Expose variables so that they can be used in all injection roles - name: Reinitialize 'inj_enabled' for '{{ domain }}', after modification by CDN set_fact: @@ -42,25 +13,37 @@ inj_head_features: "{{ SRV_WEB_INJ_COMP_FEATURES_ALL | inj_features('head') }}" inj_body_features: "{{ SRV_WEB_INJ_COMP_FEATURES_ALL | inj_features('body') }}" +- name: "Activate Desktop iFrame notifier for '{{ domain }}'" + include_role: + name: sys-front-inj-desktop + public: true # Vars used in templates + when: inj_enabled.desktop + - name: "Activate Corporate CSS for '{{ domain }}'" include_role: - name: sys-srv-web-inj-css - when: - - inj_enabled.css - - run_once_sys_srv_web_inj_css is not defined + name: sys-front-inj-css + when: inj_enabled.css - name: "Activate Matomo Tracking for '{{ domain }}'" include_role: - name: sys-srv-web-inj-matomo + name: sys-front-inj-matomo when: inj_enabled.matomo - name: "Activate Javascript for '{{ domain }}'" include_role: - name: sys-srv-web-inj-javascript + name: sys-front-inj-javascript when: inj_enabled.javascript - name: "Activate logout proxy for '{{ domain }}'" include_role: - name: sys-srv-web-inj-logout + name: sys-front-inj-logout public: true # Vars used in templates when: inj_enabled.logout + +- block: + - name: Include dependency 'srv-core' + include_role: + name: srv-core + when: run_once_srv_core is not defined + - include_tasks: utils/run_once.yml + when: run_once_sys_front_inj_all is not defined \ No newline at end of file diff --git a/roles/sys-srv-web-inj-compose/templates/location.lua.j2 b/roles/sys-front-inj-all/templates/location.lua.j2 similarity index 97% rename from roles/sys-srv-web-inj-compose/templates/location.lua.j2 rename to roles/sys-front-inj-all/templates/location.lua.j2 index d0dcfa5c..304c9b87 100644 --- a/roles/sys-srv-web-inj-compose/templates/location.lua.j2 +++ b/roles/sys-front-inj-all/templates/location.lua.j2 @@ -3,7 +3,7 @@ {% set kind = list_name | regex_replace('_snippets$','') %} {% for f in features if inj_enabled.get(f) -%} {{ list_name }}[#{{ list_name }} + 1] = [=[ - {%- include 'roles/sys-srv-web-inj-' ~ f ~ '/templates/' ~ kind ~ '_sub.j2' -%} + {%- include 'roles/sys-front-inj-' ~ f ~ '/templates/' ~ kind ~ '_sub.j2' -%} ]=] {% endfor -%} {%- endmacro %} diff --git a/roles/sys-srv-web-inj-compose/templates/server.conf.j2 b/roles/sys-front-inj-all/templates/server.conf.j2 similarity index 50% rename from roles/sys-srv-web-inj-compose/templates/server.conf.j2 rename to roles/sys-front-inj-all/templates/server.conf.j2 index 97fae282..e1bec5e5 100644 --- a/roles/sys-srv-web-inj-compose/templates/server.conf.j2 +++ b/roles/sys-front-inj-all/templates/server.conf.j2 @@ -1,7 +1,3 @@ -{% if inj_enabled.css %} -{% include 'roles/sys-srv-web-inj-css/templates/location.conf.j2' %} -{% endif %} - {% if inj_enabled.logout %} {% include 'roles/web-svc-logout/templates/logout-proxy.conf.j2' %} {% endif %} \ No newline at end of file diff --git a/roles/sys-srv-web-inj-compose/vars/main.yml b/roles/sys-front-inj-all/vars/main.yml similarity index 100% rename from roles/sys-srv-web-inj-compose/vars/main.yml rename to roles/sys-front-inj-all/vars/main.yml diff --git a/roles/sys-srv-web-inj-css/README.md b/roles/sys-front-inj-css/README.md similarity index 77% rename from roles/sys-srv-web-inj-css/README.md rename to roles/sys-front-inj-css/README.md index e0332311..d1901d4e 100644 --- a/roles/sys-srv-web-inj-css/README.md +++ b/roles/sys-front-inj-css/README.md @@ -2,12 +2,12 @@ ## Description -This Ansible role ensures **consistent global theming** across all Nginx-served applications by injecting a unified `global.css` file. +This Ansible role ensures **consistent global theming** across all Nginx-served applications by injecting CSS files. The role leverages [`colorscheme-generator`](https://github.com/kevinveenbirkenbach/colorscheme-generator/) to generate a dynamic, customizable color palette for light and dark mode, compatible with popular web tools like **Bootstrap**, **Keycloak**, **Nextcloud**, **Taiga**, **Mastodon**, and many more. ## Overview -This role deploys a centralized global stylesheet (`global.css`) that overrides the default theming of web applications served via Nginx. It's optimized to run only once per deployment and generates a **cache-busting version number** based on file modification timestamps. +This role deploys a centralized global stylesheet that overrides the default theming of web applications served via Nginx. It's optimized to run only once per deployment and generates a **cache-busting version number** based on file modification timestamps. It includes support for **dark mode**, **custom fonts**, and **extensive Bootstrap and UI component overrides**. ## Purpose @@ -18,7 +18,7 @@ It makes all applications feel like part of the same ecosystem — visually and ## Features - 🎨 **Dynamic Theming** via [`colorscheme-generator`](https://github.com/kevinveenbirkenbach/colorscheme-generator/) -- 📁 **Unified global.css** deployment for all Nginx applications +- 📁 **Unified CSS Base Configuration** deployment for all Nginx applications - 🌒 **Dark mode support** out of the box - 🚫 **No duplication** – tasks run once per deployment - ⏱️ **Versioning logic** to bust browser cache diff --git a/roles/sys-srv-web-inj-css/meta/main.yml b/roles/sys-front-inj-css/meta/main.yml similarity index 100% rename from roles/sys-srv-web-inj-css/meta/main.yml rename to roles/sys-front-inj-css/meta/main.yml diff --git a/roles/sys-front-inj-css/tasks/01_core.yml b/roles/sys-front-inj-css/tasks/01_core.yml new file mode 100644 index 00000000..1bacec75 --- /dev/null +++ b/roles/sys-front-inj-css/tasks/01_core.yml @@ -0,0 +1,21 @@ +- name: Include dependency 'srv-core' + include_role: + name: srv-core + when: run_once_srv_core is not defined + +- name: Generate color palette with colorscheme-generator + set_fact: + color_palette: "{{ lookup('colorscheme', CSS_BASE_COLOR, count=CSS_COUNT, shades=CSS_SHADES) }}" + +- name: Generate inverted color palette with colorscheme-generator + set_fact: + inverted_color_palette: "{{ lookup('colorscheme', CSS_BASE_COLOR, count=CSS_COUNT, shades=CSS_SHADES, invert_lightness=True) }}" + +- name: Deploy default CSS files + template: + src: "{{ ['css', item ~ '.j2'] | path_join }}" + dest: "{{ [cdn.shared.css, item] | path_join }}" + owner: "{{ NGINX.USER }}" + group: "{{ NGINX.USER }}" + mode: '0644' + loop: "{{ CSS_FILES }}" diff --git a/roles/sys-front-inj-css/tasks/main.yml b/roles/sys-front-inj-css/tasks/main.yml new file mode 100644 index 00000000..5529a772 --- /dev/null +++ b/roles/sys-front-inj-css/tasks/main.yml @@ -0,0 +1,25 @@ +- block: + - include_tasks: 01_core.yml + - include_tasks: utils/run_once.yml + when: run_once_sys_front_inj_css is not defined + +- name: "Resolve optional app style.css source for '{{ application_id }}'" + vars: + app_role_dir: "{{ playbook_dir }}/roles/{{ application_id }}" + _app_style_src: >- + {{ lookup('first_found', { + 'files': ['templates/style.css.j2','files/style.css'], + 'paths': [app_role_dir] + }, errors='ignore') | default('', true) }} + set_fact: + app_style_src: "{{ _app_style_src }}" + app_style_present: "{{ _app_style_src | length > 0 }}" + +- name: "Deploy per-app '{{ app_style_src }}' to '{{ css_app_dst }}'" + when: app_style_present + copy: + content: "{{ lookup('template', app_style_src) }}" + dest: "{{ css_app_dst }}" + owner: "{{ NGINX.USER }}" + group: "{{ NGINX.USER }}" + mode: '0644' \ No newline at end of file diff --git a/roles/sys-front-inj-css/templates/css/bootstrap.css.j2 b/roles/sys-front-inj-css/templates/css/bootstrap.css.j2 new file mode 100644 index 00000000..70b01ba7 --- /dev/null +++ b/roles/sys-front-inj-css/templates/css/bootstrap.css.j2 @@ -0,0 +1,69 @@ + +/* Buttons (Background, Text, Border, and Shadow) + Now using a button background that is only slightly darker than the overall background */ +html[native-dark-active] .btn, .btn { + background-color: var(--color-01-87); + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-70), var(--color-01-91), var(--color-01-95), var(--color-01-95)); + color: var(--color-01-50); + border-color: var(--color-01-80); + cursor: pointer; +} + +/* Navigation (Background and Text Colors) */ +.navbar, .navbar-light, .navbar-dark, .navbar.bg-light { + background-color: var(--color-01-90); + /* New Gradient based on original background (90 -5, 90, 90 +1, 90 +5) */ + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-85), var(--color-01-90), var(--color-01-91), var(--color-01-95)); + color: var(--color-01-50); + border-color: var(--color-01-85); +} + +.navbar a { + color: var(--color-01-40); +} + +.navbar a.dropdown-item { + color: var(--color-01-43); +} + +/* Cards / Containers (Background, Border, and Shadow) + Cards now use a slightly lighter background and a bold, clear shadow */ +.card { + background-color: var(--color-01-90); + /* New Gradient based on original background (90 -5, 90, 90 +1, 90 +5) */ + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-85), var(--color-01-90), var(--color-01-91), var(--color-01-95)); + border-color: var(--color-01-85); + color: var(--color-01-12); +} + +.card-body { + color: var(--color-01-40); +} + +/* Dropdown Menu and Submenu (Background, Text, and Shadow) */ +.navbar .dropdown-menu, +.nav-item .dropdown-menu { + background-color: var(--color-01-80); + /* New Gradient based on original background (80 -5, 80, 80 +1, 80 +5) */ + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-75), var(--color-01-80), var(--color-01-81), var(--color-01-85)); + color: var(--color-01-40); +} + +.navbar-nav { + --bs-nav-link-hover-color: var(--color-01-17); +} + +.dropdown-item { + color: var(--color-01-40); + background-color: var(--color-01-80); + /* New Gradient based on original background (80 -5, 80, 80 +1, 80 +5) */ + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-75), var(--color-01-80), var(--color-01-81), var(--color-01-85)); +} + +.dropdown-item:hover, +.dropdown-item:focus { + background-color: var(--color-01-65); + /* New Gradient based on original background (65 -5, 65, 65 +1, 65 +5) */ + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-60), var(--color-01-65), var(--color-01-66), var(--color-01-70)); + color: var(--color-01-40); +} diff --git a/roles/sys-front-inj-css/templates/css/default.css.j2 b/roles/sys-front-inj-css/templates/css/default.css.j2 new file mode 100644 index 00000000..667af01d --- /dev/null +++ b/roles/sys-front-inj-css/templates/css/default.css.j2 @@ -0,0 +1,297 @@ +/*** + +Global Theming Styles – Color and Shadow Variables + +HINT: +- Better overwritte CSS variables instead of individual elements. +- Don't use !important. If possible use a specific selector. + +*/ + +{% if design.font.import_url %} +@import url('{{ design.font.import_url }}'); +{% endif %} + +/* Auto-generated by colorscheme-generator */ + +:root { + {% for var_name, color in color_palette.items() %} + {{ var_name }}: {{ color }}; + {% endfor %} +} + +@media (prefers-color-scheme: dark) { + :root { + {% for var_name, color in inverted_color_palette.items() %} + {{ var_name }}: {{ color }}; + {% endfor %} + } +} + +:root, ::after, ::before, ::backdrop { + /* For Dark Mode Plugin + * @See https://chromewebstore.google.com/detail/dark-mode/dmghijelimhndkbmpgbldicpogfkceaj + */ + --native-dark-accent-color: var(--color-01-60); /* was #a9a9a9 */ + --native-dark-bg-color: var(--color-01-10); /* was #292929 */ + --native-dark-bg-image-color: rgba(var(--color-01-rgb-01), 0.10); /* remains the same, or adjust if needed */ + --native-dark-border-color: var(--color-01-40); /* was #555555 */ + --native-dark-box-shadow: 0 0 0 1px rgb(var(--color-01-rgb-99), / 10%); + --native-dark-cite-color: var(--color-01-70); /* was #92de92 – you might adjust if a green tone is needed */ + --native-dark-fill-color: var(--color-01-50); /* was #7d7d7d */ + --native-dark-font-color: var(--color-01-95); /* was #dcdcdc */ + --native-dark-link-color: var(--color-01-80); /* was #8db2e5 */ + --native-dark-visited-link-color: var(--color-01-85); /* was #c76ed7 */ +} + +/* Bootstrap Overrides (Color/Shadow Variables Only) */ +:root { + --bs-black: var(--color-01-01); /* Original tone: Black (#000) */ + --bs-white: var(--color-01-99); /* Original tone: White (#fff) */ + --bs-gray: var(--color-01-50); /* Original tone: Gray (#6c757d) */ + --bs-gray-dark: var(--color-01-20); /* Original tone: Dark Gray (#343a40) */ +{% for i in range(1, 10) %} +{# @see https://chatgpt.com/share/67bcd94e-bb44-800f-bf63-06d1ae0f5096 #} + {% set gray = i * 100 %} + {% set color = 100 - i * 10 %} + --bs-gray-{{ gray }}: var(--color-01-{{ "%02d" % color }}); +{% endfor %} + --bs-primary: var(--color-01-65); /* Original tone: Blue (#0d6efd) */ + --bs-light: var(--color-01-95); /* Original tone: Light (#f8f9fa) */ + --bs-dark: var(--color-01-10); /* Original tone: Dark (#212529) */ + --bs-primary-rgb: var(--color-01-rgb-65); /* Original tone: Blue (13, 110, 253) */ + --bs-secondary-rgb: var(--color-01-rgb-50); /* Original tone: Grayish (#6c757d / 108, 117, 125) */ + --bs-light-rgb: var(--color-01-rgb-95); /* Original tone: Light (248, 249, 250) */ + --bs-dark-rgb: var(--color-01-rgb-10); /* Original tone: Dark (33, 37, 41) */ + --bs-white-rgb: var(--color-01-rgb-99); /* Original tone: White (255, 255, 255) */ + --bs-black-rgb: var(--color-01-rgb-01); /* Original tone: Black (0, 0, 0) */ + --bs-body-color-rgb: var(--color-01-rgb-10); /* Original tone: Dark (#212529 / 33, 37, 41) */ + --bs-body-bg-rgb: var(--color-01-rgb-99); /* Original tone: White (#fff / 255, 255, 255) */ + --bs-body-color: var(--color-01-10); /* Original tone: Dark (#212529) */ + --bs-body-bg: var(--color-01-99); /* Original tone: White (#fff) */ + --bs-border-color: var(--color-01-85); /* Original tone: Gray (#dee2e6) */ + --bs-link-color: var(--color-01-65); /* Original tone: Blue (#0d6efd) */ + --bs-link-hover-color: var(--color-01-60); /* Original tone: Darker Blue (#0a58ca) */ + --bs-code-color: var(--color-01-55); /* Original tone: Pink (#d63384) */ + --bs-highlight-bg: var(--color-01-93); /* Original tone: Light Yellow (#fff3cd) */ + --bs-list-group-bg: var(--color-01-40); + --bs-emphasis-color: var(--color-01-01); /* Gemappt von #000 */ + --bs-emphasis-color-rgb: var(--color-01-rgb-01); /* Gemappt von 0, 0, 0 */ + --bs-secondary-color: rgba(var(--color-01-rgb-10), 0.75); /* Gemappt von rgba(33, 37, 41, 0.75) */ + --bs-secondary-color-rgb: var(--color-01-rgb-10); /* Gemappt von 33, 37, 41 */ + --bs-secondary-bg: var(--color-01-90); /* Gemappt von #e9ecef */ + --bs-secondary-bg-rgb: var(--color-01-rgb-90); /* Gemappt von 233, 236, 239 */ + --bs-tertiary-color: rgba(var(--color-01-rgb-10), 0.5); /* Gemappt von rgba(33, 37, 41, 0.5) */ + --bs-tertiary-color-rgb: var(--color-01-rgb-10); /* Gemappt von 33, 37, 41 */ + --bs-tertiary-bg: var(--color-01-95); /* Gemappt von #f8f9fa */ + --bs-tertiary-bg-rgb: var(--color-01-rgb-95); /* Gemappt von 248, 249, 250 */ + --bs-link-color-rgb: var(--color-01-rgb-65); /* Gemappt von 13, 110, 253 */ + --bs-link-hover-color-rgb: var(--color-01-rgb-60); /* Gemappt von 10, 88, 202 */ + --bs-highlight-color: var(--color-01-10); /* Gemappt von #212529 */ + --bs-border-color-translucent: rgba(var(--color-01-rgb-01), 0.175); /* Gemappt von rgba(0, 0, 0, 0.175) */ + --bs-focus-ring-color: rgba(var(--color-01-rgb-65), 0.25); /* Gemappt von rgba(13, 110, 253, 0.25) */ + + --bs-table-color: var(--bs-emphasis-color); + --bs-table-bg: var(--color-01-99); /* White (#fff) */ + --bs-table-border-color: var(--color-01-99); /* White (#fff) */ + --bs-table-striped-bg: var(--color-01-85); /* Light Gray (entspricht ca. #dee2e6) */ + --bs-table-hover-color: var(--color-01-01); /* Black (#000) */ + --bs-table-hover-bg: rgba(var(--bs-emphasis-color-rgb), 0.075); +} + +/* Global Defaults (Colors Only) */ +body, html[native-dark-active] { + background-color: var(--color-01-93); + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-93), var(--color-01-91), var(--color-01-95), var(--color-01-93)); + background-attachment: fixed; + color: var(--color-01-40); + font-family: {{design.font.type}}; +} + +{# All links (applies to all anchor elements regardless of state) #} +a { + color: var(--color-01-50); +} + +{# Unvisited links (applies only to links that have not been visited) #} +a:link { + color: var(--color-01-55); +} + +{# Visited links (applies only to links that have been visited) #} +a:visited { + color: var(--color-01-45); +} + +{# Hover state (applies when the mouse pointer is over the link) #} +a:hover { + color: var(--color-01-60); +} + +{# Active state (applies during the time the link is being activated, e.g., on click) #} +a:active { + color: var(--color-01-65); +} + +/** Set default buttons transparent **/ +html[native-dark-active] button, button{ + background-color: var(--color-01-87); +} + +button:hover, .btn:hover { + filter: brightness(0.9); +} + +/* {# Invalid state: when the input value fails validation criteria. Use danger color for error indication. #} */ +input:invalid, +textarea:invalid, +select:invalid { + background-color: var(--color-01-01); + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-01), var(--color-01-10)); + /* Use Bootstrap danger color for error messages */ + color: var(--bs-danger); + border-color: var(--color-01-20); +} + +/* {# Valid state: when the input value meets all validation criteria. Use success color for confirmation. #} */ +input:valid, +textarea:valid, +select:valid { + background-color: var(--color-01-80); + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-80), var(--color-01-90)); + /* Use Bootstrap success color for confirmation messages */ + color: var(--bs-success); + border-color: var(--color-01-70); +} + +/* {# Required field: applied to elements that must be filled out by the user. Use warning color for emphasis. #} */ +input:required, +textarea:required, +select:required { + background-color: var(--color-01-50); + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-50), var(--color-01-60)); + /* Use Bootstrap warning color to indicate a required field */ + color: var(--bs-warning); + border-color: var(--color-01-70); +} + +/* {# Optional field: applied to elements that are not mandatory. Use info color to denote additional information. #} */ +input:optional, +textarea:optional, +select:optional { + background-color: var(--color-01-60); + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-60), var(--color-01-70)); + /* Use Bootstrap info color to indicate optional information */ + color: var(--bs-info); + border-color: var(--color-01-70); +} + +/* {# Read-only state: when an element is not editable by the user. #} */ +input:read-only, +textarea:read-only, +select:read-only { + background-color: var(--color-01-80); + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-90), var(--color-01-70)); + color: var(--color-01-20); + border-color: var(--color-01-50); +} + +/* {# Read-write state: when an element is editable by the user. #} */ +input:read-write, +textarea:read-write, +select:read-write { + background-color: var(--color-01-70); + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-70), var(--color-01-80)); + color: var(--color-01-40); + border-color: var(--color-01-70); +} + +/* {# In-range: for inputs with a defined range, when the value is within the allowed limits. #} */ +input:in-range, +textarea:in-range, +select:in-range { + background-color: var(--color-01-70); + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-70), var(--color-01-85)); + color: var(--color-01-40); + border-color: var(--color-01-70); +} + +/* {# Out-of-range: for inputs with a defined range, when the value falls outside the allowed limits. #} */ +input:out-of-range, +textarea:out-of-range, +select:out-of-range { + background-color: var(--color-01-10); + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-10), var(--color-01-30)); + color: var(--color-01-10); + border-color: var(--color-01-50); +} + +/* {# Placeholder-shown: when the input field is displaying its placeholder text. #} */ +input:placeholder-shown, +textarea:placeholder-shown, +select:placeholder-shown { + background-color: var(--color-01-82); + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-82), var(--color-01-90)); + color: var(--color-01-40); + border-color: var(--color-01-70); +} + +/* {# Focus state: when the element is focused by the user. #} */ +input:focus, +textarea:focus, +select:focus { + background-color: var(--color-01-75); + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-75), var(--color-01-85)); + color: var(--color-01-40); + border-color: var(--color-01-50); +} + +/* {# Hover state: when the mouse pointer is over the element. #} */ +input:hover, +textarea:hover, +select:hover { + background-color: var(--color-01-78); + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-78), var(--color-01-88)); + color: var(--color-01-40); + border-color: var(--color-01-65); +} + +/* {# Active state: when the element is being activated (e.g., clicked). #} */ +input:active, +textarea:active, +select:active { + background-color: var(--color-01-68); + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-68), var(--color-01-78)); + color: var(--color-01-40); + border-color: var(--color-01-60); +} + +/* {# Checked state: specifically for radio buttons and checkboxes when selected. #} */ +input:checked { + background-color: var(--color-01-90); + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-90), var(--color-01-99)); + color: var(--color-01-40); + border-color: var(--color-01-70); +} + +option { + background-color: var(--color-01-82); + color: var(--color-01-07); +} + +/* Tables (Borders and Header Colors) */ +th, td { + border-color: var(--color-01-70); +} + +thead { + background-color: var(--color-01-80); + /* New Gradient based on original background (80 -5, 80, 80 +1, 80 +5) */ + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-75), var(--color-01-80), var(--color-01-81), var(--color-01-85)); + color: var(--color-01-40); +} + +/* Headings (Text Color) */ +h1, h2, h3, h4, h5, h6, p{ + color: var(--color-01-10); +} diff --git a/roles/sys-front-inj-css/templates/head_sub.j2 b/roles/sys-front-inj-css/templates/head_sub.j2 new file mode 100644 index 00000000..740ce22a --- /dev/null +++ b/roles/sys-front-inj-css/templates/head_sub.j2 @@ -0,0 +1,8 @@ +{% set __css_tpl_dir = [playbook_dir, 'roles', 'sys-front-inj-css', 'templates', 'css'] | path_join %} + +{% for css_file in ['default.css','bootstrap.css'] %} + +{% endfor %} +{% if app_style_present | bool %} + +{% endif %} \ No newline at end of file diff --git a/roles/sys-front-inj-css/vars/main.yml b/roles/sys-front-inj-css/vars/main.yml new file mode 100644 index 00000000..9e0f84f2 --- /dev/null +++ b/roles/sys-front-inj-css/vars/main.yml @@ -0,0 +1,8 @@ +# Constants +CSS_FILES: ['default.css','bootstrap.css'] +CSS_BASE_COLOR: "{{ design.css.colors.base }}" +CSS_COUNT: 7 +CSS_SHADES: 100 + +# Variables +css_app_dst: "{{ [cdn.role.release.css, 'style.css'] | path_join }}" \ No newline at end of file diff --git a/roles/sys-srv-web-inj-desktop/README.md b/roles/sys-front-inj-desktop/README.md similarity index 100% rename from roles/sys-srv-web-inj-desktop/README.md rename to roles/sys-front-inj-desktop/README.md diff --git a/roles/sys-srv-web-inj-desktop/meta/main.yml b/roles/sys-front-inj-desktop/meta/main.yml similarity index 100% rename from roles/sys-srv-web-inj-desktop/meta/main.yml rename to roles/sys-front-inj-desktop/meta/main.yml diff --git a/roles/sys-front-inj-desktop/tasks/01_deploy.yml b/roles/sys-front-inj-desktop/tasks/01_deploy.yml new file mode 100644 index 00000000..ca1dd08d --- /dev/null +++ b/roles/sys-front-inj-desktop/tasks/01_deploy.yml @@ -0,0 +1,7 @@ +- name: Deploy {{ INJ_DESKTOP_JS_FILE_NAME }} + template: + src: "{{ INJ_DESKTOP_JS_FILE_NAME }}.j2" + dest: "{{ INJ_DESKTOP_JS_FILE_DESTINATION }}" + owner: "{{ NGINX.USER }}" + group: "{{ NGINX.USER }}" + mode: '0644' diff --git a/roles/sys-srv-web-inj-desktop/tasks/main.yml b/roles/sys-front-inj-desktop/tasks/main.yml similarity index 93% rename from roles/sys-srv-web-inj-desktop/tasks/main.yml rename to roles/sys-front-inj-desktop/tasks/main.yml index a96b19e0..30ee17c1 100644 --- a/roles/sys-srv-web-inj-desktop/tasks/main.yml +++ b/roles/sys-front-inj-desktop/tasks/main.yml @@ -5,7 +5,7 @@ when: run_once_srv_core is not defined - include_tasks: 01_deploy.yml - include_tasks: utils/run_once.yml - when: run_once_sys_srv_web_inj_desktop is not defined + when: run_once_sys_front_inj_desktop is not defined # --- Build tiny inline initializer (CSP-hashed) --- - name: "Load iFrame init code for '{{ application_id }}'" diff --git a/roles/sys-srv-web-inj-desktop/templates/body_sub.j2 b/roles/sys-front-inj-desktop/templates/body_sub.j2 similarity index 100% rename from roles/sys-srv-web-inj-desktop/templates/body_sub.j2 rename to roles/sys-front-inj-desktop/templates/body_sub.j2 diff --git a/roles/sys-front-inj-desktop/templates/head_sub.j2 b/roles/sys-front-inj-desktop/templates/head_sub.j2 new file mode 100644 index 00000000..e01e2d1e --- /dev/null +++ b/roles/sys-front-inj-desktop/templates/head_sub.j2 @@ -0,0 +1 @@ + diff --git a/roles/sys-srv-web-inj-desktop/templates/iframe-handler.js.j2 b/roles/sys-front-inj-desktop/templates/iframe-handler.js.j2 similarity index 100% rename from roles/sys-srv-web-inj-desktop/templates/iframe-handler.js.j2 rename to roles/sys-front-inj-desktop/templates/iframe-handler.js.j2 diff --git a/roles/sys-srv-web-inj-desktop/templates/iframe-init_one_liner.js.j2 b/roles/sys-front-inj-desktop/templates/iframe-init_one_liner.js.j2 similarity index 100% rename from roles/sys-srv-web-inj-desktop/templates/iframe-init_one_liner.js.j2 rename to roles/sys-front-inj-desktop/templates/iframe-init_one_liner.js.j2 diff --git a/roles/sys-front-inj-desktop/vars/main.yml b/roles/sys-front-inj-desktop/vars/main.yml new file mode 100644 index 00000000..3db900b5 --- /dev/null +++ b/roles/sys-front-inj-desktop/vars/main.yml @@ -0,0 +1,2 @@ +INJ_DESKTOP_JS_FILE_NAME: "iframe-handler.js" +INJ_DESKTOP_JS_FILE_DESTINATION: "{{ [cdn.shared.js, INJ_DESKTOP_JS_FILE_NAME] | path_join }}" diff --git a/roles/sys-srv-web-inj-javascript/README.md b/roles/sys-front-inj-javascript/README.md similarity index 100% rename from roles/sys-srv-web-inj-javascript/README.md rename to roles/sys-front-inj-javascript/README.md diff --git a/roles/sys-srv-web-inj-javascript/meta/main.yml b/roles/sys-front-inj-javascript/meta/main.yml similarity index 100% rename from roles/sys-srv-web-inj-javascript/meta/main.yml rename to roles/sys-front-inj-javascript/meta/main.yml diff --git a/roles/sys-srv-web-inj-javascript/tasks/main.yml b/roles/sys-front-inj-javascript/tasks/main.yml similarity index 92% rename from roles/sys-srv-web-inj-javascript/tasks/main.yml rename to roles/sys-front-inj-javascript/tasks/main.yml index 4bf9e9be..63eca016 100644 --- a/roles/sys-srv-web-inj-javascript/tasks/main.yml +++ b/roles/sys-front-inj-javascript/tasks/main.yml @@ -5,7 +5,7 @@ name: srv-core when: run_once_srv_core is not defined - include_tasks: utils/run_once.yml - when: run_once_sys_srv_web_inj_javascript is not defined + when: run_once_sys_front_inj_javascript is not defined - name: "Load JavaScript code for '{{ application_id }}'" set_fact: diff --git a/roles/sys-srv-web-inj-javascript/templates/head_sub.j2 b/roles/sys-front-inj-javascript/templates/head_sub.j2 similarity index 100% rename from roles/sys-srv-web-inj-javascript/templates/head_sub.j2 rename to roles/sys-front-inj-javascript/templates/head_sub.j2 diff --git a/roles/sys-srv-web-inj-javascript/vars/main.yml b/roles/sys-front-inj-javascript/vars/main.yml similarity index 100% rename from roles/sys-srv-web-inj-javascript/vars/main.yml rename to roles/sys-front-inj-javascript/vars/main.yml diff --git a/roles/sys-srv-web-inj-logout/README.md b/roles/sys-front-inj-logout/README.md similarity index 78% rename from roles/sys-srv-web-inj-logout/README.md rename to roles/sys-front-inj-logout/README.md index 5698569f..74fa5400 100644 --- a/roles/sys-srv-web-inj-logout/README.md +++ b/roles/sys-front-inj-logout/README.md @@ -1,10 +1,10 @@ -# sys-srv-web-inj-logout +# sys-front-inj-logout This role injects a catcher that intercepts all logout elements in HTML pages served by Nginx and redirects them to a centralized logout endpoint via JavaScript. ## Description -The `sys-srv-web-inj-logout` Ansible role automatically embeds a lightweight JavaScript snippet into your web application's HTML responses. This script identifies logout links, buttons, forms, and other elements, overrides their target URLs, and ensures users are redirected to a central OIDC logout endpoint, providing a consistent single sign‑out experience. +The `sys-front-inj-logout` Ansible role automatically embeds a lightweight JavaScript snippet into your web application's HTML responses. This script identifies logout links, buttons, forms, and other elements, overrides their target URLs, and ensures users are redirected to a central OIDC logout endpoint, providing a consistent single sign‑out experience. ## Overview diff --git a/roles/sys-srv-web-inj-logout/meta/main.yml b/roles/sys-front-inj-logout/meta/main.yml similarity index 92% rename from roles/sys-srv-web-inj-logout/meta/main.yml rename to roles/sys-front-inj-logout/meta/main.yml index dea58256..87004aa5 100644 --- a/roles/sys-srv-web-inj-logout/meta/main.yml +++ b/roles/sys-front-inj-logout/meta/main.yml @@ -1,6 +1,6 @@ galaxy_info: author: "Kevin Veen‑Birkenbach" - role_name: "sys-srv-web-inj-logout" + role_name: "sys-front-inj-logout" description: > Injects a JavaScript snippet via Nginx sub_filter that intercepts all logout actions (links, buttons, forms) and redirects users to a centralized OIDC logout endpoint. @@ -21,4 +21,4 @@ galaxy_info: Kevin Veen‑Birkenbach Consulting & Coaching Solutions https://www.veen.world repository: "https://s.infinito.nexus/code" issue_tracker_url: "https://s.infinito.nexus/issues" - documentation: "https://s.infinito.nexus/code/tree/main/roles/sys-srv-web-inj-logout" + documentation: "https://s.infinito.nexus/code/tree/main/roles/sys-front-inj-logout" diff --git a/roles/sys-srv-web-inj-logout/tasks/01_core.yml b/roles/sys-front-inj-logout/tasks/01_core.yml similarity index 100% rename from roles/sys-srv-web-inj-logout/tasks/01_core.yml rename to roles/sys-front-inj-logout/tasks/01_core.yml diff --git a/roles/sys-srv-web-inj-logout/tasks/02_deploy.yml b/roles/sys-front-inj-logout/tasks/02_deploy.yml similarity index 100% rename from roles/sys-srv-web-inj-logout/tasks/02_deploy.yml rename to roles/sys-front-inj-logout/tasks/02_deploy.yml diff --git a/roles/sys-srv-web-inj-logout/tasks/main.yml b/roles/sys-front-inj-logout/tasks/main.yml similarity index 85% rename from roles/sys-srv-web-inj-logout/tasks/main.yml rename to roles/sys-front-inj-logout/tasks/main.yml index 2285a67f..c7773761 100644 --- a/roles/sys-srv-web-inj-logout/tasks/main.yml +++ b/roles/sys-front-inj-logout/tasks/main.yml @@ -1,8 +1,8 @@ - block: - include_tasks: 01_core.yml - set_fact: - run_once_sys_srv_web_inj_logout: true - when: run_once_sys_srv_web_inj_logout is not defined + run_once_sys_front_inj_logout: true + when: run_once_sys_front_inj_logout is not defined - name: "Load logout code for '{{ application_id }}'" set_fact: diff --git a/roles/sys-srv-web-inj-logout/templates/body_sub.j2 b/roles/sys-front-inj-logout/templates/body_sub.j2 similarity index 100% rename from roles/sys-srv-web-inj-logout/templates/body_sub.j2 rename to roles/sys-front-inj-logout/templates/body_sub.j2 diff --git a/roles/sys-front-inj-logout/templates/head_sub.j2 b/roles/sys-front-inj-logout/templates/head_sub.j2 new file mode 100644 index 00000000..88f81729 --- /dev/null +++ b/roles/sys-front-inj-logout/templates/head_sub.j2 @@ -0,0 +1 @@ + diff --git a/roles/sys-srv-web-inj-logout/templates/logout.js.j2 b/roles/sys-front-inj-logout/templates/logout.js.j2 similarity index 100% rename from roles/sys-srv-web-inj-logout/templates/logout.js.j2 rename to roles/sys-front-inj-logout/templates/logout.js.j2 diff --git a/roles/sys-srv-web-inj-logout/templates/logout_one_liner.js.j2 b/roles/sys-front-inj-logout/templates/logout_one_liner.js.j2 similarity index 100% rename from roles/sys-srv-web-inj-logout/templates/logout_one_liner.js.j2 rename to roles/sys-front-inj-logout/templates/logout_one_liner.js.j2 diff --git a/roles/sys-front-inj-logout/vars/main.yml b/roles/sys-front-inj-logout/vars/main.yml new file mode 100644 index 00000000..ceb03f1d --- /dev/null +++ b/roles/sys-front-inj-logout/vars/main.yml @@ -0,0 +1,2 @@ +INJ_LOGOUT_JS_FILE_NAME: "logout.js" +INJ_LOGOUT_JS_DESTINATION: "{{ [cdn.shared.js, INJ_LOGOUT_JS_FILE_NAME] | path_join }}" diff --git a/roles/sys-srv-web-inj-matomo/README.md b/roles/sys-front-inj-matomo/README.md similarity index 100% rename from roles/sys-srv-web-inj-matomo/README.md rename to roles/sys-front-inj-matomo/README.md diff --git a/roles/sys-srv-web-inj-matomo/meta/main.yml b/roles/sys-front-inj-matomo/meta/main.yml similarity index 96% rename from roles/sys-srv-web-inj-matomo/meta/main.yml rename to roles/sys-front-inj-matomo/meta/main.yml index 25e543fd..bcb151fa 100644 --- a/roles/sys-srv-web-inj-matomo/meta/main.yml +++ b/roles/sys-front-inj-matomo/meta/main.yml @@ -13,7 +13,7 @@ galaxy_info: - analytics repository: "https://s.infinito.nexus/code" issue_tracker_url: "https://s.infinito.nexus/issues" - documentation: "https://s.infinito.nexus/code/tree/main/roles/sys-srv-web-inj-matomo" + documentation: "https://s.infinito.nexus/code/tree/main/roles/sys-front-inj-matomo" min_ansible_version: "2.9" platforms: - name: Any diff --git a/roles/sys-srv-web-inj-matomo/tasks/main.yml b/roles/sys-front-inj-matomo/tasks/main.yml similarity index 97% rename from roles/sys-srv-web-inj-matomo/tasks/main.yml rename to roles/sys-front-inj-matomo/tasks/main.yml index 5996ba81..0cce22f7 100644 --- a/roles/sys-srv-web-inj-matomo/tasks/main.yml +++ b/roles/sys-front-inj-matomo/tasks/main.yml @@ -4,7 +4,7 @@ name: srv-core when: run_once_srv_core is not defined - include_tasks: utils/run_once.yml - when: run_once_sys_srv_web_inj_matomo is not defined + when: run_once_sys_front_inj_matomo is not defined - name: "Relevant variables for role: {{ role_path | basename }}" debug: diff --git a/roles/sys-srv-web-inj-matomo/templates/body_sub.j2 b/roles/sys-front-inj-matomo/templates/body_sub.j2 similarity index 100% rename from roles/sys-srv-web-inj-matomo/templates/body_sub.j2 rename to roles/sys-front-inj-matomo/templates/body_sub.j2 diff --git a/roles/sys-srv-web-inj-matomo/templates/head_sub.j2 b/roles/sys-front-inj-matomo/templates/head_sub.j2 similarity index 100% rename from roles/sys-srv-web-inj-matomo/templates/head_sub.j2 rename to roles/sys-front-inj-matomo/templates/head_sub.j2 diff --git a/roles/sys-srv-web-inj-matomo/templates/matomo-tracking.js.j2 b/roles/sys-front-inj-matomo/templates/matomo-tracking.js.j2 similarity index 100% rename from roles/sys-srv-web-inj-matomo/templates/matomo-tracking.js.j2 rename to roles/sys-front-inj-matomo/templates/matomo-tracking.js.j2 diff --git a/roles/sys-srv-web-inj-matomo/vars/main.yml b/roles/sys-front-inj-matomo/vars/main.yml similarity index 100% rename from roles/sys-srv-web-inj-matomo/vars/main.yml rename to roles/sys-front-inj-matomo/vars/main.yml diff --git a/roles/sys-service/defaults/main.yml b/roles/sys-service/defaults/main.yml index b1729647..d9d4a5d3 100644 --- a/roles/sys-service/defaults/main.yml +++ b/roles/sys-service/defaults/main.yml @@ -1,4 +1,4 @@ -SYS_SERVICE_ALL_ENABLED: "{{ MODE_DEBUG }}" +SYS_SERVICE_ALL_ENABLED: "{{ MODE_DEBUG | bool }}" SYS_SERVICE_DEFAULT_STATE: "{{ 'restarted' if MODE_DEBUG else omit }}" SYS_SERVICE_DEFAULT_RUNTIME: "86400s" # Maximum total runtime a service is allowed to run before being stopped SYS_SERVICE_SUPPRESS_FLUSH: [] # Services where the flush should be suppressed \ No newline at end of file diff --git a/roles/sys-service/tasks/05_service.yml b/roles/sys-service/tasks/05_service.yml index 6c5b4296..be6ad891 100644 --- a/roles/sys-service/tasks/05_service.yml +++ b/roles/sys-service/tasks/05_service.yml @@ -46,5 +46,5 @@ command: /bin/true notify: refresh systemctl service when: not system_service_uses_at - when: (SYS_SERVICE_ALL_ENABLED | bool or system_force_flush | bool) + when: system_force_flush | bool diff --git a/roles/sys-service/vars/main.yml b/roles/sys-service/vars/main.yml index bb8711d3..bfc1ecfc 100644 --- a/roles/sys-service/vars/main.yml +++ b/roles/sys-service/vars/main.yml @@ -6,7 +6,7 @@ system_service_role_dir: "{{ [ playbook_dir, 'roles', system_service_role_ system_service_script_dir: "{{ [ PATH_SYSTEMCTL_SCRIPTS, system_service_id ] | path_join }}" ## Settings -system_force_flush: false # When set to true it activates the flushing of services :) +system_force_flush: "{{ SYS_SERVICE_ALL_ENABLED | bool }}" # When set to true it activates the flushing of services. defaults to SYS_SERVICE_ALL_ENABLED system_service_suppress_flush: "{{ (system_service_id in SYS_SERVICE_SUPPRESS_FLUSH) | bool }}" # When set to true it suppresses the flushing of services system_service_copy_files: true # When set to false file copying will be skipped system_service_timer_enabled: false # When set to true timer will be loaded diff --git a/roles/sys-srv-web-inj-css/tasks/01_core.yml b/roles/sys-srv-web-inj-css/tasks/01_core.yml deleted file mode 100644 index 862688ab..00000000 --- a/roles/sys-srv-web-inj-css/tasks/01_core.yml +++ /dev/null @@ -1,29 +0,0 @@ -- name: Include dependency 'srv-core' - include_role: - name: srv-core - when: run_once_srv_core is not defined - -- name: Generate color palette with colorscheme-generator - set_fact: - color_palette: "{{ lookup('colorscheme', global_css_base_color, count=global_css_count, shades=global_css_shades) }}" - -- name: Generate inverted color palette with colorscheme-generator - set_fact: - inverted_color_palette: "{{ lookup('colorscheme', global_css_base_color, count=global_css_count, shades=global_css_shades, invert_lightness=True) }}" - -- name: Deploy global.css - template: - src: global.css.j2 - dest: "{{ global_css_destination }}" - owner: "{{ NGINX.USER }}" - group: "{{ NGINX.USER }}" - mode: '0644' - -- name: Get stat for global.css - stat: - path: "{{ global_css_destination }}" - register: global_css_stat - -- name: Set global_css_version - set_fact: - global_css_version: "{{ global_css_stat.stat.mtime }}" diff --git a/roles/sys-srv-web-inj-css/tasks/main.yml b/roles/sys-srv-web-inj-css/tasks/main.yml deleted file mode 100644 index 350826b7..00000000 --- a/roles/sys-srv-web-inj-css/tasks/main.yml +++ /dev/null @@ -1,4 +0,0 @@ -- block: - - include_tasks: 01_core.yml - - include_tasks: utils/run_once.yml - when: run_once_sys_srv_web_inj_css is not defined \ No newline at end of file diff --git a/roles/sys-srv-web-inj-css/templates/global.css.j2 b/roles/sys-srv-web-inj-css/templates/global.css.j2 deleted file mode 100644 index 040a0625..00000000 --- a/roles/sys-srv-web-inj-css/templates/global.css.j2 +++ /dev/null @@ -1,1204 +0,0 @@ -/*** - -Global Theming Styles – Color and Shadow Variables - -HINT: -- Better overwritte CSS variables instead of individual elements. -- Don't use !important. If possible use a specific selector. - -*/ - -{% if design.font.import_url %} -@import url('{{design.font.import_url}}'); -{% endif %} - -/* Auto-generated by colorscheme-generator */ - -:root { - {% for var_name, color in color_palette.items() %} - {{ var_name }}: {{ color }}; - {% endfor %} -} - -@media (prefers-color-scheme: dark) { - :root { - {% for var_name, color in inverted_color_palette.items() %} - {{ var_name }}: {{ color }}; - {% endfor %} - } -} - -:root, ::after, ::before, ::backdrop { - /* For Dark Mode Plugin - * @See https://chromewebstore.google.com/detail/dark-mode/dmghijelimhndkbmpgbldicpogfkceaj - */ - --native-dark-accent-color: var(--color-01-60); /* was #a9a9a9 */ - --native-dark-bg-color: var(--color-01-10); /* was #292929 */ - --native-dark-bg-image-color: rgba(var(--color-01-rgb-01), 0.10); /* remains the same, or adjust if needed */ - --native-dark-border-color: var(--color-01-40); /* was #555555 */ - --native-dark-box-shadow: 0 0 0 1px rgb(var(--color-01-rgb-99), / 10%); - --native-dark-cite-color: var(--color-01-70); /* was #92de92 – you might adjust if a green tone is needed */ - --native-dark-fill-color: var(--color-01-50); /* was #7d7d7d */ - --native-dark-font-color: var(--color-01-95); /* was #dcdcdc */ - --native-dark-link-color: var(--color-01-80); /* was #8db2e5 */ - --native-dark-visited-link-color: var(--color-01-85); /* was #c76ed7 */ -} - -/* Bootstrap Overrides (Color/Shadow Variables Only) */ -:root { - --bs-black: var(--color-01-01); /* Original tone: Black (#000) */ - --bs-white: var(--color-01-99); /* Original tone: White (#fff) */ - --bs-gray: var(--color-01-50); /* Original tone: Gray (#6c757d) */ - --bs-gray-dark: var(--color-01-20); /* Original tone: Dark Gray (#343a40) */ -{% for i in range(1, 10) %} -{# @see https://chatgpt.com/share/67bcd94e-bb44-800f-bf63-06d1ae0f5096 #} - {% set gray = i * 100 %} - {% set color = 100 - i * 10 %} - --bs-gray-{{ gray }}: var(--color-01-{{ "%02d" % color }}); -{% endfor %} - --bs-primary: var(--color-01-65); /* Original tone: Blue (#0d6efd) */ - --bs-light: var(--color-01-95); /* Original tone: Light (#f8f9fa) */ - --bs-dark: var(--color-01-10); /* Original tone: Dark (#212529) */ - --bs-primary-rgb: var(--color-01-rgb-65); /* Original tone: Blue (13, 110, 253) */ - --bs-secondary-rgb: var(--color-01-rgb-50); /* Original tone: Grayish (#6c757d / 108, 117, 125) */ - --bs-light-rgb: var(--color-01-rgb-95); /* Original tone: Light (248, 249, 250) */ - --bs-dark-rgb: var(--color-01-rgb-10); /* Original tone: Dark (33, 37, 41) */ - --bs-white-rgb: var(--color-01-rgb-99); /* Original tone: White (255, 255, 255) */ - --bs-black-rgb: var(--color-01-rgb-01); /* Original tone: Black (0, 0, 0) */ - --bs-body-color-rgb: var(--color-01-rgb-10); /* Original tone: Dark (#212529 / 33, 37, 41) */ - --bs-body-bg-rgb: var(--color-01-rgb-99); /* Original tone: White (#fff / 255, 255, 255) */ - --bs-body-color: var(--color-01-10); /* Original tone: Dark (#212529) */ - --bs-body-bg: var(--color-01-99); /* Original tone: White (#fff) */ - --bs-border-color: var(--color-01-85); /* Original tone: Gray (#dee2e6) */ - --bs-link-color: var(--color-01-65); /* Original tone: Blue (#0d6efd) */ - --bs-link-hover-color: var(--color-01-60); /* Original tone: Darker Blue (#0a58ca) */ - --bs-code-color: var(--color-01-55); /* Original tone: Pink (#d63384) */ - --bs-highlight-bg: var(--color-01-93); /* Original tone: Light Yellow (#fff3cd) */ - --bs-list-group-bg: var(--color-01-40); - --bs-emphasis-color: var(--color-01-01); /* Gemappt von #000 */ - --bs-emphasis-color-rgb: var(--color-01-rgb-01); /* Gemappt von 0, 0, 0 */ - --bs-secondary-color: rgba(var(--color-01-rgb-10), 0.75); /* Gemappt von rgba(33, 37, 41, 0.75) */ - --bs-secondary-color-rgb: var(--color-01-rgb-10); /* Gemappt von 33, 37, 41 */ - --bs-secondary-bg: var(--color-01-90); /* Gemappt von #e9ecef */ - --bs-secondary-bg-rgb: var(--color-01-rgb-90); /* Gemappt von 233, 236, 239 */ - --bs-tertiary-color: rgba(var(--color-01-rgb-10), 0.5); /* Gemappt von rgba(33, 37, 41, 0.5) */ - --bs-tertiary-color-rgb: var(--color-01-rgb-10); /* Gemappt von 33, 37, 41 */ - --bs-tertiary-bg: var(--color-01-95); /* Gemappt von #f8f9fa */ - --bs-tertiary-bg-rgb: var(--color-01-rgb-95); /* Gemappt von 248, 249, 250 */ - --bs-link-color-rgb: var(--color-01-rgb-65); /* Gemappt von 13, 110, 253 */ - --bs-link-hover-color-rgb: var(--color-01-rgb-60); /* Gemappt von 10, 88, 202 */ - --bs-highlight-color: var(--color-01-10); /* Gemappt von #212529 */ - --bs-border-color-translucent: rgba(var(--color-01-rgb-01), 0.175); /* Gemappt von rgba(0, 0, 0, 0.175) */ - --bs-focus-ring-color: rgba(var(--color-01-rgb-65), 0.25); /* Gemappt von rgba(13, 110, 253, 0.25) */ - - --bs-table-color: var(--bs-emphasis-color); - --bs-table-bg: var(--color-01-99); /* White (#fff) */ - --bs-table-border-color: var(--color-01-99); /* White (#fff) */ - --bs-table-striped-bg: var(--color-01-85); /* Light Gray (entspricht ca. #dee2e6) */ - --bs-table-hover-color: var(--color-01-01); /* Black (#000) */ - --bs-table-hover-bg: rgba(var(--bs-emphasis-color-rgb), 0.075); -} - - -/* Discourse */ -:root section#main{ - /* Base Colors */ - --primary: var(--color-01-20); /* originally #203243 */ - --secondary: var(--color-01-95); /* originally #eef4f7 */ - --tertiary: var(--color-01-40); /* originally #416376 */ - --quaternary: var(--color-01-50); /* originally #5e99b9 */ - - /* Header & Highlight */ - --header_background: var(--color-01-86); /* originally #86bddb */ - --header_primary: var(--color-01-20); /* same as --primary */ - --highlight: var(--color-01-70); /* same as header_background */ - --d-selected: var(--color-01-85); /* originally #bee0f2 */ - --d-hover: var(--color-01-90); /* originally #d2efff */ - - /* Normally refers to secondary. Somehow this reference does not work.*/ - --d-sidebar-background: var(--color-01-92); - --d-sidebar-footer-fade: var(--color-01-92); - - - /* RGB values */ - --always-black-rgb: var(--color-01-rgb-01); - --primary-rgb: var(--color-01-rgb-20); - --primary-low-rgb: var(--color-01-rgb-95); - --primary-very-low-rgb: var(--color-01-rgb-99); - --secondary-rgb: var(--color-01-rgb-95); - --header_background-rgb: var(--color-01-rgb-70); - --tertiary-rgb: var(--color-01-rgb-40); - --highlight-rgb: var(--color-01-rgb-70); - --success-rgb: var(--color-01-rgb-50); - - - /* Primary Scale */ - --primary-very-low: var(--color-01-99); /* originally #f7f9fb */ - --primary-low: var(--color-01-95); /* originally #e3ebf2 */ - --primary-low-mid: var(--color-01-75); /* originally #acc2d7 */ - --primary-medium: var(--color-01-60); /* originally #7499bd */ - --primary-high: var(--color-01-40); /* originally #487096 */ - --primary-very-high: var(--color-01-20); /* originally #34516d */ - --primary-50: var(--color-01-99); /* originally #f7f9fb */ -{% for i in range(1, 10) %} -{# @see https://chatgpt.com/share/67bcd94e-bb44-800f-bf63-06d1ae0f5096 #} - {% set primary = i * 100 %} - {% set color = 100 - i * 8 %} - --primary-{{ primary }}: var(--color-01-{{ "%02d" % color }}); -{% endfor %} - - /* Header Primary Scale */ - --header_primary-low: rgb(var(--color-01-rgb-75)); /* rgb(128, 180, 209) */ - --header_primary-low-mid: rgb(var(--color-01-rgb-70)); /* rgb(110, 155, 181) */ - --header_primary-medium: rgb(var(--color-01-rgb-60)); /* rgb(93, 132, 155) */ - --header_primary-high: rgb(var(--color-01-rgb-50)); /* rgb(78, 112, 132) */ - --header_primary-very-high: rgb(var(--color-01-rgb-20)); /* rgb(52, 76, 94) */ - - /* Secondary Scale */ - --secondary-low: var(--color-01-20); /* originally #2f5163 */ - --secondary-medium: var(--color-01-40); /* originally #4e88a5 */ - --secondary-high: var(--color-01-60); /* originally #7ba9c1 */ - --secondary-very-high: var(--color-01-90); /* originally #d7e5ec */ - - /* Tertiary Scale */ - --tertiary-very-low: var(--color-01-99); /* originally #eaf0f3 */ - --tertiary-low: var(--color-01-95); /* originally #dfe8ee */ - --tertiary-medium: var(--color-01-60); /* originally #96b4c5 */ - --tertiary-high: var(--color-01-40); /* originally #5886a0 */ - --tertiary-hover: var(--color-01-20); /* originally #314a59 */ - --tertiary-50: var(--color-01-99); /* originally #eaf0f3 */ -{% for i in range(1, 10) %} -{# @see https://chatgpt.com/share/67bcd94e-bb44-800f-bf63-06d1ae0f5096 #} - {% set tertiary = i * 100 %} - {% set color = 100 - i * 5 %} - --tertiary-{{ tertiary }}: var(--color-01-{{ "%02d" % color }}); -{% endfor %} - - /* Quaternary */ - --quaternary-low: var(--color-01-80); /* originally #cfe0ea */ - - /* Highlight */ - --highlight-bg: var(--color-01-90); /* originally #dbebf4 */ - --highlight-low: var(--color-01-90); /* originally #dbebf4 */ - --highlight-medium: var(--color-01-80); /* originally #c3deed */ - --highlight-high: var(--color-01-30); /* originally #286688 */ - - /* Combination Variables */ - --blend-primary-secondary-5: rgb(var(--color-01-rgb-95)); /* originally rgb(232, 238, 241) */ - --primary-med-or-secondary-med: var(--color-01-60); /* originally #7499bd */ - --primary-med-or-secondary-high: var(--color-01-60); /* originally #7499bd */ - --primary-high-or-secondary-low: var(--color-01-40); /* originally #487096 */ - --primary-low-mid-or-secondary-high: var(--color-01-75); /* originally #acc2d7 */ - --primary-low-mid-or-secondary-low: var(--color-01-75); /* originally #acc2d7 */ - --primary-or-primary-low-mid: var(--color-01-20); /* originally #203243 */ - --highlight-low-or-medium: var(--color-01-90); /* originally #dbebf4 */ - --tertiary-or-tertiary-low: var(--color-01-40); /* originally #416376 */ - --tertiary-low-or-tertiary-high: var(--color-01-95); /* originally #dfe8ee */ - --tertiary-med-or-tertiary: var(--color-01-60); /* originally #96b4c5 */ - --secondary-or-primary: var(--color-01-95); /* originally #eef4f7 */ - --tertiary-or-white: var(--color-01-40); /* originally #416376 */ - - /* Float Kit */ - --float-kit-arrow-stroke-color: var(--primary-low); /* already mapped above */ - --float-kit-arrow-fill-color: var(--secondary); /* already mapped above */ -} - -/* gitea */ -:root { - /* Base and derived colors are now referenced from the computed scale */ - --color-01-primary: var(--color-01-50); - --color-01-primary-contrast: var(--color-01-99); - --color-01-primary-dark-1: var(--color-01-48); - --color-01-primary-dark-2: var(--color-01-47); - --color-01-primary-dark-3: var(--color-01-46); - --color-01-primary-dark-4: var(--color-01-45); - --color-01-primary-dark-5: var(--color-01-44); - --color-01-primary-dark-6: var(--color-01-43); - --color-01-primary-dark-7: var(--color-01-42); - --color-01-primary-light-1: var(--color-01-52); - --color-01-primary-light-2: var(--color-01-53); - --color-01-primary-light-3: var(--color-01-54); - --color-01-primary-light-4: var(--color-01-55); - --color-01-primary-light-5: var(--color-01-57); - --color-01-primary-light-6: var(--color-01-59); - --color-01-primary-light-7: var(--color-01-61); - - /* Alpha variants reference the base RGB variable */ -{% for i in range(1, 10) %} -{# @see https://chatgpt.com/share/67bcd94e-bb44-800f-bf63-06d1ae0f5096 #} - {% set alpha = i * 10 %} - --color-01-primary-alpha-{{ alpha }}: rgba(var(--color-01-rgb-50), 0.{{ alpha }}); -{% endfor %} - - --color-01-primary-hover: var(--color-01-primary-dark-1); - --color-01-primary-active: var(--color-01-primary-dark-2); - - /* Secondary colors */ - --color-01-secondary: var(--color-01-80); - --color-01-secondary-dark-1: var(--color-01-78); - --color-01-secondary-dark-2: var(--color-01-76); - --color-01-secondary-dark-3: var(--color-01-74); - --color-01-secondary-dark-4: var(--color-01-72); - --color-01-secondary-dark-5: var(--color-01-70); - --color-01-secondary-dark-6: var(--color-01-68); - --color-01-secondary-dark-7: var(--color-01-66); - --color-01-secondary-dark-8: var(--color-01-64); - --color-01-secondary-dark-9: var(--color-01-62); - --color-01-secondary-dark-10: var(--color-01-60); - --color-01-secondary-dark-11: var(--color-01-58); - --color-01-secondary-dark-12: var(--color-01-56); - --color-01-secondary-dark-13: var(--color-01-54); - --color-01-secondary-light-1: var(--color-01-92); - --color-01-secondary-light-2: var(--color-01-93); - --color-01-secondary-light-3: var(--color-01-94); - --color-01-secondary-light-4: var(--color-01-95); - -{% for i in range(1, 10) %} -{# @see https://chatgpt.com/share/67bcd94e-bb44-800f-bf63-06d1ae0f5096 #} - {% set alpha = i * 10 %} - --color-01-secondary-alpha-{{ alpha }}: rgba(var(--color-01-rgb-80), 0.{{ alpha }}); -{% endfor %} - - --color-01-secondary-button: var(--color-01-secondary-dark-4); - --color-01-secondary-hover: var(--color-01-secondary-dark-5); - --color-01-secondary-active: var(--color-01-secondary-dark-6); - - /* Console Colors */ - --color-01-console-fg: var(--color-01-98); - --color-01-console-fg-subtle: var(--color-01-85); - --color-01-console-bg: var(--color-01-10); - --color-01-console-border: var(--color-01-40); - --color-01-console-hover-bg: var(--color-01-42); - --color-01-console-active-bg: var(--color-01-40); - --color-01-console-menu-bg: var(--color-01-38); - --color-01-console-menu-border:var(--color-01-45); - - /* Body, Text, and Miscellaneous Colors */ - --color-01-white: var(--color-01-99); - --color-01-grey: var(--color-01-60); - --color-01-grey-light: var(--color-01-65); - - --color-01-body: var(--color-01-white); - --color-01-text-dark: var(--color-01-10); - --color-01-text: var(--color-01-40); - --color-01-text-light: var(--color-01-60); - --color-01-text-light-1: var(--color-01-65); - --color-01-text-light-2: var(--color-01-70); - --color-01-text-light-3: var(--color-01-75); - - --color-01-footer: var(--color-01-nav-bg); - --color-01-timeline: var(--color-01-80); - - /* Input Colors */ - --color-01-input-text: var(--color-01-10); - --color-01-input-background: var(--color-01-white); - --color-01-input-toggle-background: var(--color-01-80); - --color-01-input-border: var(--color-01-secondary); - --color-01-input-border-hover: var(--color-01-secondary-dark-1); - - /* Effects */ - --color-01-light: var(--color-01-05); - --color-01-light-mimic-enabled: rgba(var(--color-01-rgb-05), calc(6 / 255 * 222 / 255 / 0.55)); - --color-01-light-border: var(--color-01-05); - --color-01-hover: var(--color-01-05); - --color-01-hover-opaque: var(--color-01-95); - --color-01-active: var(--color-01-05); - - /* Menu, Card, and Markup Colors */ - --color-01-menu: var(--color-01-99); - --color-01-card: var(--color-01-99); - --color-01-markup-table-row: var(--color-01-01); - --color-01-markup-code-block: var(--color-01-01); - --color-01-markup-code-inline: var(--color-01-01); - --color-01-button: var(--color-01-99); - --color-01-code-bg: var(--color-01-99); - --color-01-shadow: var(--color-01-05); - --color-01-shadow-opaque: var(--color-01-85); - --color-01-secondary-bg: var(--color-01-95); - --color-01-expand-button: var(--color-01-98); - --color-01-placeholder-text: var(--color-01-text-light-3); - --color-01-editor-line-highlight: var(--color-01-primary-light-6); - --color-01-project-column-bg: var(--color-01-secondary-light-4); - --color-01-caret: var(--color-01-10); - - /* Reaction and Tooltip Colors */ - --color-01-reaction-bg: var(--color-01-05); - --color-01-reaction-hover-bg: var(--color-01-primary-light-5); - --color-01-reaction-active-bg: var(--color-01-primary-light-6); - --color-01-tooltip-text: var(--color-01-99); - --color-01-tooltip-bg: var(--color-01-05); - - /* Navigation Colors */ - --color-01-nav-bg: var(--color-01-99); - --color-01-nav-hover-bg: var(--color-01-secondary-light-1); - --color-01-nav-text: var(--color-01-40); - --color-01-secondary-nav-bg: var(--color-01-99); - - /* Label and Accent Colors */ - --color-01-label-text: var(--color-01-40); - --color-01-label-bg: var(--color-01-50); - --color-01-label-hover-bg: var(--color-01-60); - --color-01-label-active-bg: var(--color-01-70); - --color-01-accent: var(--color-01-primary-light-1); - --color-01-small-accent: var(--color-01-primary-light-6); - --color-01-highlight-fg: var(--color-01-10); - --color-01-highlight-bg: var(--color-01-99); - --color-01-overlay-backdrop: var(--color-01-05); -} - -/* Keycloak */ -:root{ - /* --- Palette Black (Graustufen) --- */ -{% for i in range(1, 21) %} -{# @see https://chatgpt.com/share/67bcd94e-bb44-800f-bf63-06d1ae0f5096 #} - {% set black = i * 50 %} - {% set color = 100 - i * 5 %} - --pf-v5-global--palette--black-{{ black }}: var(--color-01-{{ "%02d" % color }}); -{% endfor %} - - /* --- White --- */ - --pf-v5-global--palette--white: var(--color-01-99); - - /* --- Background Colors --- */ - --pf-v5-global--BackgroundColor--100: var(--color-01-99); - --pf-v5-global--BackgroundColor--150: var(--color-01-95); - --pf-v5-global--BackgroundColor--200: var(--color-01-85); - --pf-v5-global--BackgroundColor--300: var(--color-01-75); - --pf-v5-global--BackgroundColor--400: var(--color-01-65); - --pf-v5-global--BackgroundColor--light-100: var(--color-01-99); - --pf-v5-global--BackgroundColor--light-200: var(--color-01-95); - --pf-v5-global--BackgroundColor--light-300: var(--color-01-85); - --pf-v5-global--BackgroundColor--dark-100: var(--color-01-10); - --pf-v5-global--BackgroundColor--dark-200: var(--color-01-25); - --pf-v5-global--BackgroundColor--dark-300: var(--color-01-20); - --pf-v5-global--BackgroundColor--dark-400: var(--color-01-30); - --pf-v5-global--BackgroundColor--dark-transparent-100: rgba(var(--color-01-rgb-10),0.7); - --pf-v5-global--BackgroundColor--dark-transparent-200: rgba(var(--color-01-rgb-20),0.8); - - /* --- Color Variables --- */ - --pf-v5-global--color-01--100: var(--color-01-10); - --pf-v5-global--color-01--200: var(--color-01-40); - --pf-v5-global--color-01--300: var(--color-01-25); - --pf-v5-global--color-01--400: var(--color-01-50); - --pf-v5-global--color-01--light-100: var(--color-01-99); - --pf-v5-global--color-01--light-200: var(--color-01-85); - --pf-v5-global--color-01--light-300: var(--color-01-75); - --pf-v5-global--color-01--dark-100: var(--color-01-10); - --pf-v5-global--color-01--dark-200: var(--color-01-40); - - /* --- Active Colors --- */ - --pf-v5-global--active-color--100: var(--color-01-65); - --pf-v5-global--active-color--200: var(--color-01-95); - --pf-v5-global--active-color--300: var(--color-01-75); - --pf-v5-global--active-color--400: var(--color-01-85); - - /* --- Disabled Colors --- */ - --pf-v5-global--disabled-color--100: var(--color-01-40); - --pf-v5-global--disabled-color--200: var(--color-01-75); - --pf-v5-global--disabled-color--300: var(--color-01-85); - - /* --- Primary Colors --- */ - --pf-v5-global--primary-color--100: var(--color-01-65); - --pf-v5-global--primary-color--200: var(--color-01-40); - --pf-v5-global--primary-color--light-100: var(--color-01-75); - --pf-v5-global--primary-color--dark-100: var(--color-01-65); - - /* --- Secondary Colors --- */ - --pf-v5-global--secondary-color--100: var(--color-01-40); - - /* --- Custom Colors --- */ - --pf-v5-global--custom-color--100: var(--color-01-65); - --pf-v5-global--custom-color--200: var(--color-01-65); - --pf-v5-global--custom-color--300: var(--color-01-30); - - /* --- Link Colors --- */ - --pf-v5-global--link--Color: var(--color-01-65); - --pf-v5-global--link--color-01--hover: var(--color-01-40); - --pf-v5-global--link--color-01--light: var(--color-01-75); - --pf-v5-global--link--color-01--light--hover: var(--color-01-85); - --pf-v5-global--link--color-01--dark: var(--color-01-65); - --pf-v5-global--link--color-01--dark--hover: var(--color-01-40); - --pf-v5-global--link--color-01--visited: var(--color-01-40); - - /* --- Border Colors --- */ - --pf-v5-global--BorderColor--100: var(--color-01-75); - --pf-v5-global--BorderColor--200: var(--color-01-50); - --pf-v5-global--BorderColor--300: var(--color-01-85); - --pf-v5-global--BorderColor--400: var(--color-01-65); - --pf-v5-global--BorderColor--dark-100: var(--color-01-75); - --pf-v5-global--BorderColor--light-100: var(--color-01-65); - - /* --- Icon Colors --- */ - --pf-v5-global--icon--color-01--light: var(--color-01-40); - --pf-v5-global--icon--color-01--dark: var(--color-01-10); - --pf-v5-global--icon--color-01--light--light: var(--color-01-85); - --pf-v5-global--icon--color-01--dark--light: var(--color-01-99); - --pf-v5-global--icon--color-01--light--dark: var(--color-01-40); - --pf-v5-global--icon--color-01--dark--dark: var(--color-01-10); - -} - -.pf-v5-c-button.pf-m-tertiary { - --pf-v5-c-button--m-tertiary--BackgroundColor: var(--color-01-70); - {# Assume that the following line is necessary due to load order #} - background-color: var(--pf-v5-c-button--m-tertiary--BackgroundColor); -} - -/* Additional Keykloak Configuration */ -a.pf-v5-c-nav__link{ - --pf-v5-c-nav__link--BackgroundColor: rgba(var(--color-01-rgb-56), 0.4); -} - -/* LAM */ -:root { ---lam-background-color-default: var(--color-01-99); {# from #FFFFFF (very bright white) #} ---lam-input-bg-color: var(--color-01-98); {# from #fcfcfc (almost white) #} ---lam-text-color-default: var(--color-01-01); {# from #000000 (pure black) #} ---lam-border-color: var(--color-01-90); {# from #e8e8e8 (light grey) #} ---lam-border-color-primary: var(--color-01-15); {# from #01689e (dark blue) #} ---lam-border-color-secondary: var(--color-01-85); {# from #ffcb1d (bright yellow) #} ---lam-background-color-primary: var(--color-01-50); {# from #3daee9 (mid-tone blue) #} ---lam-background-color-secondary: var(--color-01-90); {# from #ffe233 (bright yellow) #} ---lam-text-color-primary: var(--color-01-99); {# from #ffffff (pure white) #} ---lam-text-color-secondary: var(--color-01-01); {# from #000000 (pure black) #} ---lam-text-color-ok: var(--color-01-10); {# from #237d0c (dark green) #} ---lam-table-background-color-bright: var(--color-01-98); {# from #fbfbfb (very light grey) #} ---lam-table-background-color-dark: var(--color-01-92); {# from #e8f3ff (light blue) #} ---lam-table-background-color-hover: var(--color-01-50); {# from #3daee9 (mid-tone blue) #} ---lam-table-text-color-hover: var(--color-01-99); {# from #ffffff (pure white) #} ---lam-table-border-color: var(--color-01-50); {# from #3daee9 (mid-tone blue) #} -} - -/** Mastodon Overrides **/ -div#mastodon, div#admin-wrapper { - /* Dropdown */ - --dropdown-border-color: var(--color-01-35); - --dropdown-background-color: rgba(var(--color-01-rgb-03), 0.9); - --dropdown-shadow: 0 20px 25px -5px rgba(var(--color-01-rgb-01), 0.25), - 0 8px 10px -6px rgba(var(--color-01-rgb-01), 0.25); - - /* Modal */ - --modal-background-color: rgba(var(--color-01-rgb-03), 0.7); - --modal-background-variant-color: rgba(var(--color-01-rgb-05), 0.7); - --modal-border-color: var(--color-01-35); - - /* Background */ - --background-border-color: var(--color-01-82); - --background-filter: blur(10px) saturate(180%) contrast(75%) brightness(70%); - --background-color: var(--color-01-93); - --background-color-tint: rgba(var(--color-01-rgb-03), 0.9); - - /* Surface */ - --surface-background-color: var(--color-01-90); - --surface-variant-background-color: var(--color-01-89); - --surface-variant-active-background-color: var(--color-01-35); - --on-surface-color: rgba(var(--color-01-rgb-05), 0.5); - - /* Media & Overlay */ - --media-outline-color: rgba(var(--color-01-rgb-99), 0.15); - --overlay-icon-shadow: drop-shadow(0 0 8px rgba(var(--color-01-rgb-01), 0.25)); -} - -.swal2-popup { - color: #000; -} - -/* Modal Overwrittes */ -div.modal div.modal-content { - /* Colors – adjusted to the existing scheme */ - --bs-modal-color: var(--color-01-21); /* Text color: dark contrast against the light modal background */ - --bs-modal-bg: var(--color-01-82); /* Background color, as desired */ - --bs-modal-border-color: var(--color-01-82); /* A slightly darker border than the background */ - --bs-modal-header-border-color: var(--color-01-87); /* Same shade as the modal border */ - --bs-modal-footer-bg: var(--color-01-87); /* A slightly offset footer background (a bit darker than the main area) */ - --bs-modal-footer-border-color: var(--color-01-87); -} - -/** Nextcloud Specific**/ -:root{ - --color-01-main-background: var(--color-01-84); - --color-01-main-background-rgb: rgba(var(--color-01-rgb-84),0.83); - --color-01-primary-element: var(--color-01-80); - --color-01-main-text: var(--color-01-40); - --color-01-background-hover: var(--color-01-65); - - /** Calendar **/ - --color-01-background-dark: var(--color-01-73); /** Days which aren't in the current month **/ - --color-01-primary-element-light: var(--color-01-65); -} - -/** Peertube **/ -body#custom-css { - --mainColor: var(--color-01-60); /* Original tone: hsl(24, 90%, 50%) – vibrant orange */ - --mainColorLighter: var(--color-01-70); /* Original tone: #f5873d – lighter orange */ - --mainColorLightest: var(--color-01-90); /* Original tone: #fce1cf – very light orange/beige */ - --mainColorVeryLight: var(--color-01-95); /* Original tone: #fff5eb – almost white */ - --mainHoverColor: var(--color-01-64); /* Original tone: #f47825 – hover orange */ - --mainBackgroundHoverColor: var(--color-01-92); /* Original tone: #e9ecef – light gray */ - --mainBackgroundColor: var(--color-01-99); /* Original tone: #fff – white */ - --mainForegroundColor: var(--color-01-10); /* Original tone: #212529 – dark gray/black */ - --greyForegroundColor: var(--color-01-50); /* Original tone: #585858 – medium gray */ - --greyBackgroundColor: var(--color-01-90); /* Original tone: #E5E5E5 – light gray */ - --greySecondaryBackgroundColor: var(--color-01-91); /* Original tone: #EFEFEF – very light gray */ - --menuBackgroundColor: var(--color-01-82); /* Original tone: #000 – black */ - --menuForegroundColor: var(--color-01-99); /* Original tone: #fff – white */ - --submenuBackgroundColor: var(--color-01-95); /* Original tone: #F7F7F7 – off-white/light gray */ - --channelBackgroundColor: var(--color-01-93); /* Original tone: #f6ede8 – warm light beige */ - --inputForegroundColor: var(--color-01-10); /* Original tone: #212529 – dark gray */ - --inputBackgroundColor: var(--color-01-99); /* Original tone: #fff – white */ - --inputPlaceholderColor: var(--color-01-55); /* Original tone: #797676 – medium gray */ - --inputBorderColor: var(--color-01-80); /* Original tone: #C6C6C6 – light gray */ - --textareaForegroundColor: var(--color-01-10); /* Original tone: #212529 – dark gray */ - --textareaBackgroundColor: var(--color-01-99); /* Original tone: #fff – white */ - --markdownTextareaBackgroundColor: var(--color-01-91); /* Original tone: #EFEFEF – very light gray */ - --actionButtonColor: var(--color-01-50); /* Original tone: #585858 – medium gray */ - --supportButtonColor: var(--actionButtonColor); /* Original tone: same as actionButtonColor (#585858) */ - --activatedActionButtonColor: var(--color-01-10); /* Original tone: #212529 – dark gray */ - color: var(--mainForegroundColor); - background-color: var(--mainBackgroundColor); -} - -div.searchbox input.autocomplete-input{ - background-position: 12px; - background-repeat: no-repeat; -} - -html[native-dark-active] my-app button, button { - background-color: transparent; -} - -/** Pixelfed **/ - -:root { - /* Base Colors */ - --light: var(--color-01-05); /* Very dark (was #000) */ - --dark: var(--color-01-99); /* Very light (was #fff) */ - - /* Backgrounds */ - --body-bg: var(--color-01-05); /* Main background: very dark */ - --nav-bg: var(--color-01-05); /* Navigation background: very dark */ - - /* Text Colors */ - --body-color: var(--color-01-70); /* Main text – mid brightness */ - --text-lighter: var(--color-01-60); /* Lighter text for less prominent elements */ - - /* Section Backgrounds and Cards */ - --bg-light: var(--color-01-95); /* Lighter background areas */ - --card-bg: var(--color-01-90); /* Card background */ - --light-gray: var(--color-01-75); /* For less dominant elements */ - --light-hover-bg: var(--color-01-85); /* Slightly lighter hover background */ - - /* Borders and Input Fields */ - --btn-light-border: var(--color-01-10); /* Dark border for buttons */ - --input-border: var(--color-01-10); /* Border color for inputs */ - --border-color: var(--color-01-85); /* General border: slightly lighter than background */ - - /* Other Areas */ - --comment-bg: var(--color-01-85); /* Background for comments */ - --card-header-accent: var(--color-01-85); /* Accent color in card headers */ - - /* Dropdown Menus */ - --dropdown-item-hover-bg: var(--color-01-05); /* Hover background: very dark */ - --dropdown-item-hover-color: var(--color-01-60); /* Hover text: a bit lighter */ - --dropdown-item-color: var(--color-01-70); /* Regular dropdown item text */ - --dropdown-item-active-color: var(--color-01-99); /* Active state: very light (white) */ -} - -/** Sphinx **/ -.bg-background\/95 { - background-color: var(--color-01-96); -} - -.border-border { - border-color: var(--color-01-85); -} - -{# Hide Toogle Button #} -nav.flex.items-center.space-x-1{ - display:none; -} - -/** Taiga **/ -:host, :root { - --color-01-solid-primary: var(--color-01-83); - --color-01-link-primary: var(--color-01-44); - --color-01-link-tertiary: var(--color-01-45); - --color-01-gray100: var(--color-01-97); - --color-01-gray200: var(--color-01-93); - --color-01-gray300: var(--color-01-90); - --color-01-gray400: var(--color-01-86); - --color-01-black600: var(--color-01-34); - --color-01-black700: var(--color-01-30); - --color-01-black800: var(--color-01-26); - --color-01-black900: var(--color-01-21); - --color-01-black: var(--color-01-01); - --color-01-white: var(--color-01-99); -} - -/* Global Defaults (Colors Only) */ -body, html[native-dark-active] { - background-color: var(--color-01-93); - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-93), var(--color-01-91), var(--color-01-95), var(--color-01-93)); - background-attachment: fixed; - color: var(--color-01-40); - font-family: {{design.font.type}}; -} - -{# All links (applies to all anchor elements regardless of state) #} -a { - color: var(--color-01-50); -} - -{# Unvisited links (applies only to links that have not been visited) #} -a:link { - color: var(--color-01-55); -} - -{# Visited links (applies only to links that have been visited) #} -a:visited { - color: var(--color-01-45); -} - -{# Hover state (applies when the mouse pointer is over the link) #} -a:hover { - color: var(--color-01-60); -} - -{# Active state (applies during the time the link is being activated, e.g., on click) #} -a:active { - color: var(--color-01-65); -} - -/** Set default buttons transparent **/ -html[native-dark-active] button, button{ - background-color: var(--color-01-87); -} - -button:hover, .btn:hover { - filter: brightness(0.9); -} - -/* {# Invalid state: when the input value fails validation criteria. Use danger color for error indication. #} */ -input:invalid, -textarea:invalid, -select:invalid { - background-color: var(--color-01-01); - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-01), var(--color-01-10)); - /* Use Bootstrap danger color for error messages */ - color: var(--bs-danger); - border-color: var(--color-01-20); -} - -/* {# Valid state: when the input value meets all validation criteria. Use success color for confirmation. #} */ -input:valid, -textarea:valid, -select:valid { - background-color: var(--color-01-80); - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-80), var(--color-01-90)); - /* Use Bootstrap success color for confirmation messages */ - color: var(--bs-success); - border-color: var(--color-01-70); -} - -/* {# Required field: applied to elements that must be filled out by the user. Use warning color for emphasis. #} */ -input:required, -textarea:required, -select:required { - background-color: var(--color-01-50); - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-50), var(--color-01-60)); - /* Use Bootstrap warning color to indicate a required field */ - color: var(--bs-warning); - border-color: var(--color-01-70); -} - -/* {# Optional field: applied to elements that are not mandatory. Use info color to denote additional information. #} */ -input:optional, -textarea:optional, -select:optional { - background-color: var(--color-01-60); - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-60), var(--color-01-70)); - /* Use Bootstrap info color to indicate optional information */ - color: var(--bs-info); - border-color: var(--color-01-70); -} - -/* {# Read-only state: when an element is not editable by the user. #} */ -input:read-only, -textarea:read-only, -select:read-only { - background-color: var(--color-01-80); - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-90), var(--color-01-70)); - color: var(--color-01-20); - border-color: var(--color-01-50); -} - -/* {# Read-write state: when an element is editable by the user. #} */ -input:read-write, -textarea:read-write, -select:read-write { - background-color: var(--color-01-70); - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-70), var(--color-01-80)); - color: var(--color-01-40); - border-color: var(--color-01-70); -} - -/* {# In-range: for inputs with a defined range, when the value is within the allowed limits. #} */ -input:in-range, -textarea:in-range, -select:in-range { - background-color: var(--color-01-70); - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-70), var(--color-01-85)); - color: var(--color-01-40); - border-color: var(--color-01-70); -} - -/* {# Out-of-range: for inputs with a defined range, when the value falls outside the allowed limits. #} */ -input:out-of-range, -textarea:out-of-range, -select:out-of-range { - background-color: var(--color-01-10); - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-10), var(--color-01-30)); - color: var(--color-01-10); - border-color: var(--color-01-50); -} - -/* {# Placeholder-shown: when the input field is displaying its placeholder text. #} */ -input:placeholder-shown, -textarea:placeholder-shown, -select:placeholder-shown { - background-color: var(--color-01-82); - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-82), var(--color-01-90)); - color: var(--color-01-40); - border-color: var(--color-01-70); -} - -/* {# Focus state: when the element is focused by the user. #} */ -input:focus, -textarea:focus, -select:focus { - background-color: var(--color-01-75); - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-75), var(--color-01-85)); - color: var(--color-01-40); - border-color: var(--color-01-50); -} - -/* {# Hover state: when the mouse pointer is over the element. #} */ -input:hover, -textarea:hover, -select:hover { - background-color: var(--color-01-78); - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-78), var(--color-01-88)); - color: var(--color-01-40); - border-color: var(--color-01-65); -} - -/* {# Active state: when the element is being activated (e.g., clicked). #} */ -input:active, -textarea:active, -select:active { - background-color: var(--color-01-68); - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-68), var(--color-01-78)); - color: var(--color-01-40); - border-color: var(--color-01-60); -} - -/* {# Checked state: specifically for radio buttons and checkboxes when selected. #} */ -input:checked { - background-color: var(--color-01-90); - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-90), var(--color-01-99)); - color: var(--color-01-40); - border-color: var(--color-01-70); -} - -option { - background-color: var(--color-01-82); - color: var(--color-01-07); -} - -/* Tables (Borders and Header Colors) */ -th, td { - border-color: var(--color-01-70); -} - -thead { - background-color: var(--color-01-80); - /* New Gradient based on original background (80 -5, 80, 80 +1, 80 +5) */ - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-75), var(--color-01-80), var(--color-01-81), var(--color-01-85)); - color: var(--color-01-40); -} - -/* Headings (Text Color) */ -h1, h2, h3, h4, h5, h6, p{ - color: var(--color-01-10); -} - -/* Buttons (Background, Text, Border, and Shadow) - Now using a button background that is only slightly darker than the overall background */ -html[native-dark-active] .btn, .btn { - background-color: var(--color-01-87); - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-70), var(--color-01-91), var(--color-01-95), var(--color-01-95)); - color: var(--color-01-50); - border-color: var(--color-01-80); - cursor: pointer; -} - -/* Navigation (Background and Text Colors) */ -.navbar, .navbar-light, .navbar-dark, .navbar.bg-light { - background-color: var(--color-01-90); - /* New Gradient based on original background (90 -5, 90, 90 +1, 90 +5) */ - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-85), var(--color-01-90), var(--color-01-91), var(--color-01-95)); - color: var(--color-01-50); - border-color: var(--color-01-85); -} - -.navbar a { - color: var(--color-01-40); -} - -.navbar a.dropdown-item { - color: var(--color-01-43); -} - -/* Cards / Containers (Background, Border, and Shadow) - Cards now use a slightly lighter background and a bold, clear shadow */ -.card { - background-color: var(--color-01-90); - /* New Gradient based on original background (90 -5, 90, 90 +1, 90 +5) */ - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-85), var(--color-01-90), var(--color-01-91), var(--color-01-95)); - border-color: var(--color-01-85); - color: var(--color-01-12); -} - -.card-body { - color: var(--color-01-40); -} - -/* Dropdown Menu and Submenu (Background, Text, and Shadow) */ -.navbar .dropdown-menu, -.nav-item .dropdown-menu { - background-color: var(--color-01-80); - /* New Gradient based on original background (80 -5, 80, 80 +1, 80 +5) */ - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-75), var(--color-01-80), var(--color-01-81), var(--color-01-85)); - color: var(--color-01-40); -} - -.navbar-nav { - --bs-nav-link-hover-color: var(--color-01-17); -} - -.dropdown-item { - color: var(--color-01-40); - background-color: var(--color-01-80); - /* New Gradient based on original background (80 -5, 80, 80 +1, 80 +5) */ - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-75), var(--color-01-80), var(--color-01-81), var(--color-01-85)); -} - -.dropdown-item:hover, -.dropdown-item:focus { - background-color: var(--color-01-65); - /* New Gradient based on original background (65 -5, 65, 65 +1, 65 +5) */ - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-60), var(--color-01-65), var(--color-01-66), var(--color-01-70)); - color: var(--color-01-40); -} - -/* Keycloak */ -div#app header{ - background-color: var(--color-01-60); - /* New Gradient based on original background (60 -5, 60, 60 +1, 60 +5) */ - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-55), var(--color-01-60), var(--color-01-61), var(--color-01-65)); - color: var(--color-01-98); -} - -/** LAM Specific **/ -.lam-vertical-tabs-navigation li, .lam-vertical-tabs-navigation{ - background-color: transparent !important; - border-color: transparent; -} - -ul.lam-tab-navigation { - background: rgba(var(--color-01-rgb-90), 0.1); - border-color: transparent; -} - -/* Not changable due to inline css */ -.roundedShadowBox { - color: #000000; -} - -.titleBar { - background-image: linear-gradient(var(--color-01-83), var(--color-01-92)); - /* New Gradient based on original background (83 -5, 83, 83 +1, 83 +5) */ - background-image: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-78), var(--color-01-83), var(--color-01-84), var(--color-01-88)); - border-top-color: var(--color-01-78); - border-left-color: var(--color-01-87); - border-right-color: var(--color-01-87); -} - -div.statusInfo { - background-color: var(--color-01-81); - /* New Gradient based on original background (81 -5, 81, 81 +1, 81 +5) */ - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-76), var(--color-01-81), var(--color-01-82), var(--color-01-86)); - color: var(--color-01-23); -} - -/** Mailu **/ -[class*=sidebar-dark-], .bg-mailu-logo { - background-color: var(--color-01-90); - /* New Gradient based on original background (90 -5, 90, 90 +1, 90 +5) */ - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-85), var(--color-01-90), var(--color-01-91), var(--color-01-95)); -} - -div.statusError { - background-color: var(--color-01-60); - /* New Gradient based on original background (60 -5, 60, 60 +1, 60 +5) */ - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-55), var(--color-01-60), var(--color-01-61), var(--color-01-65)); -} - -div.wrapper footer.main-footer, div.wrapper div.content-wrapper{ - background-color: var(--color-01-85); - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-89), var(--color-01-85), var(--color-01-80), var(--color-01-79)); - color: var(--color-01-39); -} - -html.dark-mode #layout-menu .special-buttons a:not(:focus) { - background: none; -} - -html.dark-mode #taskmenu a.selected, html.dark-mode .menu.toolbar a.selected { - background-color: rgba(var(--color-01-rgb-82), 0.5); -} - -html.dark-mode .listing li.selected, html.dark-mode .listing li.selected>a, html.dark-mode .listing li.selected>div>a, html.dark-mode .listing tr.selected td { - color: var(--color-01-30); - background-color: rgba(var(--color-01-rgb-82), 0.5); -} - -html.dark-mode .message-htmlpart { - background-color: rgba(var(--color-01-rgb-99), 0.08); - color: var(--color-01-15); -} - -/** Nextcloud specific **/ -html.ng-csp header#header{ - background-color: var(--color-01-80); - /* New Gradient based on original background (80 -5, 80, 80 +1, 80 +5) */ - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-75), var(--color-01-80), var(--color-01-81), var(--color-01-85)); - color: var(--color-01-17); -} - -.files-list__row-name button, button.button-vue{ - background: transparent; -} - -html.ng-csp div#postsetupchecks ul.info{ - background-color: transparent; -} - -div#mastodon .column-link{ - color: var(--color-01-55); -} - -div#mastodon .column-back-button { - color: var(--color-01-58); -} - -div#mastodon textarea, div#mastodon input, div#mastodon .compose-form__highlightable { - background-color: var(--color-01-89); - /* New Gradient based on original background (89 -5, 89, 89 +1, 89 +5) */ - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-84), var(--color-01-89), var(--color-01-90), var(--color-01-94)); - color: var(--color-01-19); -} - -div#mastodon .status-card__title, div#mastodon .display-name strong{ - color: var(--color-01-33); -} - -div#mastodon a.unhandled-link, div#mastodon .dropdown-button, div#mastodon .status__content a, div#mastodon .status-card__author{ - color: var(--color-01-29); -} -div#mastodon .dropdown-button{ - border: 1px solid #8c8dff; -} - -div#mastodon .button, div#mastodon .button:active, div#mastodon .button:focus, div#mastodon .button:hover{ - background-color: var(--color-01-71); - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-66), var(--color-01-71), var(--color-01-72), var(--color-01-76)); -} - -.compose-form__actions .icon-button { - color: var(--color-01-28); -} - -/** OpenProject **/ -header.op-app-header{ - background-color: var(--color-01-40); - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-35), var(--color-01-40), var(--color-01-41), var(--color-01-45)); - color: var(--color-01-40); -} - -div#wrapper button, div#wrapper input, button.top-menu-search-button, div.menu-sidebar a{ - background-color: transparent; -} - -/* OAuth2 Proxy */ -{# The variables look like they are bootstrap variables. @todo Verify and generalize if possible #} -.box { - background-color: var(--color-01-92); - color: var(--color-01-10); -} - -.subtitle { - color: inherit; -} - -.has-background-light { - background-color: var(--color-01-96) !important; -} - -/* Pixelfed */ -div.page-wrapper{ - background: none; - background-color: none; -} - -.card-header-title { - color: var(--color-01-37); -} - -/* PHP MyAdmin */ -#pma_navigation { - background: linear-gradient(to right, var(--color-01-95), var(--color-01-85)); - color: var(--color-01-05); -} - -#pma_navigation_tree a { - color: var(--color-01-05); -} - -#pma_navigation_tree li.activePointer, #pma_navigation_tree li.selected { - color: var(--color-01-05); - background-color: var(--color-01-70); -} - -.breadcrumb-navbar { - background-color: var(--color-01-86); -} - -.navbar-nav .nav-item { - background: linear-gradient(var(--color-01-99), var(--color-01-85)); - border-right-color: var(--color-01-99); - border-left-color: var(--color-01-80); - border-bottom-color: var(--color-01-80); -} - -.result_query div.sqlOuter { - background: var(--color-01-50); -} - -.table { - --bs-table-bg: var(--color-01-99); /* #fff → white */ - --bs-table-border-color: var(--color-01-99); /* #fff → white */ - --bs-table-striped-bg: var(--color-01-90); /* #dfdfdf → light gray */ - --bs-table-hover-color: var(--color-01-01); /* #000 → black */ -} - -.table thead th { - background-image: linear-gradient(var(--color-01-90), var(--color-01-80)); - border-color: var(--color-01-99); -} - -.table th, .table td { - text-shadow: 0 1px 0 var(--color-01-60); -} - -div.tools, .tblFooters { - color: var(--color-01-01); - background: var(--color-01-62); -} - - -.navigation { - background: linear-gradient(var(--color-01-87), var(--color-01-69)); -} - -.pma-fieldset { - border-color: var(--color-01-17); - background: var(--color-01-80); -} - -/** Taiga specific configuration **/ - -section.main.kanban{ - background-color: transparent; -} - -div.master, div.kanban-header, div.kanban-table-inner, section.kanban button,a.dropdown-project-list-projects{ - background-color: var(--color-01-92); - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-87), var(--color-01-92), var(--color-01-93), var(--color-01-97)); - color: var(--color-01-40); -} - -section.kanban h1, section.kanban h2{ - color: var(--color-01-40); -} - -.home-project { - background: var(--color-01-88); - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-83), var(--color-01-88), var(--color-01-89), var(--color-01-93)); - border-color: var(--color-01-60); - color: var(--color-01-12); -} - -.home-wrapper .title-bar { - background: var(--color-01-75); - background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-70), var(--color-01-75), var(--color-01-76), var(--color-01-80)); -} - -.kanban.swimlane .kanban-header { - background: none; -} - -.kanban-table-header .task-colum-name { - background-color: var(--color-01-70); - color: var(--color-01-10); -} - -input.ng-empty::placeholder,.ng-empty::placeholder { - color: rgba(var(--color-01-rgb-03),0.6); -} - -.lightbox { - background: rgba(var(--color-01-rgb-97), .93); - color: var(--color-01-03); -} - -.kanban-filter tg-filter { - border-color: var(--color-01-70); -} - -.discover-header { - background: none; -} - -/* Portfolio */ -.card-img-top i { - filter: drop-shadow(4px 4px 4px rgba(var(--color-01-rgb-23), 0.6)); -} - -.kanban-table-body .kanban-uses-box { - background-color: var(--color-01-81); -} - -.kanban-swimlane-title { - border-bottom: none; -} - -.navbar-toggler { - background-color: rgba(var(--color-rgb-01-75), 0.9); - border-color: var(--color-01-67) -} - -.sys-ctl-alm-info { - --bs-sys-ctl-alm-color: var(--color-03-14); - --bs-sys-ctl-alm-bg: var(--color-01-86); -} \ No newline at end of file diff --git a/roles/sys-srv-web-inj-css/templates/head_sub.j2 b/roles/sys-srv-web-inj-css/templates/head_sub.j2 deleted file mode 100644 index fa1120bd..00000000 --- a/roles/sys-srv-web-inj-css/templates/head_sub.j2 +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/roles/sys-srv-web-inj-css/templates/location.conf.j2 b/roles/sys-srv-web-inj-css/templates/location.conf.j2 deleted file mode 100644 index d204c4d4..00000000 --- a/roles/sys-srv-web-inj-css/templates/location.conf.j2 +++ /dev/null @@ -1,3 +0,0 @@ -location = /global.css { - root {{ NGINX.DIRECTORIES.DATA.CDN }}; -} \ No newline at end of file diff --git a/roles/sys-srv-web-inj-css/vars/main.yml b/roles/sys-srv-web-inj-css/vars/main.yml deleted file mode 100644 index 10d51cb4..00000000 --- a/roles/sys-srv-web-inj-css/vars/main.yml +++ /dev/null @@ -1,4 +0,0 @@ -global_css_destination: "{{ NGINX.DIRECTORIES.DATA.CDN }}global.css" -global_css_base_color: "{{ design.css.colors.base }}" -global_css_count: 7 -global_css_shades: 100 \ No newline at end of file diff --git a/roles/sys-srv-web-inj-desktop/tasks/01_deploy.yml b/roles/sys-srv-web-inj-desktop/tasks/01_deploy.yml deleted file mode 100644 index 82ae7684..00000000 --- a/roles/sys-srv-web-inj-desktop/tasks/01_deploy.yml +++ /dev/null @@ -1,16 +0,0 @@ -- name: Deploy iframe-handler.js - template: - src: iframe-handler.js.j2 - dest: "{{ INJ_DESKTOP_JS_FILE_DESTINATION }}" - owner: "{{ NGINX.USER }}" - group: "{{ NGINX.USER }}" - mode: '0644' - -- name: Get stat for iframe-handler.js - stat: - path: "{{ INJ_DESKTOP_JS_FILE_DESTINATION }}" - register: inj_port_ui_js_stat - -- name: Set inj_port_ui_js_version - set_fact: - inj_port_ui_js_version: "{{ inj_port_ui_js_stat.stat.mtime }}" diff --git a/roles/sys-srv-web-inj-desktop/templates/head_sub.j2 b/roles/sys-srv-web-inj-desktop/templates/head_sub.j2 deleted file mode 100644 index f4a3f642..00000000 --- a/roles/sys-srv-web-inj-desktop/templates/head_sub.j2 +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/roles/sys-srv-web-inj-desktop/vars/main.yml b/roles/sys-srv-web-inj-desktop/vars/main.yml deleted file mode 100644 index ca67d07b..00000000 --- a/roles/sys-srv-web-inj-desktop/vars/main.yml +++ /dev/null @@ -1,2 +0,0 @@ -INJ_DESKTOP_JS_FILE_NAME: "iframe-handler.js" -INJ_DESKTOP_JS_FILE_DESTINATION: "{{ [ NGINX.DIRECTORIES.DATA.CDN, INJ_DESKTOP_JS_FILE_NAME ] | path_join }}" diff --git a/roles/sys-srv-web-inj-logout/templates/head_sub.j2 b/roles/sys-srv-web-inj-logout/templates/head_sub.j2 deleted file mode 100644 index 317ac0a7..00000000 --- a/roles/sys-srv-web-inj-logout/templates/head_sub.j2 +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/roles/sys-srv-web-inj-logout/vars/main.yml b/roles/sys-srv-web-inj-logout/vars/main.yml deleted file mode 100644 index 385f6e8f..00000000 --- a/roles/sys-srv-web-inj-logout/vars/main.yml +++ /dev/null @@ -1,2 +0,0 @@ -INJ_LOGOUT_JS_FILE_NAME: "logout.js" -INJ_LOGOUT_JS_DESTINATION: "{{ [ NGINX.DIRECTORIES.DATA.CDN, INJ_LOGOUT_JS_FILE_NAME ] | path_join }}" \ No newline at end of file diff --git a/roles/sys-stk-front-proxy/README.md b/roles/sys-stk-front-proxy/README.md index 5effc606..5580c522 100644 --- a/roles/sys-stk-front-proxy/README.md +++ b/roles/sys-stk-front-proxy/README.md @@ -8,7 +8,7 @@ This role bootstraps **per-domain Nginx configuration**: it requests TLS certifi A higher-level orchestration wrapper, *sys-stk-front-proxy* ties together several lower-level roles: -1. **`sys-srv-web-inj-compose`** – applies global tweaks and includes. +1. **`sys-front-inj-all`** – applies global tweaks and includes. 2. **`sys-svc-certs`** – obtains Let’s Encrypt certificates. 3. **Domain template deployment** – copies a Jinja2 vHost from *srv-proxy-core*. 4. **`web-app-oauth2-proxy`** *(optional)* – protects the site with OAuth2. diff --git a/roles/sys-svc-cdn/README.md b/roles/sys-svc-cdn/README.md new file mode 100644 index 00000000..d37f9a3b --- /dev/null +++ b/roles/sys-svc-cdn/README.md @@ -0,0 +1,19 @@ +# sys-svc-cdn + +CDN helper role for building a consistent asset tree, URLs, and on-disk layout. + +## Description + +Provides compact filters and defaults to define CDN paths, turn them into public URLs, collect required directories, and prepare the filesystem (including a `latest` release link). + +## Overview + +Defines a per-role CDN structure under `roles//` plus shared and vendor areas. Exposes ready-to-use variables (`cdn`, `cdn_dirs`, `cdn_urls`) and ensures directories exist. Optionally links the current release to `latest`. + +## Features + +* Jinja filters: `cdn_paths`, `cdn_urls`, `cdn_dirs` +* Variables: `CDN_ROOT`, `CDN_VERSION`, `CDN_BASE_URL`, `cdn`, `cdn_dirs`, `cdn_urls` +* Creates shared/vendor/release directories +* Maintains `roles//latest` symlink (when version ≠ `latest`) +* Plays nicely with `web-svc-cdn` without circular inclusion diff --git a/tests/unit/roles/sys-srv-web-inj-compose/__init__.py b/roles/sys-svc-cdn/__init__.py similarity index 100% rename from tests/unit/roles/sys-srv-web-inj-compose/__init__.py rename to roles/sys-svc-cdn/__init__.py diff --git a/tests/unit/roles/sys-srv-web-inj-compose/filter_plugins/__init__.py b/roles/sys-svc-cdn/filter_plugins/__init__.py similarity index 100% rename from tests/unit/roles/sys-srv-web-inj-compose/filter_plugins/__init__.py rename to roles/sys-svc-cdn/filter_plugins/__init__.py diff --git a/roles/sys-svc-cdn/filter_plugins/cdn_dirs.py b/roles/sys-svc-cdn/filter_plugins/cdn_dirs.py new file mode 100644 index 00000000..d2490dd6 --- /dev/null +++ b/roles/sys-svc-cdn/filter_plugins/cdn_dirs.py @@ -0,0 +1,17 @@ +import os + +def cdn_dirs(tree): + out = set() + def walk(v): + if isinstance(v, dict): + for x in v.values(): walk(x) + elif isinstance(v, list): + for x in v: walk(x) + elif isinstance(v, str) and os.path.isabs(v): + out.add(v) + walk(tree) + return sorted(out) + +class FilterModule(object): + def filters(self): + return {"cdn_dirs": cdn_dirs} diff --git a/roles/sys-svc-cdn/filter_plugins/cdn_paths.py b/roles/sys-svc-cdn/filter_plugins/cdn_paths.py new file mode 100644 index 00000000..a0e10445 --- /dev/null +++ b/roles/sys-svc-cdn/filter_plugins/cdn_paths.py @@ -0,0 +1,46 @@ +import datetime +import os + +def cdn_paths(cdn_root, application_id, version): + """ + Build a structured dictionary of all CDN paths for a given application. + + Args: + cdn_root (str): Base CDN root, e.g. /var/www/cdn + application_id (str): Role/application identifier + version (str): Release version string (default: current UTC timestamp) + + Returns: + dict: Hierarchical CDN path structure + """ + cdn_root = os.path.abspath(cdn_root) + + return { + "root": cdn_root, + "shared": { + "root": os.path.join(cdn_root, "_shared"), + "css": os.path.join(cdn_root, "_shared", "css"), + "js": os.path.join(cdn_root, "_shared", "js"), + "img": os.path.join(cdn_root, "_shared", "img"), + "fonts": os.path.join(cdn_root, "_shared", "fonts"), + }, + "vendor": os.path.join(cdn_root, "vendor"), + "role": { + "id": application_id, + "root": os.path.join(cdn_root, "roles", application_id), + "version": version, + "release": { + "root": os.path.join(cdn_root, "roles", application_id, version), + "css": os.path.join(cdn_root, "roles", application_id, version, "css"), + "js": os.path.join(cdn_root, "roles", application_id, version, "js"), + "img": os.path.join(cdn_root, "roles", application_id, version, "img"), + "fonts": os.path.join(cdn_root, "roles", application_id, version, "fonts"), + }, + }, + } + +class FilterModule(object): + def filters(self): + return { + "cdn_paths": cdn_paths, + } diff --git a/roles/sys-svc-cdn/filter_plugins/cdn_urls.py b/roles/sys-svc-cdn/filter_plugins/cdn_urls.py new file mode 100644 index 00000000..c3e38e7c --- /dev/null +++ b/roles/sys-svc-cdn/filter_plugins/cdn_urls.py @@ -0,0 +1,60 @@ +# filter_plugins/cdn_urls.py +import os + +def _to_url_tree(obj, cdn_root, base_url): + """ + Recursively walk a nested dict and replace any string paths under cdn_root + with URLs based on base_url. Non-path strings (e.g. role.id, role.version) + are left untouched. + """ + if isinstance(obj, dict): + return {k: _to_url_tree(v, cdn_root, base_url) for k, v in obj.items()} + + if isinstance(obj, list): + return [_to_url_tree(v, cdn_root, base_url) for v in obj] + + if isinstance(obj, str): + # Normalize inputs + norm_root = os.path.abspath(cdn_root) + norm_val = os.path.abspath(obj) + + if norm_val.startswith(norm_root): + # Compute path relative to CDN root and map to URL + rel = os.path.relpath(norm_val, norm_root) + # Handle root itself ('.') → empty path + if rel == ".": + rel = "" + # Always forward slashes for URLs + rel_url = rel.replace(os.sep, "/") + base = base_url.rstrip("/") + return f"{base}/{rel_url}" if rel_url else f"{base}/" + # Non-CDN string → leave as-is (e.g., role.id / role.version) + return obj + + # Any other type → return as-is + return obj + + +def cdn_urls(cdn_dict, base_url): + """ + Create a URL-structured dict from a CDN path dict. + + Args: + cdn_dict (dict): output of cdn_paths(...), containing absolute paths + base_url (str): CDN base URL, e.g. https://cdn.example.com + + Returns: + dict: same shape as cdn_dict, but with URLs instead of filesystem paths + for any strings pointing under cdn_dict['root']. + Keys like role.id and role.version remain strings as-is. + """ + if not isinstance(cdn_dict, dict) or "root" not in cdn_dict: + raise ValueError("cdn_urls expects a dict from cdn_paths with a 'root' key") + return _to_url_tree(cdn_dict, cdn_dict["root"], base_url) + + +class FilterModule(object): + def filters(self): + return { + "cdn_urls": cdn_urls, + } diff --git a/roles/sys-svc-cdn/meta/main.yml b/roles/sys-svc-cdn/meta/main.yml new file mode 100644 index 00000000..99732ef7 --- /dev/null +++ b/roles/sys-svc-cdn/meta/main.yml @@ -0,0 +1,24 @@ +--- +galaxy_info: + author: "Kevin Veen-Birkenbach" + description: "Prepares and manages the CDN folder structure with shared, vendor, and per-role release directories." + license: "Infinito.Nexus NonCommercial License" + license_url: "https://s.infinito.nexus/license" + company: | + Kevin Veen-Birkenbach + Consulting & Coaching Solutions + https://www.veen.world + min_ansible_version: "2.9" + platforms: + - name: Any + versions: + - all + galaxy_tags: + - cdn + - nginx + - assets + - roles + - versioning + repository: "https://s.infinito.nexus/code" + issue_tracker_url: "https://s.infinito.nexus/issues" + documentation: "https://s.infinito.nexus/code/tree/main/roles/sys-svc-cdn" diff --git a/roles/sys-svc-cdn/tasks/main.yml b/roles/sys-svc-cdn/tasks/main.yml new file mode 100644 index 00000000..38defcd5 --- /dev/null +++ b/roles/sys-svc-cdn/tasks/main.yml @@ -0,0 +1,41 @@ +--- +- block: + - name: "Load CDN for '{{ domain }}'" + include_role: + name: web-svc-cdn + public: false + when: + #- inj_enabled.logout + #- inj_enabled.desktop + - application_id != 'web-svc-cdn' + - run_once_web_svc_cdn is not defined + + - name: Overwritte CDN handlers with neutral handlers + ansible.builtin.include_tasks: "{{ [ playbook_dir, 'tasks/utils/load_handlers.yml'] | path_join }}" + loop: + - svc-prx-openresty + - docker-compose + loop_control: + label: "{{ item }}" + vars: + handler_role_name: "{{ item }}" + - include_tasks: utils/run_once.yml + when: + - run_once_sys_svc_cdn is not defined + +- name: Ensure CDN directories exist + file: + path: "{{ item }}" + state: directory + owner: "{{ NGINX.USER }}" + group: "{{ NGINX.USER }}" + mode: "0755" + loop: "{{ cdn_dirs }}" + +- name: Ensure 'latest' symlink points to current release + file: + src: "{{ cdn.role.release.root }}" + dest: "{{ [cdn.role.root, 'latest'] | path_join }}" + state: link + force: true + when: CDN_VERSION != 'latest' diff --git a/roles/sys-svc-cdn/vars/main.yml b/roles/sys-svc-cdn/vars/main.yml new file mode 100644 index 00000000..6ee87117 --- /dev/null +++ b/roles/sys-svc-cdn/vars/main.yml @@ -0,0 +1,19 @@ +# Base CDN root (shared across all roles) +CDN_ROOT: "{{ NGINX.DIRECTORIES.DATA.CDN }}" + +# Default version identifier: UTC timestamp +CDN_VERSION: "latest" # Latest is used atm because timestamp based would just lead to an unneccessary overhead + +# Base Url to deliver the files +CDN_BASE_URL: "{{ domains | get_url('web-svc-cdn', WEB_PROTOCOL) }}" + +# Role specific CDN paths + +## Build CDN path structure (via filter) +cdn: "{{ CDN_ROOT | cdn_paths(application_id, CDN_VERSION) }}" + +## Flatten CDN dict to list of all string paths +cdn_dirs: "{{ cdn | cdn_dirs }}" + +# Dictionary with all urls +cdn_urls: "{{ cdn | cdn_urls(CDN_BASE_URL) }}" diff --git a/roles/update-docker/templates/script.py.j2 b/roles/update-docker/templates/script.py.j2 index eea21548..facfdec9 100644 --- a/roles/update-docker/templates/script.py.j2 +++ b/roles/update-docker/templates/script.py.j2 @@ -144,14 +144,6 @@ def update_discourse(directory): else: print("Discourse update skipped. No changes in git repository.") -def update_mastodon(): - """ - Runs the database migration for Mastodon to ensure all required tables are up to date. - """ - print("Starting Mastodon database migration.") - run_command("docker compose exec -T web bash -c 'RAILS_ENV={{ ENVIRONMENT | lower }} bin/rails db:migrate'") - print("Mastodon database migration complete.") - def upgrade_listmonk(): """ Runs the upgrade for Listmonk @@ -220,8 +212,6 @@ if __name__ == "__main__": update_discourse(dir_path) elif os.path.basename(dir_path) == "listmonk": upgrade_listmonk() - elif os.path.basename(dir_path) == "mastodon": - update_mastodon() # @todo implement dedicated procedure for bluesky # @todo implement dedicated procedure for taiga diff --git a/roles/web-app-desktop/config/main.yml b/roles/web-app-desktop/config/main.yml index 68788d80..c8f5671d 100644 --- a/roles/web-app-desktop/config/main.yml +++ b/roles/web-app-desktop/config/main.yml @@ -13,7 +13,7 @@ server: - https://cdn.jsdelivr.net - https://kit.fontawesome.com - https://code.jquery.com/ - style-src: + style-src-elem: - https://cdn.jsdelivr.net font-src: - https://ka-f.fontawesome.com @@ -24,12 +24,8 @@ server: frame-src: - "{{ WEB_PROTOCOL }}://*.{{ PRIMARY_DOMAIN }}" flags: - style-src: - unsafe-inline: true script-src: unsafe-inline: true - script-src-elem: - unsafe-inline: true domains: canonical: - "{{ PRIMARY_DOMAIN }}" diff --git a/roles/web-app-desktop/templates/style.css.j2 b/roles/web-app-desktop/templates/style.css.j2 new file mode 100644 index 00000000..928289fd --- /dev/null +++ b/roles/web-app-desktop/templates/style.css.j2 @@ -0,0 +1,21 @@ +.card-img-top i { + filter: drop-shadow(4px 4px 4px rgba(var(--color-01-rgb-23), 0.6)); +} + +.kanban-table-body .kanban-uses-box { + background-color: var(--color-01-81); +} + +.kanban-swimlane-title { + border-bottom: none; +} + +.navbar-toggler { + background-color: rgba(var(--color-01-rgb-75), 0.9); + border-color: var(--color-01-67) +} + +.sys-ctl-alm-info { + --bs-sys-ctl-alm-color: var(--color-03-14); + --bs-sys-ctl-alm-bg: var(--color-01-86); +} \ No newline at end of file diff --git a/roles/web-app-discourse/templates/style.css.j2 b/roles/web-app-discourse/templates/style.css.j2 new file mode 100644 index 00000000..41821e5f --- /dev/null +++ b/roles/web-app-discourse/templates/style.css.j2 @@ -0,0 +1,102 @@ +/* Discourse */ +:root section#main{ + /* Base Colors */ + --primary: var(--color-01-20); /* originally #203243 */ + --secondary: var(--color-01-95); /* originally #eef4f7 */ + --tertiary: var(--color-01-40); /* originally #416376 */ + --quaternary: var(--color-01-50); /* originally #5e99b9 */ + + /* Header & Highlight */ + --header_background: var(--color-01-86); /* originally #86bddb */ + --header_primary: var(--color-01-20); /* same as --primary */ + --highlight: var(--color-01-70); /* same as header_background */ + --d-selected: var(--color-01-85); /* originally #bee0f2 */ + --d-hover: var(--color-01-90); /* originally #d2efff */ + + /* Normally refers to secondary. Somehow this reference does not work.*/ + --d-sidebar-background: var(--color-01-92); + --d-sidebar-footer-fade: var(--color-01-92); + + + /* RGB values */ + --always-black-rgb: var(--color-01-rgb-01); + --primary-rgb: var(--color-01-rgb-20); + --primary-low-rgb: var(--color-01-rgb-95); + --primary-very-low-rgb: var(--color-01-rgb-99); + --secondary-rgb: var(--color-01-rgb-95); + --header_background-rgb: var(--color-01-rgb-70); + --tertiary-rgb: var(--color-01-rgb-40); + --highlight-rgb: var(--color-01-rgb-70); + --success-rgb: var(--color-01-rgb-50); + + + /* Primary Scale */ + --primary-very-low: var(--color-01-99); /* originally #f7f9fb */ + --primary-low: var(--color-01-95); /* originally #e3ebf2 */ + --primary-low-mid: var(--color-01-75); /* originally #acc2d7 */ + --primary-medium: var(--color-01-60); /* originally #7499bd */ + --primary-high: var(--color-01-40); /* originally #487096 */ + --primary-very-high: var(--color-01-20); /* originally #34516d */ + --primary-50: var(--color-01-99); /* originally #f7f9fb */ +{% for i in range(1, 10) %} +{# @see https://chatgpt.com/share/67bcd94e-bb44-800f-bf63-06d1ae0f5096 #} + {% set primary = i * 100 %} + {% set color = 100 - i * 8 %} + --primary-{{ primary }}: var(--color-01-{{ "%02d" % color }}); +{% endfor %} + + /* Header Primary Scale */ + --header_primary-low: rgb(var(--color-01-rgb-75)); /* rgb(128, 180, 209) */ + --header_primary-low-mid: rgb(var(--color-01-rgb-70)); /* rgb(110, 155, 181) */ + --header_primary-medium: rgb(var(--color-01-rgb-60)); /* rgb(93, 132, 155) */ + --header_primary-high: rgb(var(--color-01-rgb-50)); /* rgb(78, 112, 132) */ + --header_primary-very-high: rgb(var(--color-01-rgb-20)); /* rgb(52, 76, 94) */ + + /* Secondary Scale */ + --secondary-low: var(--color-01-20); /* originally #2f5163 */ + --secondary-medium: var(--color-01-40); /* originally #4e88a5 */ + --secondary-high: var(--color-01-60); /* originally #7ba9c1 */ + --secondary-very-high: var(--color-01-90); /* originally #d7e5ec */ + + /* Tertiary Scale */ + --tertiary-very-low: var(--color-01-99); /* originally #eaf0f3 */ + --tertiary-low: var(--color-01-95); /* originally #dfe8ee */ + --tertiary-medium: var(--color-01-60); /* originally #96b4c5 */ + --tertiary-high: var(--color-01-40); /* originally #5886a0 */ + --tertiary-hover: var(--color-01-20); /* originally #314a59 */ + --tertiary-50: var(--color-01-99); /* originally #eaf0f3 */ +{% for i in range(1, 10) %} +{# @see https://chatgpt.com/share/67bcd94e-bb44-800f-bf63-06d1ae0f5096 #} + {% set tertiary = i * 100 %} + {% set color = 100 - i * 5 %} + --tertiary-{{ tertiary }}: var(--color-01-{{ "%02d" % color }}); +{% endfor %} + + /* Quaternary */ + --quaternary-low: var(--color-01-80); /* originally #cfe0ea */ + + /* Highlight */ + --highlight-bg: var(--color-01-90); /* originally #dbebf4 */ + --highlight-low: var(--color-01-90); /* originally #dbebf4 */ + --highlight-medium: var(--color-01-80); /* originally #c3deed */ + --highlight-high: var(--color-01-30); /* originally #286688 */ + + /* Combination Variables */ + --blend-primary-secondary-5: rgb(var(--color-01-rgb-95)); /* originally rgb(232, 238, 241) */ + --primary-med-or-secondary-med: var(--color-01-60); /* originally #7499bd */ + --primary-med-or-secondary-high: var(--color-01-60); /* originally #7499bd */ + --primary-high-or-secondary-low: var(--color-01-40); /* originally #487096 */ + --primary-low-mid-or-secondary-high: var(--color-01-75); /* originally #acc2d7 */ + --primary-low-mid-or-secondary-low: var(--color-01-75); /* originally #acc2d7 */ + --primary-or-primary-low-mid: var(--color-01-20); /* originally #203243 */ + --highlight-low-or-medium: var(--color-01-90); /* originally #dbebf4 */ + --tertiary-or-tertiary-low: var(--color-01-40); /* originally #416376 */ + --tertiary-low-or-tertiary-high: var(--color-01-95); /* originally #dfe8ee */ + --tertiary-med-or-tertiary: var(--color-01-60); /* originally #96b4c5 */ + --secondary-or-primary: var(--color-01-95); /* originally #eef4f7 */ + --tertiary-or-white: var(--color-01-40); /* originally #416376 */ + + /* Float Kit */ + --float-kit-arrow-stroke-color: var(--primary-low); /* already mapped above */ + --float-kit-arrow-fill-color: var(--secondary); /* already mapped above */ +} diff --git a/roles/web-app-gitea/templates/style.css.j2 b/roles/web-app-gitea/templates/style.css.j2 new file mode 100644 index 00000000..eb38808b --- /dev/null +++ b/roles/web-app-gitea/templates/style.css.j2 @@ -0,0 +1,141 @@ +:root { + /* Base and derived colors are now referenced from the computed scale */ + --color-01-primary: var(--color-01-50); + --color-01-primary-contrast: var(--color-01-99); + --color-01-primary-dark-1: var(--color-01-48); + --color-01-primary-dark-2: var(--color-01-47); + --color-01-primary-dark-3: var(--color-01-46); + --color-01-primary-dark-4: var(--color-01-45); + --color-01-primary-dark-5: var(--color-01-44); + --color-01-primary-dark-6: var(--color-01-43); + --color-01-primary-dark-7: var(--color-01-42); + --color-01-primary-light-1: var(--color-01-52); + --color-01-primary-light-2: var(--color-01-53); + --color-01-primary-light-3: var(--color-01-54); + --color-01-primary-light-4: var(--color-01-55); + --color-01-primary-light-5: var(--color-01-57); + --color-01-primary-light-6: var(--color-01-59); + --color-01-primary-light-7: var(--color-01-61); + + /* Alpha variants reference the base RGB variable */ +{% for i in range(1, 10) %} +{# @see https://chatgpt.com/share/67bcd94e-bb44-800f-bf63-06d1ae0f5096 #} + {% set alpha = i * 10 %} + --color-01-primary-alpha-{{ alpha }}: rgba(var(--color-01-rgb-50), 0.{{ alpha }}); +{% endfor %} + + --color-01-primary-hover: var(--color-01-primary-dark-1); + --color-01-primary-active: var(--color-01-primary-dark-2); + + /* Secondary colors */ + --color-01-secondary: var(--color-01-80); + --color-01-secondary-dark-1: var(--color-01-78); + --color-01-secondary-dark-2: var(--color-01-76); + --color-01-secondary-dark-3: var(--color-01-74); + --color-01-secondary-dark-4: var(--color-01-72); + --color-01-secondary-dark-5: var(--color-01-70); + --color-01-secondary-dark-6: var(--color-01-68); + --color-01-secondary-dark-7: var(--color-01-66); + --color-01-secondary-dark-8: var(--color-01-64); + --color-01-secondary-dark-9: var(--color-01-62); + --color-01-secondary-dark-10: var(--color-01-60); + --color-01-secondary-dark-11: var(--color-01-58); + --color-01-secondary-dark-12: var(--color-01-56); + --color-01-secondary-dark-13: var(--color-01-54); + --color-01-secondary-light-1: var(--color-01-92); + --color-01-secondary-light-2: var(--color-01-93); + --color-01-secondary-light-3: var(--color-01-94); + --color-01-secondary-light-4: var(--color-01-95); + +{% for i in range(1, 10) %} +{# @see https://chatgpt.com/share/67bcd94e-bb44-800f-bf63-06d1ae0f5096 #} + {% set alpha = i * 10 %} + --color-01-secondary-alpha-{{ alpha }}: rgba(var(--color-01-rgb-80), 0.{{ alpha }}); +{% endfor %} + + --color-01-secondary-button: var(--color-01-secondary-dark-4); + --color-01-secondary-hover: var(--color-01-secondary-dark-5); + --color-01-secondary-active: var(--color-01-secondary-dark-6); + + /* Console Colors */ + --color-01-console-fg: var(--color-01-98); + --color-01-console-fg-subtle: var(--color-01-85); + --color-01-console-bg: var(--color-01-10); + --color-01-console-border: var(--color-01-40); + --color-01-console-hover-bg: var(--color-01-42); + --color-01-console-active-bg: var(--color-01-40); + --color-01-console-menu-bg: var(--color-01-38); + --color-01-console-menu-border:var(--color-01-45); + + /* Body, Text, and Miscellaneous Colors */ + --color-01-white: var(--color-01-99); + --color-01-grey: var(--color-01-60); + --color-01-grey-light: var(--color-01-65); + + --color-01-body: var(--color-01-white); + --color-01-text-dark: var(--color-01-10); + --color-01-text: var(--color-01-40); + --color-01-text-light: var(--color-01-60); + --color-01-text-light-1: var(--color-01-65); + --color-01-text-light-2: var(--color-01-70); + --color-01-text-light-3: var(--color-01-75); + + --color-01-footer: var(--color-01-nav-bg); + --color-01-timeline: var(--color-01-80); + + /* Input Colors */ + --color-01-input-text: var(--color-01-10); + --color-01-input-background: var(--color-01-white); + --color-01-input-toggle-background: var(--color-01-80); + --color-01-input-border: var(--color-01-secondary); + --color-01-input-border-hover: var(--color-01-secondary-dark-1); + + /* Effects */ + --color-01-light: var(--color-01-05); + --color-01-light-mimic-enabled: rgba(var(--color-01-rgb-05), calc(6 / 255 * 222 / 255 / 0.55)); + --color-01-light-border: var(--color-01-05); + --color-01-hover: var(--color-01-05); + --color-01-hover-opaque: var(--color-01-95); + --color-01-active: var(--color-01-05); + + /* Menu, Card, and Markup Colors */ + --color-01-menu: var(--color-01-99); + --color-01-card: var(--color-01-99); + --color-01-markup-table-row: var(--color-01-01); + --color-01-markup-code-block: var(--color-01-01); + --color-01-markup-code-inline: var(--color-01-01); + --color-01-button: var(--color-01-99); + --color-01-code-bg: var(--color-01-99); + --color-01-shadow: var(--color-01-05); + --color-01-shadow-opaque: var(--color-01-85); + --color-01-secondary-bg: var(--color-01-95); + --color-01-expand-button: var(--color-01-98); + --color-01-placeholder-text: var(--color-01-text-light-3); + --color-01-editor-line-highlight: var(--color-01-primary-light-6); + --color-01-project-column-bg: var(--color-01-secondary-light-4); + --color-01-caret: var(--color-01-10); + + /* Reaction and Tooltip Colors */ + --color-01-reaction-bg: var(--color-01-05); + --color-01-reaction-hover-bg: var(--color-01-primary-light-5); + --color-01-reaction-active-bg: var(--color-01-primary-light-6); + --color-01-tooltip-text: var(--color-01-99); + --color-01-tooltip-bg: var(--color-01-05); + + /* Navigation Colors */ + --color-01-nav-bg: var(--color-01-99); + --color-01-nav-hover-bg: var(--color-01-secondary-light-1); + --color-01-nav-text: var(--color-01-40); + --color-01-secondary-nav-bg: var(--color-01-99); + + /* Label and Accent Colors */ + --color-01-label-text: var(--color-01-40); + --color-01-label-bg: var(--color-01-50); + --color-01-label-hover-bg: var(--color-01-60); + --color-01-label-active-bg: var(--color-01-70); + --color-01-accent: var(--color-01-primary-light-1); + --color-01-small-accent: var(--color-01-primary-light-6); + --color-01-highlight-fg: var(--color-01-10); + --color-01-highlight-bg: var(--color-01-99); + --color-01-overlay-backdrop: var(--color-01-05); +} diff --git a/roles/web-app-keycloak/templates/style.css.j2 b/roles/web-app-keycloak/templates/style.css.j2 new file mode 100644 index 00000000..e1de6a67 --- /dev/null +++ b/roles/web-app-keycloak/templates/style.css.j2 @@ -0,0 +1,109 @@ +:root{ + /* --- Palette Black (Gray Scale) --- */ +{% for i in range(1, 21) %} +{# @see https://chatgpt.com/share/67bcd94e-bb44-800f-bf63-06d1ae0f5096 #} + {% set black = i * 50 %} + {% set color = 100 - i * 5 %} + --pf-v5-global--palette--black-{{ black }}: var(--color-01-{{ "%02d" % color }}); +{% endfor %} + + /* --- White --- */ + --pf-v5-global--palette--white: var(--color-01-99); + + /* --- Background Colors --- */ + --pf-v5-global--BackgroundColor--100: var(--color-01-99); + --pf-v5-global--BackgroundColor--150: var(--color-01-95); + --pf-v5-global--BackgroundColor--200: var(--color-01-85); + --pf-v5-global--BackgroundColor--300: var(--color-01-75); + --pf-v5-global--BackgroundColor--400: var(--color-01-65); + --pf-v5-global--BackgroundColor--light-100: var(--color-01-99); + --pf-v5-global--BackgroundColor--light-200: var(--color-01-95); + --pf-v5-global--BackgroundColor--light-300: var(--color-01-85); + --pf-v5-global--BackgroundColor--dark-100: var(--color-01-10); + --pf-v5-global--BackgroundColor--dark-200: var(--color-01-25); + --pf-v5-global--BackgroundColor--dark-300: var(--color-01-20); + --pf-v5-global--BackgroundColor--dark-400: var(--color-01-30); + --pf-v5-global--BackgroundColor--dark-transparent-100: rgba(var(--color-01-rgb-10),0.7); + --pf-v5-global--BackgroundColor--dark-transparent-200: rgba(var(--color-01-rgb-20),0.8); + + /* --- Color Variables --- */ + --pf-v5-global--color-01--100: var(--color-01-10); + --pf-v5-global--color-01--200: var(--color-01-40); + --pf-v5-global--color-01--300: var(--color-01-25); + --pf-v5-global--color-01--400: var(--color-01-50); + --pf-v5-global--color-01--light-100: var(--color-01-99); + --pf-v5-global--color-01--light-200: var(--color-01-85); + --pf-v5-global--color-01--light-300: var(--color-01-75); + --pf-v5-global--color-01--dark-100: var(--color-01-10); + --pf-v5-global--color-01--dark-200: var(--color-01-40); + + /* --- Active Colors --- */ + --pf-v5-global--active-color--100: var(--color-01-65); + --pf-v5-global--active-color--200: var(--color-01-95); + --pf-v5-global--active-color--300: var(--color-01-75); + --pf-v5-global--active-color--400: var(--color-01-85); + + /* --- Disabled Colors --- */ + --pf-v5-global--disabled-color--100: var(--color-01-40); + --pf-v5-global--disabled-color--200: var(--color-01-75); + --pf-v5-global--disabled-color--300: var(--color-01-85); + + /* --- Primary Colors --- */ + --pf-v5-global--primary-color--100: var(--color-01-65); + --pf-v5-global--primary-color--200: var(--color-01-40); + --pf-v5-global--primary-color--light-100: var(--color-01-75); + --pf-v5-global--primary-color--dark-100: var(--color-01-65); + + /* --- Secondary Colors --- */ + --pf-v5-global--secondary-color--100: var(--color-01-40); + + /* --- Custom Colors --- */ + --pf-v5-global--custom-color--100: var(--color-01-65); + --pf-v5-global--custom-color--200: var(--color-01-65); + --pf-v5-global--custom-color--300: var(--color-01-30); + + /* --- Link Colors --- */ + --pf-v5-global--link--Color: var(--color-01-65); + --pf-v5-global--link--color-01--hover: var(--color-01-40); + --pf-v5-global--link--color-01--light: var(--color-01-75); + --pf-v5-global--link--color-01--light--hover: var(--color-01-85); + --pf-v5-global--link--color-01--dark: var(--color-01-65); + --pf-v5-global--link--color-01--dark--hover: var(--color-01-40); + --pf-v5-global--link--color-01--visited: var(--color-01-40); + + /* --- Border Colors --- */ + --pf-v5-global--BorderColor--100: var(--color-01-75); + --pf-v5-global--BorderColor--200: var(--color-01-50); + --pf-v5-global--BorderColor--300: var(--color-01-85); + --pf-v5-global--BorderColor--400: var(--color-01-65); + --pf-v5-global--BorderColor--dark-100: var(--color-01-75); + --pf-v5-global--BorderColor--light-100: var(--color-01-65); + + /* --- Icon Colors --- */ + --pf-v5-global--icon--color-01--light: var(--color-01-40); + --pf-v5-global--icon--color-01--dark: var(--color-01-10); + --pf-v5-global--icon--color-01--light--light: var(--color-01-85); + --pf-v5-global--icon--color-01--dark--light: var(--color-01-99); + --pf-v5-global--icon--color-01--light--dark: var(--color-01-40); + --pf-v5-global--icon--color-01--dark--dark: var(--color-01-10); + +} + +.pf-v5-c-button.pf-m-tertiary { + --pf-v5-c-button--m-tertiary--BackgroundColor: var(--color-01-70); + {# Assume that the following line is necessary due to load order #} + background-color: var(--pf-v5-c-button--m-tertiary--BackgroundColor); +} + +/* Additional Keykloak Configuration */ +a.pf-v5-c-nav__link{ + --pf-v5-c-nav__link--BackgroundColor: rgba(var(--color-01-rgb-56), 0.4); +} + +/* Keycloak */ +div#app header{ + background-color: var(--color-01-60); + /* New Gradient based on original background (60 -5, 60, 60 +1, 60 +5) */ + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-55), var(--color-01-60), var(--color-01-61), var(--color-01-65)); + color: var(--color-01-98); +} diff --git a/roles/web-app-lam/templates/style.css.j2 b/roles/web-app-lam/templates/style.css.j2 new file mode 100644 index 00000000..f79b242a --- /dev/null +++ b/roles/web-app-lam/templates/style.css.j2 @@ -0,0 +1,51 @@ +:root { +--lam-background-color-default: var(--color-01-99); {# from #FFFFFF (very bright white) #} +--lam-input-bg-color: var(--color-01-98); {# from #fcfcfc (almost white) #} +--lam-text-color-default: var(--color-01-01); {# from #000000 (pure black) #} +--lam-border-color: var(--color-01-90); {# from #e8e8e8 (light grey) #} +--lam-border-color-primary: var(--color-01-15); {# from #01689e (dark blue) #} +--lam-border-color-secondary: var(--color-01-85); {# from #ffcb1d (bright yellow) #} +--lam-background-color-primary: var(--color-01-50); {# from #3daee9 (mid-tone blue) #} +--lam-background-color-secondary: var(--color-01-90); {# from #ffe233 (bright yellow) #} +--lam-text-color-primary: var(--color-01-99); {# from #ffffff (pure white) #} +--lam-text-color-secondary: var(--color-01-01); {# from #000000 (pure black) #} +--lam-text-color-ok: var(--color-01-10); {# from #237d0c (dark green) #} +--lam-table-background-color-bright: var(--color-01-98); {# from #fbfbfb (very light grey) #} +--lam-table-background-color-dark: var(--color-01-92); {# from #e8f3ff (light blue) #} +--lam-table-background-color-hover: var(--color-01-50); {# from #3daee9 (mid-tone blue) #} +--lam-table-text-color-hover: var(--color-01-99); {# from #ffffff (pure white) #} +--lam-table-border-color: var(--color-01-50); {# from #3daee9 (mid-tone blue) #} +} + + +/** LAM Specific **/ +.lam-vertical-tabs-navigation li, .lam-vertical-tabs-navigation{ + background-color: transparent !important; + border-color: transparent; +} + +ul.lam-tab-navigation { + background: rgba(var(--color-01-rgb-90), 0.1); + border-color: transparent; +} + +/* Not changable due to inline css */ +.roundedShadowBox { + color: #000000; +} + +.titleBar { + background-image: linear-gradient(var(--color-01-83), var(--color-01-92)); + /* New Gradient based on original background (83 -5, 83, 83 +1, 83 +5) */ + background-image: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-78), var(--color-01-83), var(--color-01-84), var(--color-01-88)); + border-top-color: var(--color-01-78); + border-left-color: var(--color-01-87); + border-right-color: var(--color-01-87); +} + +div.statusInfo { + background-color: var(--color-01-81); + /* New Gradient based on original background (81 -5, 81, 81 +1, 81 +5) */ + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-76), var(--color-01-81), var(--color-01-82), var(--color-01-86)); + color: var(--color-01-23); +} diff --git a/roles/web-app-mailu/templates/style.css.j2 b/roles/web-app-mailu/templates/style.css.j2 new file mode 100644 index 00000000..b4474e27 --- /dev/null +++ b/roles/web-app-mailu/templates/style.css.j2 @@ -0,0 +1,35 @@ +[class*=sidebar-dark-], .bg-mailu-logo { + background-color: var(--color-01-90); + /* New Gradient based on original background (90 -5, 90, 90 +1, 90 +5) */ + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-85), var(--color-01-90), var(--color-01-91), var(--color-01-95)); +} + +div.statusError { + background-color: var(--color-01-60); + /* New Gradient based on original background (60 -5, 60, 60 +1, 60 +5) */ + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-55), var(--color-01-60), var(--color-01-61), var(--color-01-65)); +} + +div.wrapper footer.main-footer, div.wrapper div.content-wrapper{ + background-color: var(--color-01-85); + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-89), var(--color-01-85), var(--color-01-80), var(--color-01-79)); + color: var(--color-01-39); +} + +html.dark-mode #layout-menu .special-buttons a:not(:focus) { + background: none; +} + +html.dark-mode #taskmenu a.selected, html.dark-mode .menu.toolbar a.selected { + background-color: rgba(var(--color-01-rgb-82), 0.5); +} + +html.dark-mode .listing li.selected, html.dark-mode .listing li.selected>a, html.dark-mode .listing li.selected>div>a, html.dark-mode .listing tr.selected td { + color: var(--color-01-30); + background-color: rgba(var(--color-01-rgb-82), 0.5); +} + +html.dark-mode .message-htmlpart { + background-color: rgba(var(--color-01-rgb-99), 0.08); + color: var(--color-01-15); +} diff --git a/roles/web-app-mastodon/templates/style.css.j2 b/roles/web-app-mastodon/templates/style.css.j2 new file mode 100644 index 00000000..4da574aa --- /dev/null +++ b/roles/web-app-mastodon/templates/style.css.j2 @@ -0,0 +1,43 @@ +div#mastodon, div#admin-wrapper { + /* Dropdown */ + --dropdown-border-color: var(--color-01-35); + --dropdown-background-color: rgba(var(--color-01-rgb-03), 0.9); + --dropdown-shadow: 0 20px 25px -5px rgba(var(--color-01-rgb-01), 0.25), + 0 8px 10px -6px rgba(var(--color-01-rgb-01), 0.25); + + /* Modal */ + --modal-background-color: rgba(var(--color-01-rgb-03), 0.7); + --modal-background-variant-color: rgba(var(--color-01-rgb-05), 0.7); + --modal-border-color: var(--color-01-35); + + /* Background */ + --background-border-color: var(--color-01-82); + --background-filter: blur(10px) saturate(180%) contrast(75%) brightness(70%); + --background-color: var(--color-01-93); + --background-color-tint: rgba(var(--color-01-rgb-03), 0.9); + + /* Surface */ + --surface-background-color: var(--color-01-90); + --surface-variant-background-color: var(--color-01-89); + --surface-variant-active-background-color: var(--color-01-35); + --on-surface-color: rgba(var(--color-01-rgb-05), 0.5); + + /* Media & Overlay */ + --media-outline-color: rgba(var(--color-01-rgb-99), 0.15); + --overlay-icon-shadow: drop-shadow(0 0 8px rgba(var(--color-01-rgb-01), 0.25)); +} + +.swal2-popup { + color: #000; +} + +/* Modal Overwrittes */ +div.modal div.modal-content { + /* Colors – adjusted to the existing scheme */ + --bs-modal-color: var(--color-01-21); /* Text color: dark contrast against the light modal background */ + --bs-modal-bg: var(--color-01-82); /* Background color, as desired */ + --bs-modal-border-color: var(--color-01-82); /* A slightly darker border than the background */ + --bs-modal-header-border-color: var(--color-01-87); /* Same shade as the modal border */ + --bs-modal-footer-bg: var(--color-01-87); /* A slightly offset footer background (a bit darker than the main area) */ + --bs-modal-footer-border-color: var(--color-01-87); +} diff --git a/roles/web-app-nextcloud/templates/nginx/host.conf.j2 b/roles/web-app-nextcloud/templates/nginx/host.conf.j2 index bc7ec963..2326e6aa 100644 --- a/roles/web-app-nextcloud/templates/nginx/host.conf.j2 +++ b/roles/web-app-nextcloud/templates/nginx/host.conf.j2 @@ -4,7 +4,7 @@ server {% include 'roles/srv-letsencrypt/templates/ssl_header.j2' %} - {% include 'roles/sys-srv-web-inj-compose/templates/server.conf.j2'%} + {% include 'roles/sys-front-inj-all/templates/server.conf.j2'%} # Remove X-Powered-By, which is an information leak fastcgi_hide_header X-Powered-By; diff --git a/roles/web-app-nextcloud/templates/style.css.j2 b/roles/web-app-nextcloud/templates/style.css.j2 new file mode 100644 index 00000000..32edf0dc --- /dev/null +++ b/roles/web-app-nextcloud/templates/style.css.j2 @@ -0,0 +1,65 @@ +:root{ + --color-01-main-background: var(--color-01-84); + --color-01-main-background-rgb: rgba(var(--color-01-rgb-84),0.83); + --color-01-primary-element: var(--color-01-80); + --color-01-main-text: var(--color-01-40); + --color-01-background-hover: var(--color-01-65); + + /** Calendar **/ + --color-01-background-dark: var(--color-01-73); /** Days which aren't in the current month **/ + --color-01-primary-element-light: var(--color-01-65); +} + +/** Nextcloud specific **/ +html.ng-csp header#header{ + background-color: var(--color-01-80); + /* New Gradient based on original background (80 -5, 80, 80 +1, 80 +5) */ + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-75), var(--color-01-80), var(--color-01-81), var(--color-01-85)); + color: var(--color-01-17); +} + +.files-list__row-name button, button.button-vue{ + background: transparent; +} + +html.ng-csp div#postsetupchecks ul.info{ + background-color: transparent; +} + + +/** I wounder if this is correct here or if it belongs to the mastodon role */ + +div#mastodon .column-link{ + color: var(--color-01-55); +} + +div#mastodon .column-back-button { + color: var(--color-01-58); +} + +div#mastodon textarea, div#mastodon input, div#mastodon .compose-form__highlightable { + background-color: var(--color-01-89); + /* New Gradient based on original background (89 -5, 89, 89 +1, 89 +5) */ + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-84), var(--color-01-89), var(--color-01-90), var(--color-01-94)); + color: var(--color-01-19); +} + +div#mastodon .status-card__title, div#mastodon .display-name strong{ + color: var(--color-01-33); +} + +div#mastodon a.unhandled-link, div#mastodon .dropdown-button, div#mastodon .status__content a, div#mastodon .status-card__author{ + color: var(--color-01-29); +} +div#mastodon .dropdown-button{ + border: 1px solid #8c8dff; +} + +div#mastodon .button, div#mastodon .button:active, div#mastodon .button:focus, div#mastodon .button:hover{ + background-color: var(--color-01-71); + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-66), var(--color-01-71), var(--color-01-72), var(--color-01-76)); +} + +.compose-form__actions .icon-button { + color: var(--color-01-28); +} \ No newline at end of file diff --git a/roles/web-app-oauth2-proxy/templates/style.css.j2 b/roles/web-app-oauth2-proxy/templates/style.css.j2 new file mode 100644 index 00000000..aec9289f --- /dev/null +++ b/roles/web-app-oauth2-proxy/templates/style.css.j2 @@ -0,0 +1,12 @@ +.box { + background-color: var(--color-01-92); + color: var(--color-01-10); +} + +.subtitle { + color: inherit; +} + +.has-background-light { + background-color: var(--color-01-96) !important; +} diff --git a/roles/web-app-openproject/templates/style.css.j2 b/roles/web-app-openproject/templates/style.css.j2 new file mode 100644 index 00000000..33b56055 --- /dev/null +++ b/roles/web-app-openproject/templates/style.css.j2 @@ -0,0 +1,9 @@ +header.op-app-header{ + background-color: var(--color-01-40); + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-35), var(--color-01-40), var(--color-01-41), var(--color-01-45)); + color: var(--color-01-40); +} + +div#wrapper button, div#wrapper input, button.top-menu-search-button, div.menu-sidebar a{ + background-color: transparent; +} diff --git a/roles/web-app-peertube/templates/peertube.conf.j2 b/roles/web-app-peertube/templates/peertube.conf.j2 index 5296ba90..2875350a 100644 --- a/roles/web-app-peertube/templates/peertube.conf.j2 +++ b/roles/web-app-peertube/templates/peertube.conf.j2 @@ -3,7 +3,7 @@ server { {% include 'roles/srv-letsencrypt/templates/ssl_header.j2' %} - {% include 'roles/sys-srv-web-inj-compose/templates/server.conf.j2'%} + {% include 'roles/sys-front-inj-all/templates/server.conf.j2'%} {% include 'roles/srv-proxy-core/templates/headers/content_security_policy.conf.j2' %} diff --git a/roles/web-app-peertube/templates/style.css.j2 b/roles/web-app-peertube/templates/style.css.j2 new file mode 100644 index 00000000..5a0ef2e3 --- /dev/null +++ b/roles/web-app-peertube/templates/style.css.j2 @@ -0,0 +1,38 @@ +body#custom-css { + --mainColor: var(--color-01-60); /* Original tone: hsl(24, 90%, 50%) – vibrant orange */ + --mainColorLighter: var(--color-01-70); /* Original tone: #f5873d – lighter orange */ + --mainColorLightest: var(--color-01-90); /* Original tone: #fce1cf – very light orange/beige */ + --mainColorVeryLight: var(--color-01-95); /* Original tone: #fff5eb – almost white */ + --mainHoverColor: var(--color-01-64); /* Original tone: #f47825 – hover orange */ + --mainBackgroundHoverColor: var(--color-01-92); /* Original tone: #e9ecef – light gray */ + --mainBackgroundColor: var(--color-01-99); /* Original tone: #fff – white */ + --mainForegroundColor: var(--color-01-10); /* Original tone: #212529 – dark gray/black */ + --greyForegroundColor: var(--color-01-50); /* Original tone: #585858 – medium gray */ + --greyBackgroundColor: var(--color-01-90); /* Original tone: #E5E5E5 – light gray */ + --greySecondaryBackgroundColor: var(--color-01-91); /* Original tone: #EFEFEF – very light gray */ + --menuBackgroundColor: var(--color-01-82); /* Original tone: #000 – black */ + --menuForegroundColor: var(--color-01-99); /* Original tone: #fff – white */ + --submenuBackgroundColor: var(--color-01-95); /* Original tone: #F7F7F7 – off-white/light gray */ + --channelBackgroundColor: var(--color-01-93); /* Original tone: #f6ede8 – warm light beige */ + --inputForegroundColor: var(--color-01-10); /* Original tone: #212529 – dark gray */ + --inputBackgroundColor: var(--color-01-99); /* Original tone: #fff – white */ + --inputPlaceholderColor: var(--color-01-55); /* Original tone: #797676 – medium gray */ + --inputBorderColor: var(--color-01-80); /* Original tone: #C6C6C6 – light gray */ + --textareaForegroundColor: var(--color-01-10); /* Original tone: #212529 – dark gray */ + --textareaBackgroundColor: var(--color-01-99); /* Original tone: #fff – white */ + --markdownTextareaBackgroundColor: var(--color-01-91); /* Original tone: #EFEFEF – very light gray */ + --actionButtonColor: var(--color-01-50); /* Original tone: #585858 – medium gray */ + --supportButtonColor: var(--actionButtonColor); /* Original tone: same as actionButtonColor (#585858) */ + --activatedActionButtonColor: var(--color-01-10); /* Original tone: #212529 – dark gray */ + color: var(--mainForegroundColor); + background-color: var(--mainBackgroundColor); +} + +div.searchbox input.autocomplete-input{ + background-position: 12px; + background-repeat: no-repeat; +} + +html[native-dark-active] my-app button, button { + background-color: transparent; +} diff --git a/roles/web-app-phpmyadmin/templates/style.css.j2 b/roles/web-app-phpmyadmin/templates/style.css.j2 new file mode 100644 index 00000000..fa28d3f7 --- /dev/null +++ b/roles/web-app-phpmyadmin/templates/style.css.j2 @@ -0,0 +1,60 @@ + +#pma_navigation { + background: linear-gradient(to right, var(--color-01-95), var(--color-01-85)); + color: var(--color-01-05); +} + +#pma_navigation_tree a { + color: var(--color-01-05); +} + +#pma_navigation_tree li.activePointer, #pma_navigation_tree li.selected { + color: var(--color-01-05); + background-color: var(--color-01-70); +} + +.breadcrumb-navbar { + background-color: var(--color-01-86); +} + +.navbar-nav .nav-item { + background: linear-gradient(var(--color-01-99), var(--color-01-85)); + border-right-color: var(--color-01-99); + border-left-color: var(--color-01-80); + border-bottom-color: var(--color-01-80); +} + +.result_query div.sqlOuter { + background: var(--color-01-50); +} + +.table { + --bs-table-bg: var(--color-01-99); /* #fff → white */ + --bs-table-border-color: var(--color-01-99); /* #fff → white */ + --bs-table-striped-bg: var(--color-01-90); /* #dfdfdf → light gray */ + --bs-table-hover-color: var(--color-01-01); /* #000 → black */ +} + +.table thead th { + background-image: linear-gradient(var(--color-01-90), var(--color-01-80)); + border-color: var(--color-01-99); +} + +.table th, .table td { + text-shadow: 0 1px 0 var(--color-01-60); +} + +div.tools, .tblFooters { + color: var(--color-01-01); + background: var(--color-01-62); +} + + +.navigation { + background: linear-gradient(var(--color-01-87), var(--color-01-69)); +} + +.pma-fieldset { + border-color: var(--color-01-17); + background: var(--color-01-80); +} diff --git a/roles/web-app-pixelfed/templates/style.css.j2 b/roles/web-app-pixelfed/templates/style.css.j2 new file mode 100644 index 00000000..8423051e --- /dev/null +++ b/roles/web-app-pixelfed/templates/style.css.j2 @@ -0,0 +1,43 @@ +:root { + /* Base Colors */ + --light: var(--color-01-05); /* Very dark (was #000) */ + --dark: var(--color-01-99); /* Very light (was #fff) */ + + /* Backgrounds */ + --body-bg: var(--color-01-05); /* Main background: very dark */ + --nav-bg: var(--color-01-05); /* Navigation background: very dark */ + + /* Text Colors */ + --body-color: var(--color-01-70); /* Main text – mid brightness */ + --text-lighter: var(--color-01-60); /* Lighter text for less prominent elements */ + + /* Section Backgrounds and Cards */ + --bg-light: var(--color-01-95); /* Lighter background areas */ + --card-bg: var(--color-01-90); /* Card background */ + --light-gray: var(--color-01-75); /* For less dominant elements */ + --light-hover-bg: var(--color-01-85); /* Slightly lighter hover background */ + + /* Borders and Input Fields */ + --btn-light-border: var(--color-01-10); /* Dark border for buttons */ + --input-border: var(--color-01-10); /* Border color for inputs */ + --border-color: var(--color-01-85); /* General border: slightly lighter than background */ + + /* Other Areas */ + --comment-bg: var(--color-01-85); /* Background for comments */ + --card-header-accent: var(--color-01-85); /* Accent color in card headers */ + + /* Dropdown Menus */ + --dropdown-item-hover-bg: var(--color-01-05); /* Hover background: very dark */ + --dropdown-item-hover-color: var(--color-01-60); /* Hover text: a bit lighter */ + --dropdown-item-color: var(--color-01-70); /* Regular dropdown item text */ + --dropdown-item-active-color: var(--color-01-99); /* Active state: very light (white) */ +} + +div.page-wrapper{ + background: none; + background-color: none; +} + +.card-header-title { + color: var(--color-01-37); +} diff --git a/roles/web-app-sphinx/templates/style.css.j2 b/roles/web-app-sphinx/templates/style.css.j2 new file mode 100644 index 00000000..4fcbc614 --- /dev/null +++ b/roles/web-app-sphinx/templates/style.css.j2 @@ -0,0 +1,12 @@ +.bg-background\/95 { + background-color: var(--color-01-96); +} + +.border-border { + border-color: var(--color-01-85); +} + +{# Hide Toogle Button #} +nav.flex.items-center.space-x-1{ + display:none; +} diff --git a/roles/web-app-syncope/templates/proxy.conf b/roles/web-app-syncope/templates/proxy.conf index cba2936e..aecbd020 100644 --- a/roles/web-app-syncope/templates/proxy.conf +++ b/roles/web-app-syncope/templates/proxy.conf @@ -8,7 +8,7 @@ server {% include 'roles/web-app-oauth2-proxy/templates/endpoint.conf.j2'%} {% endif %} - {% include 'roles/sys-srv-web-inj-compose/templates/server.conf.j2'%} + {% include 'roles/sys-front-inj-all/templates/server.conf.j2'%} {% if proxy_extra_configuration is defined %} {# Additional Domain Specific Configuration #} diff --git a/roles/web-app-taiga/templates/style.css.j2 b/roles/web-app-taiga/templates/style.css.j2 new file mode 100644 index 00000000..3f24e1fd --- /dev/null +++ b/roles/web-app-taiga/templates/style.css.j2 @@ -0,0 +1,67 @@ +:host, :root { + --color-01-solid-primary: var(--color-01-83); + --color-01-link-primary: var(--color-01-44); + --color-01-link-tertiary: var(--color-01-45); + --color-01-gray100: var(--color-01-97); + --color-01-gray200: var(--color-01-93); + --color-01-gray300: var(--color-01-90); + --color-01-gray400: var(--color-01-86); + --color-01-black600: var(--color-01-34); + --color-01-black700: var(--color-01-30); + --color-01-black800: var(--color-01-26); + --color-01-black900: var(--color-01-21); + --color-01-black: var(--color-01-01); + --color-01-white: var(--color-01-99); +} + +section.main.kanban{ + background-color: transparent; +} + +div.master, div.kanban-header, div.kanban-table-inner, section.kanban button,a.dropdown-project-list-projects{ + background-color: var(--color-01-92); + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-87), var(--color-01-92), var(--color-01-93), var(--color-01-97)); + color: var(--color-01-40); +} + +section.kanban h1, section.kanban h2{ + color: var(--color-01-40); +} + +.home-project { + background: var(--color-01-88); + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-83), var(--color-01-88), var(--color-01-89), var(--color-01-93)); + border-color: var(--color-01-60); + color: var(--color-01-12); +} + +.home-wrapper .title-bar { + background: var(--color-01-75); + background: linear-gradient({{ range(0, 361) | random }}deg, var(--color-01-70), var(--color-01-75), var(--color-01-76), var(--color-01-80)); +} + +.kanban.swimlane .kanban-header { + background: none; +} + +.kanban-table-header .task-colum-name { + background-color: var(--color-01-70); + color: var(--color-01-10); +} + +input.ng-empty::placeholder,.ng-empty::placeholder { + color: rgba(var(--color-01-rgb-03),0.6); +} + +.lightbox { + background: rgba(var(--color-01-rgb-97), .93); + color: var(--color-01-03); +} + +.kanban-filter tg-filter { + border-color: var(--color-01-70); +} + +.discover-header { + background: none; +} diff --git a/roles/web-svc-cdn/config/main.yml b/roles/web-svc-cdn/config/main.yml index 41f563bc..8c156c5d 100644 --- a/roles/web-svc-cdn/config/main.yml +++ b/roles/web-svc-cdn/config/main.yml @@ -1,7 +1,7 @@ features: - matomo: true - css: true - desktop: false + matomo: true + css: true + desktop: false server: domains: canonical: diff --git a/roles/web-svc-cdn/tasks/01_core.yml b/roles/web-svc-cdn/tasks/01_core.yml index 027ea0f8..b1193aa5 100644 --- a/roles/web-svc-cdn/tasks/01_core.yml +++ b/roles/web-svc-cdn/tasks/01_core.yml @@ -11,8 +11,8 @@ vars: http_port: "{{ ports.localhost.http[application_id] }}" -- name: "generate '{{ CDN_NGINX_FILE }}'" +- name: "deploy '{{ CDN_NGINX_PATH }}'" template: src: "nginx.conf.j2" - dest: "{{ NGINX.DIRECTORIES.HTTP.SERVERS }}{{ CDN_NGINX_FILE }}" + dest: "{{ CDN_NGINX_PATH }}" notify: restart openresty \ No newline at end of file diff --git a/roles/web-svc-cdn/templates/nginx.conf.j2 b/roles/web-svc-cdn/templates/nginx.conf.j2 index 8464204f..0c6b597c 100644 --- a/roles/web-svc-cdn/templates/nginx.conf.j2 +++ b/roles/web-svc-cdn/templates/nginx.conf.j2 @@ -4,7 +4,7 @@ server {% include 'roles/srv-letsencrypt/templates/ssl_header.j2' %} - {% include 'roles/sys-srv-web-inj-compose/templates/server.conf.j2'%} + {% include 'roles/sys-front-inj-all/templates/server.conf.j2'%} {% include 'roles/srv-proxy-core/templates/headers/content_security_policy.conf.j2' %} @@ -16,11 +16,11 @@ server autoindex on; {# Enable directory listing #} autoindex_exact_size off; {# Display sizes in a human-readable format #} autoindex_localtime on; {# Show local time #} - {% include 'roles/sys-srv-web-inj-compose/templates/location.lua.j2' %} + {% include 'roles/sys-front-inj-all/templates/location.lua.j2' %} } location /.well-known/ { - alias {{NGINX.DIRECTORIES.DATA.WELL_KNOWN}}; + alias {{ NGINX.DIRECTORIES.DATA.WELL_KNOWN }}; allow all; default_type "text/plain"; autoindex on; diff --git a/roles/web-svc-cdn/vars/main.yml b/roles/web-svc-cdn/vars/main.yml index e919592b..f37c6205 100644 --- a/roles/web-svc-cdn/vars/main.yml +++ b/roles/web-svc-cdn/vars/main.yml @@ -4,3 +4,4 @@ domain: "{{ domains | get_domain(application_id) }}" # CDN CDN_NGINX_FILE: "{{ domain }}.conf" +CDN_NGINX_PATH: "{{ [ NGINX.DIRECTORIES.HTTP.SERVERS, CDN_NGINX_FILE ] | path_join }}" diff --git a/roles/web-svc-collabora/templates/nginx.conf.j2 b/roles/web-svc-collabora/templates/nginx.conf.j2 index 2867241f..05ca2498 100644 --- a/roles/web-svc-collabora/templates/nginx.conf.j2 +++ b/roles/web-svc-collabora/templates/nginx.conf.j2 @@ -2,7 +2,7 @@ server { server_name {{ domain }}; {% include 'roles/srv-letsencrypt/templates/ssl_header.j2' %} - {% include 'roles/sys-srv-web-inj-compose/templates/server.conf.j2'%} + {% include 'roles/sys-front-inj-all/templates/server.conf.j2'%} {% include 'roles/srv-proxy-core/templates/headers/content_security_policy.conf.j2' %} diff --git a/roles/web-svc-file/templates/nginx.conf.j2 b/roles/web-svc-file/templates/nginx.conf.j2 index 8e42af95..54d83292 100644 --- a/roles/web-svc-file/templates/nginx.conf.j2 +++ b/roles/web-svc-file/templates/nginx.conf.j2 @@ -4,7 +4,7 @@ server {% include 'roles/srv-letsencrypt/templates/ssl_header.j2' %} - {% include 'roles/sys-srv-web-inj-compose/templates/server.conf.j2'%} + {% include 'roles/sys-front-inj-all/templates/server.conf.j2'%} {% include 'roles/srv-proxy-core/templates/headers/content_security_policy.conf.j2' %} @@ -16,11 +16,11 @@ server autoindex on; {# Enable directory listing #} autoindex_exact_size off; {# Display sizes in a human-readable format #} autoindex_localtime on; {# Show local time #} - {% include 'roles/sys-srv-web-inj-compose/templates/location.lua.j2' %} + {% include 'roles/sys-front-inj-all/templates/location.lua.j2' %} } location /.well-known/ { - alias {{NGINX.DIRECTORIES.DATA.WELL_KNOWN}}; + alias {{ NGINX.DIRECTORIES.DATA.WELL_KNOWN }}; allow all; default_type "text/plain"; autoindex on; diff --git a/roles/web-svc-html/templates/nginx.conf.j2 b/roles/web-svc-html/templates/nginx.conf.j2 index 586a8ce6..435493ae 100644 --- a/roles/web-svc-html/templates/nginx.conf.j2 +++ b/roles/web-svc-html/templates/nginx.conf.j2 @@ -4,7 +4,7 @@ server {% include 'roles/srv-letsencrypt/templates/ssl_header.j2' %} - {% include 'roles/sys-srv-web-inj-compose/templates/server.conf.j2'%} + {% include 'roles/sys-front-inj-all/templates/server.conf.j2'%} {% include 'roles/srv-proxy-core/templates/headers/content_security_policy.conf.j2' %} @@ -14,11 +14,11 @@ server { root {{NGINX.DIRECTORIES.DATA.HTML}}; index index.html index.htm; - {% include 'roles/sys-srv-web-inj-compose/templates/location.lua.j2' %} + {% include 'roles/sys-front-inj-all/templates/location.lua.j2' %} } location /.well-known/ { - alias {{NGINX.DIRECTORIES.DATA.WELL_KNOWN}}; + alias {{ NGINX.DIRECTORIES.DATA.WELL_KNOWN }}; allow all; default_type "text/plain"; autoindex on; diff --git a/templates/roles/web-app/templates/style.css.j2 b/templates/roles/web-app/templates/style.css.j2 new file mode 100644 index 00000000..357c1e5e --- /dev/null +++ b/templates/roles/web-app/templates/style.css.j2 @@ -0,0 +1 @@ +/** Custom CSS for 'sys-front-inj-css' if features.css is enabled **/ \ No newline at end of file diff --git a/tests/integration/test_csp_configuration_consistency.py b/tests/integration/test_csp_configuration_consistency.py index 1cd00594..804aaab3 100644 --- a/tests/integration/test_csp_configuration_consistency.py +++ b/tests/integration/test_csp_configuration_consistency.py @@ -12,6 +12,7 @@ class TestCspConfigurationConsistency(unittest.TestCase): 'script-src', 'script-src-elem', 'style-src', + 'style-src-elem', 'font-src', 'worker-src', 'manifest-src', diff --git a/tests/integration/test_variable_definitions.py b/tests/integration/test_variable_definitions.py index a37dfaa9..26700154 100644 --- a/tests/integration/test_variable_definitions.py +++ b/tests/integration/test_variable_definitions.py @@ -218,7 +218,7 @@ class TestVariableDefinitions(unittest.TestCase): if var in ( 'lookup', 'role_name', 'domains', 'item', 'host_type', 'inventory_hostname', 'role_path', 'playbook_dir', - 'ansible_become_password', 'inventory_dir', 'ansible_memtotal_mb', 'omit', 'group_names' + 'ansible_become_password', 'inventory_dir', 'ansible_memtotal_mb', 'omit', 'group_names', 'ansible_processor_vcpus' ): continue diff --git a/tests/unit/lookup_plugins/test_local_mtime_qs.py b/tests/unit/lookup_plugins/test_local_mtime_qs.py new file mode 100644 index 00000000..de750c0f --- /dev/null +++ b/tests/unit/lookup_plugins/test_local_mtime_qs.py @@ -0,0 +1,50 @@ +import os +import sys +import tempfile +import time +import unittest + +# ensure repo root on path +ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) +sys.path.insert(0, ROOT) + +from ansible.errors import AnsibleError # type: ignore +from lookup_plugins.local_mtime_qs import LookupModule + + +class TestLocalMtimeQs(unittest.TestCase): + def setUp(self): + self.tmpdir = tempfile.TemporaryDirectory() + self.path = os.path.join(self.tmpdir.name, "file.css") + with open(self.path, "w", encoding="utf-8") as f: + f.write("body{}") + # set stable mtime + self.mtime = int(time.time()) - 123 + os.utime(self.path, (self.mtime, self.mtime)) + + def tearDown(self): + self.tmpdir.cleanup() + + def test_single_path_qs_default(self): + res = LookupModule().run([self.path]) + self.assertEqual(res, [f"?version={self.mtime}"]) + + def test_single_path_epoch(self): + res = LookupModule().run([self.path], mode="epoch") + self.assertEqual(res, [str(self.mtime)]) + + def test_multiple_paths(self): + path2 = os.path.join(self.tmpdir.name, "a.js") + with open(path2, "w", encoding="utf-8") as f: + f.write("// js") + os.utime(path2, (self.mtime + 1, self.mtime + 1)) + res = LookupModule().run([self.path, path2], param="v") + self.assertEqual(res, [f"?v={self.mtime}", f"?v={self.mtime + 1}"]) + + def test_missing_raises(self): + with self.assertRaises(AnsibleError): + LookupModule().run([os.path.join(self.tmpdir.name, "nope.css")]) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/unit/roles/sys-front-inj-all/__init__.py b/tests/unit/roles/sys-front-inj-all/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/roles/sys-front-inj-all/filter_plugins/__init__.py b/tests/unit/roles/sys-front-inj-all/filter_plugins/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/roles/sys-srv-web-inj-compose/filter_plugins/test_inj_enabled.py b/tests/unit/roles/sys-front-inj-all/filter_plugins/test_inj_enabled.py similarity index 92% rename from tests/unit/roles/sys-srv-web-inj-compose/filter_plugins/test_inj_enabled.py rename to tests/unit/roles/sys-front-inj-all/filter_plugins/test_inj_enabled.py index 400d8ae0..51390a4c 100644 --- a/tests/unit/roles/sys-srv-web-inj-compose/filter_plugins/test_inj_enabled.py +++ b/tests/unit/roles/sys-front-inj-all/filter_plugins/test_inj_enabled.py @@ -1,4 +1,3 @@ -# tests/unit/roles/srv-web-inj-compose/filter_plugins/test_inj_enabled.py import importlib.util from importlib import import_module from pathlib import Path @@ -8,7 +7,7 @@ import unittest THIS_FILE = Path(__file__) def find_repo_root(start: Path) -> Path: - target_rel = Path("roles") / "sys-srv-web-inj-compose" / "filter_plugins" / "inj_enabled.py" + target_rel = Path("roles") / "sys-front-inj-all" / "filter_plugins" / "inj_enabled.py" cur = start for _ in range(12): if (cur / target_rel).is_file(): @@ -17,7 +16,7 @@ def find_repo_root(start: Path) -> Path: return start.parents[6] REPO_ROOT = find_repo_root(THIS_FILE) -PLUGIN_PATH = REPO_ROOT / "roles" / "sys-srv-web-inj-compose" / "filter_plugins" / "inj_enabled.py" +PLUGIN_PATH = REPO_ROOT / "roles" / "sys-front-inj-all" / "filter_plugins" / "inj_enabled.py" # Ensure 'module_utils' is importable under its canonical package name if str(REPO_ROOT) not in sys.path: diff --git a/tests/unit/roles/sys-srv-web-inj-compose/filter_plugins/test_inj_snippets.py b/tests/unit/roles/sys-front-inj-all/filter_plugins/test_inj_snippets.py similarity index 92% rename from tests/unit/roles/sys-srv-web-inj-compose/filter_plugins/test_inj_snippets.py rename to tests/unit/roles/sys-front-inj-all/filter_plugins/test_inj_snippets.py index ec3f8660..9fb8e1c1 100644 --- a/tests/unit/roles/sys-srv-web-inj-compose/filter_plugins/test_inj_snippets.py +++ b/tests/unit/roles/sys-front-inj-all/filter_plugins/test_inj_snippets.py @@ -1,6 +1,6 @@ -# tests/unit/roles/sys-srv-web-inj-compose/filter_plugins/test_inj_snippets.py +# tests/unit/roles/sys-front-inj-all/filter_plugins/test_inj_snippets.py """ -Unit tests for roles/sys-srv-web-inj-compose/filter_plugins/inj_snippets.py +Unit tests for roles/sys-front-inj-all/filter_plugins/inj_snippets.py - Uses tempfile.TemporaryDirectory for an isolated roles/ tree. - Loads inj_snippets.py by absolute path (no sys.path issues). @@ -22,7 +22,7 @@ class TestInjSnippets(unittest.TestCase): cls.test_dir = os.path.dirname(__file__) root = cls.test_dir inj_rel = os.path.join( - "roles", "sys-srv-web-inj-compose", "filter_plugins", "inj_snippets.py" + "roles", "sys-front-inj-all", "filter_plugins", "inj_snippets.py" ) while True: @@ -67,7 +67,7 @@ class TestInjSnippets(unittest.TestCase): @classmethod def _mkrole(cls, feature, head=False, body=False): - role_dir = os.path.join(cls.roles_dir, f"sys-srv-web-inj-{feature}") + role_dir = os.path.join(cls.roles_dir, f"sys-front-inj-{feature}") tmpl_dir = os.path.join(role_dir, "templates") os.makedirs(tmpl_dir, exist_ok=True) if head: diff --git a/tests/unit/roles/sys-svc-cdn/__init__.py b/tests/unit/roles/sys-svc-cdn/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/roles/sys-svc-cdn/filter_plugins/__init__.py b/tests/unit/roles/sys-svc-cdn/filter_plugins/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/roles/sys-svc-cdn/filter_plugins/test_cdn_paths_urls_dirs.py b/tests/unit/roles/sys-svc-cdn/filter_plugins/test_cdn_paths_urls_dirs.py new file mode 100644 index 00000000..386958b9 --- /dev/null +++ b/tests/unit/roles/sys-svc-cdn/filter_plugins/test_cdn_paths_urls_dirs.py @@ -0,0 +1,108 @@ +import os +import tempfile +import unittest +import importlib.util + +HERE = os.path.abspath(os.path.dirname(__file__)) + +def _find_repo_root(start_dir: str, probe_parts: list[str]) -> str: + """ + Walk upwards from start_dir until a path joined with probe_parts exists. + Returns the directory considered the repo root. + """ + cur = os.path.abspath(start_dir) + for _ in range(15): # plenty of headroom + candidate = os.path.join(cur, *probe_parts) + if os.path.exists(candidate): + return cur + parent = os.path.dirname(cur) + if parent == cur: + break + cur = parent + raise RuntimeError( + f"Could not locate {'/'.join(probe_parts)} starting from {start_dir}" + ) + +PROBE = ["roles", "sys-svc-cdn", "filter_plugins", "cdn_paths.py"] +ROOT = _find_repo_root(HERE, PROBE) + +def _load_module(mod_name: str, rel_path_from_root: str): + """Load a python module from an absolute file path (hyphen-safe).""" + path = os.path.join(ROOT, rel_path_from_root) + if not os.path.isfile(path): + raise FileNotFoundError(path) + spec = importlib.util.spec_from_file_location(mod_name, path) + module = importlib.util.module_from_spec(spec) + assert spec and spec.loader, f"Cannot load spec for {path}" + spec.loader.exec_module(module) # type: ignore[attr-defined] + return module + +cdn_paths_mod = _load_module( + "cdn_paths_mod", os.path.join("roles", "sys-svc-cdn", "filter_plugins", "cdn_paths.py") +) +cdn_urls_mod = _load_module( + "cdn_urls_mod", os.path.join("roles", "sys-svc-cdn", "filter_plugins", "cdn_urls.py") +) +cdn_dirs_mod = _load_module( + "cdn_dirs_mod", os.path.join("roles", "sys-svc-cdn", "filter_plugins", "cdn_dirs.py") +) + +class TestCdnPathsUrlsDirs(unittest.TestCase): + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.root = self.tmp.name + self.app = "web-app-desktop" + self.ver = "20250101" + + self.cdn_paths = cdn_paths_mod.cdn_paths + self.cdn_urls = cdn_urls_mod.cdn_urls + self.cdn_dirs = cdn_dirs_mod.cdn_dirs + + self.tree = self.cdn_paths(self.root, self.app, self.ver) + + def tearDown(self): + self.tmp.cleanup() + + # ---- cdn_paths ---- + def test_paths_shape_and_values(self): + t = self.tree + self.assertTrue(os.path.isabs(t["root"])) + self.assertEqual(t["role"]["id"], self.app) + self.assertEqual(t["role"]["version"], self.ver) + self.assertTrue(t["shared"]["css"].endswith(os.path.join("_shared", "css"))) + self.assertTrue( + t["role"]["release"]["css"].endswith(os.path.join(self.app, self.ver, "css")) + ) + + # ---- cdn_urls ---- + def test_urls_mapping_and_root_trailing_slash(self): + base = "https://cdn.example.com" + urls = self.cdn_urls(self.tree, base) + + # Non-path strings remain untouched + self.assertEqual(urls["role"]["id"], self.app) + self.assertEqual(urls["role"]["version"], self.ver) + + # Paths are mapped to URLs + self.assertTrue(urls["shared"]["js"].startswith(base + "/")) + self.assertTrue(urls["vendor"].startswith(base + "/vendor")) + + # Root always ends with '/' + self.assertEqual(urls["root"], base.rstrip("/") + "/") + + def test_urls_invalid_input_raises(self): + with self.assertRaises(ValueError): + self.cdn_urls({}, "https://cdn.example.com") + with self.assertRaises(ValueError): + self.cdn_urls("nope", "https://cdn.example.com") # type: ignore[arg-type] + + # ---- cdn_dirs ---- + def test_dirs_collects_all_abs_dirs_sorted_unique(self): + dirs = self.cdn_dirs(self.tree) + self.assertIn(os.path.join(self.root, "_shared", "css"), dirs) + self.assertIn(os.path.join(self.root, "roles", self.app, self.ver, "img"), dirs) + self.assertEqual(dirs, sorted(dirs)) + self.assertEqual(len(dirs), len(set(dirs))) + +if __name__ == "__main__": + unittest.main()