1# frozen_string_literal: true 2 3constraints(::Constraints::ProjectUrlConstrainer.new) do 4 # If the route has a wildcard segment, the segment has a regex constraint, 5 # the segment is potentially followed by _another_ wildcard segment, and 6 # the `format` option is not set to false, we need to specify that 7 # regex constraint _outside_ of `constraints: {}`. 8 # 9 # Otherwise, Rails will overwrite the constraint with `/.+?/`, 10 # which breaks some of our wildcard routes like `/blob/*id` 11 # and `/tree/*id` that depend on the negative lookahead inside 12 # `Gitlab::PathRegex.full_namespace_route_regex`, which helps the router 13 # determine whether a certain path segment is part of `*namespace_id`, 14 # `:project_id`, or `*id`. 15 # 16 # See https://github.com/rails/rails/blob/v4.2.8/actionpack/lib/action_dispatch/routing/mapper.rb#L155 17 scope(path: '*namespace_id', 18 as: :namespace, 19 namespace_id: Gitlab::PathRegex.full_namespace_route_regex) do 20 scope(path: ':project_id', 21 constraints: { project_id: Gitlab::PathRegex.project_route_regex }, 22 module: :projects, 23 as: :project) do 24 # Begin of the /-/ scope. 25 # Use this scope for all new project routes. 26 scope '-' do 27 get 'archive/*id', constraints: { format: Gitlab::PathRegex.archive_formats_regex, id: /.+?/ }, to: 'repositories#archive', as: 'archive' 28 # Since the page parameter can contain slashes (panel/new), use Rails' 29 # "Route Globbing" syntax (/*page) so that the route helpers do not encode 30 # the slash character. 31 get 'metrics(/:dashboard_path)(/*page)', constraints: { dashboard_path: /.+\.yml/, page: 'panel/new' }, 32 to: 'metrics_dashboard#show', as: :metrics_dashboard, format: false 33 34 namespace :metrics, module: :metrics do 35 namespace :dashboards do 36 post :builder, to: 'builder#panel_preview' 37 end 38 end 39 40 namespace :security do 41 resource :configuration, only: [:show], controller: :configuration do 42 resource :sast, only: [:show], controller: :sast_configuration 43 end 44 end 45 46 resources :artifacts, only: [:index, :destroy] 47 48 resources :packages, only: [:index, :show, :destroy], module: :packages 49 resources :package_files, only: [], module: :packages do 50 member do 51 get :download 52 end 53 end 54 55 resources :infrastructure_registry, only: [:index, :show], module: :packages 56 57 resources :jobs, only: [:index, :show], constraints: { id: /\d+/ } do 58 collection do 59 resources :artifacts, only: [] do 60 collection do 61 get :latest_succeeded, 62 path: '*ref_name_and_path', 63 format: false 64 end 65 end 66 end 67 68 member do 69 get :status 70 post :cancel 71 post :unschedule 72 post :retry 73 post :play 74 post :erase 75 get :trace, defaults: { format: 'json' } 76 get :raw 77 get :terminal 78 get :proxy 79 80 # These routes are also defined in gitlab-workhorse. Make sure to update accordingly. 81 get '/terminal.ws/authorize', to: 'jobs#terminal_websocket_authorize', format: false 82 get '/proxy.ws/authorize', to: 'jobs#proxy_websocket_authorize', format: false 83 end 84 85 resource :artifacts, only: [] do 86 get :download 87 get :browse, path: 'browse(/*path)', format: false 88 get :file, path: 'file/*path', format: false 89 get :raw, path: 'raw/*path', format: false 90 post :keep 91 end 92 end 93 94 get :learn_gitlab, action: :index, controller: 'learn_gitlab' 95 96 namespace :ci do 97 resource :lint, only: [:show, :create] 98 resource :pipeline_editor, only: [:show], controller: :pipeline_editor, path: 'editor' 99 resources :daily_build_group_report_results, only: [:index], constraints: { format: /(csv|json)/ } 100 namespace :prometheus_metrics do 101 resources :histograms, only: [:create], constraints: { format: 'json' } 102 end 103 end 104 105 resources :runners, only: [:index, :edit, :update, :destroy, :show] do 106 member do 107 post :resume 108 post :pause 109 end 110 111 collection do 112 post :toggle_shared_runners 113 post :toggle_group_runners 114 end 115 end 116 117 namespace :settings do 118 resource :ci_cd, only: [:show, :update], controller: 'ci_cd' do 119 post :reset_cache 120 put :reset_registration_token 121 post :create_deploy_token, path: 'deploy_token/create', to: 'repository#create_deploy_token' 122 get :runner_setup_scripts, format: :json 123 end 124 125 resource :operations, only: [:show, :update] do 126 member do 127 post :reset_alerting_token 128 post :reset_pagerduty_token 129 end 130 end 131 132 resource :integrations, only: [:show] 133 134 resource :repository, only: [:show], controller: :repository do 135 # TODO: Removed this "create_deploy_token" route after change was made in app/helpers/ci_variables_helper.rb:14 136 # See MR comment for more detail: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27059#note_311585356 137 post :create_deploy_token, path: 'deploy_token/create' 138 post :cleanup 139 end 140 141 resources :access_tokens, only: [:index, :create] do 142 member do 143 put :revoke 144 end 145 end 146 147 resource :packages_and_registries, only: [:show] 148 end 149 150 resources :usage_quotas, only: [:index] 151 152 resources :autocomplete_sources, only: [] do 153 collection do 154 get 'members' 155 get 'issues' 156 get 'merge_requests' 157 get 'labels' 158 get 'milestones' 159 get 'commands' 160 get 'snippets' 161 end 162 end 163 164 resources :project_members, except: [:show, :new, :edit], constraints: { id: %r{[a-zA-Z./0-9_\-#%+:]+} }, concerns: :access_requestable do 165 collection do 166 delete :leave 167 168 # Used for import team 169 # from another project 170 get :import 171 post :apply_import 172 end 173 174 member do 175 post :resend_invite 176 end 177 end 178 179 resources :deploy_keys, constraints: { id: /\d+/ }, only: [:index, :new, :create, :edit, :update] do 180 member do 181 put :enable 182 put :disable 183 end 184 end 185 186 resources :deploy_tokens, constraints: { id: /\d+/ }, only: [] do 187 member do 188 put :revoke 189 end 190 end 191 192 resources :milestones, constraints: { id: /\d+/ } do 193 member do 194 post :promote 195 get :issues 196 get :merge_requests 197 get :participants 198 get :labels 199 end 200 end 201 202 resources :labels, except: [:show], constraints: { id: /\d+/ } do 203 collection do 204 post :generate 205 post :set_priorities 206 end 207 208 member do 209 post :promote 210 post :toggle_subscription 211 delete :remove_priority 212 end 213 end 214 215 resources :services, constraints: { id: %r{[^/]+} }, only: [:edit, :update] do 216 member do 217 put :test 218 end 219 220 resources :hook_logs, only: [:show], controller: :service_hook_logs do 221 member do 222 post :retry 223 end 224 end 225 end 226 227 resources :boards, only: [:index, :show, :create, :update, :destroy], constraints: { id: /\d+/ } do 228 collection do 229 get :recent 230 end 231 end 232 233 resources :releases, only: [:index, :new, :show, :edit], param: :tag, constraints: { tag: %r{[^/]+} } do 234 member do 235 get :downloads, path: 'downloads/*filepath', format: false 236 scope module: :releases do 237 resources :evidences, only: [:show] 238 end 239 end 240 end 241 242 resources :logs, only: [:index] do 243 collection do 244 get :k8s 245 get :elasticsearch 246 end 247 end 248 249 resources :starrers, only: [:index] 250 resources :forks, only: [:index, :new, :create] 251 resources :group_links, only: [:create, :update, :destroy], constraints: { id: /\d+|:id/ } 252 253 resource :import, only: [:new, :create, :show] 254 resource :avatar, only: [:show, :destroy] 255 256 scope :grafana, as: :grafana_api do 257 get 'proxy/:datasource_id/*proxy_path', to: 'grafana_api#proxy' 258 get :metrics_dashboard, to: 'grafana_api#metrics_dashboard' 259 end 260 261 resource :mattermost, only: [:new, :create] 262 resource :variables, only: [:show, :update] 263 resources :triggers, only: [:index, :create, :edit, :update, :destroy] 264 265 resource :mirror, only: [:show, :update] do 266 member do 267 get :ssh_host_keys, constraints: { format: :json } 268 post :update_now 269 end 270 end 271 272 resource :cycle_analytics, only: :show, path: 'value_stream_analytics' 273 scope module: :cycle_analytics, as: 'cycle_analytics', path: 'value_stream_analytics' do 274 scope :events, controller: 'events' do 275 get :issue 276 get :plan 277 get :code 278 get :test 279 get :review 280 get :staging 281 get :production 282 end 283 end 284 get '/cycle_analytics', to: redirect('%{namespace_id}/%{project_id}/-/value_stream_analytics') 285 286 namespace :analytics do 287 resource :cycle_analytics, only: :show, path: 'value_stream_analytics' 288 scope module: :cycle_analytics, as: 'cycle_analytics', path: 'value_stream_analytics' do 289 resources :value_streams, only: [:index] do 290 resources :stages, only: [:index] do 291 member do 292 get :median 293 get :average 294 get :records 295 get :count 296 end 297 end 298 end 299 resource :summary, controller: :summary, only: :show 300 end 301 end 302 303 resources :cluster_agents, only: [:show], param: :name 304 305 concerns :clusterable 306 307 namespace :serverless do 308 scope :functions do 309 get '/:environment_id/:id', to: 'functions#show' 310 get '/:environment_id/:id/metrics', to: 'functions#metrics', as: :metrics 311 end 312 313 resources :functions, only: [:index] 314 end 315 316 resources :terraform, only: [:index] 317 318 resources :google_cloud, only: [:index] 319 320 namespace :google_cloud do 321 resources :service_accounts, only: [:index, :create] 322 end 323 324 resources :environments, except: [:destroy] do 325 member do 326 post :stop 327 post :cancel_auto_stop 328 get :terminal 329 get :metrics 330 get :additional_metrics 331 get :metrics_dashboard 332 333 # This route is also defined in gitlab-workhorse. Make sure to update accordingly. 334 get '/terminal.ws/authorize', to: 'environments#terminal_websocket_authorize', format: false 335 336 get '/prometheus/api/v1/*proxy_path', to: 'environments/prometheus_api#prometheus_proxy', as: :prometheus_api 337 338 get '/sample_metrics', to: 'environments/sample_metrics#query' 339 end 340 341 collection do 342 get :metrics, action: :metrics_redirect 343 get :folder, path: 'folders/*id', constraints: { format: /(html|json)/ } 344 get :search 345 end 346 347 resources :deployments, only: [:index] do 348 member do 349 get :metrics 350 get :additional_metrics 351 end 352 end 353 end 354 355 namespace :performance_monitoring do 356 resources :dashboards, only: [:create] do 357 collection do 358 put '/:file_name', to: 'dashboards#update', constraints: { file_name: /.+\.yml/ } 359 end 360 end 361 end 362 363 resources :alert_management, only: [:index] do 364 get 'details', on: :member 365 end 366 367 get 'alert_management/:id', to: 'alert_management#details', as: 'alert_management_alert' 368 369 get 'work_items/*work_items_path' => 'work_items#index', as: :work_items 370 371 resource :tracing, only: [:show] 372 373 post 'incidents/integrations/pagerduty', to: 'incident_management/pager_duty_incidents#create' 374 375 resources :incidents, only: [:index] 376 377 get 'issues/incident/:id' => 'incidents#show', as: :issues_incident 378 379 namespace :error_tracking do 380 resources :projects, only: :index 381 end 382 383 resources :product_analytics, only: [:index] do 384 collection do 385 get :setup 386 get :test 387 get :graphs 388 end 389 end 390 391 resources :error_tracking, only: [:index], controller: :error_tracking do 392 collection do 393 get ':issue_id/details', 394 to: 'error_tracking#details', 395 as: 'details' 396 get ':issue_id/stack_trace', 397 to: 'error_tracking/stack_traces#index', 398 as: 'stack_trace' 399 put ':issue_id', 400 to: 'error_tracking#update', 401 as: 'update' 402 end 403 end 404 405 namespace :design_management do 406 namespace :designs, path: 'designs/:design_id(/:sha)', constraints: -> (params) { params[:sha].nil? || Gitlab::Git.commit_id?(params[:sha]) } do 407 resource :raw_image, only: :show 408 resources :resized_image, only: :show, constraints: -> (params) { ::DesignManagement::DESIGN_IMAGE_SIZES.include?(params[:id]) } 409 end 410 end 411 412 get '/snippets/:snippet_id/raw/:ref/*path', 413 to: 'snippets/blobs#raw', 414 format: false, 415 as: :snippet_blob_raw, 416 constraints: { snippet_id: /\d+/ } 417 418 draw :issues 419 draw :merge_requests 420 draw :pipelines 421 422 # The wiki and repository routing contains wildcard characters so 423 # its preferable to keep it below all other project routes 424 draw :repository 425 draw :wiki 426 427 namespace :import do 428 resource :jira, only: [:show], controller: :jira 429 end 430 431 resources :snippets, except: [:create, :update, :destroy], concerns: :awardable, constraints: { id: /\d+/ } do 432 member do 433 get :raw 434 post :mark_as_spam 435 end 436 end 437 438 resources :feature_flags, param: :iid 439 resource :feature_flags_client, only: [] do 440 post :reset_token 441 end 442 resources :feature_flags_user_lists, param: :iid, only: [:index, :new, :edit, :show] 443 444 get '/schema/:branch/*filename', 445 to: 'web_ide_schemas#show', 446 format: false, 447 as: :schema 448 449 resources :hooks, only: [:index, :create, :edit, :update, :destroy], constraints: { id: /\d+/ } do 450 member do 451 post :test 452 end 453 454 resources :hook_logs, only: [:show] do 455 member do 456 post :retry 457 end 458 end 459 end 460 461 namespace :integrations do 462 resource :shimo, only: [:show] 463 end 464 end 465 # End of the /-/ scope. 466 467 # All new routes should go under /-/ scope. 468 # Look for scope '-' at the top of the file. 469 470 # 471 # Service Desk 472 # 473 get '/service_desk' => 'service_desk#show', as: :service_desk # rubocop:todo Cop/PutProjectRoutesUnderScope 474 put '/service_desk' => 'service_desk#update', as: :service_desk_refresh # rubocop:todo Cop/PutProjectRoutesUnderScope 475 476 # 477 # Templates 478 # 479 get '/templates/:template_type' => 'templates#index', # rubocop:todo Cop/PutProjectRoutesUnderScope 480 as: :templates, 481 defaults: { format: 'json' }, 482 constraints: { template_type: %r{issue|merge_request}, format: 'json' } 483 484 get '/templates/:template_type/:key' => 'templates#show', # rubocop:todo Cop/PutProjectRoutesUnderScope 485 as: :template, 486 defaults: { format: 'json' }, 487 constraints: { key: %r{[^/]+}, template_type: %r{issue|merge_request}, format: 'json' } 488 489 get '/description_templates/names/:template_type', # rubocop:todo Cop/PutProjectRoutesUnderScope 490 to: 'templates#names', 491 as: :template_names, 492 defaults: { format: 'json' }, 493 constraints: { template_type: %r{issue|merge_request}, format: 'json' } 494 495 resource :pages, only: [:show, :update, :destroy] do # rubocop: disable Cop/PutProjectRoutesUnderScope 496 resources :domains, except: :index, controller: 'pages_domains', constraints: { id: %r{[^/]+} } do # rubocop: disable Cop/PutProjectRoutesUnderScope 497 member do 498 post :verify # rubocop:todo Cop/PutProjectRoutesUnderScope 499 post :retry_auto_ssl # rubocop:todo Cop/PutProjectRoutesUnderScope 500 delete :clean_certificate # rubocop:todo Cop/PutProjectRoutesUnderScope 501 end 502 end 503 end 504 505 namespace :prometheus do 506 resources :alerts, constraints: { id: /\d+/ }, only: [:index, :create, :show, :update, :destroy] do # rubocop: disable Cop/PutProjectRoutesUnderScope 507 post :notify, on: :collection # rubocop:todo Cop/PutProjectRoutesUnderScope 508 member do 509 get :metrics_dashboard # rubocop:todo Cop/PutProjectRoutesUnderScope 510 end 511 end 512 513 resources :metrics, constraints: { id: %r{[^\/]+} }, only: [:index, :new, :create, :edit, :update, :destroy] do # rubocop: disable Cop/PutProjectRoutesUnderScope 514 get :active_common, on: :collection # rubocop:todo Cop/PutProjectRoutesUnderScope 515 post :validate_query, on: :collection # rubocop:todo Cop/PutProjectRoutesUnderScope 516 end 517 end 518 519 post 'alerts/notify', to: 'alerting/notifications#create' # rubocop:todo Cop/PutProjectRoutesUnderScope 520 post 'alerts/notify/:name/:endpoint_identifier', # rubocop:todo Cop/PutProjectRoutesUnderScope 521 to: 'alerting/notifications#create', 522 as: :alert_http_integration, 523 constraints: { endpoint_identifier: /[A-Za-z0-9]+/ } 524 525 draw :legacy_builds 526 527 resources :container_registry, only: [:index, :destroy, :show], # rubocop: disable Cop/PutProjectRoutesUnderScope 528 controller: 'registry/repositories' 529 530 namespace :registry do 531 resources :repository, only: [] do # rubocop: disable Cop/PutProjectRoutesUnderScope 532 # We default to JSON format in the controller to avoid ambiguity. 533 # `latest.json` could either be a request for a tag named `latest` 534 # in JSON format, or a request for tag named `latest.json`. 535 scope format: false do 536 resources :tags, only: [:index, :destroy], # rubocop: disable Cop/PutProjectRoutesUnderScope 537 constraints: { id: Gitlab::Regex.container_registry_tag_regex } do 538 collection do 539 delete :bulk_destroy # rubocop:todo Cop/PutProjectRoutesUnderScope 540 end 541 end 542 end 543 end 544 end 545 546 resources :notes, only: [:create, :destroy, :update], concerns: :awardable, constraints: { id: /\d+/ } do # rubocop: disable Cop/PutProjectRoutesUnderScope 547 member do 548 delete :delete_attachment # rubocop:todo Cop/PutProjectRoutesUnderScope 549 post :resolve # rubocop:todo Cop/PutProjectRoutesUnderScope 550 delete :resolve, action: :unresolve # rubocop:todo Cop/PutProjectRoutesUnderScope 551 get :outdated_line_change # rubocop:todo Cop/PutProjectRoutesUnderScope 552 end 553 end 554 555 get 'noteable/:target_type/:target_id/notes' => 'notes#index', as: 'noteable_notes' # rubocop:todo Cop/PutProjectRoutesUnderScope 556 557 resources :todos, only: [:create] # rubocop: disable Cop/PutProjectRoutesUnderScope 558 559 resources :uploads, only: [:create] do # rubocop: disable Cop/PutProjectRoutesUnderScope 560 collection do 561 get ":secret/:filename", action: :show, as: :show, constraints: { filename: %r{[^/]+} }, format: false, defaults: { format: nil } # rubocop:todo Cop/PutProjectRoutesUnderScope 562 post :authorize # rubocop:todo Cop/PutProjectRoutesUnderScope 563 end 564 end 565 566 resources :runner_projects, only: [:create, :destroy] # rubocop: disable Cop/PutProjectRoutesUnderScope 567 resources :badges, only: [:index] do # rubocop: disable Cop/PutProjectRoutesUnderScope 568 collection do 569 scope '*ref', constraints: { ref: Gitlab::PathRegex.git_reference_regex } do 570 constraints format: /svg/ do 571 get :pipeline # rubocop:todo Cop/PutProjectRoutesUnderScope 572 get :coverage # rubocop:todo Cop/PutProjectRoutesUnderScope 573 end 574 end 575 end 576 end 577 578 scope :service_ping, controller: :service_ping do 579 post :web_ide_clientside_preview # rubocop:todo Cop/PutProjectRoutesUnderScope 580 post :web_ide_pipelines_count # rubocop:todo Cop/PutProjectRoutesUnderScope 581 end 582 583 resources :web_ide_terminals, path: :ide_terminals, only: [:create, :show], constraints: { id: /\d+/, format: :json } do # rubocop: disable Cop/PutProjectRoutesUnderScope 584 member do 585 post :cancel # rubocop:todo Cop/PutProjectRoutesUnderScope 586 post :retry # rubocop:todo Cop/PutProjectRoutesUnderScope 587 end 588 589 collection do 590 post :check_config # rubocop:todo Cop/PutProjectRoutesUnderScope 591 end 592 end 593 594 # Deprecated unscoped routing. 595 scope as: 'deprecated' do 596 # Issue https://gitlab.com/gitlab-org/gitlab/issues/118849 597 draw :repository_deprecated 598 599 # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/223719 600 # rubocop: disable Cop/PutProjectRoutesUnderScope 601 get '/snippets/:id/raw', 602 to: 'snippets#raw', 603 format: false, 604 constraints: { id: /\d+/ } 605 # rubocop: enable Cop/PutProjectRoutesUnderScope 606 end 607 608 # All new routes should go under /-/ scope. 609 # Look for scope '-' at the top of the file. 610 611 # Legacy routes. 612 # Introduced in 12.0. 613 # Should be removed with https://gitlab.com/gitlab-org/gitlab/issues/28848. 614 Gitlab::Routing.redirect_legacy_paths(self, :mirror, :tags, :hooks, 615 :commits, :commit, :find_file, :files, :compare, 616 :cycle_analytics, :mattermost, :variables, :triggers, 617 :environments, :protected_environments, :error_tracking, :alert_management, 618 :tracing, 619 :serverless, :clusters, :audit_events, :wikis, :merge_requests, 620 :vulnerability_feedback, :security, :dependencies, :issues, 621 :pipelines, :pipeline_schedules, :runners, :snippets) 622 end 623 # rubocop: disable Cop/PutProjectRoutesUnderScope 624 resources(:projects, 625 path: '/', 626 constraints: { id: Gitlab::PathRegex.project_route_regex }, 627 only: [:edit, :show, :update, :destroy]) do 628 member do 629 put :transfer 630 delete :remove_fork 631 post :archive 632 post :unarchive 633 post :housekeeping 634 post :toggle_star 635 post :preview_markdown 636 post :export 637 post :remove_export 638 post :generate_new_export 639 get :download_export 640 get :activity 641 get :refs 642 put :new_issuable_address 643 get :unfoldered_environment_names 644 end 645 end 646 # rubocop: enable Cop/PutProjectRoutesUnderScope 647 end 648end 649 650# It's under /-/jira scope but cop is only checking /-/ 651# rubocop: disable Cop/PutProjectRoutesUnderScope 652scope path: '(/-/jira)', constraints: ::Constraints::JiraEncodedUrlConstrainer.new, as: :jira do 653 scope path: '*namespace_id/:project_id', 654 namespace_id: Gitlab::Jira::Dvcs::ENCODED_ROUTE_REGEX, 655 project_id: Gitlab::Jira::Dvcs::ENCODED_ROUTE_REGEX do 656 get '/', to: redirect { |params, req| 657 ::Gitlab::Jira::Dvcs.restore_full_path( 658 namespace: params[:namespace_id], 659 project: params[:project_id] 660 ) 661 } 662 663 get 'commit/:id', constraints: { id: /\h{7,40}/ }, to: redirect { |params, req| 664 project_full_path = ::Gitlab::Jira::Dvcs.restore_full_path( 665 namespace: params[:namespace_id], 666 project: params[:project_id] 667 ) 668 669 "/#{project_full_path}/commit/#{params[:id]}" 670 } 671 672 get 'tree/*id', as: nil, to: redirect { |params, req| 673 project_full_path = ::Gitlab::Jira::Dvcs.restore_full_path( 674 namespace: params[:namespace_id], 675 project: params[:project_id] 676 ) 677 678 "/#{project_full_path}/-/tree/#{params[:id]}" 679 } 680 end 681end 682# rubocop: enable Cop/PutProjectRoutesUnderScope 683