diff --git a/ansible/playbooks/paas/main.yml b/ansible/playbooks/paas/main.yml index 4a7f7c1a..dd1a0f03 100644 --- a/ansible/playbooks/paas/main.yml +++ b/ansible/playbooks/paas/main.yml @@ -58,10 +58,14 @@ - unattended-upgrades - ansible-ufw +- name: Configure sshd + ansible.builtin.import_playbook: sshd.yml - name: Configure timesyncd ansible.builtin.import_playbook: timesyncd.yml - name: Configure systemd resolved ansible.builtin.import_playbook: systemd-resolved.yml +- name: Configure nvidia + ansible.builtin.import_playbook: nvidia.yml - name: Configure docker ansible.builtin.import_playbook: docker.yml - name: Configure nomad diff --git a/ansible/playbooks/paas/metrology.yml b/ansible/playbooks/paas/metrology.yml index 78b63ab1..85542550 100644 --- a/ansible/playbooks/paas/metrology.yml +++ b/ansible/playbooks/paas/metrology.yml @@ -5,9 +5,9 @@ gather_facts: true become: true tasks: - - name: End the play for hosts that are not in admins group - ansible.builtin.meta: end_host - when: fact_instance.location != 'admins' + # - name: End the play for hosts that are not in admins group + # ansible.builtin.meta: end_host + # when: fact_instance.location != 'admins' - name: Install prometheus ansible.builtin.import_role: diff --git a/ansible/playbooks/saas/nomad-clean-errors.yml b/ansible/playbooks/paas/nomad-clean-errors.yml similarity index 100% rename from ansible/playbooks/saas/nomad-clean-errors.yml rename to ansible/playbooks/paas/nomad-clean-errors.yml diff --git a/ansible/playbooks/paas/nvidia.yml b/ansible/playbooks/paas/nvidia.yml index d8f2314d..81aae179 100644 --- a/ansible/playbooks/paas/nvidia.yml +++ b/ansible/playbooks/paas/nvidia.yml @@ -18,6 +18,10 @@ pre_tasks: + - name: End the play for hosts that don't have nvidia gpu + ansible.builtin.meta: end_host + when: not nvidia_enable + - name: Créer le répertoire du keyring s'il n'existe pas ansible.builtin.file: path: "{{ nvidia_keyring_path | dirname }}" diff --git a/ansible/playbooks/paas/scan_exporter.yml b/ansible/playbooks/paas/scan_exporter.yml index cfb1171f..d199cf36 100644 --- a/ansible/playbooks/paas/scan_exporter.yml +++ b/ansible/playbooks/paas/scan_exporter.yml @@ -1,10 +1,10 @@ --- -- name: Uninstall scan_exporter +- name: Install scan_exporter any_errors_fatal: true hosts: "{{ hosts_limit | default('infrastructure') }}" gather_facts: true become: true pre_tasks: - - name: Uninstall scan_exporter + - name: Install scan_exporter ansible.builtin.include_role: name: scan_exporter \ No newline at end of file diff --git a/ansible/playbooks/paas/sshd.yml b/ansible/playbooks/paas/sshd.yml index 0ecbd445..24728021 100644 --- a/ansible/playbooks/paas/sshd.yml +++ b/ansible/playbooks/paas/sshd.yml @@ -4,5 +4,9 @@ hosts: "{{ hosts_limit | default('infrastructure') }}" gather_facts: true become: true + pre_tasks: + - name: End the play for hosts that are not in frontends group + ansible.builtin.meta: end_host + when: fact_instance.location != 'frontends' roles: - sshd diff --git a/ansible/playbooks/paas/timesyncd.yml b/ansible/playbooks/paas/timesyncd.yml index e96b42aa..9fe1055c 100644 --- a/ansible/playbooks/paas/timesyncd.yml +++ b/ansible/playbooks/paas/timesyncd.yml @@ -27,6 +27,8 @@ - name: Use RTC in UTC ansible.builtin.command: timedatectl set-local-rtc 0 + register: timedatectl + changed_when: false handlers: - name: Restart timesyncd diff --git a/ansible/playbooks/saas/image-forkable.yml b/ansible/playbooks/saas/image-forkable.yml index 9cbdd56b..13b6ae63 100644 --- a/ansible/playbooks/saas/image-forkable.yml +++ b/ansible/playbooks/saas/image-forkable.yml @@ -93,15 +93,15 @@ failed_when: ui_update.status != 200 become: false + post_tasks: + - name: Trigger cleanup on failure + ansible.builtin.meta: clear_host_errors + when: ansible_failed_result is defined + notify: Cleanup build directory + handlers: - name: Cleanup build directory ansible.builtin.file: path: "{{ build_work_dir }}" state: absent listen: cleanup_build - - post_tasks: - - name: Trigger cleanup on failure - ansible.builtin.meta: clear_host_errors - when: ansible_failed_result is defined - notify: Cleanup build directory diff --git a/ansible/playbooks/saas/image.yml b/ansible/playbooks/saas/image.yml index cc8bb94a..da6da51a 100644 --- a/ansible/playbooks/saas/image.yml +++ b/ansible/playbooks/saas/image.yml @@ -45,13 +45,16 @@ - name: Build when: image_definition.build block: - - name: Build and publish image + - name: Build and publish multi-arch image community.docker.docker_image_build: name: "{{ docker_private_registry.url }}/{% if docker_private_registry.project is defined %}{{ docker_private_registry.project }}/{% endif %}{{ image_definition.name }}:{{ image_version }}" tag: latest path: "{{ build_work_dir }}" dockerfile: Dockerfile labels: "{{ image_definition.labels }}" + platform: + - linux/amd64 + - linux/arm64 rebuild: always outputs: - type: image diff --git a/ansible/playbooks/saas/roles/grafana/README.md b/ansible/playbooks/saas/roles/grafana/README.md index b3a0ae23..bc2400eb 100644 --- a/ansible/playbooks/saas/roles/grafana/README.md +++ b/ansible/playbooks/saas/roles/grafana/README.md @@ -12,3 +12,44 @@ www.domain.com: domain_alias: domain.com # (string) Primary domain name for the application. ipfilter: [] # (list) List of allowed IPs for access control (empty for unrestricted access). basic_auth: False # (bool) Enable/disable HTTP Basic Authentication (True/False). +``` + +## variable + +```yaml +passwd: s3cret! +user: myuser +``` + +## Secret + +```yaml +plugins: + - disabled: false + jsonData: + models: + default: base + mapping: + base: gpt-oss-120b + large: gpt-oss-120b + openAI: + apiPath: /api/openai_compat/v1 + url: https://llm.public.api + provider: custom + vector: + embed: + grafanaVectorAPI: + authType: no-auth + url: http://vectorStore.default.service.nomad:8687 + type: grafana/vectorapi + enabled: true + model: BAAI/bge-small-en-v1.5 + store: + grafanaVectorAPI: + authType: no-auth + url: http://vectorStore.default.service.nomad:8687 + type: grafana/vectorapi + secureJsonData: + openAIKey: + type: grafana-llm-app +``` \ No newline at end of file diff --git a/ansible/playbooks/saas/roles/grafana/templates/provisioning/plugins/llm.yaml.j2 b/ansible/playbooks/saas/roles/grafana/templates/provisioning/plugins/llm.yaml.j2 index 2d5cdd9e..fc1d0196 100644 --- a/ansible/playbooks/saas/roles/grafana/templates/provisioning/plugins/llm.yaml.j2 +++ b/ansible/playbooks/saas/roles/grafana/templates/provisioning/plugins/llm.yaml.j2 @@ -1,4 +1,4 @@ apiVersion: 1 apps: -{{ (lookup('simple-stack-ui', type='secret', key=domain, subkey='plugins', missing='error') | from_json) | to_nice_yaml }} \ No newline at end of file +{{ (lookup('simple-stack-ui', type='secret', key=domain, subkey='plugins', missing='error') | from_json) | to_nice_yaml | default() }} \ No newline at end of file diff --git a/ansible/playbooks/saas/roles/litellm/README.md b/ansible/playbooks/saas/roles/litellm/README.md index dcb700f4..32ff3ce4 100644 --- a/ansible/playbooks/saas/roles/litellm/README.md +++ b/ansible/playbooks/saas/roles/litellm/README.md @@ -1 +1,53 @@ # Role: `litellm` + +## variable + +```yaml +litellm_dbhost: postgresql +litellm_config: + general_settings: + store_model_in_db: true + supported_db_objects: + - mcp + litellm_settings: + drop_params: true + mcp_servers: + news_mcp: + description: My MCP description + transport: http + url: http://192.168.0.46:8001/mcp + model_list: + - litellm_params: + api_key: os.environ/OVHCLOUD_API_KEY + model: ovhcloud/gpt-oss-120b + model_name: ovhcloud/gpt-oss-120b + - litellm_params: + api_key: os.environ/OVHCLOUD_API_KEY + model: ovhcloud/bge-multilingual-gemma2 + model_name: ovhcloud/bge-multilingual-gemma2 + - litellm_params: + api_key: os.environ/OVHCLOUD_API_KEY + model: ovhcloud/Deepseek-R1-Distill-Llama-70B + model_name: ovhcloud/Deepseek-R1-Distill-Llama-70B + - litellm_params: + api_key: os.environ/OVHCLOUD_API_KEY + model: ovhcloud/BGE-M3 + model_name: ovhcloud/BGE-M3 +``` + +## Secret + +```yaml +litellm_dbpasswd: 123456 +litellm_env: + - key: LITELLM_MASTER_KEY + value: sk-123456789 + - key: LITELLM_SALT_KEY + value: sk-12345678-123456789-12345678 + - key: DATABASE_URL + value: postgresql://user:passwd@postgresql.default.service.nomad:5432/litellm + - key: STORE_MODEL_IN_DB + value: true + - key: OVHCLOUD_API_KEY + value: APIKEY +``` \ No newline at end of file diff --git a/ansible/playbooks/saas/roles/milvus/README.md b/ansible/playbooks/saas/roles/milvus/README.md index 680a4325..370e59b6 100644 --- a/ansible/playbooks/saas/roles/milvus/README.md +++ b/ansible/playbooks/saas/roles/milvus/README.md @@ -1 +1,8 @@ # Role: `milvus` + + +## Secret + +```yaml +passwd: 123456 +``` \ No newline at end of file diff --git a/ansible/playbooks/saas/roles/postgresql/README.md b/ansible/playbooks/saas/roles/postgresql/README.md index 823c28ba..85c6ec61 100644 --- a/ansible/playbooks/saas/roles/postgresql/README.md +++ b/ansible/playbooks/saas/roles/postgresql/README.md @@ -13,3 +13,17 @@ www.domain.com: ipfilter: [] # (list) List of allowed IPs for access control (empty for unrestricted access). basic_auth: False # (bool) Enable/disable HTTP Basic Authentication (True/False). size: small +``` + +## variable + +```yaml +static_port: 5432 +``` + +## Secret + +```yaml +passwd: 123456 +``` + diff --git a/ansible/playbooks/saas/roles/wordpress/vars/main.yml b/ansible/playbooks/saas/roles/wordpress/vars/main.yml index 3aa5fbe2..454938e1 100644 --- a/ansible/playbooks/saas/roles/wordpress/vars/main.yml +++ b/ansible/playbooks/saas/roles/wordpress/vars/main.yml @@ -5,10 +5,10 @@ image: upstream: source: apk repository: community - package: php83-fpm + package: php84-fpm labels: - version: 83 - conf: /etc/php83 + version: 84 + conf: /etc/php84 name: wordpress origin: alpine:latest dependances: diff --git a/ansible/playbooks/saas/setup-buildx.yml b/ansible/playbooks/saas/setup-buildx.yml new file mode 100644 index 00000000..3f78dcc2 --- /dev/null +++ b/ansible/playbooks/saas/setup-buildx.yml @@ -0,0 +1,65 @@ +--- +- name: Setup Docker Buildx for multi-architecture builds + hosts: "{{ hosts_limit | default('infrastructure') }}" + become: true + gather_facts: true + + tasks: + - name: Ensure Docker is installed and running + ansible.builtin.service: + name: docker + state: started + enabled: true + + - name: Install QEMU user static binaries + ansible.builtin.package: + name: + - qemu-user-static + - binfmt-support + state: present + + - name: Register QEMU binfmt handlers + ansible.builtin.command: + cmd: docker run --rm --privileged tonistiigi/binfmt --install all + register: binfmt_result + changed_when: "'installing' in binfmt_result.stdout" + + - name: Check if buildx builder exists + ansible.builtin.command: + cmd: docker buildx inspect multiarch-builder + register: buildx_inspect + failed_when: false + changed_when: false + + - name: Create buildx builder for multi-arch + ansible.builtin.command: + cmd: docker buildx create --name multiarch-builder --driver docker-container --use --bootstrap + when: buildx_inspect.rc != 0 + register: buildx_create + + - name: Set multiarch-builder as default + ansible.builtin.command: + cmd: docker buildx use multiarch-builder + when: buildx_inspect.rc == 0 + changed_when: false + + - name: Verify buildx platforms support + ansible.builtin.command: + cmd: docker buildx inspect --bootstrap + register: buildx_platforms + changed_when: false + + - name: Display supported platforms + ansible.builtin.debug: + msg: "{{ buildx_platforms.stdout_lines }}" + + - name: Verify multi-arch support is working + ansible.builtin.command: + cmd: docker buildx ls + register: buildx_ls + changed_when: false + + - name: Show buildx configuration + ansible.builtin.debug: + var: buildx_ls.stdout_lines + diff --git a/ansible/rulebook.yml b/ansible/rulebook.yml index 61aa3a4c..205b6d2f 100644 --- a/ansible/rulebook.yml +++ b/ansible/rulebook.yml @@ -28,39 +28,19 @@ extra_vars: hosts_limit: "{{ event.payload.meta.hosts }}" - - name: paas - condition: event.payload.type == "paas/main" + - name: paas-main + condition: event.payload.type == "paas-main" actions: - run_playbook: name: playbooks/paas/main.yml extra_vars: hosts_limit: "{{ event.payload.meta.hosts }}" + + - name: paas-nomad-clean-errors + condition: event.payload.type == "paas-nomad-clean-errors" + actions: - run_playbook: - name: playbooks/paas/timesyncd.yml - extra_vars: - hosts_limit: "{{ event.payload.meta.hosts }}" - - run_playbook: - name: playbooks/paas/firewall.yml - extra_vars: - hosts_limit: "{{ event.payload.meta.hosts }}" - - run_playbook: - name: playbooks/paas/docker.yml - extra_vars: - hosts_limit: "{{ event.payload.meta.hosts }}" - - run_playbook: - name: playbooks/paas/nomad.yml - extra_vars: - hosts_limit: "{{ event.payload.meta.hosts }}" - - run_playbook: - name: playbooks/paas/coredns.yml - extra_vars: - hosts_limit: "{{ event.payload.meta.hosts }}" - - run_playbook: - name: playbooks/paas/metrology.yml - extra_vars: - hosts_limit: "{{ event.payload.meta.hosts }}" - - run_playbook: - name: playbooks/paas/sshd.yml + name: playbooks/paas/nomad-clean-errors.yml extra_vars: hosts_limit: "{{ event.payload.meta.hosts }}" diff --git a/ui/controllers/api.js b/ui/controllers/api.js index b0d37632..1c894234 100644 --- a/ui/controllers/api.js +++ b/ui/controllers/api.js @@ -30,12 +30,17 @@ exports.install = function() { ROUTE('+API /api/ +catalogs_remove/{id} --> Catalogs/remove'); ROUTE('+API /api/ +catalogs_execute/{id} --> Catalogs/execute'); + ROUTE('+API /api/ +events_create --> Events/create'); + ROUTE('+API /api/ +events_read --> Events/read'); + ROUTE('+API /api/ +events_remove --> Events/remove'); + // infrastructures ROUTE('+API /api/ -infrastructures --> Infrastructures/list'); ROUTE('+API /api/ +infrastructures_read/{id} --> Infrastructures/read'); ROUTE('+API /api/ +infrastructures_create --> Infrastructures/create'); ROUTE('+API /api/ +infrastructures_update/{id} --> Infrastructures/update'); ROUTE('+API /api/ +infrastructures_remove/{id} --> Infrastructures/remove'); + ROUTE('+API /api/ +infrastructures_execute/{id} --> Infrastructures/execute'); ROUTE('+POST /api/tfstates/{id}/ --> Infrastructures/tfstates_update'); ROUTE('+GET /api/tfstates/{id}/ --> Infrastructures/tfstates_read'); diff --git a/ui/index.js.map b/ui/index.js.map index e4be137c..187fe57f 100644 --- a/ui/index.js.map +++ b/ui/index.js.map @@ -173,6 +173,30 @@ "id": "catalogs_execute", "name": "Execute a build" }, + { + "method": "API", + "url": "/api/", + "auth": 1, + "id": "events_create", + "input": "*event:{build|saas|paas}, *type:{info|warning|error}, *body:String", + "name": "Create an event" + }, + { + "method": "API", + "url": "/api/", + "auth": 1, + "id": "events_read", + "input": "*event:{build|saas|paas}", + "name": "Read a catalog item" + }, + { + "method": "API", + "url": "/api/", + "auth": 1, + "id": "events_remove", + "input": "*event:{build|saas|paas}", + "name": "Remove a type of event" + }, { "method": "API", "url": "/api/", @@ -213,6 +237,15 @@ "id": "infrastructures_remove", "name": "Remove infrastructure" }, + { + "method": "API", + "url": "/api/", + "auth": 1, + "params": "id:string", + "id": "infrastructures_execute", + "input": "*action:{main|timesyncd|systemd-resolved|docker|nomad|coredns|firewall|metrology|nomad-clean-errors|nvidia|scan_exporter}", + "name": "Execute playbook" + }, { "method": "API", "url": "/api/", @@ -275,7 +308,7 @@ "auth": 1, "params": "id:string", "id": "softwares_execute", - "input": "*action:{start|stop|main|backup|restore|destroy}", + "input": "*action:{start|stop|main|backup|restore|destroy|destroy_force}", "name": "Execute playbook" }, { @@ -444,6 +477,21 @@ "name": "Account/create", "input": "*email:Email, *password:String" }, + { + "name": "Events/create", + "input": "*event:{build|saas|paas}, *type:{info|warning|error}, *body:String", + "permissions": "events" + }, + { + "name": "Events/read", + "input": "*event:{build|saas|paas}", + "permissions": "events" + }, + { + "name": "Events/remove", + "input": "*event:{build|saas|paas}", + "permissions": "events" + }, { "name": "Graphs/list" }, @@ -482,6 +530,11 @@ "name": "Infrastructures/tfstates_update", "params": "*id:UID" }, + { + "name": "Infrastructures/execute", + "params": "*id:UID", + "input": "*action:{main|timesyncd|systemd-resolved|docker|nomad|coredns|firewall|metrology|nomad-clean-errors|nvidia|scan_exporter}" + }, { "name": "Infrastructures/export", "params": "*ids:String" @@ -493,6 +546,10 @@ { "name": "Inventory/read" }, + { + "name": "Inventory/read_hostnames", + "params": "*id:UID" + }, { "name": "Settings/import", "input": "*import:String, *password:String" @@ -536,7 +593,7 @@ { "name": "Softwares/execute", "params": "*id:UID", - "input": "*action:{start|stop|main|backup|restore|destroy}" + "input": "*action:{start|stop|main|backup|restore|destroy|destroy_force}" }, { "name": "Softwares/import", @@ -601,6 +658,10 @@ "name": "Variables/read2", "input": "*key2:String" }, + { + "name": "Variables/read3", + "input": "*key:String,*type:String" + }, { "name": "Variables/create", "input": "*type:String, *key:String, value:String" diff --git a/ui/public/css/default.css b/ui/public/css/default.css index 973a68a0..55e023e9 100644 --- a/ui/public/css/default.css +++ b/ui/public/css/default.css @@ -181,4 +181,184 @@ header .toolbar button { height: 26px; font-size: 12px; } .ui-windows-item { box-shadow: 0 0 30px rgba(0,0,0,0.3); z-index: 20; } .jc-xs .ui-windows-body { border-left: 0; border-bottom: 0; border-right: 0; } .jc-xs .ui-windows-title { border-left: 0; border-top: 0; border-right: 0; } -.ui-miniform-title > i { margin-top: 13px; } \ No newline at end of file +.ui-miniform-title > i { margin-top: 13px; } + +.dg { color: #000; position: relative; visibility: hidden; background-color: #FFF; } +.dg-body { outline: 0 !important; background-color: #FFF; } +.dg .ui-scrollbar-y { margin-top: 62px; } +.dg .dg-header-scrollbar > .ui-scrollbar-area { overflow-y: hidden; } +.dg .dg-body-scrollbar > .ui-scrollbar-area { overflow-x: hidden; } +.dg-container { overflow: hidden; border-top: 1px solid #E0E0E0; border-left: 1px solid #E0E0E0; border-right: 1px solid #E0E0E0; position: relative; } +.jc-19 .dg-container, .jc-20 .dg-container { border-bottom: 1px solid #E0E0E0; } +.dg-mobile .dg-container { border-right: 1px solid #E0E0E0; border-bottom: 1px solid #E0E0E0; } +.dg-header { position: relative; border-bottom: 1px solid #E0E0E0; } +.dg-hrow { height: 60px; } +.dg-hcol { float: left; border-left: 1px solid #E0E0E0; height: 60px; font-weight: bold; position: relative; background-color: #F9F9F9; } +.dg-sort { float: right; font-size: 11px; width: 20px; text-align: center; padding: 12px 0 0 0; color: var(--color); } +.dg-monospace { font-family: Menlo,Consolas,monospace; font-size: 11px; } +.dg-btn-columns { cursor: pointer; color: #D0D0D0; font-size: 20px; position: absolute; right: 0; height: 61px; background-color: #D0D0D0; width: 13px; text-align: left; z-index: 3; } +.dg-btn-columns span { display: block; padding-top: 4px; font-size: 6px; color: gray; text-align: center; } +.dg-btn-columns:hover { background-color: #D9D9D9; color: #D9D9D9; } +.dg-sorting { cursor: pointer; } +.dg-label.right { padding-right: 0; } +.dg-sorting .ti-arrows-v { color: #C5C5C5; } +.dg-hcol:first-child { border-left: 0; } +.dg-resize { position: absolute; width: 10px; cursor: col-resize; height: 38px; border-right: 1px solid #E0E0E0; z-index: 2; } +.dg-label { padding: 8px 8px 0; height: 37px; font-size: 12px; user-select: none; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; } +.dg-sorting .dg-label.center { padding-left: 25px; padding-right: 0; } +.dg-sorting .dg-label { margin-right: 24px; } +.dg-filter { padding: 0 8px; border-top: 1px solid #E0E0E0; height: 23px; background-color: #FFF; } +.dg-filter input, .dg-filter label { width: 100%; outline: 0; font-size: 11px; background-color: transparent; border: 0; margin: 0; padding: 0; line-height: 23px; height: 23px; font-weight: normal; } +.dg-filter label { color: #A0A0A0; cursor: pointer; display: block; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } +.dg-filter.center input { text-align: center; } +.dg-filter.right input { text-align: right; } +.dg-filter-empty { border-top: 1px solid #E0E0E0; height: 23px; cursor: not-allowed !important; background: repeating-linear-gradient(45deg,#FFF,#FFF 10px,#F9F9F9 10px,#F9F9F9 20px); } +.dg-hcol .dg-label { padding-top: 11px; } +.dg-filter-selected { background-color: #FBF0CA !important; } +.dg-filter-selected label { color: #000; } +.dg-filter > i { position: absolute; right: 8px; margin-top: 6px; font-size: 10px; color: red; display: none; cursor: pointer; } +.dg-filter-selected > i { display: block; } +.dg-filter-selected input { padding-right: 12px; } +.dg-clickable .dg-row { cursor: pointer; } +.dg-row { height: 30px; font-size: 12px; border-bottom: 1px solid #E0E0E0; -webkit-transform: translateZ(0); } +.dg-row-changed { background-color: rgba(208,31,33,0.07); } +.dg-row.dg-selected { background-color: rgba(249,232,196,0.5) !important; } +.dg-col { float: left; border-left: 1px solid #E0E0E0; height: 29px; overflow: hidden; min-width: 30px; } +.dg-col:first-child { border-left: 0; } +.dg-col-changed { background-image: url(data:image/gif;base64,R0lGODdhBgAGAIAAAP8mAAAAACH5BAEAAAEALAAAAAAGAAYAAAIKhI8QGGvt4JKhAAA7); background-repeat: no-repeat; background-position: 0 0; } +.dg-value { padding: 6px 8px 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; height: 30px; } +.dg-number { padding: 6px 5px 0; font-size: 11px; text-align: right; height: 30px; color: #A0A0A0; } +.dg-col--1 { width: 40px; } +.dg-col--1 .dg-label { text-overflow: clip; } +.dg-row-empty { height: 30px; font-size: 12px; border-bottom: 1px solid #F0F0F0; background-color: #FFF !important; cursor: default; } +.dg-footer { border: 1px solid #E0E0E0; border-top: 0; height: 33px; } +.dg-pagination { float: left; } +.dg-pagination button { background-color: #FFF; border: 0; border-left: 1px solid #E0E0E0; height: 31px; width: 40px; color: #000; font-size: 12px; text-align: center; outline: 0; } +.dg-pagination button:hover { background-color: #F0F0F0; } +.dg-pagination button:disabled { background-color: #F9F9F9; color: #A0A0A0; } +.dg-pagination button:first-child { border-left: 0; } +.dg-pagination button[name='page-prev'] { border-right: 1px solid #E0E0E0; width: 41px; } +.dg-pagination button[name='page-last'] { border-right: 1px solid #E0E0E0; } +.dg-pagination input { width: 40px; text-align: center; border: 0; outline: 0; font-size: 12px; padding: 0; margin: 0; background: transparent; } +.dg-pagination > div { position: relative; display: inline-block; } +.dg-pagination-items { float: right; font-size: 12px; margin: 7px 10px 0 0; font-weight: bold; } +.dg-pagination-pages { float: left; font-size: 12px; margin: 7px 0 0 10px; } +.dg-visible { visibility: visible; } +.dg-columns { position: absolute; right: 0; top: 35px; width: 182px; background-color: #FFF; z-index: 1; box-shadow: 0 5px 20px rgba(0,0,0,0.1); border-radius: var(--color); font-size: 12px; line-height: 16px; z-index: 6; color: #000; border: 1px solid #E0E0E0; user-select: none; } +.dg-columns-checkbox > span { border-radius: 4px; border: 1px solid #D0D0D0; margin: 2px 5px 0 0; font-size: 8px; color: #FFF; width: 12px; height: 12px; line-height: 12px; float: left; text-align: center; } +.dg-columns-checkbox > span i { visibility: hidden; } +.dg-columns-checkbox-checked > span { background-color: #000; border-color: #000; } +.dg-columns-checkbox-checked > span i { visibility: visible; } +.dg-columns > div { border-bottom: 1px solid #D0D0D0; overflow: hidden; } +.dg-columns-body { max-height: 180px; overflow-scrolling: touch; overflow-y: auto; padding: 8px 50px 10px 3px; width: 210px; overflow-x: hidden; } +.dg-columns label { display: block; cursor: pointer; overflow-x: hidden; text-overflow: ellipsis; white-space: nowrap; } +.dg-columns label input { vertical-align: middle; } +.dg-columns label span { vertical-align: middle; margin-left: 5px; } +.dg-columns-button { margin: 10px 10px 0; display: block; width: 160px; background-color: #000; border-radius: var(--radius); color: #FFF; border: 0; font-size: 11px; height: 24px; } +.dg-columns-button:hover { background-color: #404040; } +.dg-columns-button i { margin-right: 6px; } +.dt-columns-reset { display: block; font-size: 11px; padding: 3px 0 8px 10px; cursor: pointer; color: #888888; } +.dt-columns-reset:hover { text-decoration: underline; } +.dg-hcol .dg-checkbox { margin-top: 0; } +.dg-checkbox { width: 15px; height: 15px; border: 1px solid #D0D0D0; vertical-align: middle; font-size: 11px; text-align: center; padding: 1px 0 0 1px; background-color: #FFF; border-radius: 4px; margin: 0 auto 0; } +.dg-checkbox i { display: none; } +.dg-checked { background-color: var(--color); border-color: var(--color); color: #FFF; } +.dg-checked i { display: block; } +.dg-required:before { content: '*'; font-size: 14px; color: red; font-weight: bold; margin-right: 3px; vertical-align: top; } +.dg input:-ms-input-placeholder { color: #A0A0A0 !important; } +.dg input::placeholder { color: #A0A0A0; opacity: 1; } +.dg input::-ms-input-placeholder { color: #A0A0A0 !important; } +.dg-col button { margin: 4px 0 0 1px; border: 0; background-color: #F0F0F0; font-size: 12px; color: #404040; height: 19px; border: 1px solid #F0F0F0; border-left: 0; padding: 0 3px; outline: 0; line-height: 10px; min-width: 20px; } +.dg-col button:hover { background-color: var(--color); border-color: var(--color); color: #FFF; } +.dg-col button[name='remove'] { background-color: #FFE5DD; border-color: #FFE5DD; color: red; } +.dg-col button[name='remove']:hover { background-color: #E8483F; border-color: #E8483F; color: #FFF; } +.dg-col button:first-child { border-top-left-radius: 3px; border-bottom-left-radius: 3px; margin-left: 0; } +.dg-col button:last-child { border-top-right-radius: 3px; border-bottom-right-radius: 3px; } +.dg-col button:disabled { background-color: #F0F0F0 !important; color: #A0A0A0 !important; cursor: not-allowed; } +.dg-editable .dg-checkbox { position: relative; display: inline-block; margin-left: 5px; margin-right: 5px; } +.dg-editable > div > .dg-checkbox { border-color: var(--color); } +.dg-checkbox-main { margin-top: 7px; } +.dg-bool { height: 30px; padding-top: 7px; } +.dg-bool .dg-checked { background-color: #D0D0D0; border-color: #D0D0D0; color: gray; } +.dg-editable { background-color: rgba(255,241,164,0.1); } +.dg-editable input { width: 100%; height: 28px; background-color: transparent; border: 0; outline: 0; padding: 0 8px; } +.dg-noscroll .ui-scrollbar-area { overflow-y: hidden; } +.dg-noscroll .ui-scrollbar-y span { visibility: hidden; } +.dg-noborder .dg-container { border: 0; } +.dg-noborder .dg-footer { border: 0; border-top: 1px solid #E0E0E0; } +.dg-header-scrollbar { overflow-y: hidden; } +.dg-header-scrollbar-container { height: 58px; overflow: hidden; } +.dg-resize-line { position: absolute; width: 1px; background-color: #E0E0E0; z-index: 6; } +.dg-colorize { padding: 2px 3px; border-radius: 4px; color: #FFF; } +.dg-fluid { border-right: 1px solid #E0E0E0; } +.dg-noborder.dg-fluid { border-right: 0; } +.dg-link { color: inherit; } +.dg-link i { margin-right: 5px; } +.dg-controls { position: absolute; right: 20px; background-color: #FFF; border: 1px solid #D0D0D0; height: 34px; color: #000; z-index: 1; margin-right: 5px; border-radius: var(--radius); box-shadow: 1px 0 15px rgba(0,0,0,0.1); transition: all 0.1s; transform: scale(0); } +.dg-controls button { border: 0; min-width: 34px; height: 32px; background-color: #F0F0F0; border-left: 1px solid #E0E0E0; background-color: transparent; font-size: 12px; float: left; color: #000; padding: 0 10px; } +.dg-controls button i { font-size: 14px; } +.dg-controls button span { padding: 0 5px; } +.dg-controls button:hover { background-color: #F0F0F0; } +.dg-controls button:first-child { border-left: 0; } +.dg-controls button:disabled { color: #A0A0A0; cursor: not-allowed; background-color: transparent !important; } +.dg-controls button:disabled i { color: #A0A0A0 !important; } +.dg-controls-visible { transform: scale(1); } +.dg-row:hover, .dg-row-hover { background-color:rgba(130,130,130,0.07); } + +.ui-dark .dg, .ui-dark .dg-body { background-color: #232323; } +.ui-dark .dg-hcol { background-color: #292929; } +.ui-dark .dg-footer { border-color: #404040; } +.ui-dark .dg-container { border-top-color: #353535; border-left-color: #353535; border-right-color: #353535; } +.jc-19.ui-dark .dg-container { border-bottom-color: #353535; } +.ui-dark .dg-mobile .dg-container { border-right-color: #404040; border-bottom-color: #404040; } +.ui-dark .dg-header { border-bottom-color: #353535; } +.ui-dark .dg-noborder .dg-footer { border-color: #404040; } +.ui-dark .dg-filter-selected { background-color: #3c3a32 !important; color: #FFF; } +.ui-dark .dg-filter-selected label { color: #FFF; } +.ui-dark .dg { color: #E0E0E0; } +.ui-dark .dg-sorting .ti-arrows-v { color: #A0A0A0; } +.ui-dark .dg-filter { border-top-color: #353535; background-color: #202020; } +.ui-dark .dg-resize { border-right-color: #353535; } +.ui-dark .dg-filter-empty { border-top-color: #353535; height: 23px; background: repeating-linear-gradient(45deg,#252525,#252525 10px,#202020 10px,#202020 20px); } +.ui-dark .dg-number { background-color: #282828; } +.ui-dark .dg-col { border-left-color: #353535; } +.ui-dark .dg-hcol { border-left-color: #353535; } +.ui-dark .dg-hrow { color: #F0F0F0; } +.ui-dark .dg-row-empty { background-color: #202020 !important; border-bottom-color: #353535; } +.ui-dark .dg-row { border-bottom-color: #353535; } +.ui-dark .dg-selected { background-color: rgba(200,200,200,0.06) !important; } +.ui-dark .dg-columns { background-color: #232323; color: #FFF; border-color: #505050; } +.ui-dark .dg-columns-checkbox > span { border-color: #505050; color: #202020; } +.ui-dark .dg-columns-checkbox-checked > span { background-color: #505050; border-color: #505050; color: #D0D0D0; } +.ui-dark .dg-columns > div { border-bottom-color: #505050; } +/*.ui-dark .dg-body > div > div:nth-child(even) > .dg-row { background-color: rgba(0,0,0,0.1); }*/ +.ui-dark .dg-row-changed:hover { background-color: rgba(208,31,33,0.2) !important; } +.ui-dark .dg-row-changed { background-color: rgba(208,31,33,0.12) !important; } +.ui-dark .dg-row-changed.dg-row:nth-child(even) { background-color: rgba(208,31,33,0.12); } +.ui-dark .dg-btn-columns:hover { color: #FFF; } +.ui-dark .dg-col button { background-color: #303030; color: #A0A0A0; border-color: #404040; } +.ui-dark .dg-col button:hover { background-color: #404040; color: #FFF; } +.ui-dark .dg-col button:first-child { border-left-color: #404040; } +.ui-dark .dg-col button[name='remove'] { background-color: #282828; color: red; } +.ui-dark .dg-col button:disabled { background-color: #252525 !important; color: #505050 !important; } +.ui-dark .dg-checkbox { background-color: #303030; border-color: #3A3A3A; } +.ui-dark .dg-checked { background-color: var(--color); border-color: var(--color); color: #FFF; } +.ui-dark .dg-btn-columns { color: #323232; background-color: #323232; } +.ui-dark .dg-btn-columns span { color: #606060; } +.ui-dark .dg-btn-columns:hover { background-color: #353535; color: #353535; } +.ui-dark .dg-pagination button { background-color: #303030; border-left-color: #404040; color: #FFF; } +.ui-dark .dg-pagination button:hover { background-color: #353535; } +.ui-dark .dg-pagination button:disabled { background-color: #202020; color: gray; } +.ui-dark .dg-pagination button[name='page-prev'] { border-right-color: #404040; } +.ui-dark .dg-pagination button[name='page-last'] { border-right-color: #404040; } +.ui-dark .dg-bool .dg-checked { background-color: #505050; border-color: #505050; color: gray; } +.ui-dark .dg-editable { background-color: rgba(255,241,164,0.05); } +.ui-dark .dg-resize-line { background-color: #404040; } +.ui-dark .dg-fluid { border-right-color: #404040; } +.ui-dark .dg-controls { background-color: #333; border-color: #404040; box-shadow: 1px 0 15px rgba(0,0,0,0.3); } +.ui-dark .dg-controls button { border-left-color: #404040; color: #FFF; } +.ui-dark .dg-controls button:disabled { color: #505050; } +.ui-dark .dg-controls button:disabled i { color: #505050 !important; } +.ui-dark .dg-controls button:hover { background-color: #404040; } +.ui-dark .dg-row:hover, .ui-dark .dg-row-hover { background-color: rgba(0,0,0,0.2); } +.dg-hfunc { width: 15px; height: 15px; vertical-align: middle; font-size: 11px; text-align: center; background-color: #FFF; margin: 0px auto 0; } \ No newline at end of file diff --git a/ui/public/forms/catalogs.html b/ui/public/forms/catalogs.html index ddd84de3..622d5203 100644 --- a/ui/public/forms/catalogs.html +++ b/ui/public/forms/catalogs.html @@ -14,44 +14,30 @@ } -