diff --git a/.dockerignore b/.dockerignore index 23ff73ca..f5bb4b9c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -31,6 +31,7 @@ db.sqlite3 .mypy_cache .pylint.d .pytest_cache +**/celerybeat-schedule* # Frontend node_modules diff --git a/.gitignore b/.gitignore index 083bf553..2f9a22de 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ share/python-wheels/ MANIFEST .DS_Store .next/ +celerybeat-schedule* # Translations # Translations *.mo diff --git a/CHANGELOG.md b/CHANGELOG.md index 6afa9005..e530059c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,10 +16,15 @@ and this project adheres to - 🔥(backend) remove usage of atomic transaction for item creation +### Fixed + +- 🐛(backend) correctly configure celery beat to run wopi configuration + ### Security - 🔒️(backend) prevent mismatch mimetype between object storage and application + ## [v0.11.1] - 2026-01-13 ### Fixed diff --git a/docs/env.md b/docs/env.md index 29fc9884..f882590a 100644 --- a/docs/env.md +++ b/docs/env.md @@ -108,3 +108,15 @@ This document lists all configurable environment variables for the Drive applica | `SPECTACULAR_SETTINGS_ENABLE_DJANGO_DEPLOY_CHECK` | Enable Django deploy check in Spectacular | `False` | | `STORAGES_STATICFILES_BACKEND` | Backend for static files storage | `whitenoise.storage.CompressedManifestStaticFilesStorage` | | `TRASHBIN_CUTOFF_DAYS` | Number of days before items are permanently deleted from trash | `30` | +| `WOPI_CLIENTS` | List of client name. These client names will be used in the post_setup | [] | +| `WOPI_{CLIENT_NAME}_DISCOVERY_URL` | The discovery url for each client present in the `WOPI_CLIENTS`. if `WOPI_CLIENTS=vendorA` then set `WOPI_VENDORA_DISCOVERY_URL` | | +| `WOPI_EXCLUDED_MIMETYPES` | List of mimetypes excluded when parsing the discovery url | See settings.py module | +| `WOPI_EXCLUDED_EXTENSIONS` | List of extensions excluded when parsing the discovery url | See settings.py module | +| `WOPI_SRC_BASE_URL` | The backend url | None | +| `WOPI_ACCESS_TOKEN_TIMEOUT` | TTL in seconds for the access_token_ttl sent to the WOPI client | `36000` (10H) | +| `WOPI_LOCK_TIMEOUT` | TTL for the lock acquired by a WOPI client | `1800` (30 min) | +| `WOPI_DISABLE_CHAT` | Disable chat in the WOPI client interface | `0` | +| `WOPI_CONFIGURATION_CRONTAB_MINUTE` | Used to configure the celery beat crontab, See https://docs.celeryq.dev/en/main/reference/celery.schedules.html#celery.schedules.crontab | `0` | +| `WOPI_CONFIGURATION_CRONTAB_HOUR` | Used to configure the celery beat crontab, See https://docs.celeryq.dev/en/main/reference/celery.schedules.html#celery.schedules.crontab | `3` | +| `WOPI_CONFIGURATION_CRONTAB_DAY_OF_MONTH` | Used to configure the celery beat crontab, See https://docs.celeryq.dev/en/main/reference/celery.schedules.html#celery.schedules.crontab | `*` | +| `WOPI_CONFIGURATION_CRONTAB_MONTH_OF_YEAR` | Used to configure the celery beat crontab, See https://docs.celeryq.dev/en/main/reference/celery.schedules.html#celery.schedules.crontab | `*` | diff --git a/src/backend/drive/settings.py b/src/backend/drive/settings.py index 1fb2efb1..093d8a34 100755 --- a/src/backend/drive/settings.py +++ b/src/backend/drive/settings.py @@ -833,7 +833,6 @@ class Base(Configuration): "drf_standardized_errors", # Third party apps "corsheaders", - "django_celery_beat", "django_filters", "dockerflow.django", "rest_framework", @@ -1049,7 +1048,6 @@ class Base(Configuration): # Celery CELERY_BROKER_URL = values.Value("redis://redis:6379/0") CELERY_BROKER_TRANSPORT_OPTIONS = values.DictValue({}) - CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler" # Session SESSION_ENGINE = "django.contrib.sessions.backends.cache" @@ -1391,6 +1389,21 @@ class Base(Configuration): 0, environ_name="WOPI_DISABLE_CHAT", environ_prefix=None ) + WOPI_CONFIGURATION_CRONTAB_MINUTE = values.Value( + 0, environ_name="WOPI_CONFIGURATION_CRONTAB_MINUTE", environ_prefix=None + ) + WOPI_CONFIGURATION_CRONTAB_HOUR = values.Value( + 3, environ_name="WOPI_CONFIGURATION_CRONTAB_HOUR", environ_prefix=None + ) + WOPI_CONFIGURATION_CRONTAB_DAY_OF_MONTH = values.Value( + "*", environ_name="WOPI_CONFIGURATION_CRONTAB_DAY_OF_MONTH", environ_prefix=None + ) + WOPI_CONFIGURATION_CRONTAB_MONTH_OF_YEAR = values.Value( + "*", + environ_name="WOPI_CONFIGURATION_CRONTAB_MONTH_OF_YEAR", + environ_prefix=None, + ) + # Malware detection MALWARE_DETECTION = { "BACKEND": values.Value( diff --git a/src/backend/pyproject.toml b/src/backend/pyproject.toml index 43ffc379..e9b54873 100644 --- a/src/backend/pyproject.toml +++ b/src/backend/pyproject.toml @@ -30,7 +30,6 @@ dependencies = [ "celery[redis]==5.6.2", "defusedxml==0.7.1", "django<6.0.0", - "django-celery-beat==2.8.1", "django-configurations==2.5.1", "django-cors-headers==4.9.0", "django-countries==8.2.0", diff --git a/src/backend/uv.lock b/src/backend/uv.lock index ce8b166b..87c0cc02 100644 --- a/src/backend/uv.lock +++ b/src/backend/uv.lock @@ -289,18 +289,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ce/a3/43b749004e3c09452e39bb56347a008f0a0668aad37324a99b5c8ca91d9e/coverage-7.12.0-py3-none-any.whl", hash = "sha256:159d50c0b12e060b15ed3d39f87ed43d4f7f7ad40b8a534f4dd331adbb51104a", size = 209503, upload-time = "2025-11-18T13:34:18.892Z" }, ] -[[package]] -name = "cron-descriptor" -version = "2.0.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7c/31/0b21d1599656b2ffa6043e51ca01041cd1c0f6dacf5a3e2b620ed120e7d8/cron_descriptor-2.0.6.tar.gz", hash = "sha256:e39d2848e1d8913cfb6e3452e701b5eec662ee18bea8cc5aa53ee1a7bb217157", size = 49456, upload-time = "2025-09-03T16:30:22.434Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/21/cc/361326a54ad92e2e12845ad15e335a4e14b8953665007fb514d3393dfb0f/cron_descriptor-2.0.6-py3-none-any.whl", hash = "sha256:3a1c0d837c0e5a32e415f821b36cf758eb92d510e6beff8fbfe4fa16573d93d6", size = 74446, upload-time = "2025-09-03T16:30:21.397Z" }, -] - [[package]] name = "cryptography" version = "46.0.3" @@ -404,23 +392,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/17/b0/7f42bfc38b8f19b78546d47147e083ed06e12fc29c42da95655e0962c6c2/django-5.2.9-py3-none-any.whl", hash = "sha256:3a4ea88a70370557ab1930b332fd2887a9f48654261cdffda663fef5976bb00a", size = 8290652, upload-time = "2025-12-02T14:01:03.485Z" }, ] -[[package]] -name = "django-celery-beat" -version = "2.8.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "celery" }, - { name = "cron-descriptor" }, - { name = "django" }, - { name = "django-timezone-field" }, - { name = "python-crontab" }, - { name = "tzdata" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/aa/11/0c8b412869b4fda72828572068312b10aafe7ccef7b41af3633af31f9d4b/django_celery_beat-2.8.1.tar.gz", hash = "sha256:dfad0201c0ac50c91a34700ef8fa0a10ee098cc7f3375fe5debed79f2204f80a", size = 175802, upload-time = "2025-05-13T06:58:29.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/61/e5/3a0167044773dee989b498e9a851fc1663bea9ab879f1179f7b8a827ac10/django_celery_beat-2.8.1-py3-none-any.whl", hash = "sha256:da2b1c6939495c05a551717509d6e3b79444e114a027f7b77bf3727c2a39d171", size = 104833, upload-time = "2025-05-13T06:58:27.309Z" }, -] - [[package]] name = "django-configurations" version = "2.5.1" @@ -669,7 +640,6 @@ dependencies = [ { name = "defusedxml" }, { name = "dj-database-url" }, { name = "django" }, - { name = "django-celery-beat" }, { name = "django-configurations" }, { name = "django-cors-headers" }, { name = "django-countries" }, @@ -732,7 +702,6 @@ requires-dist = [ { name = "defusedxml", specifier = "==0.7.1" }, { name = "dj-database-url", specifier = "==3.0.1" }, { name = "django", specifier = "<6.0.0" }, - { name = "django-celery-beat", specifier = "==2.8.1" }, { name = "django-configurations", specifier = "==2.5.1" }, { name = "django-cors-headers", specifier = "==4.9.0" }, { name = "django-countries", specifier = "==8.2.0" }, @@ -1416,15 +1385,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88", size = 46396, upload-time = "2025-07-01T13:30:56.632Z" }, ] -[[package]] -name = "python-crontab" -version = "3.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/99/7f/c54fb7e70b59844526aa4ae321e927a167678660ab51dda979955eafb89a/python_crontab-3.3.0.tar.gz", hash = "sha256:007c8aee68dddf3e04ec4dce0fac124b93bd68be7470fc95d2a9617a15de291b", size = 57626, upload-time = "2025-07-13T20:05:35.535Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/47/42/bb4afa5b088f64092036221843fc989b7db9d9d302494c1f8b024ee78a46/python_crontab-3.3.0-py3-none-any.whl", hash = "sha256:739a778b1a771379b75654e53fd4df58e5c63a9279a63b5dfe44c0fcc3ee7884", size = 27533, upload-time = "2025-07-13T20:05:34.266Z" }, -] - [[package]] name = "python-dateutil" version = "2.9.0.post0" diff --git a/src/backend/wopi/tasks/configure_wopi.py b/src/backend/wopi/tasks/configure_wopi.py index 082eb83a..cf3965c0 100644 --- a/src/backend/wopi/tasks/configure_wopi.py +++ b/src/backend/wopi/tasks/configure_wopi.py @@ -17,13 +17,18 @@ WOPI_DEFAULT_CONFIGURATION = { } -@celery_app.on_after_configure.connect +@celery_app.on_after_finalize.connect def setup_periodic_tasks(sender: Celery, **kwargs): """Setup periodic tasks.""" sender.add_periodic_task( - crontab(minute="0"), + crontab( + minute=settings.WOPI_CONFIGURATION_CRONTAB_MINUTE, + hour=settings.WOPI_CONFIGURATION_CRONTAB_HOUR, + day_of_month=settings.WOPI_CONFIGURATION_CRONTAB_DAY_OF_MONTH, + month_of_year=settings.WOPI_CONFIGURATION_CRONTAB_MONTH_OF_YEAR, + ), configure_wopi_clients.s(), - name="configure_wopi_clients_every_hour", + name="configure_wopi_clients", serializer="json", )