From d1cfb7715412a9e56012c9e29ab554097399d576 Mon Sep 17 00:00:00 2001 From: Ludovic <54670129+lbr38@users.noreply.github.com> Date: Wed, 27 May 2026 11:30:58 +0200 Subject: [PATCH] 6.0.0 --- .github/workflows/build-6.0.0-image.yml | 45 + .github/workflows/test-database-update.yml | 8 +- CLAUDE.md | 7 + docker-compose-dev-6.0.0.yml | 27 + docker/config/nginx/repomanager.conf | 8 +- www/bin/repomanager | 3 +- www/config/tasks/create.php | 1 + www/config/tasks/duplicate.php | 1 + www/config/tasks/env.php | 7 +- www/config/tasks/rebuild.php | 5 +- www/config/tasks/rename.php | 7 +- www/config/tasks/update.php | 5 +- www/controllers/App/Config/Env.php | 22 +- www/controllers/App/Main.php | 2 +- www/controllers/App/Permissions.php | 2 +- www/controllers/Environment.php | 47 +- www/controllers/Group/Group.php | 8 + www/controllers/Host/Host.php | 37 +- www/controllers/Host/Package/Package.php | 25 +- .../vars/repo-accesses-chart.vars.inc.php | 6 +- .../vars/repo-snapshots-chart.vars.inc.php | 18 +- .../repo-snapshots-size-chart.vars.inc.php | 18 +- .../vars/repo-storage-chart.vars.inc.php | 44 - .../vars/browse/actions.vars.inc.php | 68 -- .../Container/vars/browse/list.vars.inc.php | 60 -- .../Container/vars/header/menu.vars.inc.php | 10 +- .../Container/vars/host/packages.vars.inc.php | 2 +- .../vars/hosts/overview.vars.inc.php | 16 +- .../kpi.vars.inc.php} | 10 +- .../Container/vars/repos/kpi.vars.inc.php | 45 + .../Container/vars/repos/list.vars.inc.php | 73 +- .../Container/vars/snapshot/kpi.vars.inc.php | 59 ++ .../Container/vars/snapshot/list.vars.inc.php | 60 ++ .../Container/vars/tasks/log.vars.inc.php | 187 ++-- .../Container/vars/tasks/tasks.vars.inc.php | 13 + www/controllers/Layout/ContainerReload.php | 8 + .../Panel/vars/hosts/settings.vars.inc.php | 3 + .../Layout/Panel/vars/repos/edit.vars.inc.php | 2 +- .../Panel/vars/repos/install.vars.inc.php | 15 +- .../Layout/Panel/vars/repos/new.vars.inc.php | 29 +- .../Panel/vars/repos/rename.vars.inc.php | 24 + .../vars/repos/scheduled-task.vars.inc.php | 38 + .../Panel/vars/repos/settings.vars.inc.php | 12 + .../settings/user/permissions.vars.inc.php | 32 +- .../Panel/vars/snapshot/rebuild.vars.inc.php | 38 + .../Panel/vars/snapshot/upload.vars.inc.php | 47 + .../Layout/Panel/vars/tasks/edit.vars.inc.php | 6 +- www/controllers/Layout/Tab/Repos.php | 1 + .../Layout/Tab/{Stats.php => Repository.php} | 7 +- www/controllers/Layout/Tab/Run.php | 23 +- .../Layout/Tab/{Browse.php => Snapshot.php} | 6 +- www/controllers/Layout/Tab/Task.php | 20 + www/controllers/Layout/Tab/Tasks.php | 20 + www/controllers/Layout/Table/Render.php | 4 +- .../vars/host/installed-packages.vars.inc.php | 34 + .../Table/vars/stats/access.vars.inc.php | 16 +- .../Table/vars/stats/ip-access.vars.inc.php | 12 +- .../vars/tasks/list-children.vars.inc.php | 24 + .../Table/vars/tasks/list-done.vars.inc.php | 22 +- .../Table/vars/tasks/list-queued.vars.inc.php | 22 +- .../vars/tasks/list-running.vars.inc.php | 22 +- .../vars/tasks/list-scheduled.vars.inc.php | 22 +- www/controllers/Repo/Browse.php | 10 +- www/controllers/Repo/Deb.php | 10 - www/controllers/Repo/Edit/Form.php | 11 +- www/controllers/Repo/Environment.php | 26 +- www/controllers/Repo/Listing.php | 16 + www/controllers/Repo/Package/Sync.php | 4 +- www/controllers/Repo/Repo.php | 188 ++-- www/controllers/Repo/Rpm.php | 10 - www/controllers/Repo/Snapshot/Package.php | 10 + www/controllers/Repo/Snapshot/Snapshot.php | 18 + www/controllers/Repo/Task/Create.php | 139 +-- www/controllers/Repo/Task/Delete.php | 2 +- www/controllers/Repo/Task/Duplicate.php | 119 +-- www/controllers/Repo/Task/Env.php | 85 +- www/controllers/Repo/Task/Finalize.php | 28 +- www/controllers/Repo/Task/Rebuild.php | 18 +- www/controllers/Repo/Task/RemoveEnv.php | 14 +- www/controllers/Repo/Task/Rename.php | 52 +- www/controllers/Repo/Task/Update.php | 72 +- .../Service/Unit/ScheduledTask.php | 55 +- www/controllers/Task/Execution.php | 25 +- www/controllers/Task/Form/Create.php | 3 + www/controllers/Task/Form/Delete.php | 26 +- www/controllers/Task/Form/Duplicate.php | 20 +- www/controllers/Task/Form/Env.php | 25 +- www/controllers/Task/Form/Form.php | 145 ++- .../Task/Form/Param/Environment.php | 15 +- www/controllers/Task/Form/Param/Repo.php | 20 + www/controllers/Task/Form/Param/Schedule.php | 6 +- www/controllers/Task/Form/Param/Tags.php | 22 + www/controllers/Task/Form/Rebuild.php | 25 +- www/controllers/Task/Form/RemoveEnv.php | 13 +- www/controllers/Task/Form/Rename.php | 24 +- www/controllers/Task/Form/Update.php | 27 +- www/controllers/Task/Listing.php | 71 ++ www/controllers/Task/Target.php | 259 ++++++ www/controllers/Task/Task.php | 164 +++- www/controllers/User/Create.php | 2 +- .../User/{ => Permission}/Permission.php | 25 +- www/controllers/User/Permission/Repo.php | 44 + .../User/Preference/Preference.php | 180 ++++ www/controllers/Utils/Generate/Html/Label.php | 49 +- .../Websocket/BrowserClient/Process.php | 10 +- www/controllers/ajax/environment.php | 26 +- www/controllers/ajax/hosts/settings.php | 4 +- www/controllers/ajax/repo/edit.php | 56 +- www/controllers/ajax/repo/environment.php | 18 - www/controllers/ajax/repo/snapshot/browse.php | 24 +- www/controllers/ajax/settings/user.php | 2 +- www/controllers/ajax/task.php | 93 +- www/controllers/ajax/user/preferences.php | 17 + www/models/Connection.php | 316 ++----- www/models/Environment.php | 31 +- www/models/Group/Group.php | 22 + www/models/Host/Host.php | 5 +- www/models/Host/Package/Package.php | 70 +- www/models/Layout/ContainerReload.php | 24 + www/models/Repo/Deb.php | 34 - www/models/Repo/Environment.php | 29 +- www/models/Repo/Listing.php | 143 ++- www/models/Repo/Repo.php | 45 +- www/models/Repo/Rpm.php | 34 +- www/models/Task/Listing.php | 86 ++ www/models/Task/Task.php | 7 +- www/models/User/Preference.php | 69 ++ www/public/assets/icons/calendar.svg | 40 + www/public/assets/icons/cloud-download.svg | 42 + www/public/assets/icons/deb.svg | 69 -- www/public/assets/icons/disk.svg | 46 + www/public/assets/icons/key2.svg | 38 - www/public/assets/icons/locked-red.svg | 38 + www/public/assets/icons/locked.svg | 40 + www/public/assets/icons/save.svg | 44 + www/public/assets/icons/server.svg | 47 +- www/public/assets/icons/shield-warning.svg | 62 ++ www/public/assets/icons/shield.svg | 58 ++ www/public/assets/icons/tag.svg | 38 + www/public/assets/icons/trash.svg | 38 + www/public/assets/icons/unlocked.svg | 38 + www/public/assets/icons/upload.svg | 42 + .../resources/js/app/checkbox.config.js | 11 +- .../resources/js/app/container.config.js | 26 + www/public/resources/js/classes/Checkbox.js | 14 +- www/public/resources/js/classes/ConfirmBox.js | 4 +- www/public/resources/js/classes/Container.js | 82 +- .../resources/js/classes/Environment.js | 74 +- www/public/resources/js/classes/Host.js | 4 - .../resources/js/classes/Host/HostSearch.js | 5 +- .../resources/js/classes/Permission/Repo.js | 27 + .../resources/js/classes/Permission/Tasks.js | 46 + www/public/resources/js/classes/Repo.js | 467 +++++----- .../resources/js/classes/ScheduledTask.js | 180 ++++ www/public/resources/js/classes/Snapshot.js | 350 +++++++ .../resources/js/events/browse/repository.js | 144 --- www/public/resources/js/events/checkbox.js | 17 + .../resources/js/events/host/actions.js | 137 ++- .../resources/js/events/hosts/layout.js | 2 +- .../resources/js/events/hosts/settings.js | 1 + www/public/resources/js/events/repo/edit.js | 249 +++-- www/public/resources/js/events/repo/env.js | 46 +- .../resources/js/{ => events/repo}/group.js | 12 +- www/public/resources/js/events/repo/list.js | 27 +- .../resources/js/events/repo/preferences.js | 39 + .../js/events/repo/scheduled-task.js | 20 + .../js/events/settings/environment.js | 59 +- .../resources/js/events/snapshot/browse.js | 120 +++ .../resources/js/events/task/actions.js | 64 +- www/public/resources/js/events/task/stop.js | 9 +- www/public/resources/js/functions.js | 19 +- www/public/resources/js/functions/browse.js | 82 -- .../resources/js/functions/environment.js | 61 -- www/public/resources/js/functions/task.js | 19 +- www/public/resources/js/host.js | 138 +-- www/public/resources/js/task.js | 285 +++--- www/public/resources/styles/common.css | 78 +- .../resources/styles/components/button.css | 167 +++- .../resources/styles/components/card.css | 10 +- .../styles/components/confirmbox.css | 38 +- .../resources/styles/components/input.css | 45 +- .../resources/styles/components/label.css | 40 +- .../resources/styles/components/layout.css | 28 +- www/public/resources/styles/main.css | 866 +++++++++++++----- www/public/resources/styles/select2.css | 51 +- .../styles/{browse.css => snapshot.css} | 3 - www/public/resources/styles/stats-hosts.css | 217 ++++- .../resources/styles/{run.css => tasks.css} | 137 ++- www/update/database/6.0.0.php | 129 +++ www/update/database/ci/deb/rename.php | 2 +- www/update/database/ci/rpm/rename.php | 2 +- www/version | 2 +- .../containers/browse/actions.inc.php | 120 --- .../includes/containers/browse/list.inc.php | 65 -- .../containers/header/debug-mode.inc.php | 4 +- .../header/general-error-messages.inc.php | 6 +- .../header/general-log-messages.inc.php | 4 +- .../includes/containers/header/menu.inc.php | 268 ++---- .../containers/header/service-status.inc.php | 4 +- .../includes/containers/host/packages.inc.php | 35 +- .../includes/containers/host/requests.inc.php | 4 +- .../includes/containers/host/summary.inc.php | 33 +- .../includes/containers/hosts/list.inc.php | 578 ++++++------ .../containers/hosts/overview.inc.php | 1 - .../includes/containers/repo/kpi.inc.php | 39 + .../repo.inc.php => repo/stats.inc.php} | 34 +- .../includes/containers/repos/group.inc.php | 106 +++ .../includes/containers/repos/kpi.inc.php | 92 ++ .../includes/containers/repos/list.inc.php | 158 ++-- .../containers/repos/properties.inc.php | 124 --- .../includes/containers/repos/repo.inc.php | 205 +++++ .../containers/settings/right-section.inc.php | 3 +- .../containers/settings/settings.inc.php | 44 +- .../containers/settings/status.inc.php | 6 - .../includes/containers/snapshot/kpi.inc.php | 79 ++ .../includes/containers/snapshot/list.inc.php | 50 + .../includes/containers/status/health.inc.php | 41 +- .../includes/containers/tasks/log.inc.php | 46 +- .../containers/tasks/log/log-found.inc.php | 164 ++++ .../tasks/log/log-not-found.inc.php | 15 + .../containers/tasks/log/step.inc.php | 15 +- .../includes/containers/tasks/tasks.inc.php | 59 ++ www/views/includes/footer.inc.php | 45 +- www/views/includes/forms/edit.inc.php | 1 - www/views/includes/forms/tasks/delete.inc.php | 2 - .../includes/forms/tasks/duplicate.inc.php | 47 +- www/views/includes/forms/tasks/env.inc.php | 6 - .../includes/forms/tasks/rebuild.inc.php | 3 - www/views/includes/forms/tasks/rename.inc.php | 12 - .../includes/forms/tasks/schedule.inc.php | 31 +- www/views/includes/forms/tasks/update.inc.php | 3 - www/views/includes/head.inc.php | 48 +- .../package/event-packages-details.inc.php | 2 +- .../includes/host/package/timeline.inc.php | 2 +- .../panels/general/notification.inc.php | 32 +- .../includes/panels/general/userspace.inc.php | 18 +- .../panels/header/menu-burger.inc.php | 4 +- .../includes/panels/hosts/groups/list.inc.php | 28 +- .../includes/panels/hosts/profiles.inc.php | 6 +- .../includes/panels/hosts/settings.inc.php | 8 + .../includes/panels/repos/groups/list.inc.php | 24 +- .../includes/panels/repos/install.inc.php | 2 +- www/views/includes/panels/repos/new.inc.php | 42 +- .../includes/panels/repos/rename.inc.php | 9 + .../panels/repos/scheduled-task.inc.php | 136 +++ .../includes/panels/repos/settings.inc.php | 30 + .../panels/repos/sources/list.inc.php | 3 +- .../panels/settings/user/permissions.inc.php | 14 + .../includes/panels/snapshot/rebuild.inc.php | 18 + .../includes/panels/snapshot/upload.inc.php | 52 ++ www/views/includes/repos-list.inc.php | 427 +-------- .../includes/tables/history/list.inc.php | 20 +- .../tables/host/available-packages.inc.php | 110 ++- .../includes/tables/host/history.inc.php | 121 +-- .../tables/host/installed-packages.inc.php | 40 + .../includes/tables/host/requests.inc.php | 719 ++++++++------- .../tables/repos/sources/gpgkeys.inc.php | 2 +- .../tables/repos/sources/list.inc.php | 18 +- .../tables/tasks/list-children.inc.php | 5 + .../includes/tables/tasks/list-done.inc.php | 4 +- .../includes/tables/tasks/list-queued.inc.php | 4 +- .../tables/tasks/list-running.inc.php | 4 +- .../tables/tasks/list-scheduled.inc.php | 4 +- www/views/includes/tables/tasks/list.inc.php | 713 +++++++------- www/views/layout.html.php | 43 +- www/views/templates/tasks/create.inc.php | 78 +- www/views/templates/tasks/delete.inc.php | 40 +- www/views/templates/tasks/duplicate.inc.php | 84 +- www/views/templates/tasks/env.inc.php | 55 +- www/views/templates/tasks/rebuild.inc.php | 44 +- www/views/templates/tasks/removeEnv.inc.php | 40 +- www/views/templates/tasks/rename.inc.php | 56 +- www/views/templates/tasks/update.inc.php | 50 +- 273 files changed, 9817 insertions(+), 5936 deletions(-) create mode 100644 .github/workflows/build-6.0.0-image.yml create mode 100644 CLAUDE.md create mode 100644 docker-compose-dev-6.0.0.yml delete mode 100644 www/controllers/Layout/Chart/vars/repo-storage-chart.vars.inc.php delete mode 100644 www/controllers/Layout/Container/vars/browse/actions.vars.inc.php delete mode 100644 www/controllers/Layout/Container/vars/browse/list.vars.inc.php rename www/controllers/Layout/Container/vars/{stats/repo.vars.inc.php => repo/kpi.vars.inc.php} (78%) create mode 100644 www/controllers/Layout/Container/vars/repos/kpi.vars.inc.php create mode 100644 www/controllers/Layout/Container/vars/snapshot/kpi.vars.inc.php create mode 100644 www/controllers/Layout/Container/vars/snapshot/list.vars.inc.php create mode 100644 www/controllers/Layout/Container/vars/tasks/tasks.vars.inc.php create mode 100644 www/controllers/Layout/Panel/vars/repos/rename.vars.inc.php create mode 100644 www/controllers/Layout/Panel/vars/repos/scheduled-task.vars.inc.php create mode 100644 www/controllers/Layout/Panel/vars/repos/settings.vars.inc.php create mode 100644 www/controllers/Layout/Panel/vars/snapshot/rebuild.vars.inc.php create mode 100644 www/controllers/Layout/Panel/vars/snapshot/upload.vars.inc.php rename www/controllers/Layout/Tab/{Stats.php => Repository.php} (79%) rename www/controllers/Layout/Tab/{Browse.php => Snapshot.php} (81%) create mode 100644 www/controllers/Layout/Tab/Task.php create mode 100644 www/controllers/Layout/Tab/Tasks.php create mode 100644 www/controllers/Layout/Table/vars/host/installed-packages.vars.inc.php create mode 100644 www/controllers/Layout/Table/vars/tasks/list-children.vars.inc.php create mode 100644 www/controllers/Task/Form/Param/Repo.php create mode 100644 www/controllers/Task/Form/Param/Tags.php create mode 100644 www/controllers/Task/Target.php rename www/controllers/User/{ => Permission}/Permission.php (88%) create mode 100644 www/controllers/User/Preference/Preference.php delete mode 100644 www/controllers/ajax/repo/environment.php create mode 100644 www/controllers/ajax/user/preferences.php create mode 100644 www/models/User/Preference.php create mode 100644 www/public/assets/icons/calendar.svg create mode 100644 www/public/assets/icons/cloud-download.svg delete mode 100644 www/public/assets/icons/deb.svg create mode 100644 www/public/assets/icons/disk.svg delete mode 100644 www/public/assets/icons/key2.svg create mode 100644 www/public/assets/icons/locked-red.svg create mode 100644 www/public/assets/icons/locked.svg create mode 100644 www/public/assets/icons/save.svg create mode 100644 www/public/assets/icons/shield-warning.svg create mode 100644 www/public/assets/icons/shield.svg create mode 100644 www/public/assets/icons/tag.svg create mode 100644 www/public/assets/icons/trash.svg create mode 100644 www/public/assets/icons/unlocked.svg create mode 100644 www/public/assets/icons/upload.svg create mode 100644 www/public/resources/js/classes/Permission/Repo.js create mode 100644 www/public/resources/js/classes/Permission/Tasks.js create mode 100644 www/public/resources/js/classes/ScheduledTask.js create mode 100644 www/public/resources/js/classes/Snapshot.js delete mode 100644 www/public/resources/js/events/browse/repository.js rename www/public/resources/js/{ => events/repo}/group.js (87%) create mode 100644 www/public/resources/js/events/repo/preferences.js create mode 100644 www/public/resources/js/events/repo/scheduled-task.js create mode 100644 www/public/resources/js/events/snapshot/browse.js delete mode 100644 www/public/resources/js/functions/browse.js delete mode 100644 www/public/resources/js/functions/environment.js rename www/public/resources/styles/{browse.css => snapshot.css} (95%) rename www/public/resources/styles/{run.css => tasks.css} (53%) create mode 100644 www/update/database/6.0.0.php delete mode 100644 www/views/includes/containers/browse/actions.inc.php delete mode 100644 www/views/includes/containers/browse/list.inc.php create mode 100644 www/views/includes/containers/repo/kpi.inc.php rename www/views/includes/containers/{stats/repo.inc.php => repo/stats.inc.php} (83%) create mode 100644 www/views/includes/containers/repos/group.inc.php create mode 100644 www/views/includes/containers/repos/kpi.inc.php delete mode 100644 www/views/includes/containers/repos/properties.inc.php create mode 100644 www/views/includes/containers/repos/repo.inc.php delete mode 100644 www/views/includes/containers/settings/status.inc.php create mode 100644 www/views/includes/containers/snapshot/kpi.inc.php create mode 100644 www/views/includes/containers/snapshot/list.inc.php create mode 100644 www/views/includes/containers/tasks/log/log-found.inc.php create mode 100644 www/views/includes/containers/tasks/log/log-not-found.inc.php create mode 100644 www/views/includes/containers/tasks/tasks.inc.php create mode 100644 www/views/includes/panels/repos/rename.inc.php create mode 100644 www/views/includes/panels/repos/scheduled-task.inc.php create mode 100644 www/views/includes/panels/repos/settings.inc.php create mode 100644 www/views/includes/panels/snapshot/rebuild.inc.php create mode 100644 www/views/includes/panels/snapshot/upload.inc.php create mode 100644 www/views/includes/tables/host/installed-packages.inc.php create mode 100644 www/views/includes/tables/tasks/list-children.inc.php diff --git a/.github/workflows/build-6.0.0-image.yml b/.github/workflows/build-6.0.0-image.yml new file mode 100644 index 000000000..89ca269e9 --- /dev/null +++ b/.github/workflows/build-6.0.0-image.yml @@ -0,0 +1,45 @@ +name: Build Docker 6.0.0 image + +on: + push: + branches: [ ui ] + +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Check out the repo + uses: actions/checkout@v6 + with: + ref: 'ui' + + - name: Retrieve Repomanager version + run: | + echo "VERSION=$(cat ${GITHUB_WORKSPACE}/www/version)" >> $GITHUB_ENV + echo "Version: ${{ env.VERSION }}" + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to the container registry + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_TOKEN }} + + # Build the images + - name: Build and push docker + uses: docker/build-push-action@v6 + with: + file: Dockerfile + push: true + tags: lbr38/repomanager:6.0.0-beta + platforms: linux/amd64,linux/arm64,linux/arm/v7 + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/test-database-update.yml b/.github/workflows/test-database-update.yml index 0b187807a..527cda5a5 100644 --- a/.github/workflows/test-database-update.yml +++ b/.github/workflows/test-database-update.yml @@ -1,4 +1,6 @@ -name: Database update tests +# TODO debug +# Crash en boucle - à corriger une fois 6.0.0 mergé dans devel +# name: Database update tests on: push: @@ -91,7 +93,9 @@ jobs: cd ${GITHUB_WORKSPACE} - sed -i 's/branch:.*/branch: devel/g' docker-compose.yml + # TODO debug + # sed -i 's/branch:.*/branch: devel/g' docker-compose.yml + sed -i 's/branch:.*/branch: ui/g' docker-compose.yml sed -i 's/FQDN:.*/FQDN: repomanager.test.com/g' docker-compose.yml docker-compose -f docker-compose.yml up -d diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..dfa4cd630 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,7 @@ +## Approach +- Read existing files before writing. Don't re-read unless changed. +- Thorough in reasoning, concise in output. +- Skip files over 100KB unless required. +- No sycophantic openers or closing fluff. +- No emojis or em-dashes. +- Do not guess APIs, versions, flags, commit SHAs, or package names. Verify by reading code or docs before asserting. \ No newline at end of file diff --git a/docker-compose-dev-6.0.0.yml b/docker-compose-dev-6.0.0.yml new file mode 100644 index 000000000..64165c77e --- /dev/null +++ b/docker-compose-dev-6.0.0.yml @@ -0,0 +1,27 @@ +--- +# TODO debug +# For my dev environment only +version: "3" +services: + repomanager: + container_name: repomanager-dev-600 + image: repomanager + network_mode: host + environment: + FQDN: repomanager-dev-600.localhost + MAX_UPLOAD_SIZE: 64M + NGINX_LISTEN_PORT: 7777 + PUID: 1000 + PGID: 1000 + build: + context: . + dockerfile: Dockerfile + args: + branch: ui + restart: unless-stopped + volumes: + - /etc/localtime:/etc/localtime:ro + - /var/lib/docker/volumes/repomanager-dev-600-logs:/var/log/nginx + - /var/lib/docker/volumes/repomanager-dev-600-www:/var/www/repomanager + - /var/lib/docker/volumes/repomanager-dev-600-data:/var/lib/repomanager + - /var/lib/docker/volumes/repomanager-dev-600-repo:/home/repo diff --git a/docker/config/nginx/repomanager.conf b/docker/config/nginx/repomanager.conf index 9e910ce86..268db58f9 100644 --- a/docker/config/nginx/repomanager.conf +++ b/docker/config/nginx/repomanager.conf @@ -121,8 +121,12 @@ server { access_log off; } - location /repo { - alias $REPOS_DIR; + # Matches /repo/ and /repo/xxx but not /repository + location ^~ /repo/ { + # Final slash is important, otherwise it could break the download of packages + alias $REPOS_DIR/; + + # Uncomment the following lines to enable directory listing (autoindex) for repositories # autoindex on; # autoindex_exact_size off; # autoindex_localtime on; diff --git a/www/bin/repomanager b/www/bin/repomanager index cc7778301..91fbf4a16 100755 --- a/www/bin/repomanager +++ b/www/bin/repomanager @@ -92,9 +92,10 @@ echo ' | | \| ___/| |_> ) (_) ) Y Y \/ __ \| | \/ __ \/ /_/ > ___/| | \/ |__| \___ > __/ \____/|__|_| (____ /___| (____ |___ / \___ >__| \/|__| \/ \/ \/ \/_____/ \/ - ' +echo -e "Version $(cat $WWW_DIR/version)\n" + while [ $# -ge 1 ];do case "$1" in --help|-help|-h) diff --git a/www/config/tasks/create.php b/www/config/tasks/create.php index c541837c7..cc0894345 100644 --- a/www/config/tasks/create.php +++ b/www/config/tasks/create.php @@ -18,6 +18,7 @@ 'env', 'group', 'description', + 'tags', 'advanced-params' // Advanced params include package include/exclude and metadata custom fields, it is optional ], diff --git a/www/config/tasks/duplicate.php b/www/config/tasks/duplicate.php index 7e0bb3690..76dca44c5 100644 --- a/www/config/tasks/duplicate.php +++ b/www/config/tasks/duplicate.php @@ -19,6 +19,7 @@ 'env', 'group', 'description', + 'tags' ], // Conditional params must be compared with form values diff --git a/www/config/tasks/env.php b/www/config/tasks/env.php index 7f0fdce96..61ac71f5f 100644 --- a/www/config/tasks/env.php +++ b/www/config/tasks/env.php @@ -24,5 +24,10 @@ // Form configuration $formConfig = [ // Allowed schedule types for this task - 'allowed-schedule-types' => ['unique'] + // Recurring is only allowed when the task targets a dynamic set of repositories, + // as pointing an environment on a fixed snapshot repeatedly makes no sense + 'allowed-schedule-types' => ['unique'], + + // This task can target a dynamic set of repositories (all latest snapshots matching filters) + 'allow-dynamic-target' => true ]; diff --git a/www/config/tasks/rebuild.php b/www/config/tasks/rebuild.php index 2859ca75c..ddeb035ec 100644 --- a/www/config/tasks/rebuild.php +++ b/www/config/tasks/rebuild.php @@ -16,5 +16,8 @@ // Form configuration $formConfig = [ // Allowed schedule types for this task - 'allowed-schedule-types' => ['unique', 'recurring'] + 'allowed-schedule-types' => ['unique', 'recurring'], + + // This task can target a dynamic set of repositories (all latest snapshots matching filters) + 'allow-dynamic-target' => true ]; diff --git a/www/config/tasks/rename.php b/www/config/tasks/rename.php index d09a29e90..121368a7a 100644 --- a/www/config/tasks/rename.php +++ b/www/config/tasks/rename.php @@ -3,14 +3,13 @@ $taskConfig = [ 'description' => 'Rename repository', - // Retrieve repository info from snap id - 'retrieve-repo-from-snap-id' => true, + // Retrieve repository info from repo id + 'retrieve-repo-from-repo-id' => true, // Required params 'required-params' => [ 'name', - 'snap-id', - 'gpg-sign' // Required to make sure deb metadata is regenerated correctly + 'repo-id' ], // Conditional params must be compared with form values diff --git a/www/config/tasks/update.php b/www/config/tasks/update.php index 9f7018162..c1bda04f2 100644 --- a/www/config/tasks/update.php +++ b/www/config/tasks/update.php @@ -46,5 +46,8 @@ // Form configuration $formConfig = [ // Allowed schedule types for this task - 'allowed-schedule-types' => ['unique', 'recurring'] + 'allowed-schedule-types' => ['unique', 'recurring'], + + // This task can target a dynamic set of repositories (all latest snapshots matching filters) + 'allow-dynamic-target' => true ]; diff --git a/www/controllers/App/Config/Env.php b/www/controllers/App/Config/Env.php index f58cec2ab..c997e8fad 100644 --- a/www/controllers/App/Config/Env.php +++ b/www/controllers/App/Config/Env.php @@ -2,33 +2,25 @@ namespace Controllers\App\Config; -use Exception; +use Controllers\Environment; class Env { /** * Load environments */ - public static function get() + public static function get(): void { - $myenv = new \Controllers\Environment(); + $envController = new Environment(); if (!defined('ENVS')) { - define('ENVS', $myenv->listAll()); - } - if (!defined('ENVS_TOTAL')) { - define('ENVS_TOTAL', $myenv->total()); + define('ENVS', $envController->listAll()); } if (!defined('DEFAULT_ENV')) { - define('DEFAULT_ENV', $myenv->default()); - } - if (!defined('LAST_ENV')) { - define('LAST_ENV', $myenv->last()); + define('DEFAULT_ENV', $envController->default()); } - /** - * If there is no environment configured then __LOAD_ERROR_EMPTY_ENVS = 1 - */ + // If there is no environment configured then __LOAD_ERROR_EMPTY_ENVS = 1 if (empty(ENVS)) { if (!defined('__LOAD_ERROR_EMPTY_ENVS')) { define('__LOAD_ERROR_EMPTY_ENVS', 1); @@ -39,6 +31,6 @@ public static function get() } } - unset($myenv); + unset($envController); } } diff --git a/www/controllers/App/Main.php b/www/controllers/App/Main.php index 7a4aa99ca..a950fd8e9 100644 --- a/www/controllers/App/Main.php +++ b/www/controllers/App/Main.php @@ -89,7 +89,7 @@ public function __construct(string $level = 'all') // Errors related to the loading of the environments if (defined('__LOAD_ERROR_EMPTY_ENVS') && __LOAD_ERROR_EMPTY_ENVS > 0) { - $__LOAD_ERROR_MESSAGES[] = 'You must at least configure 1 environment.'; + $__LOAD_ERROR_MESSAGES[] = 'You should at least configure one environment.'; $__LOAD_GENERAL_ERROR++; } diff --git a/www/controllers/App/Permissions.php b/www/controllers/App/Permissions.php index d1f7e1a4e..9b754cd5c 100644 --- a/www/controllers/App/Permissions.php +++ b/www/controllers/App/Permissions.php @@ -12,7 +12,7 @@ class Permissions public static function load() { $userController = new \Controllers\User\User(); - $userPermissionController = new \Controllers\User\Permission(); + $userPermissionController = new \Controllers\User\Permission\Permission(); $isAdmin = false; $isSuperAdmin = false; diff --git a/www/controllers/Environment.php b/www/controllers/Environment.php index 43da91a80..a2ec2513d 100644 --- a/www/controllers/Environment.php +++ b/www/controllers/Environment.php @@ -59,7 +59,7 @@ public function add(string $name, string $color) : void * Check if environment already exists */ if ($this->exists($name)) { - throw new Exception('Environment ' . $name . ' already exists'); + throw new Exception('Environment ' . $name . ' already exists'); } /** @@ -72,7 +72,7 @@ public function add(string $name, string $color) : void /** * Add env to database */ - $this->model->add($name, $color); + $this->model->add($name, $color, 'false'); } /** @@ -106,17 +106,14 @@ public function edit(array $envs) : void throw new Exception('You are not allowed to perform this action'); } - /** - * Delete all envs from database before inserting the new ones - */ + // Delete all envs from database before inserting the new ones $this->model->deleteAll(); - /** - * Check if all specified envs are valid then add them to $envsToInsert array - */ + // Check that all specified env values are valid foreach ($envs as $env) { $name = Validate::string($env['name']); $color = Validate::string($env['color']); + $protected = Validate::string($env['protected']); if (empty($name)) { throw new Exception('Environment name is empty'); @@ -126,30 +123,54 @@ public function edit(array $envs) : void throw new Exception('Environment color is empty'); } + if (!in_array($protected, ['true', 'false'])) { + throw new Exception('Environment protected value is invalid'); + } + if (!Validate::alphaNumericHyphen($name)) { - throw new Exception('Environment ' . $name . ' contains invalid characters'); + throw new Exception('Environment ' . $name . ' contains invalid characters'); } if ($this->exists($name)) { - throw new Exception('Environment ' . $name . ' already exists'); + throw new Exception('Environment ' . $name . ' already exists'); } - $this->model->add($name, $color); + $this->model->add($name, $color, $protected); } } /** * Return all environments list */ - public function listAll() + public function listAll(): array { return $this->model->listAll(); } + /** + * Return the list of protected environments + */ + public function getProtected(): array + { + return $this->model->getProtected(); + } + + /** + * Return true if the environment is protected + */ + public function isProtected(string $name): bool + { + if (in_array($name, $this->getProtected())) { + return true; + } + + return false; + } + /** * Return default environment */ - public function default() + public function default(): string { return $this->model->default(); } diff --git a/www/controllers/Group/Group.php b/www/controllers/Group/Group.php index 212ae4752..643b68d83 100644 --- a/www/controllers/Group/Group.php +++ b/www/controllers/Group/Group.php @@ -206,6 +206,14 @@ public function getReposMembers(int $id): array return $this->model->getReposMembers($id); } + /** + * Return the list of group Ids the specified repo is member of + */ + public function getRepoGroupsIds(int $repoId): array + { + return $this->model->getRepoGroupsIds($repoId); + } + /** * Return the list of repos not in any group */ diff --git a/www/controllers/Host/Host.php b/www/controllers/Host/Host.php index 1c51ceef9..42c2dbc5f 100644 --- a/www/controllers/Host/Host.php +++ b/www/controllers/Host/Host.php @@ -89,13 +89,14 @@ public function getSettings(): array /** * Edit the display settings on the hosts page */ - public function setSettings(int $complianceThresholdCount, int $complianceThresholdDays, int $complianceRebootRequired): void + public function setSettings(int $complianceThresholdCount, int $complianceThresholdDays, int $complianceSecurityUpdate, int $complianceRebootRequired): void { if (!HostPermission::allowedAction('edit-settings')) { throw new Exception('You are not allowed to perform this action'); } - if (!is_numeric($complianceThresholdCount) or !is_numeric($complianceThresholdDays) or !is_numeric($complianceRebootRequired)) { + // Settings must be numeric + if (!is_numeric($complianceThresholdCount) or !is_numeric($complianceThresholdDays) or !is_numeric($complianceSecurityUpdate) or !is_numeric($complianceRebootRequired)) { throw new Exception('Setting must be numeric'); } @@ -104,11 +105,15 @@ public function setSettings(int $complianceThresholdCount, int $complianceThresh throw new Exception('Value must be greater than 0'); } + if ($complianceSecurityUpdate !== 0 and $complianceSecurityUpdate !== 1) { + throw new Exception('Invalid value for compliance security update setting'); + } + if ($complianceRebootRequired !== 0 and $complianceRebootRequired !== 1) { throw new Exception('Invalid value for compliance reboot required setting'); } - $this->model->setSettings($complianceThresholdCount, $complianceThresholdDays, $complianceRebootRequired); + $this->model->setSettings($complianceThresholdCount, $complianceThresholdDays, $complianceSecurityUpdate, $complianceRebootRequired); } /** @@ -233,11 +238,21 @@ public function getCompliance(int $hostId): array $host = $this->get($hostId); $settings = $this->getSettings(); + $securityUpdate = false; $compliant = true; $reason = ''; // Retrieve the total number of available packages - $available = count($hostPackageController->getAvailable()); + $available = $hostPackageController->getAvailable(); + $availableCount = count($available); + + // Check if there is at least one security update available in the list of available packages ('security' field is set to true) + foreach ($available as $package) { + if (!empty($package['Security']) and $package['Security'] == 'true') { + $securityUpdate = true; + break; + } + } // Retrieve the date of the last package upgrade event $latestUpdate = $hostPackageEventController->getLastPackageUpgradeEvent()['Date'] ?? null; @@ -246,11 +261,12 @@ public function getCompliance(int $hostId): array $thresholdDate = strtotime('-' . $settings['compliance_threshold_days'] . ' days'); // The host is not compliant if the available updates count is >= threshold - if ($available >= $settings['compliance_threshold_count']) { + if ($availableCount >= $settings['compliance_threshold_count']) { $compliant = false; - $reason = 'Pending updates count (' . $available . ') is greater than or equal to the threshold (' . $settings['compliance_threshold_count'] . ')'; + $reason = 'Pending updates count (' . $availableCount . ') is greater than or equal to the threshold (' . $settings['compliance_threshold_count'] . ')'; } + // The host is not compliant if no package update has been performed yet if (!$latestUpdate) { $compliant = false; $reason = 'No package update has been performed yet'; @@ -262,6 +278,12 @@ public function getCompliance(int $hostId): array $reason = 'Latest update date (' . $latestUpdate . ') is older than the compliance threshold (' . $settings['compliance_threshold_days'] . ' days)'; } + // Optional rule: host is not compliant when there is a security update available + if ((int) $settings['compliance_security_update'] === 1 and $securityUpdate) { + $compliant = false; + $reason = 'Host has a security update available'; + } + // Optional rule: host is not compliant when a reboot is required $rebootRequired = $host['Reboot_required'] ?? 'false'; if ((int) $settings['compliance_reboot_required'] === 1 and ($rebootRequired === 'true')) { @@ -272,8 +294,9 @@ public function getCompliance(int $hostId): array return [ 'compliant' => $compliant, 'reason' => $reason, - 'available_updates_count' => $available, + 'available_updates_count' => $availableCount, 'latest_update' => $latestUpdate, + 'security_update' => $securityUpdate, 'reboot_required' => $rebootRequired ]; } diff --git a/www/controllers/Host/Package/Package.php b/www/controllers/Host/Package/Package.php index 975420120..826d58e50 100644 --- a/www/controllers/Host/Package/Package.php +++ b/www/controllers/Host/Package/Package.php @@ -50,10 +50,19 @@ private function getPackageState(string $packageName, string|null $packageVersio /** * Return the list of inventoried packages on the host + * Supports optional pagination and search filter */ - public function getInventory() : array + public function getInventory(string $search = '', bool $withOffset = false, int $offset = 0) : array { - return $this->model->getInventory(); + return $this->model->getInventory($search, $withOffset, $offset); + } + + /** + * Return the total count of inventoried packages, optionally filtered + */ + public function countInventory(string $search = ''): int + { + return $this->model->countInventory($search); } /** @@ -143,17 +152,17 @@ private function addPackage(string $name, string $version, string $state, string /** * Add a package in the table available packages list */ - private function addPackageAvailable(string $name, string $version, string $repository) : void + private function addPackageAvailable(string $name, string $version, string $security, string $repository) : void { - $this->model->addPackageAvailable($name, $version, $repository); + $this->model->addPackageAvailable($name, $version, $security, $repository); } /** * Update a package in the available packages list */ - private function updatePackageAvailable(string $name, string $version, string $repository) : void + private function updatePackageAvailable(string $name, string $version, string $security, string $repository) : void { - $this->model->updatePackageAvailable($name, $version, $repository); + $this->model->updatePackageAvailable($name, $version, $security, $repository); } /** @@ -341,14 +350,14 @@ public function setPackagesAvailable(array $packages) : void // If the package does not exist in available packages list, add it if (!$this->packageAvailableExists($package['name'])) { - $this->addPackageAvailable($package['name'], $package['version'], $package['repository']); + $this->addPackageAvailable($package['name'], $package['version'], $package['security'], $package['repository']); continue; } // If it exists in the database, also check the version present in the database // If the version in the database is different then we update the package in the database, otherwise we do nothing if ($this->packageVersionAvailableExists($package['name'], $package['version'])) { - $this->updatePackageAvailable($package['name'], $package['version'], $package['repository']); + $this->updatePackageAvailable($package['name'], $package['version'], $package['security'], $package['repository']); } } } diff --git a/www/controllers/Layout/Chart/vars/repo-accesses-chart.vars.inc.php b/www/controllers/Layout/Chart/vars/repo-accesses-chart.vars.inc.php index 778fea038..060397059 100644 --- a/www/controllers/Layout/Chart/vars/repo-accesses-chart.vars.inc.php +++ b/www/controllers/Layout/Chart/vars/repo-accesses-chart.vars.inc.php @@ -12,12 +12,12 @@ $envData = []; // Check that repository Id is specified -if (empty(__ACTUAL_URI__[3])) { +if (empty(__ACTUAL_URI__[2])) { throw new Exception('no repository ID specified'); } // Check that repository Id is valid -if (!is_numeric(__ACTUAL_URI__[3])) { +if (!is_numeric(__ACTUAL_URI__[2])) { throw new Exception('invalid repository ID'); } @@ -34,7 +34,7 @@ // } // Retrieve repo infos from DB -$repoController->getAllById(__ACTUAL_URI__[3], '', ''); +$repoController->getAllById(__ACTUAL_URI__[2], '', ''); // Get statistics directly from database for all environments $results = []; diff --git a/www/controllers/Layout/Chart/vars/repo-snapshots-chart.vars.inc.php b/www/controllers/Layout/Chart/vars/repo-snapshots-chart.vars.inc.php index 3a041c8dc..622d5377d 100644 --- a/www/controllers/Layout/Chart/vars/repo-snapshots-chart.vars.inc.php +++ b/www/controllers/Layout/Chart/vars/repo-snapshots-chart.vars.inc.php @@ -6,20 +6,18 @@ $labels = []; $options = []; -if (__ACTUAL_URI__[2] != 'repo') { - throw new Exception('Error: invalid URI specified.'); +// Check that repository Id is specified +if (empty(__ACTUAL_URI__[2])) { + throw new Exception('no repository ID specified'); } -if (empty(__ACTUAL_URI__[3])) { - throw new Exception('Error: missing repository ID.'); -} - -if (!is_numeric(__ACTUAL_URI__[3])) { - throw new Exception('Error: invalid repository ID specified.'); +// Check that repository Id is valid +if (!is_numeric(__ACTUAL_URI__[2])) { + throw new Exception('invalid repository ID'); } // Get repository info -$repoController->getAllById(__ACTUAL_URI__[3]); +$repoController->getAllById(__ACTUAL_URI__[2]); // Get all snapshots of the repository $snapshots = $repoController->getSnapshots($repoController->getRepoId()); @@ -92,7 +90,7 @@ // Configure click callback to redirect to snapshot page 'clickCallback' => [ 'enabled' => true, - 'url' => '/browse/{value}', + 'url' => '/snapshot/{value}', 'newTab' => false // '_self' for same tab, '_blank' for new tab ] ]; diff --git a/www/controllers/Layout/Chart/vars/repo-snapshots-size-chart.vars.inc.php b/www/controllers/Layout/Chart/vars/repo-snapshots-size-chart.vars.inc.php index 4b2377dd1..296d92cf3 100644 --- a/www/controllers/Layout/Chart/vars/repo-snapshots-size-chart.vars.inc.php +++ b/www/controllers/Layout/Chart/vars/repo-snapshots-size-chart.vars.inc.php @@ -5,23 +5,21 @@ $labels = []; $options = []; -if (__ACTUAL_URI__[2] != 'repo') { - throw new Exception('Error: invalid URI specified.'); +// Check that repository Id is specified +if (empty(__ACTUAL_URI__[2])) { + throw new Exception('no repository ID specified'); } -if (empty(__ACTUAL_URI__[3])) { - throw new Exception('Error: missing repository ID.'); -} - -if (!is_numeric(__ACTUAL_URI__[3])) { - throw new Exception('Error: invalid repository ID specified.'); +// Check that repository Id is valid +if (!is_numeric(__ACTUAL_URI__[2])) { + throw new Exception('invalid repository ID'); } // Get repository info -$repoController->getAllById(__ACTUAL_URI__[3]); +$repoController->getAllById(__ACTUAL_URI__[2]); // Get all statistics for the specified repo ID -$stats = $repoStatsController->getByRepoId(__ACTUAL_URI__[3]); +$stats = $repoStatsController->getByRepoId(__ACTUAL_URI__[2]); $snapData = []; diff --git a/www/controllers/Layout/Chart/vars/repo-storage-chart.vars.inc.php b/www/controllers/Layout/Chart/vars/repo-storage-chart.vars.inc.php deleted file mode 100644 index 8d08c2b30..000000000 --- a/www/controllers/Layout/Chart/vars/repo-storage-chart.vars.inc.php +++ /dev/null @@ -1,44 +0,0 @@ - 0 && $diskUsedSpacePercent <= 30) { - $datasets[0]['colors'][] = '#15bf7f'; - $datasets[0]['colors'][] = 'rgba(21, 191, 127, 0.40)'; -} -if ($diskUsedSpacePercent > 30 && $diskUsedSpacePercent <= 50) { - $datasets[0]['colors'][] = '#ffb536'; - $datasets[0]['colors'][] = 'rgba(255, 181, 54, 0.40)'; -} -if ($diskUsedSpacePercent > 50 && $diskUsedSpacePercent <= 70) { - $datasets[0]['colors'][] = '#ff7c49'; - $datasets[0]['colors'][] = 'rgba(255, 124, 73, 0.40)'; -} -if ($diskUsedSpacePercent > 70 && $diskUsedSpacePercent <= 100) { - $datasets[0]['colors'][] = '#F32F63'; - $datasets[0]['colors'][] = 'rgba(243, 47, 99, 0.40)'; -} - -$options['innerRadius'] = '65%'; -$options['outerRadius'] = '99%'; -$options['toolbox']['show'] = false; -$options['legend']['show'] = false; -$options['tooltip']['show'] = false; -$options['emphasis']['disabled'] = true; - -unset($diskTotalSpace, $diskFreeSpace, $diskUsedSpace); diff --git a/www/controllers/Layout/Container/vars/browse/actions.vars.inc.php b/www/controllers/Layout/Container/vars/browse/actions.vars.inc.php deleted file mode 100644 index a5430edce..000000000 --- a/www/controllers/Layout/Container/vars/browse/actions.vars.inc.php +++ /dev/null @@ -1,68 +0,0 @@ -getAllById('', $snapId, ''); - -/** - * Define snapshot path - */ -if ($repoController->getPackageType() == 'rpm') { - $snapshotPath = REPOS_DIR . '/rpm/' . $repoController->getName() . '/' . $repoController->getReleasever() . '/' . $repoController->getDate(); -} - -if ($repoController->getPackageType() == 'deb') { - $snapshotPath = REPOS_DIR . '/deb/' . $repoController->getName() . '/' . $repoController->getDist() . '/' . $repoController->getSection() . '/' . $repoController->getDate(); -} - -/** - * If the path does not exist on the server then we quit - * browse/list container should have already throwed an error - */ -if (!is_dir($snapshotPath)) { - throw new Exception(); -} - -/** - * Retrieve repo rebuild status - */ -$rebuild = $repoController->getRebuild(); - -/** - * Upload packages - */ -if (!empty($_POST['action']) and $_POST['action'] == 'uploadPackage' and !empty($_POST['snapId']) and is_numeric($_POST['snapId']) and !empty($_FILES['packages'])) { - try { - if (isset($_POST['overwrite']) && is_null($overwrite = Convert::toBool($_POST['overwrite']))) { - throw new Exception('Invalid overwrite value'); - } - - $repoPackageController->upload(Sort::byPostFiles($_FILES['packages']), $overwrite ?? false); - $uploadSuccessMessage = 'Packages uploaded successfully'; - } catch (AppException $e) { - $uploadErrorDetails = $e->getDetails(); - } catch (Exception $e) { - $uploadErrorMessage = $e->getMessage(); - } - - unset($repoPackageController); -} diff --git a/www/controllers/Layout/Container/vars/browse/list.vars.inc.php b/www/controllers/Layout/Container/vars/browse/list.vars.inc.php deleted file mode 100644 index 5c63fc9b0..000000000 --- a/www/controllers/Layout/Container/vars/browse/list.vars.inc.php +++ /dev/null @@ -1,60 +0,0 @@ -exists($snapId)) { - throw new Exception('Error: repository snapshot #' . $snapId . ' does not exist.'); -} - -/** - * Retrieve repo infos from DB - */ -$myrepo->getAllById('', $snapId, ''); - -/** - * Define snapshot path - */ -if ($myrepo->getPackageType() == 'rpm') { - $snapshotPath = REPOS_DIR . '/rpm/' . $myrepo->getName() . '/' . $myrepo->getReleasever() . '/' . $myrepo->getDate(); -} - -if ($myrepo->getPackageType() == 'deb') { - $snapshotPath = REPOS_DIR . '/deb/' . $myrepo->getName() . '/' . $myrepo->getDist() . '/' . $myrepo->getSection() . '/' . $myrepo->getDate(); -} - -/** - * If the path does not exist on the server then we quit - */ -if (!is_dir($snapshotPath)) { - throw new Exception('Error: repo directory ' . $snapshotPath . ' does not exist.'); -} - -/** - * Retrieve repo size and packages count - */ -if ($myrepo->getPackageType() == 'rpm') { - $packagesCount = count(\Controllers\Filesystem\File::findRecursive($snapshotPath, [], ['rpm'])); -} -if ($myrepo->getPackageType() == 'deb') { - $packagesCount = count(\Controllers\Filesystem\File::findRecursive($snapshotPath, [], ['deb'])); -} - -/** - * Get the size of the snapshot - */ -$repoSize = $repoSnapshotController->getSize($snapId, true) ?? 'Unknown'; - -unset($repoSnapshotController); diff --git a/www/controllers/Layout/Container/vars/header/menu.vars.inc.php b/www/controllers/Layout/Container/vars/header/menu.vars.inc.php index 3bc13241c..ab7111e8c 100644 --- a/www/controllers/Layout/Container/vars/header/menu.vars.inc.php +++ b/www/controllers/Layout/Container/vars/header/menu.vars.inc.php @@ -1,13 +1,9 @@ getRunning(); -/** - * Count running tasks - */ +// Count running tasks $totalRunningTasks = count($tasksRunning); diff --git a/www/controllers/Layout/Container/vars/host/packages.vars.inc.php b/www/controllers/Layout/Container/vars/host/packages.vars.inc.php index 2e6747577..b2f8c0ffb 100644 --- a/www/controllers/Layout/Container/vars/host/packages.vars.inc.php +++ b/www/controllers/Layout/Container/vars/host/packages.vars.inc.php @@ -13,7 +13,7 @@ $complianceThresholdDays = $settings['compliance_threshold_days']; // Get installed packages and its total -$packagesInventored = $hostPackageController->getInventory(); +$packagesInventoredTotal = $hostPackageController->countInventory(); $packagesInstalledCount = count($hostPackageController->getInstalled()); // Get available packages and its total diff --git a/www/controllers/Layout/Container/vars/hosts/overview.vars.inc.php b/www/controllers/Layout/Container/vars/hosts/overview.vars.inc.php index 6780808f8..ff1e6c37c 100644 --- a/www/controllers/Layout/Container/vars/hosts/overview.vars.inc.php +++ b/www/controllers/Layout/Container/vars/hosts/overview.vars.inc.php @@ -1,26 +1,18 @@ get()); -/** - * Getting a list of all hosts kernel - */ +// Getting a list of all hosts kernel $kernels = $hostListingController->getKernel(); array_multisort(array_column($kernels, 'Count'), SORT_DESC, $kernels); -/** - * Getting a list of all hosts profiles - */ +// Getting a list of all hosts profiles $profiles = $hostListingController->getProfile(); array_multisort(array_column($profiles, 'Count'), SORT_DESC, $profiles); -/** - * Getting a list of all hosts requiring a reboot - */ +// Getting a list of all hosts requiring a reboot $rebootRequiredList = $hostListingController->getRebootRequired(); $rebootRequiredCount = count($rebootRequiredList); diff --git a/www/controllers/Layout/Container/vars/stats/repo.vars.inc.php b/www/controllers/Layout/Container/vars/repo/kpi.vars.inc.php similarity index 78% rename from www/controllers/Layout/Container/vars/stats/repo.vars.inc.php rename to www/controllers/Layout/Container/vars/repo/kpi.vars.inc.php index d916a92a4..c4575451d 100644 --- a/www/controllers/Layout/Container/vars/stats/repo.vars.inc.php +++ b/www/controllers/Layout/Container/vars/repo/kpi.vars.inc.php @@ -3,16 +3,16 @@ $debRepoStatController = new \Controllers\Repo\Statistic\Deb(); $rpmRepoStatController = new \Controllers\Repo\Statistic\Rpm(); -if (empty(__ACTUAL_URI__[3])) { - throw new Exception('Error: missing repository ID.'); +if (empty(__ACTUAL_URI__[2])) { + throw new Exception('no repository ID specified.'); } -if (!is_numeric(__ACTUAL_URI__[3])) { - throw new Exception('Error: invalid repository ID specified.'); +if (!is_numeric(__ACTUAL_URI__[2])) { + throw new Exception('invalid repository ID specified.'); } // Get repository info -$repoController->getAllById(__ACTUAL_URI__[3]); +$repoController->getAllById(__ACTUAL_URI__[2]); // Get all snapshots of the repository $snapshots = $repoController->getSnapshots($repoController->getRepoId()); diff --git a/www/controllers/Layout/Container/vars/repos/kpi.vars.inc.php b/www/controllers/Layout/Container/vars/repos/kpi.vars.inc.php new file mode 100644 index 000000000..0e25c0ac7 --- /dev/null +++ b/www/controllers/Layout/Container/vars/repos/kpi.vars.inc.php @@ -0,0 +1,45 @@ +count(); + +// Get used and free disk space in bytes, and also in percent +$diskTotalSpace = disk_total_space(REPOS_DIR); +$diskFreeSpace = disk_free_space(REPOS_DIR); +$diskUsedSpace = $diskTotalSpace - $diskFreeSpace; +$diskFreeSpaceHuman = Convert::sizeToHuman($diskFreeSpace); +$diskUsedSpaceHuman = Convert::sizeToHuman($diskUsedSpace); +$diskUsedSpacePercent = round(($diskUsedSpace / $diskTotalSpace) * 100); +$diskFreeSpacePercent = round(($diskFreeSpace / $diskTotalSpace) * 100); + +// Get last and next scheduled tasks +$lastScheduledTask = $taskController->getLastScheduledTask(); +$nextScheduledTasks = $taskController->getNextScheduledTask(); +$lastScheduledTaskSummary = []; + +// If the last scheduled task dispatched sub-tasks, its real status is a summary of their statuses +if (!empty($lastScheduledTask['Id'])) { + $lastScheduledTaskSummary = (new \Controllers\Task\Listing())->getSubTasksSummary($lastScheduledTask['Id']); +} + +if (!empty($nextScheduledTasks)) { + $data = []; + + foreach ($nextScheduledTasks as $task) { + $data[] = $taskController->getDayTimeLeft($task['Id']); + } + + $nextScheduledTasks = $data; + + // Sort tasks by date and time + array_multisort(array_column($nextScheduledTasks, 'date'), SORT_ASC, array_column($nextScheduledTasks, 'time'), SORT_ASC, $nextScheduledTasks); + + // Remove duplicates (when multiple tasks are scheduled for the same date and time) + $nextScheduledTasks = array_unique($nextScheduledTasks, SORT_REGULAR); +} + +unset($repoController, $taskController, $diskTotalSpace, $diskFreeSpace, $diskUsedSpace, $data); diff --git a/www/controllers/Layout/Container/vars/repos/list.vars.inc.php b/www/controllers/Layout/Container/vars/repos/list.vars.inc.php index 6c618f2ce..bbaf95c89 100644 --- a/www/controllers/Layout/Container/vars/repos/list.vars.inc.php +++ b/www/controllers/Layout/Container/vars/repos/list.vars.inc.php @@ -1,43 +1,64 @@ get($_SESSION['id']); + +// Get protected environments +$protectedEnvs = $envController->getProtected(); // Retrieve all group names $groupsList = $groupController->listAll(true); -// Get total repos count -$totalRepos = $repoController->count(); +// Define if the user can view all repositories or only specific groups and repositories +$viewAll = IS_ADMIN || in_array('all', USER_PERMISSIONS['repositories']['view']); +$allowedGroups = $viewAll ? [] : USER_PERMISSIONS['repositories']['view']['groups']; +$allowedRepos = $viewAll ? [] : USER_PERMISSIONS['repositories']['view']['repos']; -// Get used and free disk space in bytes, and also in percent -$diskTotalSpace = disk_total_space(REPOS_DIR); -$diskFreeSpace = disk_free_space(REPOS_DIR); -$diskUsedSpace = $diskTotalSpace - $diskFreeSpace; -$diskFreeSpaceHuman = \Controllers\Utils\Convert::sizeToHuman($diskFreeSpace); -$diskUsedSpaceHuman = \Controllers\Utils\Convert::sizeToHuman($diskUsedSpace); -$diskUsedSpacePercent = round(($diskUsedSpace / $diskTotalSpace) * 100); -$diskFreeSpacePercent = round(($diskFreeSpace / $diskTotalSpace) * 100); +// Loop through groups to get repos for each group and add them to the $groups array +foreach ($groupsList as $group) { + // Group is allowed if the user can view all repositories or if the group is in the allowed groups list + $groupAllowed = $viewAll || in_array($group['Id'], $allowedGroups); + $groupRepos = []; -// Get last and next scheduled tasks -$lastScheduledTask = $taskController->getLastScheduledTask(); -$nextScheduledTasks = $taskController->getNextScheduledTask(); + foreach ($repoListingController->listByGroup($group['Name']) as $repo) { + // A repository granted individually makes its group visible, without revealing the other repositories of that group + if (!$groupAllowed and !in_array($repo['repoId'], $allowedRepos)) { + continue; + } -if (!empty($nextScheduledTasks)) { - $data = []; + // Add the whole repo to the repos array + $repos[] = $repo; - foreach ($nextScheduledTasks as $task) { - $data[] = $taskController->getDayTimeLeft($task['Id']); + // Add the repoId to the group if not already in the group repos (to avoid duplicate repos in the same group) + if (!in_array($repo['repoId'], $groupRepos)) { + $groupRepos[] = $repo['repoId']; + } } - $nextScheduledTasks = $data; + // If the group is allowed or if it has at least one repository that the user can view, add it to the visible groups array + if ($groupAllowed || !empty($groupRepos)) { + // Do not display the 'Default' group if it is empty, unless it is the only group + if ($group['Name'] == 'Default' and count($groupRepos) == 0 and count($groupsList) > 1) { + continue; + } - // Sort tasks by date and time - array_multisort(array_column($nextScheduledTasks, 'date'), SORT_ASC, array_column($nextScheduledTasks, 'time'), SORT_ASC, $nextScheduledTasks); - - // Remove duplicates (when multiple tasks are scheduled for the same date and time) - $nextScheduledTasks = array_unique($nextScheduledTasks, SORT_REGULAR); + // Add the group to the visible groups array with its name, repositories, and count of repositories + $groups[$group['Id']] = [ + 'name' => $group['Name'], + 'repos' => $groupRepos, + 'count' => count($groupRepos) + ]; + } } -unset($groupController, $repoController, $taskController, $diskTotalSpace, $diskFreeSpace, $diskUsedSpace, $data); +unset($userPreferenceController, $groupController, $repoController, $envController); diff --git a/www/controllers/Layout/Container/vars/snapshot/kpi.vars.inc.php b/www/controllers/Layout/Container/vars/snapshot/kpi.vars.inc.php new file mode 100644 index 000000000..947ea750c --- /dev/null +++ b/www/controllers/Layout/Container/vars/snapshot/kpi.vars.inc.php @@ -0,0 +1,59 @@ +exists($snapId)) { + throw new Exception('repository snapshot #' . $snapId . ' does not exist.'); +} + +// Retrieve repository infos from DB +$repoController->getAllById(null, $snapId, null); + +// Define repository name +if ($repoController->getPackageType() == 'rpm') { + $repository = $repoController->getName() . ' ❯ ' . $repoController->getReleasever(); +} +if ($repoController->getPackageType() == 'deb') { + $repository = $repoController->getName() . ' ❯ ' . $repoController->getDist() . ' ❯ ' . $repoController->getSection(); +} + +// Define snapshot path +if ($repoController->getPackageType() == 'rpm') { + $snapshotPath = REPOS_DIR . '/rpm/' . $repoController->getName() . '/' . $repoController->getReleasever() . '/' . $repoController->getDate(); +} +if ($repoController->getPackageType() == 'deb') { + $snapshotPath = REPOS_DIR . '/deb/' . $repoController->getName() . '/' . $repoController->getDist() . '/' . $repoController->getSection() . '/' . $repoController->getDate(); +} + +// Retrieve repo size and packages count +if ($repoController->getPackageType() == 'rpm') { + $repoSize = Directory::getSize($snapshotPath); + $packagesCount = count(File::findRecursive($snapshotPath, [], ['rpm'])); +} +if ($repoController->getPackageType() == 'deb') { + $repoSize = Directory::getSize($snapshotPath); + $packagesCount = count(File::findRecursive($snapshotPath, [], ['deb'])); +} + +// Convert repo size in the most suitable byte format +$repoSize = Convert::sizeToHuman($repoSize); + +// Retrieve snapshot rebuild status +$rebuild = $repoController->getRebuild(); + +unset($repoSnapshotController); diff --git a/www/controllers/Layout/Container/vars/snapshot/list.vars.inc.php b/www/controllers/Layout/Container/vars/snapshot/list.vars.inc.php new file mode 100644 index 000000000..bc3366d1d --- /dev/null +++ b/www/controllers/Layout/Container/vars/snapshot/list.vars.inc.php @@ -0,0 +1,60 @@ +exists($snapId)) { + throw new Exception('repository snapshot #' . $snapId . ' does not exist.'); +} + +// Check if the snapshot has a protected environment +$protectedEnv = $repoSnapshotController->hasProtectedEnv($snapId); + +// Check if a task is running for this snapshot +$taskRunning = $repoSnapshotController->taskRunning($snapId); + +// Retrieve repo infos from DB +$repoController->getAllById('', $snapId, ''); + +// Define snapshot path +if ($repoController->getPackageType() == 'rpm') { + $snapshotPath = REPOS_DIR . '/rpm/' . $repoController->getName() . '/' . $repoController->getReleasever() . '/' . $repoController->getDate(); +} + +if ($repoController->getPackageType() == 'deb') { + $snapshotPath = REPOS_DIR . '/deb/' . $repoController->getName() . '/' . $repoController->getDist() . '/' . $repoController->getSection() . '/' . $repoController->getDate(); +} + +// If the path does not exist on the server then we quit +if (!is_dir($snapshotPath)) { + throw new Exception('snapshot directory ' . $snapshotPath . ' does not exist.'); +} + +// Retrieve repo size and packages count +if ($repoController->getPackageType() == 'rpm') { + $repoSize = Directory::getSize($snapshotPath); + $packagesCount = count(File::findRecursive($snapshotPath, [], ['rpm'])); +} +if ($repoController->getPackageType() == 'deb') { + $repoSize = Directory::getSize($snapshotPath); + $packagesCount = count(File::findRecursive($snapshotPath, [], ['deb'])); +} + +// Convert repo size in the most suitable byte format +$repoSize = Convert::sizeToHuman($repoSize); + +unset($repoController, $repoSnapshotController); diff --git a/www/controllers/Layout/Container/vars/tasks/log.vars.inc.php b/www/controllers/Layout/Container/vars/tasks/log.vars.inc.php index 0ae7ab669..d2b0ddcf5 100644 --- a/www/controllers/Layout/Container/vars/tasks/log.vars.inc.php +++ b/www/controllers/Layout/Container/vars/tasks/log.vars.inc.php @@ -1,12 +1,14 @@ exists($taskId)) { - throw new Exception('Task #' . $taskId . ' not found.'); + throw new Exception('Task #' . $taskId . ' not found'); } // Get task info @@ -51,106 +53,115 @@ $repoId = null; $snapId = null; - $envId = null; // Get log content $content = $taskLogController->getContent(); // If log content is empty if (empty($content)) { - throw new Exception('No log found for this task.'); - } - - if (empty($rawParams['action'])) { - throw new Exception('No action found in the task.'); - } - - /** - * Get repository info - */ - - // If the action is create, the repository info is in the raw params - if ($rawParams['action'] == 'create') { - $repoController->setType($rawParams['repo-type']); + /** + * The task itself may have no log if it only dispatched sub-tasks (e.g. a scheduled + * task targeting a group of repositories). In that case, display the list of + * sub-tasks it launched instead of failing. + */ + $childTasks = $taskListingController->getByParentId($taskId); + + if (empty($childTasks)) { + // A scheduled task has no log until it has been executed at least once + if (in_array($taskInfo['Status'], ['scheduled', 'disabled'])) { + throw new Exception('This task has not been executed yet'); + } - if (!empty($rawParams['source'])) { - $repoController->setSource($rawParams['source']); - } - if (!empty($rawParams['alias'])) { - $repoController->setName($rawParams['alias']); - } else { - $repoController->setName($rawParams['source']); - } - if (!empty($rawParams['dist'])) { - $repoController->setDist($rawParams['dist']); - } - if (!empty($rawParams['section'])) { - $repoController->setSection($rawParams['section']); - } - if (!empty($rawParams['releasever'])) { - $repoController->setReleasever($rawParams['releasever']); - } - if (!empty($rawParams['arch'])) { - $repoController->setArch($rawParams['arch']); - } - if (!empty($rawParams['package-type'])) { - $repoController->setPackageType($rawParams['package-type']); - } - if (!empty($rawParams['gpg-check'])) { - $repoController->setGpgCheck($rawParams['gpg-check']); - } - if (!empty($rawParams['gpg-sign'])) { - $repoController->setGpgSign($rawParams['gpg-sign']); - } - if (!empty($rawParams['description'])) { - $repoController->setDescription($rawParams['description']); - } - if (!empty($rawParams['group'])) { - $repoController->setGroup($rawParams['group']); + throw new Exception('No log found for this task'); } - // Otherwise, we get the repository info from the database + // Used to display the real status of the parent task, based on its sub-tasks status + $subTasksSummary = $taskListingController->getSubTasksSummary($taskId); + + ob_start(); + TableRender::render('tasks/list-children'); + $output .= ob_get_clean(); } else { - if (!empty($rawParams['repo-id'])) { - $repoId = $rawParams['repo-id']; + if (empty($rawParams['action'])) { + throw new Exception('No action found in the task'); } - if (!empty($rawParams['snap-id'])) { - $snapId = $rawParams['snap-id']; - } - // If the action is removeEnv, the environment has been removed, so it - if ($rawParams['action'] != 'removeEnv') { - if (!empty($rawParams['env-id'])) { - $envId = $rawParams['env-id']; + + // If the action is create, the repository info is in the raw params + if ($rawParams['action'] == 'create') { + $repoController->setType($rawParams['repo-type']); + + if (!empty($rawParams['source'])) { + $repoController->setSource($rawParams['source']); + } + if (!empty($rawParams['alias'])) { + $repoController->setName($rawParams['alias']); + } else { + $repoController->setName($rawParams['source']); + } + if (!empty($rawParams['dist'])) { + $repoController->setDist($rawParams['dist']); + } + if (!empty($rawParams['section'])) { + $repoController->setSection($rawParams['section']); + } + if (!empty($rawParams['releasever'])) { + $repoController->setReleasever($rawParams['releasever']); + } + if (!empty($rawParams['arch'])) { + $repoController->setArch($rawParams['arch']); + } + if (!empty($rawParams['package-type'])) { + $repoController->setPackageType($rawParams['package-type']); + } + if (!empty($rawParams['gpg-check'])) { + $repoController->setGpgCheck($rawParams['gpg-check']); + } + if (!empty($rawParams['gpg-sign'])) { + $repoController->setGpgSign($rawParams['gpg-sign']); + } + if (!empty($rawParams['description'])) { + $repoController->setDescription($rawParams['description']); + } + if (!empty($rawParams['tags'])) { + $repoController->setTags($rawParams['tags']); + } + if (!empty($rawParams['group'])) { + $repoController->setGroup($rawParams['group']); } - } - $repoController->getAllById($repoId, $snapId, $envId); - } + // Otherwise, we get the repository info from the database + } else { + if (!empty($rawParams['repo-id'])) { + $repoId = $rawParams['repo-id']; + } + if (!empty($rawParams['snap-id'])) { + $snapId = $rawParams['snap-id']; + } + + $repoController->getAllById($repoId, $snapId); + } - /** - * Include table template for the task - */ - ob_start(); - include_once(ROOT . '/views/templates/tasks/' . $rawParams['action'] . '.inc.php'); - $output .= ob_get_clean(); - - /** - * The container which will contain all the steps - */ - $output .= '
' . $e->getMessage() . '
'; + $taskFound = false; + $taskErrorMessage = $e->getMessage(); + $output = '' . $taskErrorMessage . '
'; } diff --git a/www/controllers/Layout/Container/vars/tasks/tasks.vars.inc.php b/www/controllers/Layout/Container/vars/tasks/tasks.vars.inc.php new file mode 100644 index 000000000..9c6f3507f --- /dev/null +++ b/www/controllers/Layout/Container/vars/tasks/tasks.vars.inc.php @@ -0,0 +1,13 @@ +get()); + +// Get running tasks +$runningCount = count($taskListingController->getRunning()); + +// Get scheduled tasks +$scheduledCount = count($taskListingController->getScheduled()); + +unset($taskListingController); diff --git a/www/controllers/Layout/ContainerReload.php b/www/controllers/Layout/ContainerReload.php index 20069a77e..6dfe1385e 100644 --- a/www/controllers/Layout/ContainerReload.php +++ b/www/controllers/Layout/ContainerReload.php @@ -42,6 +42,14 @@ public function exists(string $name) return $this->model->exists($name); } + /** + * Delete the specified containers entries + */ + public function delete(array $names): void + { + $this->model->delete($names); + } + /** * Clean all containers entries */ diff --git a/www/controllers/Layout/Panel/vars/hosts/settings.vars.inc.php b/www/controllers/Layout/Panel/vars/hosts/settings.vars.inc.php index d33366754..e910c750d 100644 --- a/www/controllers/Layout/Panel/vars/hosts/settings.vars.inc.php +++ b/www/controllers/Layout/Panel/vars/hosts/settings.vars.inc.php @@ -17,6 +17,9 @@ // Get compliance threshold days $complianceThresholdDays = $settings['compliance_threshold_days']; +// Get compliance security update +$complianceSecurityUpdate = $settings['compliance_security_update']; + // Get compliance reboot required $complianceRebootRequired = $settings['compliance_reboot_required']; diff --git a/www/controllers/Layout/Panel/vars/repos/edit.vars.inc.php b/www/controllers/Layout/Panel/vars/repos/edit.vars.inc.php index a429abbd0..b09ef5662 100644 --- a/www/controllers/Layout/Panel/vars/repos/edit.vars.inc.php +++ b/www/controllers/Layout/Panel/vars/repos/edit.vars.inc.php @@ -14,7 +14,7 @@ * Check that action and repos params have been sent */ if (empty($item['repos'])) { - throw new Exception('Task repositories required'); + throw new Exception('Repositories required'); } $slidePanelTitle = 'EDIT REPOSITORY & SNAPSHOT PROPERTIES'; diff --git a/www/controllers/Layout/Panel/vars/repos/install.vars.inc.php b/www/controllers/Layout/Panel/vars/repos/install.vars.inc.php index 4ade1b5bc..235325404 100644 --- a/www/controllers/Layout/Panel/vars/repos/install.vars.inc.php +++ b/www/controllers/Layout/Panel/vars/repos/install.vars.inc.php @@ -45,8 +45,8 @@ $packageType = $repoController->getPackageType(); $packagesTypes[] = $packageType; ?> -= $repoController->getDateFormatted() ?>
+= $repoController->getDateFormatted() ?>
= htmlspecialchars($file, ENT_QUOTES) ?>
= $file ?>
= $dir ?>