|
1 | | -from django.urls import path, re_path |
| 1 | +from typing import Callable, Optional |
| 2 | + |
| 3 | +from django.urls import URLPattern, path, re_path |
2 | 4 |
|
3 | 5 | from . import cron_views, stats_views, views |
4 | 6 | from .contrib.prometheus import RQCollector |
5 | 7 |
|
6 | | -metrics_view = ( |
7 | | - [ |
8 | | - re_path(r'^metrics/?$', stats_views.prometheus_metrics, name='rq_metrics'), |
| 8 | + |
| 9 | +def get_api_urlpatterns() -> list[URLPattern]: |
| 10 | + """ |
| 11 | + Get URL patterns for views that have their own authentication (API tokens). |
| 12 | +
|
| 13 | + These views should NOT be wrapped with admin_view because they support |
| 14 | + API token authentication that must work without Django session auth. |
| 15 | +
|
| 16 | + Returns: |
| 17 | + List of URL patterns for API-authenticated views. |
| 18 | + """ |
| 19 | + # Conditional metrics view (only if prometheus_client is installed) |
| 20 | + metrics_view = ( |
| 21 | + [ |
| 22 | + re_path(r'^metrics/?$', stats_views.prometheus_metrics, name='rq_metrics'), |
| 23 | + ] |
| 24 | + if RQCollector is not None |
| 25 | + else [] |
| 26 | + ) |
| 27 | + |
| 28 | + return [ |
| 29 | + # Stats JSON (supports API token authentication) |
| 30 | + re_path(r'^stats.json/?$', stats_views.stats_json, name='rq_home_json'), |
| 31 | + re_path(r'^stats.json/(?P<token>[\w]+)?/?$', stats_views.stats_json, name='rq_home_json'), |
| 32 | + # Prometheus metrics (supports API token authentication) |
| 33 | + *metrics_view, |
| 34 | + ] |
| 35 | + |
| 36 | + |
| 37 | +def get_admin_urlpatterns(view_wrapper: Optional[Callable] = None) -> list[URLPattern]: |
| 38 | + """ |
| 39 | + Get URL patterns for views that should be wrapped with admin authentication. |
| 40 | +
|
| 41 | + Args: |
| 42 | + view_wrapper: Optional function to wrap each view (e.g., admin_site.admin_view). |
| 43 | +
|
| 44 | + Returns: |
| 45 | + List of URL patterns for admin-authenticated views. |
| 46 | + """ |
| 47 | + |
| 48 | + def maybe_wrap(view: Callable) -> Callable: |
| 49 | + """Apply wrapper if provided, otherwise return view as-is""" |
| 50 | + return view_wrapper(view) if view_wrapper else view |
| 51 | + |
| 52 | + return [ |
| 53 | + # Dashboard |
| 54 | + path('', maybe_wrap(stats_views.stats), name='rq_home'), |
| 55 | + # Queue views |
| 56 | + path('queues/<int:queue_index>/', maybe_wrap(views.jobs), name='rq_jobs'), |
| 57 | + path('queues/<int:queue_index>/finished/', maybe_wrap(views.finished_jobs), name='rq_finished_jobs'), |
| 58 | + path('queues/<int:queue_index>/failed/', maybe_wrap(views.failed_jobs), name='rq_failed_jobs'), |
| 59 | + path('queues/<int:queue_index>/failed/clear/', maybe_wrap(views.delete_failed_jobs), name='rq_delete_failed_jobs'), |
| 60 | + path('queues/<int:queue_index>/scheduled/', maybe_wrap(views.scheduled_jobs), name='rq_scheduled_jobs'), |
| 61 | + path('queues/<int:queue_index>/started/', maybe_wrap(views.started_jobs), name='rq_started_jobs'), |
| 62 | + path('queues/<int:queue_index>/deferred/', maybe_wrap(views.deferred_jobs), name='rq_deferred_jobs'), |
| 63 | + path('queues/<int:queue_index>/empty/', maybe_wrap(views.clear_queue), name='rq_clear'), |
| 64 | + path('queues/<int:queue_index>/requeue-all/', maybe_wrap(views.requeue_all), name='rq_requeue_all'), |
| 65 | + # Job detail and actions |
| 66 | + path('queues/<int:queue_index>/<str:job_id>/', maybe_wrap(views.job_detail), name='rq_job_detail'), |
| 67 | + path('queues/<int:queue_index>/<str:job_id>/delete/', maybe_wrap(views.delete_job), name='rq_delete_job'), |
| 68 | + path('queues/<int:queue_index>/<str:job_id>/requeue/', maybe_wrap(views.requeue_job_view), name='rq_requeue_job'), |
| 69 | + path('queues/<int:queue_index>/<str:job_id>/enqueue/', maybe_wrap(views.enqueue_job), name='rq_enqueue_job'), |
| 70 | + path('queues/<int:queue_index>/<str:job_id>/stop/', maybe_wrap(views.stop_job), name='rq_stop_job'), |
| 71 | + # Bulk actions |
| 72 | + path('queues/confirm-action/<int:queue_index>/', maybe_wrap(views.confirm_action), name='rq_confirm_action'), |
| 73 | + path('queues/actions/<int:queue_index>/', maybe_wrap(views.actions), name='rq_actions'), |
| 74 | + # Workers |
| 75 | + path('workers/<int:queue_index>/', maybe_wrap(views.workers), name='rq_workers'), |
| 76 | + path('workers/<int:queue_index>/<str:key>/', maybe_wrap(views.worker_details), name='rq_worker_details'), |
| 77 | + # Schedulers |
| 78 | + path('schedulers/<int:scheduler_index>/', maybe_wrap(views.scheduler_jobs), name='rq_scheduler_jobs'), |
| 79 | + path( |
| 80 | + 'cron-schedulers/<int:connection_index>/<str:scheduler_name>/', |
| 81 | + maybe_wrap(cron_views.cron_scheduler_detail), |
| 82 | + name='rq_cron_scheduler_detail', |
| 83 | + ), |
9 | 84 | ] |
10 | | - if RQCollector is not None |
11 | | - else [] |
12 | | -) |
13 | | - |
14 | | -urlpatterns = [ |
15 | | - path('', stats_views.stats, name='rq_home'), |
16 | | - re_path(r'^stats.json/?$', stats_views.stats_json, name='rq_home_json'), |
17 | | - re_path(r'^stats.json/(?P<token>[\w]+)?/?$', stats_views.stats_json, name='rq_home_json'), |
18 | | - *metrics_view, |
19 | | - path('queues/<int:queue_index>/', views.jobs, name='rq_jobs'), |
20 | | - path('workers/<int:queue_index>/', views.workers, name='rq_workers'), |
21 | | - path('workers/<int:queue_index>/<str:key>/', views.worker_details, name='rq_worker_details'), |
22 | | - path('queues/<int:queue_index>/finished/', views.finished_jobs, name='rq_finished_jobs'), |
23 | | - path('queues/<int:queue_index>/failed/', views.failed_jobs, name='rq_failed_jobs'), |
24 | | - path('queues/<int:queue_index>/failed/clear/', views.delete_failed_jobs, name='rq_delete_failed_jobs'), |
25 | | - path('queues/<int:queue_index>/scheduled/', views.scheduled_jobs, name='rq_scheduled_jobs'), |
26 | | - path('queues/<int:queue_index>/started/', views.started_jobs, name='rq_started_jobs'), |
27 | | - path('queues/<int:queue_index>/deferred/', views.deferred_jobs, name='rq_deferred_jobs'), |
28 | | - path('queues/<int:queue_index>/empty/', views.clear_queue, name='rq_clear'), |
29 | | - path('queues/<int:queue_index>/requeue-all/', views.requeue_all, name='rq_requeue_all'), |
30 | | - path('queues/<int:queue_index>/<str:job_id>/', views.job_detail, name='rq_job_detail'), |
31 | | - path('queues/<int:queue_index>/<str:job_id>/delete/', views.delete_job, name='rq_delete_job'), |
32 | | - path('queues/confirm-action/<int:queue_index>/', views.confirm_action, name='rq_confirm_action'), |
33 | | - path('queues/actions/<int:queue_index>/', views.actions, name='rq_actions'), |
34 | | - path('queues/<int:queue_index>/<str:job_id>/requeue/', views.requeue_job_view, name='rq_requeue_job'), |
35 | | - path('queues/<int:queue_index>/<str:job_id>/enqueue/', views.enqueue_job, name='rq_enqueue_job'), |
36 | | - path('queues/<int:queue_index>/<str:job_id>/stop/', views.stop_job, name='rq_stop_job'), |
37 | | - path('schedulers/<int:scheduler_index>/', views.scheduler_jobs, name='rq_scheduler_jobs'), |
38 | | - path( |
39 | | - 'cron-schedulers/<int:connection_index>/<str:scheduler_name>/', |
40 | | - cron_views.cron_scheduler_detail, |
41 | | - name='rq_cron_scheduler_detail', |
42 | | - ), |
43 | | -] |
| 85 | + |
| 86 | + |
| 87 | +# Standalone URL patterns (for use with include('django_rq.urls')) |
| 88 | +# Combines both API and admin patterns without wrapping |
| 89 | +urlpatterns = get_api_urlpatterns() + get_admin_urlpatterns() |
0 commit comments