From 7801b11f5132d7aa6f85807ac886726fd775ae85 Mon Sep 17 00:00:00 2001 From: sgfost Date: Wed, 14 Feb 2024 21:30:01 -0700 Subject: [PATCH 1/4] build: replace config.ini with single .env, fix templating attempts to consolidate all app configuration into environment variables .env is created from a template and generated secrets are placed in, all variables defined in .env are then fed into the server container via env_file in docker-compose.yml. The django settings module then reads from os.getenv() rather than configparser resolves comses/planning#145 --- Makefile | 68 +++++++++------------ base.yml | 4 +- deploy/conf/.env.template | 55 +++++++++++++++++ deploy/conf/config.ini.template | 51 ---------------- deploy/conf/docker.env | 7 --- deploy/conf/pgpass.template | 1 - deploy/scripts/envreplace | 26 ++++++++ django/core/settings/defaults.py | 95 +++++++++++------------------- django/core/settings/production.py | 18 ++++-- django/core/settings/staging.py | 14 ++--- django/core/settings/test.py | 10 ++-- 11 files changed, 165 insertions(+), 184 deletions(-) create mode 100644 deploy/conf/.env.template delete mode 100644 deploy/conf/config.ini.template delete mode 100644 deploy/conf/docker.env delete mode 100644 deploy/conf/pgpass.template create mode 100755 deploy/scripts/envreplace diff --git a/Makefile b/Makefile index 7bb8d1af7..88f7905f5 100644 --- a/Makefile +++ b/Makefile @@ -1,22 +1,19 @@ DEPLOY_ENVIRONMENT := dev -DB_USER=comsesnet + DOCKER_SHARED_DIR=docker/shared DOCKER_DB_DATA_DIR=docker/pgdata + BUILD_DIR=build SECRETS_DIR=${BUILD_DIR}/secrets DB_PASSWORD_PATH=${SECRETS_DIR}/db_password PGPASS_PATH=${SECRETS_DIR}/.pgpass SECRET_KEY_PATH=${SECRETS_DIR}/secret_key -SENTRY_DSN_PATH=${SECRETS_DIR}/sentry_dsn +GENERATED_SECRETS=$(DB_PASSWORD_PATH) $(PGPASS_PATH) $(SECRET_KEY_PATH) + +ENVREPLACE := deploy/scripts/envreplace DEPLOY_CONF_DIR=deploy/conf -PGPASS_TEMPLATE=${DEPLOY_CONF_DIR}/pgpass.template -CONFIG_INI_TEMPLATE=${DEPLOY_CONF_DIR}/config.ini.template -DOCKER_ENV_PATH=${DEPLOY_CONF_DIR}/docker.env -CONFIG_INI_PATH=${SECRETS_DIR}/config.ini -MAIL_API_KEY_PATH=${SECRETS_DIR}/mail_api_key -SECRETS=$(MAIL_API_KEY_PATH) $(DB_PASSWORD_PATH) $(CONFIG_INI_PATH) $(PGPASS_PATH) $(SENTRY_DSN_PATH) $(SECRET_KEY_PATH) .env -BUILD_ID=$(shell git describe --tags --abbrev=1) -BUILD_ID_PATH=${SECRETS_DIR}/.build-id.txt +ENV_TEMPLATE=${DEPLOY_CONF_DIR}/.env.template + # assumes a .tar.xz file BORG_REPO_URL := https://example.com/repo.tar.xz BORG_REPO_PATH=${BUILD_DIR}/sparse-repo.tar.xz @@ -31,16 +28,12 @@ include .env .EXPORT_ALL_VARIABLES: .PHONY: build -build: docker-compose.yml secrets $(DOCKER_SHARED_DIR) $(BUILD_ID_PATH) +build: docker-compose.yml secrets $(DOCKER_SHARED_DIR) docker compose build --pull $(BORG_REPO_PATH): wget ${BORG_REPO_URL} -P ${BUILD_DIR} -$(BUILD_ID_PATH): - BUILD_ID=${BUILD_ID} \ - echo "$${BUILD_ID}" > ${BUILD_ID_PATH} - config.mk: DEPLOY_ENVIRONMENT=${DEPLOY_ENVIRONMENT} envsubst < ${DEPLOY_CONF_DIR}/config.mk.template > config.mk @@ -54,6 +47,10 @@ $(DOCKER_SHARED_DIR): ${SECRETS_DIR}: mkdir -p ${SECRETS_DIR} +$(SECRET_KEY_PATH): | ${SECRETS_DIR} + SECRET_KEY=$$(openssl rand -base64 48); \ + echo "$${SECRET_KEY}" > $(SECRET_KEY_PATH) + $(DB_PASSWORD_PATH): | ${SECRETS_DIR} DB_PASSWORD=$$(openssl rand -base64 48); \ TODAY=$$(date +%Y-%m-%d-%H:%M:%S); \ @@ -65,34 +62,23 @@ $(DB_PASSWORD_PATH): | ${SECRETS_DIR} @echo "db password at $(DB_PASSWORD_PATH) was reset, may need to manually update existing db password" $(PGPASS_PATH): $(DB_PASSWORD_PATH) $(PGPASS_TEMPLATE) | ${SECRETS_DIR} - DB_PASSWORD=$$(cat $(DB_PASSWORD_PATH)); \ - sed -e "s|DB_PASSWORD|$$DB_PASSWORD|" -e "s|DB_HOST|${DB_HOST}|" \ - -e "s|DB_USER|${DB_USER}|" $(PGPASS_TEMPLATE) > $(PGPASS_PATH) + echo "${DB_HOST}:5432:*:${DB_USER}:$$(cat $(DB_PASSWORD_PATH))" > $(PGPASS_PATH) chmod 0600 $(PGPASS_PATH) -$(MAIL_API_KEY_PATH): | ${SECRETS_DIR} - touch "$(MAIL_API_KEY_PATH)" - -$(SENTRY_DSN_PATH): | ${SECRETS_DIR} - touch "$(SENTRY_DSN_PATH)" - -.env: $(DOCKER_ENV_PATH) - cp ${DOCKER_ENV_PATH} .env - -$(CONFIG_INI_PATH): .env $(DB_PASSWORD_PATH) $(CONFIG_INI_TEMPLATE) $(SECRET_KEY_PATH) $(BUILD_ID_PATH) - DB_HOST=${DB_HOST} DB_NAME=${DB_NAME} DB_PASSWORD=$$(cat ${DB_PASSWORD_PATH}) \ - DB_USER=${DB_USER} DB_PORT=${DB_PORT} DJANGO_SECRET_KEY=$$(cat ${SECRET_KEY_PATH}) \ - TEST_USERNAME=___test_user___ TEST_BASIC_AUTH_PASSWORD=$$(openssl rand -base64 42) \ - SENTRY_DSN=$(shell cat $(SENTRY_DSN_PATH)) \ - TEST_USER_ID=1111111 BUILD_ID=${BUILD_ID} \ - envsubst < ${CONFIG_INI_TEMPLATE} > ${CONFIG_INI_PATH} +.PHONY: build-id +build-id: .env + $(ENVREPLACE) BUILD_ID $$(git describe --tags --abbrev=1) .env -$(SECRET_KEY_PATH): | ${SECRETS_DIR} - SECRET_KEY=$$(openssl rand -base64 48); \ - echo "$${SECRET_KEY}" > $(SECRET_KEY_PATH) +.env: $(DB_PASSWORD_PATH) $(SECRET_KEY_PATH) + if [ ! -f .env ]; then \ + cp $(ENV_TEMPLATE) .env; \ + fi; \ + $(ENVREPLACE) DB_PASSWORD $$(cat $(DB_PASSWORD_PATH)) .env; \ + $(ENVREPLACE) SECRET_KEY $$(cat $(SECRET_KEY_PATH)) .env; \ + $(ENVREPLACE) TEST_BASIC_AUTH_PASSWORD $$(openssl rand -base64 42) .env .PHONY: docker-compose.yml -docker-compose.yml: base.yml dev.yml staging.yml prod.yml config.mk $(PGPASS_PATH) .env +docker-compose.yml: base.yml dev.yml staging.yml prod.yml config.mk $(PGPASS_PATH) build-id case "$(DEPLOY_ENVIRONMENT)" in \ dev|staging|e2e) docker compose -f base.yml -f $(DEPLOY_ENVIRONMENT).yml config > docker-compose.yml;; \ prod) docker compose -f base.yml -f staging.yml -f $(DEPLOY_ENVIRONMENT).yml config > docker-compose.yml;; \ @@ -100,11 +86,11 @@ docker-compose.yml: base.yml dev.yml staging.yml prod.yml config.mk $(PGPASS_PAT esac .PHONY: set-db-password -set-db-password: $(DB_PASSWORD_PATH) $(CONFIG_INI_PATH) +set-db-password: $(DB_PASSWORD_PATH) .env docker compose run --rm server psql -h db -U comsesnet comsesnet -c "ALTER USER ${DB_USER} with password '$(shell cat ${DB_PASSWORD_PATH})';" .PHONY: secrets -secrets: $(SECRETS_DIR) $(SECRETS) +secrets: $(SECRETS_DIR) $(GENERATED_SECRETS) .PHONY: deploy deploy: build @@ -137,7 +123,7 @@ restore: build $(BORG_REPO_PATH) | $(REPO_BACKUPS_PATH) .PHONY: clean clean: @echo "Backing up generated files to /tmp directory" - mv .env config.mk docker-compose.yml $(CONFIG_INI_PATH) $(shell mktemp -d) + mv .env config.mk docker-compose.yml $(shell mktemp -d) .PHONY: test test: build diff --git a/base.yml b/base.yml index 3149adfc3..a458ec441 100644 --- a/base.yml +++ b/base.yml @@ -69,8 +69,8 @@ services: condition: service_started vite: condition: service_started - environment: - CLEAN_DATABASE: "false" + env_file: + - .env volumes: esdata: driver: local diff --git a/deploy/conf/.env.template b/deploy/conf/.env.template new file mode 100644 index 000000000..1d114d30a --- /dev/null +++ b/deploy/conf/.env.template @@ -0,0 +1,55 @@ +# app +BUILD_ID= +EXPIRED_JOB_DAYS_THRESHOLD=180 +EXPIRED_EVENT_DAYS_THRESHOLD=2 + +# database +DB_NAME=comsesnet +DB_USER=comsesnet +DB_HOST=db +DB_PORT=5432 +DB_PASSWORD= +CLEAN_DATABASE="false" # allowed values: "true" or "false" + +# elastic search +ES_VERSION=7.17.10 + +# captcha +RECAPTCHA_PUBLIC_KEY= +RECAPTCHA_PRIVATE_KEY= +HCAPTCHA_SITEKEY= +HCAPTCHA_SECRET= + +# discourse +DISCOURSE_BASE_URL= +DISCOURSE_API_USERNAME= + +# email +EMAIL_SUBJECT_PREFIX=[CoMSES Net] +MAILGUN_API_KEY= +MAILGUN_SENDER_DOMAIN= + +# logging +LOG_DIRECTORY=/shared/logs +SENTRY_DSN= + +# secrets +DISCOURSE_SSO_SECRET= +DISCOURSE_API_KEY= + +SECRET_KEY= + +ORCID_CLIENT_ID= +ORCID_CLIENT_SECRET= +GITHUB_CLIENT_ID= +GITHUB_CLIENT_SECRET= + +# storage +DATA_ROOT=/shared +LIBRARY_ROOT=/shared/library +REPOSITORY_ROOT=/shared/repository + +# test +TEST_USER_ID=10000000 +TEST_USERNAME=__test_user__ +TEST_BASIC_AUTH_PASSWORD= diff --git a/deploy/conf/config.ini.template b/deploy/conf/config.ini.template deleted file mode 100644 index a7f04db6b..000000000 --- a/deploy/conf/config.ini.template +++ /dev/null @@ -1,51 +0,0 @@ -[default] -DEBUG=${DJANGO_DEBUG} -POST_DATE_DAYS_AGO_THRESHOLD=90 -BUILD_ID=${BUILD_ID} - -[database] -DB_NAME=${DB_NAME} -DB_USER=${DB_USER} -DB_HOST=${DB_HOST} -DB_PORT=5432 -DB_PASSWORD=${DB_PASSWORD} - -[captcha] -RECAPTCHA_PUBLIC_KEY= -RECAPTCHA_PRIVATE_KEY= -HCAPTCHA_SITEKEY=${HCAPTCHA_SITEKEY} -HCAPTCHA_SECRET=${HCAPTCHA_SECRET} - -[discourse] -DISCOURSE_BASE_URL=${DISCOURSE_BASE_URL} -DISCOURSE_API_USERNAME=${DISCOURSE_API_USERNAME} - -[email] -EMAIL_SUBJECT_PREFIX=[comses.net] -MAILGUN_API_KEY=${MAILGUN_API_KEY} -MAILGUN_SENDER_DOMAIN=${MAILGUN_SENDER_DOMAIN} - -[logging] -LOG_DIRECTORY=/shared/logs -SENTRY_DSN=${SENTRY_DSN} - -[secrets] -DISCOURSE_SSO_SECRET=${DISCOURSE_SSO_SECRET} -DISCOURSE_API_KEY=${DISCOURSE_API_KEY} - -SECRET_KEY=${DJANGO_SECRET_KEY} - -ORCID_CLIENT_ID=${ORCID_CLIENT_ID} -ORCID_CLIENT_SECRET=${ORCID_CLIENT_SECRET} -GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID} -GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET} - -[storage] -DATA_ROOT=/shared -LIBRARY_ROOT=/shared/library -REPOSITORY_ROOT=/shared/repository - -[test] -TEST_BASIC_AUTH_PASSWORD=${TEST_BASIC_AUTH_PASSWORD} -TEST_USER_ID=${TEST_USER_ID} -TEST_USERNAME=${TEST_USERNAME} diff --git a/deploy/conf/docker.env b/deploy/conf/docker.env deleted file mode 100644 index 0dd45448d..000000000 --- a/deploy/conf/docker.env +++ /dev/null @@ -1,7 +0,0 @@ -DB_HOST=db -DB_USER=comsesnet -DB_NAME=comsesnet -DB_PORT=5432 -ES_VERSION=7.17.10 -CLEAN_DATABASE="false" # allowed values: "true" or "false" -RELEASE_VERSION=2023.07 diff --git a/deploy/conf/pgpass.template b/deploy/conf/pgpass.template deleted file mode 100644 index 2a01e2773..000000000 --- a/deploy/conf/pgpass.template +++ /dev/null @@ -1 +0,0 @@ -DB_HOST:5432:*:DB_USER:DB_PASSWORD diff --git a/deploy/scripts/envreplace b/deploy/scripts/envreplace new file mode 100755 index 000000000..c77d3034f --- /dev/null +++ b/deploy/scripts/envreplace @@ -0,0 +1,26 @@ +#!/usr/bin/bash + +# envreplace: replace environment variables in a file +# usage: envreplace [file (default: .env)] + +VAR_NAME=$1 +NEW_VALUE=$2 +ENV_FILE=${3:-".env"} + +if [ -z "$VAR_NAME" ] || [ -z "$NEW_VALUE" ]; then + echo "usage: envreplace [file (default: .env)]" + exit 1 +fi + +if sed --version 2>&1 | grep -q "GNU"; then + SED_CMD="sed -i" # GNU sed +else + SED_CMD="sed -i ''" # BSD sed +fi + +if grep -q "^$VAR_NAME=" "$ENV_FILE"; then + $SED_CMD "s|^$VAR_NAME=.*|$VAR_NAME=$NEW_VALUE|" "$ENV_FILE" +else + echo "$VAR_NAME not found in $ENV_FILE" + # echo "$VAR_NAME=$NEW_VALUE" >> $ENV_FILE +fi diff --git a/django/core/settings/defaults.py b/django/core/settings/defaults.py index 1c374939d..6f8627a45 100644 --- a/django/core/settings/defaults.py +++ b/django/core/settings/defaults.py @@ -9,7 +9,6 @@ """ -import configparser import os from enum import Enum @@ -231,50 +230,34 @@ def is_test(self): # from now to 90 days ago ADMIN_DASHBOARD_DAYS = 90 -config = configparser.ConfigParser() -# FIXME: switch to docker secrets instead -config.read("/run/secrets/config.ini") - -RELEASE_VERSION = config.get("default", "BUILD_ID", fallback="v2023.01") +RELEASE_VERSION = os.getenv("RELEASE_VERSION", "v2024.01") # default from email for various automated emails sent by Django -DEFAULT_FROM_EMAIL = config.get( - "email", "DEFAULT_FROM_EMAIL", fallback="info@comses.net" -) +DEFAULT_FROM_EMAIL = os.getenv("DEFAULT_FROM_EMAIL", "info@comses.net") # email address used for errors emails sent to ADMINS and MANAGERS -SERVER_EMAIL = config.get("email", "SERVER_EMAIL", fallback="editors@comses.net") -EDITOR_EMAIL = config.get("email", "EDITOR_EMAIL", fallback="editors@comses.net") -REVIEW_EDITOR_EMAIL = config.get( - "email", "REVIEW_EDITOR_EMAIL", fallback="reviews@comses.net" -) +SERVER_EMAIL = os.getenv("SERVER_EMAIL", "editors@comses.net") +EDITOR_EMAIL = os.getenv("EDITOR_EMAIL", "editors@comses.net") +REVIEW_EDITOR_EMAIL = os.getenv("REVIEW_EDITOR_EMAIL", "reviews@comses.net") # default email subject prefix -EMAIL_SUBJECT_PREFIX = config.get( - "email", "EMAIL_SUBJECT_PREFIX", fallback="[CoMSES Net]" -) +EMAIL_SUBJECT_PREFIX = os.getenv("EMAIL_SUBJECT_PREFIX", "[CoMSES Net]") # number of days before a peer review invitation expires PEER_REVIEW_INVITATION_EXPIRATION = 21 -# RECAPTCHA_PUBLIC_KEY = config.get('captcha', 'RECAPTCHA_PUBLIC_KEY', fallback='') -# RECAPTCHA_PRIVATE_KEY = config.get('captcha', 'RECAPTCHA_PRIVATE_KEY', fallback='') +# RECAPTCHA_PUBLIC_KEY = os.getenv('RECAPTCHA_PUBLIC_KEY', '') +# RECAPTCHA_PRIVATE_KEY = os.getenv('RECAPTCHA_PRIVATE_KEY', '') # NOCAPTCHA = True # sentry DSN -SENTRY_DSN = config.get( - "logging", "SENTRY_DSN", fallback="https://sentry.example.com/2" -) +SENTRY_DSN = os.getenv("SENTRY_DSN", "https://sentry.example.com/2") -SECRET_KEY = config.get("secrets", "SECRET_KEY") +SECRET_KEY = os.getenv("SECRET_KEY") # regular settings -EXPIRED_JOB_DAYS_THRESHOLD = config.getint( - "default", "EXPIRED_JOB_DAYS_THRESHOLD", fallback=180 -) +EXPIRED_JOB_DAYS_THRESHOLD = int(os.getenv("EXPIRED_JOB_DAYS_THRESHOLD", 180)) -EXPIRED_EVENT_DAYS_THRESHOLD = config.getint( - "default", "EXPIRED_EVENT_DAYS_THRESHOLD", fallback=2 -) +EXPIRED_EVENT_DAYS_THRESHOLD = int(os.getenv("EXPIRED_EVENT_DAYS_THRESHOLD", 2)) # Database configuration # https://docs.djangoproject.com/en/4.2/ref/settings/#databases @@ -284,25 +267,19 @@ def is_test(self): DATABASES = { "default": { "ENGINE": "django.db.backends.postgresql", - "NAME": config.get("database", "DB_NAME"), - "USER": config.get("database", "DB_USER"), - "PASSWORD": config.get("database", "DB_PASSWORD"), - "HOST": config.get("database", "DB_HOST"), - "PORT": config.get("database", "DB_PORT"), + "NAME": os.getenv("DB_NAME"), + "USER": os.getenv("DB_USER"), + "PASSWORD": os.getenv("DB_PASSWORD"), + "HOST": os.getenv("DB_HOST"), + "PORT": os.getenv("DB_PORT"), } } SHARE_DIR = "/shared" -LOG_DIRECTORY = config.get( - "logging", "LOG_DIRECTORY", fallback=os.path.join(BASE_DIR, "logs") -) -LIBRARY_ROOT = config.get( - "storage", "LIBRARY_ROOT", fallback=os.path.join(BASE_DIR, "library") -) +LOG_DIRECTORY = os.getenv("LOG_DIRECTORY", os.path.join(BASE_DIR, "logs")) +LIBRARY_ROOT = os.getenv("LIBRARY_ROOT", os.path.join(BASE_DIR, "library")) PREVIOUS_SHARE_ROOT = os.path.join(SHARE_DIR, ".latest") -REPOSITORY_ROOT = config.get( - "storage", "REPOSITORY_ROOT", fallback=os.path.join(BASE_DIR, "repository") -) +REPOSITORY_ROOT = os.getenv("REPOSITORY_ROOT", os.path.join(BASE_DIR, "repository")) BORG_ROOT = "/shared/backups/repo" BACKUP_ROOT = "/shared/backups" EXTRACT_ROOT = "/shared/extract" @@ -417,7 +394,7 @@ def is_test(self): ] # django-vite settings -DJANGO_VITE_ASSETS_PATH = config.get("storage", "VITE_ROOT", fallback="/shared/vite") +DJANGO_VITE_ASSETS_PATH = os.getenv("VITE_ROOT", "/shared/vite") DJANGO_VITE_STATIC_URL_PREFIX = "bundles" DJANGO_VITE_DEV_SERVER_PORT = 5000 DJANG_VITE_MANIFEST_PATH = os.path.join( @@ -496,17 +473,15 @@ def is_test(self): # allow django-allauth to change emails ACCOUNT_CHANGE_EMAIL = True -ORCID_CLIENT_ID = config.get("secrets", "ORCID_CLIENT_ID", fallback="") -ORCID_CLIENT_SECRET = config.get("secrets", "ORCID_CLIENT_SECRET", fallback="") +ORCID_CLIENT_ID = os.getenv("ORCID_CLIENT_ID", "") +ORCID_CLIENT_SECRET = os.getenv("ORCID_CLIENT_SECRET", "") -GITHUB_CLIENT_ID = config.get("secrets", "GITHUB_CLIENT_ID", fallback="") -GITHUB_CLIENT_SECRET = config.get("secrets", "GITHUB_CLIENT_SECRET", fallback="") +GITHUB_CLIENT_ID = os.getenv("GITHUB_CLIENT_ID", "") +GITHUB_CLIENT_SECRET = os.getenv("GITHUB_CLIENT_SECRET", "") -TEST_BASIC_AUTH_PASSWORD = config.get( - "test", "TEST_BASIC_AUTH_PASSWORD", fallback="test password" -) -TEST_USER_ID = config.get("test", "TEST_USER_ID", fallback=1000000) -TEST_USERNAME = config.get("test", "TEST_USERNAME", fallback="__test_user__") +TEST_BASIC_AUTH_PASSWORD = os.getenv("TEST_BASIC_AUTH_PASSWORD", "test password") +TEST_USER_ID = os.getenv("TEST_USER_ID", 1000000) +TEST_USERNAME = os.getenv("TEST_USERNAME", "__test_user__") SOCIALACCOUNT_PROVIDERS = { # https://developer.github.com/apps/building-integrations/setting-up-and-registering-oauth-apps/about-scopes-for-oauth-apps/ @@ -525,16 +500,12 @@ def is_test(self): } -DISCOURSE_BASE_URL = config.get( - "discourse", "DISCOURSE_BASE_URL", fallback="https://staging-discourse.comses.net" -) -DISCOURSE_SSO_SECRET = config.get( - "secrets", "DISCOURSE_SSO_SECRET", fallback="unconfigured" -) -DISCOURSE_API_KEY = config.get("secrets", "DISCOURSE_API_KEY", fallback="unconfigured") -DISCOURSE_API_USERNAME = config.get( - "discourse", "DISCOURSE_API_USERNAME", fallback="unconfigured" +DISCOURSE_BASE_URL = os.getenv( + "DISCOURSE_BASE_URL", "https://staging-discourse.comses.net" ) +DISCOURSE_SSO_SECRET = os.getenv("DISCOURSE_SSO_SECRET", "unconfigured") +DISCOURSE_API_KEY = os.getenv("DISCOURSE_API_KEY", "unconfigured") +DISCOURSE_API_USERNAME = os.getenv("DISCOURSE_API_USERNAME", "unconfigured") # https://docs.djangoproject.com/en/4.2/ref/settings/#templates TEMPLATES = [ diff --git a/django/core/settings/production.py b/django/core/settings/production.py index c57f61ce2..407f74d70 100644 --- a/django/core/settings/production.py +++ b/django/core/settings/production.py @@ -7,9 +7,7 @@ DEBUG = False DJANGO_VITE_DEV_MODE = False DEPLOY_ENVIRONMENT = Environment.PRODUCTION -EMAIL_SUBJECT_PREFIX = config.get( - "email", "EMAIL_SUBJECT_PREFIX", fallback="[comses.net]" -) +EMAIL_SUBJECT_PREFIX = os.getenv("EMAIL_SUBJECT_PREFIX", "[comses.net]") # See http://django-allauth.readthedocs.io/en/latest/providers.html#orcid for more context. # # staging settings include a customized sandbox orcid provider that we remove in production. In production, the ORCID @@ -49,9 +47,17 @@ "https://*.google.com", "https://*.google.", ) -CSP_IMG_SRC = ("'self'", "data:", "i.ytimg.com", "https://*.google-analytics.com", "https://*.googletagmanager.com", - "https://*.analytics.google.com", "https://*.g.doubleclick.net", "https://*.google.com", - "https://*.google.",) +CSP_IMG_SRC = ( + "'self'", + "data:", + "i.ytimg.com", + "https://*.google-analytics.com", + "https://*.googletagmanager.com", + "https://*.analytics.google.com", + "https://*.g.doubleclick.net", + "https://*.google.com", + "https://*.google.", +) CSP_INCLUDE_NONCE_IN = ["script-src"] # Base URL to use when referring to full URLs within the Wagtail admin backend - diff --git a/django/core/settings/staging.py b/django/core/settings/staging.py index 9dc4692d0..5d86d9d65 100644 --- a/django/core/settings/staging.py +++ b/django/core/settings/staging.py @@ -21,13 +21,9 @@ # EMAIL_FILE_PATH = '/shared/logs/mail.log' EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend" -MAILGUN_API_KEY = config.get("email", "MAILGUN_API_KEY") -MAILGUN_SENDER_DOMAIN = config.get( - "email", "MAILGUN_SENDER_DOMAIN", fallback="mg.comses.net" -) -EMAIL_SUBJECT_PREFIX = config.get( - "email", "EMAIL_SUBJECT_PREFIX", fallback="[staging.comses.net]" -) +MAILGUN_API_KEY = os.getenv("MAILGUN_API_KEY") +MAILGUN_SENDER_DOMAIN = os.getenv("MAILGUN_SENDER_DOMAIN", "mg.comses.net") +EMAIL_SUBJECT_PREFIX = os.getenv("EMAIL_SUBJECT_PREFIX", "[staging.comses.net]") EMAIL_USE_TLS = True ALLOWED_HOSTS = [".comses.net"] @@ -107,8 +103,8 @@ # hcaptcha config -HCAPTCHA_SITEKEY = config.get("captcha", "HCAPTCHA_SITEKEY", fallback="") -HCAPTCHA_SECRET = config.get("captcha", "HCAPTCHA_SECRET", fallback="") +HCAPTCHA_SITEKEY = os.getenv("HCAPTCHA_SITEKEY", "") +HCAPTCHA_SECRET = os.getenv("HCAPTCHA_SECRET", "") WSGI_APPLICATION = "core.wsgi.application" diff --git a/django/core/settings/test.py b/django/core/settings/test.py index 8ac99eb6c..aab90c201 100644 --- a/django/core/settings/test.py +++ b/django/core/settings/test.py @@ -23,11 +23,11 @@ DATABASES["dump_restore"] = { "ENGINE": "django.db.backends.postgresql", - "NAME": "dump_restore_{}".format(config.get("database", "DB_NAME")), - "USER": config.get("database", "DB_USER"), - "PASSWORD": config.get("database", "DB_PASSWORD"), - "HOST": config.get("database", "DB_HOST"), - "PORT": config.get("database", "DB_PORT"), + "NAME": "dump_restore_{}".format(os.getenv("DB_NAME")), + "USER": os.getenv("DB_USER"), + "PASSWORD": os.getenv("DB_PASSWORD"), + "HOST": os.getenv("DB_HOST"), + "PORT": os.getenv("DB_PORT"), } From 074ca1d55bbf24481a9a662a798c4fa0f96bd446 Mon Sep 17 00:00:00 2001 From: sgfost Date: Fri, 16 Feb 2024 12:53:33 -0700 Subject: [PATCH 2/4] build: rename BUILD_ID to RELEASE_VERSION --- Makefile | 8 ++++---- deploy/conf/.env.template | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 88f7905f5..220eb83d4 100644 --- a/Makefile +++ b/Makefile @@ -65,9 +65,9 @@ $(PGPASS_PATH): $(DB_PASSWORD_PATH) $(PGPASS_TEMPLATE) | ${SECRETS_DIR} echo "${DB_HOST}:5432:*:${DB_USER}:$$(cat $(DB_PASSWORD_PATH))" > $(PGPASS_PATH) chmod 0600 $(PGPASS_PATH) -.PHONY: build-id -build-id: .env - $(ENVREPLACE) BUILD_ID $$(git describe --tags --abbrev=1) .env +.PHONY: release-version +release-version: .env + $(ENVREPLACE) RELEASE_VERSION $$(git describe --tags --abbrev=1) .env .env: $(DB_PASSWORD_PATH) $(SECRET_KEY_PATH) if [ ! -f .env ]; then \ @@ -78,7 +78,7 @@ build-id: .env $(ENVREPLACE) TEST_BASIC_AUTH_PASSWORD $$(openssl rand -base64 42) .env .PHONY: docker-compose.yml -docker-compose.yml: base.yml dev.yml staging.yml prod.yml config.mk $(PGPASS_PATH) build-id +docker-compose.yml: base.yml dev.yml staging.yml prod.yml config.mk $(PGPASS_PATH) release-version case "$(DEPLOY_ENVIRONMENT)" in \ dev|staging|e2e) docker compose -f base.yml -f $(DEPLOY_ENVIRONMENT).yml config > docker-compose.yml;; \ prod) docker compose -f base.yml -f staging.yml -f $(DEPLOY_ENVIRONMENT).yml config > docker-compose.yml;; \ diff --git a/deploy/conf/.env.template b/deploy/conf/.env.template index 1d114d30a..68d6cbc2e 100644 --- a/deploy/conf/.env.template +++ b/deploy/conf/.env.template @@ -1,5 +1,5 @@ # app -BUILD_ID= +RELEASE_VERSION= EXPIRED_JOB_DAYS_THRESHOLD=180 EXPIRED_EVENT_DAYS_THRESHOLD=2 From 17e84ea57abdd7bf6f51a79aea9a11c1c4d80efa Mon Sep 17 00:00:00 2001 From: Allen Lee Date: Wed, 21 Feb 2024 19:52:05 -0700 Subject: [PATCH 3/4] fix: move secrets from env to files --- Makefile | 16 ++++++++++++---- base.yml | 33 ++++++++++++++++++++++++++++++-- deploy/conf/.env.template | 22 ++++++++++----------- django/core/settings/defaults.py | 22 +++++++++++++++------ django/core/settings/staging.py | 4 ++-- 5 files changed, 71 insertions(+), 26 deletions(-) diff --git a/Makefile b/Makefile index 220eb83d4..cdd7000e3 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,8 @@ BUILD_DIR=build SECRETS_DIR=${BUILD_DIR}/secrets DB_PASSWORD_PATH=${SECRETS_DIR}/db_password PGPASS_PATH=${SECRETS_DIR}/.pgpass -SECRET_KEY_PATH=${SECRETS_DIR}/secret_key +SECRET_KEY_PATH=${SECRETS_DIR}/django_secret_key +EXT_SECRETS=mailgun_api_key hcaptcha_secret github_client_secret orcid_client_secret discourse_api_key discourse_sso_secret mail_api_key GENERATED_SECRETS=$(DB_PASSWORD_PATH) $(PGPASS_PATH) $(SECRET_KEY_PATH) ENVREPLACE := deploy/scripts/envreplace @@ -73,8 +74,8 @@ release-version: .env if [ ! -f .env ]; then \ cp $(ENV_TEMPLATE) .env; \ fi; \ - $(ENVREPLACE) DB_PASSWORD $$(cat $(DB_PASSWORD_PATH)) .env; \ - $(ENVREPLACE) SECRET_KEY $$(cat $(SECRET_KEY_PATH)) .env; \ + # $(ENVREPLACE) DB_PASSWORD $$(cat $(DB_PASSWORD_PATH)) .env; \ + # $(ENVREPLACE) SECRET_KEY $$(cat $(SECRET_KEY_PATH)) .env; \ $(ENVREPLACE) TEST_BASIC_AUTH_PASSWORD $$(openssl rand -base64 42) .env .PHONY: docker-compose.yml @@ -91,9 +92,12 @@ set-db-password: $(DB_PASSWORD_PATH) .env .PHONY: secrets secrets: $(SECRETS_DIR) $(GENERATED_SECRETS) + for secret_path in $(EXT_SECRETS); do \ + touch ${SECRETS_DIR}/$$secret_path; \ + done .PHONY: deploy -deploy: build +deploy: build .env docker compose pull db redis elasticsearch ifneq ($(DEPLOY_ENVIRONMENT),dev) docker compose pull nginx @@ -125,6 +129,10 @@ clean: @echo "Backing up generated files to /tmp directory" mv .env config.mk docker-compose.yml $(shell mktemp -d) +.PHONY: clean_deploy +clean_deploy: clean + +@$(MAKE) deploy + .PHONY: test test: build docker compose run --rm server /code/deploy/test.sh diff --git a/base.yml b/base.yml index a458ec441..2c1552535 100644 --- a/base.yml +++ b/base.yml @@ -33,7 +33,9 @@ services: - ./deploy/elasticsearch.conf.d/log4j2.properties:/usr/share/elasticsearch/config/log4j2.properties - esdata:/usr/share/elasticsearch/data db: - image: postgis/postgis:15-3.3 + image: postgis/postgis:15-3.4 + secrets: + - db_password volumes: - ./docker/pgdata:/var/lib/postgresql/data - ./build/secrets/db_password:/run/secrets/db_password @@ -56,8 +58,16 @@ services: server: build: django image: comses/server + secrets: + - db_password + - discourse_api_key + - discourse_sso_secret + - django_secret_key + - github_client_secret + - orcid_client_secret + - hcaptcha_secret + - mail_api_key volumes: - - ./build/secrets:/run/secrets - ./deploy/elasticsearch.conf.d:/etc/elasticsearch - ./docker/shared:/shared depends_on: @@ -71,6 +81,25 @@ services: condition: service_started env_file: - .env + +secrets: + db_password: + file: ./build/secrets/db_password + discourse_api_key: + file: ./build/secrets/discourse_api_key + discourse_sso_secret: + file: ./build/secrets/discourse_sso_secret + django_secret_key: + file: ./build/secrets/django_secret_key + github_client_secret: + file: ./build/secrets/github_client_secret + hcaptcha_secret: + file: ./build/secrets/hcaptcha_secret + mail_api_key: + file: ./build/secrets/mail_api_key + orcid_client_secret: + file: ./build/secrets/orcid_client_secret + volumes: esdata: driver: local diff --git a/deploy/conf/.env.template b/deploy/conf/.env.template index 68d6cbc2e..3dc4f380a 100644 --- a/deploy/conf/.env.template +++ b/deploy/conf/.env.template @@ -12,13 +12,11 @@ DB_PASSWORD= CLEAN_DATABASE="false" # allowed values: "true" or "false" # elastic search -ES_VERSION=7.17.10 +ES_VERSION=7.17.18 # captcha -RECAPTCHA_PUBLIC_KEY= -RECAPTCHA_PRIVATE_KEY= HCAPTCHA_SITEKEY= -HCAPTCHA_SECRET= +# HCAPTCHA_SECRET= # discourse DISCOURSE_BASE_URL= @@ -26,7 +24,7 @@ DISCOURSE_API_USERNAME= # email EMAIL_SUBJECT_PREFIX=[CoMSES Net] -MAILGUN_API_KEY= +# MAILGUN_API_KEY= MAILGUN_SENDER_DOMAIN= # logging @@ -34,15 +32,15 @@ LOG_DIRECTORY=/shared/logs SENTRY_DSN= # secrets -DISCOURSE_SSO_SECRET= -DISCOURSE_API_KEY= +# DISCOURSE_SSO_SECRET= +# DISCOURSE_API_KEY= +# django secret key +# SECRET_KEY= -SECRET_KEY= - -ORCID_CLIENT_ID= -ORCID_CLIENT_SECRET= GITHUB_CLIENT_ID= -GITHUB_CLIENT_SECRET= +# GITHUB_CLIENT_SECRET= +ORCID_CLIENT_ID= +# ORCID_CLIENT_SECRET= # storage DATA_ROOT=/shared diff --git a/django/core/settings/defaults.py b/django/core/settings/defaults.py index 6f8627a45..1d42002e5 100644 --- a/django/core/settings/defaults.py +++ b/django/core/settings/defaults.py @@ -11,9 +11,18 @@ import os from enum import Enum +from pathlib import Path from django.contrib.messages import constants as messages +def read_secret(file, fallback=''): + secrets_file_path = Path('/run/secrets', file) + if secrets_file_path.is_file(): + return secrets_file_path.read_text().strip() + else: + return fallback + + class Environment(Enum): DEVELOPMENT = "http://localhost:8000" @@ -251,7 +260,7 @@ def is_test(self): # sentry DSN SENTRY_DSN = os.getenv("SENTRY_DSN", "https://sentry.example.com/2") -SECRET_KEY = os.getenv("SECRET_KEY") +SECRET_KEY = read_secret('django_secret_key', os.getenv("SECRET_KEY")) # regular settings @@ -269,7 +278,7 @@ def is_test(self): "ENGINE": "django.db.backends.postgresql", "NAME": os.getenv("DB_NAME"), "USER": os.getenv("DB_USER"), - "PASSWORD": os.getenv("DB_PASSWORD"), + "PASSWORD": read_secret('db_password', os.getenv("DB_PASSWORD")), "HOST": os.getenv("DB_HOST"), "PORT": os.getenv("DB_PORT"), } @@ -474,10 +483,11 @@ def is_test(self): ACCOUNT_CHANGE_EMAIL = True ORCID_CLIENT_ID = os.getenv("ORCID_CLIENT_ID", "") -ORCID_CLIENT_SECRET = os.getenv("ORCID_CLIENT_SECRET", "") + +ORCID_CLIENT_SECRET = read_secret('orcid_client_secret') GITHUB_CLIENT_ID = os.getenv("GITHUB_CLIENT_ID", "") -GITHUB_CLIENT_SECRET = os.getenv("GITHUB_CLIENT_SECRET", "") +GITHUB_CLIENT_SECRET = read_secret('github_client_secret') TEST_BASIC_AUTH_PASSWORD = os.getenv("TEST_BASIC_AUTH_PASSWORD", "test password") TEST_USER_ID = os.getenv("TEST_USER_ID", 1000000) @@ -503,8 +513,8 @@ def is_test(self): DISCOURSE_BASE_URL = os.getenv( "DISCOURSE_BASE_URL", "https://staging-discourse.comses.net" ) -DISCOURSE_SSO_SECRET = os.getenv("DISCOURSE_SSO_SECRET", "unconfigured") -DISCOURSE_API_KEY = os.getenv("DISCOURSE_API_KEY", "unconfigured") +DISCOURSE_SSO_SECRET = read_secret('discourse_sso_secret', "unconfigured") +DISCOURSE_API_KEY = read_secret('discourse_api_key', "unconfigured") DISCOURSE_API_USERNAME = os.getenv("DISCOURSE_API_USERNAME", "unconfigured") # https://docs.djangoproject.com/en/4.2/ref/settings/#templates diff --git a/django/core/settings/staging.py b/django/core/settings/staging.py index 5d86d9d65..251b56c6c 100644 --- a/django/core/settings/staging.py +++ b/django/core/settings/staging.py @@ -21,7 +21,7 @@ # EMAIL_FILE_PATH = '/shared/logs/mail.log' EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend" -MAILGUN_API_KEY = os.getenv("MAILGUN_API_KEY") +MAILGUN_API_KEY = read_secret('mail_api_key') MAILGUN_SENDER_DOMAIN = os.getenv("MAILGUN_SENDER_DOMAIN", "mg.comses.net") EMAIL_SUBJECT_PREFIX = os.getenv("EMAIL_SUBJECT_PREFIX", "[staging.comses.net]") EMAIL_USE_TLS = True @@ -104,7 +104,7 @@ # hcaptcha config HCAPTCHA_SITEKEY = os.getenv("HCAPTCHA_SITEKEY", "") -HCAPTCHA_SECRET = os.getenv("HCAPTCHA_SECRET", "") +HCAPTCHA_SECRET = read_secret('hcaptcha_secret', 'unconfigured') WSGI_APPLICATION = "core.wsgi.application" From a3e044958f9c9b19c69d252a68dce5279b4114a2 Mon Sep 17 00:00:00 2001 From: Allen Lee Date: Wed, 21 Feb 2024 20:08:26 -0700 Subject: [PATCH 4/4] build(deps): npm update --save && npm install --- Makefile | 3 +- django/core/settings/defaults.py | 18 +- django/core/settings/staging.py | 4 +- django/library/models.py | 5 +- frontend/package-lock.json | 501 +++++++++++++++++-------------- frontend/package.json | 70 ++--- 6 files changed, 325 insertions(+), 276 deletions(-) diff --git a/Makefile b/Makefile index cdd7000e3..84ce9a1c2 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,4 @@ +# set DEPLOY_ENVIRONMENT in config.mk DEPLOY_ENVIRONMENT := dev DOCKER_SHARED_DIR=docker/shared @@ -8,7 +9,7 @@ SECRETS_DIR=${BUILD_DIR}/secrets DB_PASSWORD_PATH=${SECRETS_DIR}/db_password PGPASS_PATH=${SECRETS_DIR}/.pgpass SECRET_KEY_PATH=${SECRETS_DIR}/django_secret_key -EXT_SECRETS=mailgun_api_key hcaptcha_secret github_client_secret orcid_client_secret discourse_api_key discourse_sso_secret mail_api_key +EXT_SECRETS=hcaptcha_secret github_client_secret orcid_client_secret discourse_api_key discourse_sso_secret mail_api_key GENERATED_SECRETS=$(DB_PASSWORD_PATH) $(PGPASS_PATH) $(SECRET_KEY_PATH) ENVREPLACE := deploy/scripts/envreplace diff --git a/django/core/settings/defaults.py b/django/core/settings/defaults.py index 1d42002e5..2948e6dcc 100644 --- a/django/core/settings/defaults.py +++ b/django/core/settings/defaults.py @@ -15,15 +15,15 @@ from django.contrib.messages import constants as messages -def read_secret(file, fallback=''): - secrets_file_path = Path('/run/secrets', file) + +def read_secret(file, fallback=""): + secrets_file_path = Path("/run/secrets", file) if secrets_file_path.is_file(): return secrets_file_path.read_text().strip() else: return fallback - class Environment(Enum): DEVELOPMENT = "http://localhost:8000" STAGING = "https://staging.comses.net" @@ -260,7 +260,7 @@ def is_test(self): # sentry DSN SENTRY_DSN = os.getenv("SENTRY_DSN", "https://sentry.example.com/2") -SECRET_KEY = read_secret('django_secret_key', os.getenv("SECRET_KEY")) +SECRET_KEY = read_secret("django_secret_key", os.getenv("SECRET_KEY")) # regular settings @@ -278,7 +278,7 @@ def is_test(self): "ENGINE": "django.db.backends.postgresql", "NAME": os.getenv("DB_NAME"), "USER": os.getenv("DB_USER"), - "PASSWORD": read_secret('db_password', os.getenv("DB_PASSWORD")), + "PASSWORD": read_secret("db_password", os.getenv("DB_PASSWORD")), "HOST": os.getenv("DB_HOST"), "PORT": os.getenv("DB_PORT"), } @@ -484,10 +484,10 @@ def is_test(self): ORCID_CLIENT_ID = os.getenv("ORCID_CLIENT_ID", "") -ORCID_CLIENT_SECRET = read_secret('orcid_client_secret') +ORCID_CLIENT_SECRET = read_secret("orcid_client_secret") GITHUB_CLIENT_ID = os.getenv("GITHUB_CLIENT_ID", "") -GITHUB_CLIENT_SECRET = read_secret('github_client_secret') +GITHUB_CLIENT_SECRET = read_secret("github_client_secret") TEST_BASIC_AUTH_PASSWORD = os.getenv("TEST_BASIC_AUTH_PASSWORD", "test password") TEST_USER_ID = os.getenv("TEST_USER_ID", 1000000) @@ -513,8 +513,8 @@ def is_test(self): DISCOURSE_BASE_URL = os.getenv( "DISCOURSE_BASE_URL", "https://staging-discourse.comses.net" ) -DISCOURSE_SSO_SECRET = read_secret('discourse_sso_secret', "unconfigured") -DISCOURSE_API_KEY = read_secret('discourse_api_key', "unconfigured") +DISCOURSE_SSO_SECRET = read_secret("discourse_sso_secret", "unconfigured") +DISCOURSE_API_KEY = read_secret("discourse_api_key", "unconfigured") DISCOURSE_API_USERNAME = os.getenv("DISCOURSE_API_USERNAME", "unconfigured") # https://docs.djangoproject.com/en/4.2/ref/settings/#templates diff --git a/django/core/settings/staging.py b/django/core/settings/staging.py index 251b56c6c..d2206558f 100644 --- a/django/core/settings/staging.py +++ b/django/core/settings/staging.py @@ -21,7 +21,7 @@ # EMAIL_FILE_PATH = '/shared/logs/mail.log' EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend" -MAILGUN_API_KEY = read_secret('mail_api_key') +MAILGUN_API_KEY = read_secret("mail_api_key") MAILGUN_SENDER_DOMAIN = os.getenv("MAILGUN_SENDER_DOMAIN", "mg.comses.net") EMAIL_SUBJECT_PREFIX = os.getenv("EMAIL_SUBJECT_PREFIX", "[staging.comses.net]") EMAIL_USE_TLS = True @@ -104,7 +104,7 @@ # hcaptcha config HCAPTCHA_SITEKEY = os.getenv("HCAPTCHA_SITEKEY", "") -HCAPTCHA_SECRET = read_secret('hcaptcha_secret', 'unconfigured') +HCAPTCHA_SECRET = read_secret("hcaptcha_secret", "unconfigured") WSGI_APPLICATION = "core.wsgi.application" diff --git a/django/library/models.py b/django/library/models.py index f149a4f2d..629715f4b 100644 --- a/django/library/models.py +++ b/django/library/models.py @@ -2365,7 +2365,6 @@ def __init__(self, metadata: dict): @classmethod def convert_target_product(cls, codebase_release: CodebaseRelease): - target_product = { "@type": "SoftwareApplication", "name": codebase_release.title, @@ -2382,9 +2381,7 @@ def convert_target_product(cls, codebase_release: CodebaseRelease): ) image_urls = codebase_release.codebase.get_image_urls() if image_urls: - target_product.update( - screenshot=f"{settings.BASE_URL}{image_urls[0]}" - ) + target_product.update(screenshot=f"{settings.BASE_URL}{image_urls[0]}") return target_product @classmethod diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 1c5620d99..ce9b1a025 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,54 +8,54 @@ "name": "frontend", "version": "1.0.0", "dependencies": { - "@fortawesome/fontawesome-free": "^5.12.0", - "@fullcalendar/bootstrap5": "^6.1.6", - "@fullcalendar/core": "^6.1.6", - "@fullcalendar/daygrid": "^6.1.6", - "@fullcalendar/list": "^6.1.7", - "@fullcalendar/timegrid": "^6.1.7", - "@fullcalendar/vue3": "^6.1.6", - "@popperjs/core": "^2.11.6", + "@fortawesome/fontawesome-free": "^5.15.4", + "@fullcalendar/bootstrap5": "^6.1.11", + "@fullcalendar/core": "^6.1.11", + "@fullcalendar/daygrid": "^6.1.11", + "@fullcalendar/list": "^6.1.11", + "@fullcalendar/timegrid": "^6.1.11", + "@fullcalendar/vue3": "^6.1.11", + "@popperjs/core": "^2.11.8", "@vorms/core": "^1.1.0", "@vorms/resolvers": "^1.1.0", - "@vuepic/vue-datepicker": "^4.2.3", + "@vuepic/vue-datepicker": "^4.5.1", "@vueuse/core": "^9.13.0", - "axios": "^1.6.0", + "axios": "^1.6.7", "bootstrap": "5.2.3", - "highcharts": "^11.0.1", - "highcharts-vue": "^1.4.0", + "highcharts": "^11.3.0", + "highcharts-vue": "^1.4.3", "lodash-es": "^4.17.21", - "pinia": "^2.0.32", - "query-string": "^8.1.0", - "sass": "^1.58.3", - "sortablejs": "^1.15.0", - "sortablejs-vue3": "^1.2.10", + "pinia": "^2.1.7", + "query-string": "^8.2.0", + "sass": "^1.71.1", + "sortablejs": "^1.15.2", + "sortablejs-vue3": "^1.2.11", "vue": "^3.2.47", "vue-multiselect": "^3.0.0-beta.1", - "vue-router": "^4.1.6", - "yup": "^1.0.2" + "vue-router": "^4.3.0", + "yup": "^1.3.3" }, "devDependencies": { - "@rushstack/eslint-patch": "^1.2.0", - "@types/bootstrap": "^5.2.6", - "@types/jsdom": "^21.1.0", - "@types/lodash-es": "^4.17.7", - "@types/node": "^18.14.2", - "@types/sortablejs": "^1.15.0", - "@vitejs/plugin-vue": "^4.0.0", + "@rushstack/eslint-patch": "^1.7.2", + "@types/bootstrap": "^5.2.10", + "@types/jsdom": "^21.1.6", + "@types/lodash-es": "^4.17.12", + "@types/node": "^18.19.17", + "@types/sortablejs": "^1.15.8", + "@vitejs/plugin-vue": "^4.6.2", "@vue/eslint-config-prettier": "^7.1.0", - "@vue/eslint-config-typescript": "^11.0.2", - "@vue/test-utils": "^2.3.0", + "@vue/eslint-config-typescript": "^11.0.3", + "@vue/test-utils": "^2.4.4", "@vue/tsconfig": "^0.1.3", - "eslint": "^8.54.0", - "eslint-plugin-vue": "^9.9.0", - "jsdom": "^21.1.0", + "eslint": "^8.56.0", + "eslint-plugin-vue": "^9.21.1", + "jsdom": "^21.1.2", "npm-run-all": "^4.1.5", - "prettier": "^2.8.4", + "prettier": "^2.8.8", "typescript": "~4.8.4", - "vite": "^4.5.1", - "vitest": "^0.29.1", - "vue-tsc": "^1.2.0" + "vite": "^4.5.2", + "vitest": "^0.29.8", + "vue-tsc": "^1.8.27" }, "engines": { "yarn": "\n\n! Use npm !\n\n" @@ -510,54 +510,54 @@ } }, "node_modules/@fullcalendar/bootstrap5": { - "version": "6.1.10", - "resolved": "https://registry.npmjs.org/@fullcalendar/bootstrap5/-/bootstrap5-6.1.10.tgz", - "integrity": "sha512-hBJToRQqKiWFnt+KQjeSN5HkYN0UTvgFnSH0P88Pj26zl6CXargee8pGccLBbqBQtdCZhXYLX3QGPHStq7Ds7Q==", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@fullcalendar/bootstrap5/-/bootstrap5-6.1.11.tgz", + "integrity": "sha512-wZ2eYtZoi8VS2tcoe2oeca8wkkY85Nn+rH35JpFx35H77EMB5xBTtGAAEk9AClFkn+QscAwoOz3nyy1z0EqNpA==", "peerDependencies": { - "@fullcalendar/core": "~6.1.10" + "@fullcalendar/core": "~6.1.11" } }, "node_modules/@fullcalendar/core": { - "version": "6.1.10", - "resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.10.tgz", - "integrity": "sha512-oTXGJSAGpCf1oY+CKp5qYjMHkJCPBkJ3SHitl63n8Q6xKeiwQ4EF6Au451euUovREwJpLmD1AyZrCnWmtB9AVg==", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.11.tgz", + "integrity": "sha512-TjG7c8sUz+Vkui2FyCNJ+xqyu0nq653Ibe99A66LoW95oBo6tVhhKIaG1Wh0GVKymYiqAQN/OEdYTuj4ay27kA==", "dependencies": { "preact": "~10.12.1" } }, "node_modules/@fullcalendar/daygrid": { - "version": "6.1.10", - "resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-6.1.10.tgz", - "integrity": "sha512-Z4GRm1IyHKgxXFTWGcEI0nTsvYOIkpE0aMt3/o3ER2SZkF+hfwcDFhtj0c9+WhMjXFIWYeoTnA9rUOY7Zl/nxA==", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-6.1.11.tgz", + "integrity": "sha512-hF5jJB7cgUIxWD5MVjj8IU407HISyLu7BWXcEIuTytkfr8oolOXeCazqnnjmRbnFOncoJQVstTtq6SIhaT32Xg==", "peerDependencies": { - "@fullcalendar/core": "~6.1.10" + "@fullcalendar/core": "~6.1.11" } }, "node_modules/@fullcalendar/list": { - "version": "6.1.10", - "resolved": "https://registry.npmjs.org/@fullcalendar/list/-/list-6.1.10.tgz", - "integrity": "sha512-WE4vuSUCzol4tJd0ZP0cNxeyRPaZcsVVYs2I3qdf3OZQkXwDCdSyWEz0Hluf+XZWcZXt21aEYKlxRjwUpQcf4Q==", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@fullcalendar/list/-/list-6.1.11.tgz", + "integrity": "sha512-9Qx8uvik9pXD12u50FiHwNzlHv4wkhfsr+r03ycahW7vEeIAKCsIZGTkUfFP+96I5wHihrfLazu1cFQG4MPiuw==", "peerDependencies": { - "@fullcalendar/core": "~6.1.10" + "@fullcalendar/core": "~6.1.11" } }, "node_modules/@fullcalendar/timegrid": { - "version": "6.1.10", - "resolved": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-6.1.10.tgz", - "integrity": "sha512-hFKyQXJaPbNyq1reZmvkCmM64O99krHoIcJAbDS+dntCm3FzZUcDtAcRKIbMiantHrezCG/1MEYk3m9e3aKvIQ==", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-6.1.11.tgz", + "integrity": "sha512-0seUHK/ferH89IeuCvV4Bib0zWjgK0nsptNdmAc9wDBxD/d9hm5Mdti0URJX6bDoRtsSfRDu5XsRcrzwoc+AUQ==", "dependencies": { - "@fullcalendar/daygrid": "~6.1.10" + "@fullcalendar/daygrid": "~6.1.11" }, "peerDependencies": { - "@fullcalendar/core": "~6.1.10" + "@fullcalendar/core": "~6.1.11" } }, "node_modules/@fullcalendar/vue3": { - "version": "6.1.10", - "resolved": "https://registry.npmjs.org/@fullcalendar/vue3/-/vue3-6.1.10.tgz", - "integrity": "sha512-YMYBQx0TlWNuN4G6ra2dkf5cCF5aVi/2zDLGLvLqe2Nk2o7uNbTkrCSG40061OepWQlJv+hYqm1JukLRmyqi4Q==", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@fullcalendar/vue3/-/vue3-6.1.11.tgz", + "integrity": "sha512-jBoDS0WSpuOM9ZgjL3lNh6o385u/LthFZDaMUACjVVJZh3JuBbuA7ghdUvIelcTNXa5VRCkSZOpivTJWOnLfcg==", "peerDependencies": { - "@fullcalendar/core": "~6.1.10", + "@fullcalendar/core": "~6.1.11", "vue": "^3.0.11" } }, @@ -775,9 +775,9 @@ } }, "node_modules/@types/node": { - "version": "18.19.15", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.15.tgz", - "integrity": "sha512-AMZ2UWx+woHNfM11PyAEQmfSxi05jm9OlkxczuHeEqmvwPkYj6MWv44gbzDPefYOLysTOFyI3ziiy2ONmUZfpA==", + "version": "18.19.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.17.tgz", + "integrity": "sha512-SzyGKgwPzuWp2SHhlpXKzCX0pIOfcI4V2eF37nNBJOhwlegQ83omtVQ1XxZpDE06V/d6AQvfQdPfnw0tRC//Ng==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -790,9 +790,9 @@ "dev": true }, "node_modules/@types/sortablejs": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.15.7.tgz", - "integrity": "sha512-PvgWCx1Lbgm88FdQ6S7OGvLIjWS66mudKPlfdrWil0TjsO5zmoZmzoKiiwRShs1dwPgrlkr0N4ewuy0/+QUXYQ==", + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.15.8.tgz", + "integrity": "sha512-b79830lW+RZfwaztgs1aVPgbasJ8e7AXtZYHTELNXZPsERt4ymJdjV4OccDbHQAvHrCcFpbF78jkm0R6h/pZVg==", "dev": true }, "node_modules/@types/tough-cookie": { @@ -1130,36 +1130,36 @@ "integrity": "sha512-cGg9K+qBPK3D4ZikkAbxO50wbonOxpbhm27D4sxnDiYgKqJZV3yInJWjt3YQVlvJBYbRFI5QlcqD4PIMZBsFQw==" }, "node_modules/@vue/compiler-core": { - "version": "3.4.18", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.18.tgz", - "integrity": "sha512-F7YK8lMK0iv6b9/Gdk15A67wM0KKZvxDxed0RR60C1z9tIJTKta+urs4j0RTN5XqHISzI3etN3mX0uHhjmoqjQ==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.19.tgz", + "integrity": "sha512-gj81785z0JNzRcU0Mq98E56e4ltO1yf8k5PQ+tV/7YHnbZkrM0fyFyuttnN8ngJZjbpofWE/m4qjKBiLl8Ju4w==", "dependencies": { "@babel/parser": "^7.23.9", - "@vue/shared": "3.4.18", + "@vue/shared": "3.4.19", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.0.2" } }, "node_modules/@vue/compiler-dom": { - "version": "3.4.18", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.18.tgz", - "integrity": "sha512-24Eb8lcMfInefvQ6YlEVS18w5Q66f4+uXWVA+yb7praKbyjHRNuKVWGuinfSSjM0ZIiPi++QWukhkgznBaqpEA==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.19.tgz", + "integrity": "sha512-vm6+cogWrshjqEHTzIDCp72DKtea8Ry/QVpQRYoyTIg9k7QZDX6D8+HGURjtmatfgM8xgCFtJJaOlCaRYRK3QA==", "dependencies": { - "@vue/compiler-core": "3.4.18", - "@vue/shared": "3.4.18" + "@vue/compiler-core": "3.4.19", + "@vue/shared": "3.4.19" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.4.18", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.18.tgz", - "integrity": "sha512-rG5tqtnzwrVpMqAQ7FHtvHaV70G6LLfJIWLYZB/jZ9m/hrnZmIQh+H3ewnC5onwe/ibljm9+ZupxeElzqCkTAw==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.19.tgz", + "integrity": "sha512-LQ3U4SN0DlvV0xhr1lUsgLCYlwQfUfetyPxkKYu7dkfvx7g3ojrGAkw0AERLOKYXuAGnqFsEuytkdcComei3Yg==", "dependencies": { "@babel/parser": "^7.23.9", - "@vue/compiler-core": "3.4.18", - "@vue/compiler-dom": "3.4.18", - "@vue/compiler-ssr": "3.4.18", - "@vue/shared": "3.4.18", + "@vue/compiler-core": "3.4.19", + "@vue/compiler-dom": "3.4.19", + "@vue/compiler-ssr": "3.4.19", + "@vue/shared": "3.4.19", "estree-walker": "^2.0.2", "magic-string": "^0.30.6", "postcss": "^8.4.33", @@ -1167,18 +1167,18 @@ } }, "node_modules/@vue/compiler-ssr": { - "version": "3.4.18", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.18.tgz", - "integrity": "sha512-hSlv20oUhPxo2UYUacHgGaxtqP0tvFo6ixxxD6JlXIkwzwoZ9eKK6PFQN4hNK/R13JlNyldwWt/fqGBKgWJ6nQ==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.19.tgz", + "integrity": "sha512-P0PLKC4+u4OMJ8sinba/5Z/iDT84uMRRlrWzadgLA69opCpI1gG4N55qDSC+dedwq2fJtzmGald05LWR5TFfLw==", "dependencies": { - "@vue/compiler-dom": "3.4.18", - "@vue/shared": "3.4.18" + "@vue/compiler-dom": "3.4.19", + "@vue/shared": "3.4.19" } }, "node_modules/@vue/devtools-api": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.5.1.tgz", - "integrity": "sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==" + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.1.tgz", + "integrity": "sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==" }, "node_modules/@vue/eslint-config-prettier": { "version": "7.1.0", @@ -1268,48 +1268,48 @@ } }, "node_modules/@vue/reactivity": { - "version": "3.4.18", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.18.tgz", - "integrity": "sha512-7uda2/I0jpLiRygprDo5Jxs2HJkOVXcOMlyVlY54yRLxoycBpwGJRwJT9EdGB4adnoqJDXVT2BilUAYwI7qvmg==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.19.tgz", + "integrity": "sha512-+VcwrQvLZgEclGZRHx4O2XhyEEcKaBi50WbxdVItEezUf4fqRh838Ix6amWTdX0CNb/b6t3Gkz3eOebfcSt+UA==", "dependencies": { - "@vue/shared": "3.4.18" + "@vue/shared": "3.4.19" } }, "node_modules/@vue/runtime-core": { - "version": "3.4.18", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.18.tgz", - "integrity": "sha512-7mU9diCa+4e+8/wZ7Udw5pwTH10A11sZ1nldmHOUKJnzCwvZxfJqAtw31mIf4T5H2FsLCSBQT3xgioA9vIjyDQ==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.19.tgz", + "integrity": "sha512-/Z3tFwOrerJB/oyutmJGoYbuoadphDcJAd5jOuJE86THNZji9pYjZroQ2NFsZkTxOq0GJbb+s2kxTYToDiyZzw==", "dependencies": { - "@vue/reactivity": "3.4.18", - "@vue/shared": "3.4.18" + "@vue/reactivity": "3.4.19", + "@vue/shared": "3.4.19" } }, "node_modules/@vue/runtime-dom": { - "version": "3.4.18", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.18.tgz", - "integrity": "sha512-2y1Mkzcw1niSfG7z3Qx+2ir9Gb4hdTkZe5p/I8x1aTIKQE0vY0tPAEUPhZm5tx6183gG3D/KwHG728UR0sIufA==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.19.tgz", + "integrity": "sha512-IyZzIDqfNCF0OyZOauL+F4yzjMPN2rPd8nhqPP2N1lBn3kYqJpPHHru+83Rkvo2lHz5mW+rEeIMEF9qY3PB94g==", "dependencies": { - "@vue/runtime-core": "3.4.18", - "@vue/shared": "3.4.18", + "@vue/runtime-core": "3.4.19", + "@vue/shared": "3.4.19", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.4.18", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.18.tgz", - "integrity": "sha512-YJd1wa7mzUN3NRqLEsrwEYWyO+PUBSROIGlCc3J/cvn7Zu6CxhNLgXa8Z4zZ5ja5/nviYO79J1InoPeXgwBTZA==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.19.tgz", + "integrity": "sha512-eAj2p0c429RZyyhtMRnttjcSToch+kTWxFPHlzGMkR28ZbF1PDlTcmGmlDxccBuqNd9iOQ7xPRPAGgPVj+YpQw==", "dependencies": { - "@vue/compiler-ssr": "3.4.18", - "@vue/shared": "3.4.18" + "@vue/compiler-ssr": "3.4.19", + "@vue/shared": "3.4.19" }, "peerDependencies": { - "vue": "3.4.18" + "vue": "3.4.19" } }, "node_modules/@vue/shared": { - "version": "3.4.18", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.18.tgz", - "integrity": "sha512-CxouGFxxaW5r1WbrSmWwck3No58rApXgRSBxrqgnY1K+jk20F6DrXJkHdH9n4HVT+/B6G2CAn213Uq3npWiy8Q==" + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz", + "integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==" }, "node_modules/@vue/test-utils": { "version": "2.4.4", @@ -1630,10 +1630,13 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/available-typed-arrays": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz", - "integrity": "sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -1720,15 +1723,16 @@ } }, "node_modules/call-bind": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.6.tgz", - "integrity": "sha512-Mj50FLHtlsoVfRfnHaZvyrooHcrlceNZdL/QBvJJVd9Ta55qCQK0gs4ss2oZDeV9zFCs6ewzYgVE5yfVmfFpVg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dev": true, "dependencies": { + "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.3", - "set-function-length": "^1.2.0" + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -2046,18 +2050,20 @@ } }, "node_modules/define-data-property": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.2.tgz", - "integrity": "sha512-SRtsSqsDbgpJBbW3pABMCOt6rQyeM8s8RiyeSN8jYG8sYmt/kGJejbydttUsnDs1tadr19tvhT4ShwMyoqAm4g==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, "dependencies": { + "es-define-property": "^1.0.0", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.2", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-properties": { @@ -2086,9 +2092,9 @@ } }, "node_modules/diff": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", - "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", "dev": true, "engines": { "node": ">=0.3.1" @@ -2206,50 +2212,52 @@ } }, "node_modules/es-abstract": { - "version": "1.22.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", - "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", + "version": "1.22.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.4.tgz", + "integrity": "sha512-vZYJlk2u6qHYxBOTjAeg7qUxHdNfih64Uu2J8QqWgXZ2cri0ZpJAkzDUK/q593+mvKwlxyaxr6F1Q+3LKoQRgg==", "dev": true, "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.2", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.5", - "es-set-tostringtag": "^2.0.1", + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.6", + "call-bind": "^1.0.7", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.2", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.2", - "get-symbol-description": "^1.0.0", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", "globalthis": "^1.0.3", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0", + "has-property-descriptors": "^1.0.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", - "hasown": "^2.0.0", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", + "hasown": "^2.0.1", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", "is-callable": "^1.2.7", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", - "is-typed-array": "^1.1.12", + "is-typed-array": "^1.1.13", "is-weakref": "^1.0.2", "object-inspect": "^1.13.1", "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.0", + "safe-regex-test": "^1.0.3", "string.prototype.trim": "^1.2.8", "string.prototype.trimend": "^1.0.7", "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.0", + "typed-array-buffer": "^1.0.1", "typed-array-byte-length": "^1.0.0", "typed-array-byte-offset": "^1.0.0", "typed-array-length": "^1.0.4", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.13" + "which-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -2258,6 +2266,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-errors": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", @@ -2268,14 +2288,14 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", - "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.2", - "has-tostringtag": "^1.0.0", - "hasown": "^2.0.0" + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" }, "engines": { "node": ">= 0.4" @@ -2756,9 +2776,9 @@ } }, "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, "node_modules/follow-redirects": { @@ -3069,21 +3089,21 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.2" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "dev": true, "engines": { "node": ">= 0.4" @@ -3415,9 +3435,9 @@ } }, "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, "engines": { "node": ">= 0.4" @@ -3481,12 +3501,15 @@ } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3580,14 +3603,15 @@ } }, "node_modules/js-beautify": { - "version": "1.14.11", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.11.tgz", - "integrity": "sha512-rPogWqAfoYh1Ryqqh2agUpVfbxAhbjuN1SmU86dskQUKouRiggUTCO4+2ym9UPXllc2WAp0J+T5qxn7Um3lCdw==", + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.1.tgz", + "integrity": "sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==", "dev": true, "dependencies": { "config-chain": "^1.1.13", - "editorconfig": "^1.0.3", + "editorconfig": "^1.0.4", "glob": "^10.3.3", + "js-cookie": "^3.0.5", "nopt": "^7.2.0" }, "bin": { @@ -3599,6 +3623,15 @@ "node": ">=14" } }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "dev": true, + "engines": { + "node": ">=14" + } + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -3876,9 +3909,9 @@ } }, "node_modules/mlly": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.5.0.tgz", - "integrity": "sha512-NPVQvAY1xr1QoVeG0cy8yUYC7FQcOx6evl/RjT1wL5FvzPnzOysoqB/jmx/DhssT2dYa8nxECLAaFI/+gVLhDQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.6.0.tgz", + "integrity": "sha512-YOvg9hfYQmnaB56Yb+KrJE2u0Yzz5zR+sLejEvF4fzwzV1Al6hkf2vyHTwqCRyv0hCi9rVCqVoXpyYevQIRwLQ==", "dev": true, "dependencies": { "acorn": "^8.11.3", @@ -4465,6 +4498,15 @@ "pathe": "^1.1.0" } }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/postcss": { "version": "8.4.35", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", @@ -4879,9 +4921,9 @@ "dev": true }, "node_modules/sass": { - "version": "1.70.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz", - "integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==", + "version": "1.71.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz", + "integrity": "sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==", "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -4951,14 +4993,15 @@ } }, "node_modules/set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, "dependencies": { - "define-data-property": "^1.0.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5129,9 +5172,9 @@ } }, "node_modules/spdx-exceptions": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.4.0.tgz", - "integrity": "sha512-hcjppoJ68fhxA/cjbN4T8N6uCUejN8yFw69ttpqtBeCbF3u13n7mb31NB9jKwGTTWWnt9IbRA/mf1FprYS8wfw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", "dev": true }, "node_modules/spdx-expression-parse": { @@ -5521,12 +5564,12 @@ } }, "node_modules/typed-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.1.tgz", - "integrity": "sha512-RSqu1UEuSlrBhHTWC8O9FnPjOduNs4M7rJ4pRKoEjtx1zUNOPN2sSXHLDX+Y2WPbHIxbvg4JFo2DNAEfPIKWoQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.6", + "call-bind": "^1.0.7", "es-errors": "^1.3.0", "is-typed-array": "^1.1.13" }, @@ -5535,15 +5578,16 @@ } }, "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -5553,16 +5597,17 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -5572,14 +5617,20 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.5.tgz", + "integrity": "sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5825,15 +5876,15 @@ } }, "node_modules/vue": { - "version": "3.4.18", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.18.tgz", - "integrity": "sha512-0zLRYamFRe0wF4q2L3O24KQzLyLpL64ye1RUToOgOxuWZsb/FhaNRdGmeozdtVYLz6tl94OXLaK7/WQIrVCw1A==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.19.tgz", + "integrity": "sha512-W/7Fc9KUkajFU8dBeDluM4sRGc/aa4YJnOYck8dkjgZoXtVsn3OeTGni66FV1l3+nvPA7VBFYtPioaGKUmEADw==", "dependencies": { - "@vue/compiler-dom": "3.4.18", - "@vue/compiler-sfc": "3.4.18", - "@vue/runtime-dom": "3.4.18", - "@vue/server-renderer": "3.4.18", - "@vue/shared": "3.4.18" + "@vue/compiler-dom": "3.4.19", + "@vue/compiler-sfc": "3.4.19", + "@vue/runtime-dom": "3.4.19", + "@vue/server-renderer": "3.4.19", + "@vue/shared": "3.4.19" }, "peerDependencies": { "typescript": "*" @@ -5909,11 +5960,11 @@ } }, "node_modules/vue-router": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.2.5.tgz", - "integrity": "sha512-DIUpKcyg4+PTQKfFPX88UWhlagBEBEfJ5A8XDXRJLUnZOvcpMF8o/dnL90vpVkGaPbjvXazV/rC1qBKrZlFugw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.3.0.tgz", + "integrity": "sha512-dqUcs8tUeG+ssgWhcPbjHvazML16Oga5w34uCUmsk7i0BcnskoLGwjpa15fqMr2Fa5JgVBrdL2MEgqz6XZ/6IQ==", "dependencies": { - "@vue/devtools-api": "^6.5.0" + "@vue/devtools-api": "^6.5.1" }, "funding": { "url": "https://github.com/sponsors/posva" diff --git a/frontend/package.json b/frontend/package.json index 57814fd84..194cb1a3b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,54 +17,54 @@ "tls": "npm run type-check && npm run test && npm run lint && (npm run style || npm run style:fix)" }, "dependencies": { - "@fortawesome/fontawesome-free": "^5.12.0", - "@fullcalendar/bootstrap5": "^6.1.6", - "@fullcalendar/core": "^6.1.6", - "@fullcalendar/daygrid": "^6.1.6", - "@fullcalendar/list": "^6.1.7", - "@fullcalendar/timegrid": "^6.1.7", - "@fullcalendar/vue3": "^6.1.6", - "@popperjs/core": "^2.11.6", + "@fortawesome/fontawesome-free": "^5.15.4", + "@fullcalendar/bootstrap5": "^6.1.11", + "@fullcalendar/core": "^6.1.11", + "@fullcalendar/daygrid": "^6.1.11", + "@fullcalendar/list": "^6.1.11", + "@fullcalendar/timegrid": "^6.1.11", + "@fullcalendar/vue3": "^6.1.11", + "@popperjs/core": "^2.11.8", "@vorms/core": "^1.1.0", "@vorms/resolvers": "^1.1.0", - "@vuepic/vue-datepicker": "^4.2.3", + "@vuepic/vue-datepicker": "^4.5.1", "@vueuse/core": "^9.13.0", - "axios": "^1.6.0", + "axios": "^1.6.7", "bootstrap": "5.2.3", - "highcharts": "^11.0.1", - "highcharts-vue": "^1.4.0", + "highcharts": "^11.3.0", + "highcharts-vue": "^1.4.3", "lodash-es": "^4.17.21", - "pinia": "^2.0.32", - "query-string": "^8.1.0", - "sass": "^1.58.3", - "sortablejs": "^1.15.0", - "sortablejs-vue3": "^1.2.10", + "pinia": "^2.1.7", + "query-string": "^8.2.0", + "sass": "^1.71.1", + "sortablejs": "^1.15.2", + "sortablejs-vue3": "^1.2.11", "vue": "^3.2.47", "vue-multiselect": "^3.0.0-beta.1", - "vue-router": "^4.1.6", - "yup": "^1.0.2" + "vue-router": "^4.3.0", + "yup": "^1.3.3" }, "devDependencies": { - "@rushstack/eslint-patch": "^1.2.0", - "@types/bootstrap": "^5.2.6", - "@types/jsdom": "^21.1.0", - "@types/lodash-es": "^4.17.7", - "@types/node": "^18.14.2", - "@types/sortablejs": "^1.15.0", - "@vitejs/plugin-vue": "^4.0.0", + "@rushstack/eslint-patch": "^1.7.2", + "@types/bootstrap": "^5.2.10", + "@types/jsdom": "^21.1.6", + "@types/lodash-es": "^4.17.12", + "@types/node": "^18.19.17", + "@types/sortablejs": "^1.15.8", + "@vitejs/plugin-vue": "^4.6.2", "@vue/eslint-config-prettier": "^7.1.0", - "@vue/eslint-config-typescript": "^11.0.2", - "@vue/test-utils": "^2.3.0", + "@vue/eslint-config-typescript": "^11.0.3", + "@vue/test-utils": "^2.4.4", "@vue/tsconfig": "^0.1.3", - "eslint": "^8.54.0", - "eslint-plugin-vue": "^9.9.0", - "jsdom": "^21.1.0", + "eslint": "^8.56.0", + "eslint-plugin-vue": "^9.21.1", + "jsdom": "^21.1.2", "npm-run-all": "^4.1.5", - "prettier": "^2.8.4", + "prettier": "^2.8.8", "typescript": "~4.8.4", - "vite": "^4.5.1", - "vitest": "^0.29.1", - "vue-tsc": "^1.2.0" + "vite": "^4.5.2", + "vitest": "^0.29.8", + "vue-tsc": "^1.8.27" }, "engines": { "yarn": "\n\n! Use npm !\n\n"