2019-05-03 06:17:27 +08:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2013-12-24 07:50:36 +08:00
|
|
|
require "sidekiq/web"
|
2018-08-01 05:12:55 +08:00
|
|
|
require "mini_scheduler/web"
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2017-12-22 06:17:25 +08:00
|
|
|
# The following constants have been replaced with `RouteFormat` and are deprecated.
|
2019-04-23 18:22:47 +08:00
|
|
|
USERNAME_ROUTE_FORMAT = /[%\w.\-]+?/ unless defined? USERNAME_ROUTE_FORMAT
|
2017-12-22 06:17:25 +08:00
|
|
|
BACKUP_ROUTE_FORMAT = /.+\.(sql\.gz|tar\.gz|tgz)/i unless defined? BACKUP_ROUTE_FORMAT
|
|
|
|
|
2013-02-06 03:16:51 +08:00
|
|
|
Discourse::Application.routes.draw do
|
2021-08-03 21:26:48 +08:00
|
|
|
def patch(*) end # Disable PATCH requests
|
|
|
|
|
2019-07-24 01:17:44 +08:00
|
|
|
scope path: nil, constraints: { format: /(json|html|\*\/\*)/ } do
|
|
|
|
relative_url_root = (defined?(Rails.configuration.relative_url_root) && Rails.configuration.relative_url_root) ? Rails.configuration.relative_url_root + '/' : '/'
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2013-07-02 02:00:06 +08:00
|
|
|
match "/404", to: "exceptions#not_found", via: [:get, :post]
|
2014-04-30 03:17:40 +08:00
|
|
|
get "/404-body" => "exceptions#not_found_body"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2020-06-04 02:45:23 +08:00
|
|
|
get "/bootstrap" => "bootstrap#index"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2019-02-14 04:26:40 +08:00
|
|
|
post "webhooks/aws" => "webhooks#aws"
|
2016-06-07 01:47:45 +08:00
|
|
|
post "webhooks/mailgun" => "webhooks#mailgun"
|
|
|
|
post "webhooks/mailjet" => "webhooks#mailjet"
|
2016-06-13 18:31:01 +08:00
|
|
|
post "webhooks/mandrill" => "webhooks#mandrill"
|
2020-02-11 23:09:07 +08:00
|
|
|
post "webhooks/postmark" => "webhooks#postmark"
|
2019-02-14 04:26:40 +08:00
|
|
|
post "webhooks/sendgrid" => "webhooks#sendgrid"
|
2016-09-27 13:13:34 +08:00
|
|
|
post "webhooks/sparkpost" => "webhooks#sparkpost"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2019-07-24 01:17:44 +08:00
|
|
|
scope path: nil, constraints: { format: /.*/ } do
|
|
|
|
if Rails.env.development?
|
|
|
|
mount Sidekiq::Web => "/sidekiq"
|
|
|
|
mount Logster::Web => "/logs"
|
|
|
|
else
|
|
|
|
# only allow sidekiq in master site
|
2022-02-08 02:05:19 +08:00
|
|
|
mount Sidekiq::Web => "/sidekiq", constraints: AdminConstraint.new(require_master: true)
|
|
|
|
mount Logster::Web => "/logs", constraints: AdminConstraint.new
|
2020-06-05 10:49:31 +08:00
|
|
|
end
|
2019-07-24 01:17:44 +08:00
|
|
|
end
|
2014-05-07 06:23:52 +08:00
|
|
|
|
2015-08-28 05:28:33 +08:00
|
|
|
resources :about do
|
|
|
|
collection do
|
|
|
|
get "live_post_counts"
|
2020-06-05 10:49:31 +08:00
|
|
|
end
|
2015-08-28 05:28:33 +08:00
|
|
|
end
|
2014-08-12 04:59:00 +08:00
|
|
|
|
2016-10-18 23:44:25 +08:00
|
|
|
get "finish-installation" => "finish_installation#index"
|
|
|
|
get "finish-installation/register" => "finish_installation#register"
|
|
|
|
post "finish-installation/register" => "finish_installation#register"
|
|
|
|
get "finish-installation/confirm-email" => "finish_installation#confirm_email"
|
2016-10-21 23:34:19 +08:00
|
|
|
put "finish-installation/resend-email" => "finish_installation#resend_email"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2020-04-09 00:52:36 +08:00
|
|
|
get "pub/check-slug" => "published_pages#check_slug"
|
|
|
|
get "pub/by-topic/:topic_id" => "published_pages#details"
|
|
|
|
put "pub/by-topic/:topic_id" => "published_pages#upsert"
|
|
|
|
delete "pub/by-topic/:topic_id" => "published_pages#destroy"
|
|
|
|
get "pub/:slug" => "published_pages#show"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2015-03-17 03:14:33 +08:00
|
|
|
resources :directory_items
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2015-03-05 12:49:03 +08:00
|
|
|
get "site" => "site#site"
|
|
|
|
namespace :site do
|
|
|
|
get "settings"
|
|
|
|
get "custom_html"
|
|
|
|
get "banner"
|
|
|
|
get "emoji"
|
|
|
|
end
|
2016-08-12 15:10:08 +08:00
|
|
|
|
|
|
|
get "site/basic-info" => 'site#basic_info'
|
2017-03-10 21:16:00 +08:00
|
|
|
get "site/statistics" => 'site#statistics'
|
2016-08-12 15:10:08 +08:00
|
|
|
|
2013-12-24 07:50:36 +08:00
|
|
|
get "srv/status" => "forums#status"
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2016-08-26 01:14:56 +08:00
|
|
|
get "wizard" => "wizard#index"
|
|
|
|
get 'wizard/steps' => 'steps#index'
|
2016-09-08 06:04:01 +08:00
|
|
|
get 'wizard/steps/:id' => "wizard#index"
|
2016-08-26 01:14:56 +08:00
|
|
|
put 'wizard/steps/:id' => "steps#update"
|
2016-08-25 02:35:07 +08:00
|
|
|
|
2013-05-02 15:22:27 +08:00
|
|
|
namespace :admin, constraints: StaffConstraint.new do
|
2013-12-24 07:50:36 +08:00
|
|
|
get "" => "admin#index"
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2015-02-07 06:32:59 +08:00
|
|
|
get 'plugins' => 'plugins#index'
|
|
|
|
|
2013-11-14 03:02:47 +08:00
|
|
|
resources :site_settings, constraints: AdminConstraint.new do
|
|
|
|
collection do
|
2013-12-24 07:50:36 +08:00
|
|
|
get "category/:id" => "site_settings#index"
|
2013-11-14 03:02:47 +08:00
|
|
|
end
|
2019-11-18 02:39:38 +08:00
|
|
|
|
|
|
|
put "user_count" => "site_settings#user_count"
|
2013-11-14 03:02:47 +08:00
|
|
|
end
|
2013-05-02 13:15:17 +08:00
|
|
|
|
2018-06-18 18:31:56 +08:00
|
|
|
get "reports" => "reports#index"
|
2018-08-24 21:28:01 +08:00
|
|
|
get "reports/bulk" => "reports#bulk"
|
2013-12-24 07:50:36 +08:00
|
|
|
get "reports/:type" => "reports#show"
|
2013-02-28 11:39:42 +08:00
|
|
|
|
2020-10-20 04:30:21 +08:00
|
|
|
resources :groups, only: [:create] do
|
|
|
|
member do
|
|
|
|
put "owners" => "groups#add_owners"
|
|
|
|
delete "owners" => "groups#remove_owner"
|
2021-02-03 22:11:08 +08:00
|
|
|
put "primary" => "groups#set_primary"
|
2020-10-20 04:30:21 +08:00
|
|
|
end
|
|
|
|
end
|
2020-08-19 22:41:40 +08:00
|
|
|
resources :groups, except: [:create], constraints: AdminConstraint.new do
|
2013-05-08 13:20:38 +08:00
|
|
|
collection do
|
2020-04-23 00:37:39 +08:00
|
|
|
put "automatic_membership_count" => "groups#automatic_membership_count"
|
2013-05-08 13:20:38 +08:00
|
|
|
end
|
2015-01-22 03:52:48 +08:00
|
|
|
end
|
2013-05-08 13:20:38 +08:00
|
|
|
|
2015-01-22 03:52:48 +08:00
|
|
|
get "groups/:type" => "groups#show", constraints: AdminConstraint.new
|
|
|
|
get "groups/:type/:id" => "groups#show", constraints: AdminConstraint.new
|
|
|
|
|
2017-12-22 04:30:32 +08:00
|
|
|
resources :users, id: RouteFormat.username, except: [:show] do
|
2013-02-06 03:16:51 +08:00
|
|
|
collection do
|
2017-04-27 11:02:59 +08:00
|
|
|
get "list" => "users#index"
|
2013-12-24 07:50:36 +08:00
|
|
|
get "list/:query" => "users#index"
|
2014-07-08 04:18:18 +08:00
|
|
|
get "ip-info" => "users#ip_info"
|
2014-11-21 02:59:20 +08:00
|
|
|
delete "delete-others-with-same-ip" => "users#delete_other_accounts_with_same_ip"
|
2014-11-25 02:34:04 +08:00
|
|
|
get "total-others-with-same-ip" => "users#total_other_accounts_with_same_ip"
|
2013-12-24 07:50:36 +08:00
|
|
|
put "approve-bulk" => "users#approve_bulk"
|
2020-06-05 10:49:31 +08:00
|
|
|
end
|
2018-05-25 23:45:42 +08:00
|
|
|
delete "penalty_history", constraints: AdminConstraint.new
|
2013-12-24 07:50:36 +08:00
|
|
|
put "suspend"
|
2018-12-14 18:04:18 +08:00
|
|
|
put "delete_posts_batch"
|
2013-12-24 07:50:36 +08:00
|
|
|
put "unsuspend"
|
|
|
|
put "revoke_admin", constraints: AdminConstraint.new
|
|
|
|
put "grant_admin", constraints: AdminConstraint.new
|
|
|
|
put "revoke_moderation", constraints: AdminConstraint.new
|
|
|
|
put "grant_moderation", constraints: AdminConstraint.new
|
|
|
|
put "approve"
|
2014-06-06 11:02:52 +08:00
|
|
|
post "log_out", constraints: AdminConstraint.new
|
2013-12-24 07:50:36 +08:00
|
|
|
put "activate"
|
|
|
|
put "deactivate"
|
2017-11-11 01:18:08 +08:00
|
|
|
put "silence"
|
|
|
|
put "unsilence"
|
2013-12-24 07:50:36 +08:00
|
|
|
put "trust_level"
|
2014-09-14 04:55:26 +08:00
|
|
|
put "trust_level_lock"
|
2014-02-11 05:59:36 +08:00
|
|
|
put "primary_group"
|
2014-07-14 02:11:38 +08:00
|
|
|
post "groups" => "users#add_group", constraints: AdminConstraint.new
|
|
|
|
delete "groups/:group_id" => "users#remove_group", constraints: AdminConstraint.new
|
2014-03-19 22:27:21 +08:00
|
|
|
get "badges"
|
2014-09-25 08:19:26 +08:00
|
|
|
get "leader_requirements" => "users#tl3_requirements"
|
|
|
|
get "tl3_requirements"
|
2015-03-07 05:44:54 +08:00
|
|
|
put "anonymize"
|
2020-04-22 16:37:51 +08:00
|
|
|
post "merge"
|
2016-05-07 01:34:33 +08:00
|
|
|
post "reset_bounce_score"
|
2017-12-22 09:18:12 +08:00
|
|
|
put "disable_second_factor"
|
2020-09-15 22:00:10 +08:00
|
|
|
delete "sso_record"
|
2013-02-06 03:16:51 +08:00
|
|
|
end
|
2018-05-25 23:45:42 +08:00
|
|
|
get "users/:id.json" => 'users#show', defaults: { format: 'json' }
|
FEATURE: Centralized 2FA page (#15377)
2FA support in Discourse was added and grown gradually over the years: we first
added support for TOTP for logins, then we implemented backup codes, and last
but not least, security keys. 2FA usage was initially limited to logging in,
but it has been expanded and we now require 2FA for risky actions such as
adding a new admin to the site.
As a result of this gradual growth of the 2FA system, technical debt has
accumulated to the point where it has become difficult to require 2FA for more
actions. We now have 5 different 2FA UI implementations and each one has to
support all 3 2FA methods (TOTP, backup codes, and security keys) which makes
it difficult to maintain a consistent UX for these different implementations.
Moreover, there is a lot of repeated logic in the server-side code behind these
5 UI implementations which hinders maintainability even more.
This commit is the first step towards repaying the technical debt: it builds a
system that centralizes as much as possible of the 2FA server-side logic and
UI. The 2 main components of this system are:
1. A dedicated page for 2FA with support for all 3 methods.
2. A reusable server-side class that centralizes the 2FA logic (the
`SecondFactor::AuthManager` class).
From a top-level view, the 2FA flow in this new system looks like this:
1. User initiates an action that requires 2FA;
2. Server is aware that 2FA is required for this action, so it redirects the
user to the 2FA page if the user has a 2FA method, otherwise the action is
performed.
3. User submits the 2FA form on the page;
4. Server validates the 2FA and if it's successful, the action is performed and
the user is redirected to the previous page.
A more technically-detailed explanation/documentation of the new system is
available as a comment at the top of the `lib/second_factor/auth_manager.rb`
file. Please note that the details are not set in stone and will likely change
in the future, so please don't use the system in your plugins yet.
Since this is a new system that needs to be tested, we've decided to migrate
only the 2FA for adding a new admin to the new system at this time (in this
commit). Our plan is to gradually migrate the remaining 2FA implementations to
the new system.
For screenshots of the 2FA page, see PR #15377 on GitHub.
2022-02-17 17:12:59 +08:00
|
|
|
get 'users/:id/:username' => 'users#show', constraints: { username: RouteFormat.username }, as: :user_show
|
2016-03-29 04:58:45 +08:00
|
|
|
get 'users/:id/:username/badges' => 'users#show'
|
2017-03-16 23:05:28 +08:00
|
|
|
get 'users/:id/:username/tl3_requirements' => 'users#show'
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2014-10-28 08:25:02 +08:00
|
|
|
post "users/sync_sso" => "users#sync_sso", constraints: AdminConstraint.new
|
|
|
|
|
2013-05-02 13:15:17 +08:00
|
|
|
resources :impersonate, constraints: AdminConstraint.new
|
2013-06-04 04:12:24 +08:00
|
|
|
|
2014-06-02 14:52:43 +08:00
|
|
|
resources :email, constraints: AdminConstraint.new do
|
2014-11-25 00:25:48 +08:00
|
|
|
collection do
|
|
|
|
post "test"
|
2014-02-15 07:50:08 +08:00
|
|
|
get "sent"
|
2014-02-15 02:06:21 +08:00
|
|
|
get "skipped"
|
2016-05-03 05:15:32 +08:00
|
|
|
get "bounced"
|
2014-11-25 00:25:48 +08:00
|
|
|
get "received"
|
2016-01-19 07:57:55 +08:00
|
|
|
get "rejected"
|
2016-02-02 04:41:49 +08:00
|
|
|
get "/incoming/:id/raw" => "email#raw_email"
|
2016-02-11 05:00:27 +08:00
|
|
|
get "/incoming/:id" => "email#incoming"
|
2014-11-25 00:25:48 +08:00
|
|
|
get "/incoming_from_bounced/:id" => "email#incoming_from_bounced"
|
2013-12-24 07:50:36 +08:00
|
|
|
get "preview-digest" => "email#preview_digest"
|
2016-11-24 06:46:57 +08:00
|
|
|
get "send-digest" => "email#send_digest"
|
2017-04-05 14:32:50 +08:00
|
|
|
get "smtp_should_reject"
|
2015-10-19 05:33:24 +08:00
|
|
|
post "handle_mail"
|
2018-11-29 07:03:50 +08:00
|
|
|
get "advanced-test"
|
|
|
|
post "advanced-test" => "email#advanced_test"
|
2014-11-25 00:25:48 +08:00
|
|
|
end
|
|
|
|
end
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2013-12-24 07:50:36 +08:00
|
|
|
scope "/logs" do
|
2013-10-22 02:49:51 +08:00
|
|
|
resources :staff_action_logs, only: [:index]
|
2017-04-12 22:52:52 +08:00
|
|
|
get 'staff_action_logs/:id/diff' => 'staff_action_logs#diff'
|
2014-11-25 00:25:48 +08:00
|
|
|
resources :screened_emails, only: [:index, :destroy]
|
2022-01-11 15:16:51 +08:00
|
|
|
resources :screened_ip_addresses, only: [:index, :create, :update, :destroy]
|
2013-10-22 02:49:51 +08:00
|
|
|
resources :screened_urls, only: [:index]
|
2017-11-15 08:13:50 +08:00
|
|
|
resources :search_logs, only: [:index]
|
2019-03-29 09:48:20 +08:00
|
|
|
get 'search_logs/term/' => 'search_logs#term'
|
2017-08-01 05:06:26 +08:00
|
|
|
end
|
2013-08-02 09:30:13 +08:00
|
|
|
|
2014-07-14 11:21:29 +08:00
|
|
|
get "/logs" => "staff_action_logs#index"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2021-04-01 14:14:17 +08:00
|
|
|
# alias
|
2021-04-23 21:21:45 +08:00
|
|
|
get '/logs/watched_words', to: redirect(relative_url_root + 'admin/customize/watched_words')
|
|
|
|
get '/logs/watched_words/*path', to: redirect(relative_url_root + 'admin/customize/watched_words/%{path}')
|
2021-04-01 14:14:17 +08:00
|
|
|
|
2014-04-16 21:49:06 +08:00
|
|
|
get "customize" => "color_schemes#index", constraints: AdminConstraint.new
|
2017-04-12 22:52:52 +08:00
|
|
|
get "customize/themes" => "themes#index", constraints: AdminConstraint.new
|
2014-04-16 21:49:06 +08:00
|
|
|
get "customize/colors" => "color_schemes#index", constraints: AdminConstraint.new
|
2017-04-12 22:52:52 +08:00
|
|
|
get "customize/colors/:id" => "color_schemes#index", constraints: AdminConstraint.new
|
2015-07-15 20:54:28 +08:00
|
|
|
get "customize/permalinks" => "permalinks#index", constraints: AdminConstraint.new
|
2015-08-19 05:15:46 +08:00
|
|
|
get "customize/embedding" => "embedding#show", constraints: AdminConstraint.new
|
|
|
|
put "customize/embedding" => "embedding#update", constraints: AdminConstraint.new
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2017-04-12 22:52:52 +08:00
|
|
|
resources :themes, constraints: AdminConstraint.new
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2017-04-12 22:52:52 +08:00
|
|
|
post "themes/import" => "themes#import"
|
2017-05-10 05:20:28 +08:00
|
|
|
post "themes/upload_asset" => "themes#upload_asset"
|
2018-03-09 13:14:21 +08:00
|
|
|
post "themes/generate_key_pair" => "themes#generate_key_pair"
|
2017-04-15 01:35:12 +08:00
|
|
|
get "themes/:id/preview" => "themes#preview"
|
2019-06-22 01:49:14 +08:00
|
|
|
put "themes/:id/setting" => "themes#update_single_setting"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2017-01-07 03:42:36 +08:00
|
|
|
scope "/customize", constraints: AdminConstraint.new do
|
2014-09-25 23:32:08 +08:00
|
|
|
resources :user_fields, constraints: AdminConstraint.new
|
2014-12-23 08:12:26 +08:00
|
|
|
resources :emojis, constraints: AdminConstraint.new
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2017-04-12 22:52:52 +08:00
|
|
|
get 'themes/:id/:target/:field_name/edit' => 'themes#index'
|
|
|
|
get 'themes/:id' => 'themes#index'
|
2019-01-23 22:40:21 +08:00
|
|
|
get "themes/:id/export" => "themes#export"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2015-11-13 05:08:19 +08:00
|
|
|
# They have periods in their URLs often:
|
2018-06-11 12:59:21 +08:00
|
|
|
get 'site_texts' => 'site_texts#index'
|
2019-07-15 19:09:27 +08:00
|
|
|
get 'site_texts/:id.json' => 'site_texts#show', constraints: { id: /[\w.\-\+\%\&]+/i }
|
|
|
|
get 'site_texts/:id' => 'site_texts#show', constraints: { id: /[\w.\-\+\%\&]+/i }
|
|
|
|
put 'site_texts/:id.json' => 'site_texts#update', constraints: { id: /[\w.\-\+\%\&]+/i }
|
|
|
|
put 'site_texts/:id' => 'site_texts#update', constraints: { id: /[\w.\-\+\%\&]+/i }
|
|
|
|
delete 'site_texts/:id.json' => 'site_texts#revert', constraints: { id: /[\w.\-\+\%\&]+/i }
|
|
|
|
delete 'site_texts/:id' => 'site_texts#revert', constraints: { id: /[\w.\-\+\%\&]+/i }
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2019-03-19 04:09:13 +08:00
|
|
|
get 'reseed' => 'site_texts#get_reseed_options'
|
|
|
|
post 'reseed' => 'site_texts#reseed'
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2016-04-07 03:57:54 +08:00
|
|
|
get 'email_templates' => 'email_templates#index'
|
|
|
|
get 'email_templates/(:id)' => 'email_templates#show', constraints: { id: /[0-9a-z_.]+/ }
|
|
|
|
put 'email_templates/(:id)' => 'email_templates#update', constraints: { id: /[0-9a-z_.]+/ }
|
|
|
|
delete 'email_templates/(:id)' => 'email_templates#revert', constraints: { id: /[0-9a-z_.]+/ }
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2019-07-16 01:47:44 +08:00
|
|
|
get 'robots' => 'robots_txt#show'
|
|
|
|
put 'robots.json' => 'robots_txt#update'
|
|
|
|
delete 'robots.json' => 'robots_txt#reset'
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2019-07-31 03:05:08 +08:00
|
|
|
resource :email_style, only: [:show, :update]
|
|
|
|
get 'email_style/:field' => 'email_styles#show', constraints: { field: /html|css/ }
|
2021-04-23 21:21:45 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
resources :embeddable_hosts, constraints: AdminConstraint.new
|
|
|
|
resources :color_schemes, constraints: AdminConstraint.new
|
|
|
|
resources :permalinks, constraints: AdminConstraint.new
|
2021-04-01 14:14:17 +08:00
|
|
|
|
2021-04-23 21:21:45 +08:00
|
|
|
scope "/customize" do
|
2021-04-01 14:14:17 +08:00
|
|
|
resources :watched_words, only: [:index, :create, :update, :destroy] do
|
|
|
|
collection do
|
|
|
|
get "action/:id" => "watched_words#index"
|
|
|
|
get "action/:id/download" => "watched_words#download"
|
|
|
|
delete "action/:id" => "watched_words#clear_all"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
post "watched_words/upload" => "watched_words#upload"
|
2014-09-24 05:12:01 +08:00
|
|
|
end
|
|
|
|
|
2013-12-24 07:50:36 +08:00
|
|
|
get "version_check" => "versions#show"
|
2014-02-13 12:33:40 +08:00
|
|
|
|
2019-04-01 18:39:49 +08:00
|
|
|
get "dashboard" => "dashboard#index"
|
|
|
|
get "dashboard/general" => "dashboard#general"
|
|
|
|
get "dashboard/moderation" => "dashboard#moderation"
|
|
|
|
get "dashboard/security" => "dashboard#security"
|
|
|
|
get "dashboard/reports" => "dashboard#reports"
|
2021-01-22 23:09:02 +08:00
|
|
|
get "dashboard/new-features" => "dashboard#new_features"
|
|
|
|
put "dashboard/mark-new-features-as-seen" => "dashboard#mark_new_features_as_seen"
|
2018-04-16 16:42:06 +08:00
|
|
|
|
2013-03-30 03:48:26 +08:00
|
|
|
resources :dashboard, only: [:index] do
|
|
|
|
collection do
|
2013-12-24 07:50:36 +08:00
|
|
|
get "problems"
|
2020-06-05 10:49:31 +08:00
|
|
|
end
|
2013-03-30 03:48:26 +08:00
|
|
|
end
|
2014-02-13 12:33:40 +08:00
|
|
|
|
2013-05-02 13:15:17 +08:00
|
|
|
resources :api, only: [:index], constraints: AdminConstraint.new do
|
2013-03-26 09:04:28 +08:00
|
|
|
collection do
|
2019-11-05 22:10:23 +08:00
|
|
|
resources :keys, controller: 'api', only: [:index, :show, :update, :create, :destroy] do
|
2020-07-17 02:51:24 +08:00
|
|
|
collection do
|
|
|
|
get 'scopes' => 'api#scopes'
|
|
|
|
end
|
|
|
|
|
2019-11-05 22:10:23 +08:00
|
|
|
member do
|
|
|
|
post "revoke" => "api#revoke_key"
|
|
|
|
post "undo-revoke" => "api#undo_revoke_key"
|
2020-06-05 10:49:31 +08:00
|
|
|
end
|
2019-11-05 22:10:23 +08:00
|
|
|
end
|
2016-09-19 16:43:06 +08:00
|
|
|
|
|
|
|
resources :web_hooks
|
|
|
|
get 'web_hook_events/:id' => 'web_hooks#list_events', as: :web_hook_events
|
|
|
|
get 'web_hooks/:id/events' => 'web_hooks#list_events'
|
|
|
|
get 'web_hooks/:id/events/bulk' => 'web_hooks#bulk_events'
|
|
|
|
post 'web_hooks/:web_hook_id/events/:event_id/redeliver' => 'web_hooks#redeliver_event'
|
|
|
|
post 'web_hooks/:id/ping' => 'web_hooks#ping'
|
2020-06-05 10:49:31 +08:00
|
|
|
end
|
2013-03-26 09:04:28 +08:00
|
|
|
end
|
2014-02-13 12:33:40 +08:00
|
|
|
|
|
|
|
resources :backups, only: [:index, :create], constraints: AdminConstraint.new do
|
|
|
|
member do
|
2017-12-22 04:30:32 +08:00
|
|
|
get "" => "backups#show", constraints: { id: RouteFormat.backup }
|
|
|
|
put "" => "backups#email", constraints: { id: RouteFormat.backup }
|
|
|
|
delete "" => "backups#destroy", constraints: { id: RouteFormat.backup }
|
|
|
|
post "restore" => "backups#restore", constraints: { id: RouteFormat.backup }
|
2014-02-13 12:33:40 +08:00
|
|
|
end
|
|
|
|
collection do
|
2021-11-18 07:17:23 +08:00
|
|
|
# multipart uploads
|
|
|
|
post "create-multipart" => "backups#create_multipart", format: :json
|
|
|
|
post "complete-multipart" => "backups#complete_multipart", format: :json
|
|
|
|
post "abort-multipart" => "backups#abort_multipart", format: :json
|
|
|
|
post "batch-presign-multipart-parts" => "backups#batch_presign_multipart_parts", format: :json
|
|
|
|
|
2014-02-13 12:33:40 +08:00
|
|
|
get "logs" => "backups#logs"
|
|
|
|
get "status" => "backups#status"
|
2017-03-23 10:29:35 +08:00
|
|
|
delete "cancel" => "backups#cancel"
|
|
|
|
post "rollback" => "backups#rollback"
|
2014-02-13 12:33:40 +08:00
|
|
|
put "readonly" => "backups#readonly"
|
2014-05-28 04:14:37 +08:00
|
|
|
get "upload" => "backups#check_backup_chunk"
|
|
|
|
post "upload" => "backups#upload_backup_chunk"
|
2018-10-15 09:43:31 +08:00
|
|
|
get "upload_url" => "backups#create_upload_url"
|
2020-06-05 10:49:31 +08:00
|
|
|
end
|
2014-02-13 12:33:40 +08:00
|
|
|
end
|
2014-02-15 07:50:08 +08:00
|
|
|
|
2014-03-05 20:52:20 +08:00
|
|
|
resources :badges, constraints: AdminConstraint.new do
|
|
|
|
collection do
|
2020-01-13 22:20:26 +08:00
|
|
|
get "/award/:badge_id" => "badges#award"
|
|
|
|
post "/award/:badge_id" => "badges#mass_award"
|
2014-03-05 20:52:20 +08:00
|
|
|
get "types" => "badges#badge_types"
|
2014-07-27 16:22:01 +08:00
|
|
|
post "badge_groupings" => "badges#save_badge_groupings"
|
2014-07-24 16:28:09 +08:00
|
|
|
post "preview" => "badges#preview"
|
2020-06-05 10:49:31 +08:00
|
|
|
end
|
2014-03-05 20:52:20 +08:00
|
|
|
end
|
2018-11-27 05:49:57 +08:00
|
|
|
|
2013-11-12 13:42:35 +08:00
|
|
|
end # admin namespace
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2013-12-24 07:50:36 +08:00
|
|
|
get "email/unsubscribe/:key" => "email#unsubscribe", as: "email_unsubscribe"
|
2016-06-17 09:27:52 +08:00
|
|
|
get "email/unsubscribed" => "email#unsubscribed", as: "email_unsubscribed"
|
|
|
|
post "email/unsubscribe/:key" => "email#perform_unsubscribe", as: "email_perform_unsubscribe"
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2016-08-26 04:33:29 +08:00
|
|
|
get "extra-locales/:bundle" => "extra_locales#show"
|
|
|
|
|
2017-12-22 04:30:32 +08:00
|
|
|
resources :session, id: RouteFormat.username, only: [:create, :destroy, :become] do
|
2018-03-28 11:31:43 +08:00
|
|
|
if !Rails.env.production?
|
2018-03-28 11:22:43 +08:00
|
|
|
get 'become'
|
|
|
|
end
|
|
|
|
|
2013-02-15 03:11:13 +08:00
|
|
|
collection do
|
2013-12-24 07:50:36 +08:00
|
|
|
post "forgot_password"
|
2020-06-05 10:49:31 +08:00
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
end
|
|
|
|
|
2019-01-04 01:03:01 +08:00
|
|
|
get "review" => "reviewables#index" # For ember app
|
|
|
|
get "review/:reviewable_id" => "reviewables#show", constraints: { reviewable_id: /\d+/ }
|
2019-09-04 23:56:25 +08:00
|
|
|
get "review/:reviewable_id/explain" => "reviewables#explain", constraints: { reviewable_id: /\d+/ }
|
2020-08-08 00:13:02 +08:00
|
|
|
get "review/count" => "reviewables#count"
|
2019-01-04 01:03:01 +08:00
|
|
|
get "review/topics" => "reviewables#topics"
|
2019-04-03 05:00:15 +08:00
|
|
|
get "review/settings" => "reviewables#settings"
|
|
|
|
put "review/settings" => "reviewables#settings"
|
2019-01-04 01:03:01 +08:00
|
|
|
put "review/:reviewable_id/perform/:action_id" => "reviewables#perform", constraints: {
|
|
|
|
reviewable_id: /\d+/,
|
|
|
|
action_id: /[a-z\_]+/
|
|
|
|
}
|
|
|
|
put "review/:reviewable_id" => "reviewables#update", constraints: { reviewable_id: /\d+/ }
|
2019-04-12 21:55:27 +08:00
|
|
|
delete "review/:reviewable_id" => "reviewables#destroy", constraints: { reviewable_id: /\d+/ }
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2019-05-08 22:20:51 +08:00
|
|
|
resources :reviewable_claimed_topics
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2014-02-25 11:30:49 +08:00
|
|
|
get "session/sso" => "session#sso"
|
|
|
|
get "session/sso_login" => "session#sso_login"
|
2014-11-26 14:25:54 +08:00
|
|
|
get "session/sso_provider" => "session#sso_provider"
|
2014-02-06 02:46:24 +08:00
|
|
|
get "session/current" => "session#current"
|
2013-12-24 07:50:36 +08:00
|
|
|
get "session/csrf" => "session#csrf"
|
2020-10-02 07:01:40 +08:00
|
|
|
get "session/hp" => "session#get_honeypot_value"
|
2019-06-12 22:37:26 +08:00
|
|
|
get "session/email-login/:token" => "session#email_login_info"
|
2017-12-22 09:18:12 +08:00
|
|
|
post "session/email-login/:token" => "session#email_login"
|
2019-04-02 01:18:53 +08:00
|
|
|
get "session/otp/:token" => "session#one_time_password", constraints: { token: /[0-9a-f]+/ }
|
2019-06-13 01:32:13 +08:00
|
|
|
post "session/otp/:token" => "session#one_time_password", constraints: { token: /[0-9a-f]+/ }
|
FEATURE: Centralized 2FA page (#15377)
2FA support in Discourse was added and grown gradually over the years: we first
added support for TOTP for logins, then we implemented backup codes, and last
but not least, security keys. 2FA usage was initially limited to logging in,
but it has been expanded and we now require 2FA for risky actions such as
adding a new admin to the site.
As a result of this gradual growth of the 2FA system, technical debt has
accumulated to the point where it has become difficult to require 2FA for more
actions. We now have 5 different 2FA UI implementations and each one has to
support all 3 2FA methods (TOTP, backup codes, and security keys) which makes
it difficult to maintain a consistent UX for these different implementations.
Moreover, there is a lot of repeated logic in the server-side code behind these
5 UI implementations which hinders maintainability even more.
This commit is the first step towards repaying the technical debt: it builds a
system that centralizes as much as possible of the 2FA server-side logic and
UI. The 2 main components of this system are:
1. A dedicated page for 2FA with support for all 3 methods.
2. A reusable server-side class that centralizes the 2FA logic (the
`SecondFactor::AuthManager` class).
From a top-level view, the 2FA flow in this new system looks like this:
1. User initiates an action that requires 2FA;
2. Server is aware that 2FA is required for this action, so it redirects the
user to the 2FA page if the user has a 2FA method, otherwise the action is
performed.
3. User submits the 2FA form on the page;
4. Server validates the 2FA and if it's successful, the action is performed and
the user is redirected to the previous page.
A more technically-detailed explanation/documentation of the new system is
available as a comment at the top of the `lib/second_factor/auth_manager.rb`
file. Please note that the details are not set in stone and will likely change
in the future, so please don't use the system in your plugins yet.
Since this is a new system that needs to be tested, we've decided to migrate
only the 2FA for adding a new admin to the new system at this time (in this
commit). Our plan is to gradually migrate the remaining 2FA implementations to
the new system.
For screenshots of the 2FA page, see PR #15377 on GitHub.
2022-02-17 17:12:59 +08:00
|
|
|
get "session/2fa" => "session#second_factor_auth_show"
|
|
|
|
post "session/2fa" => "session#second_factor_auth_perform"
|
|
|
|
if Rails.env.test?
|
|
|
|
post "session/2fa/test-action" => "session#test_second_factor_restricted_route"
|
|
|
|
end
|
2016-06-07 01:52:24 +08:00
|
|
|
get "composer_messages" => "composer_messages#index"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2013-02-06 03:16:51 +08:00
|
|
|
resources :static
|
2019-07-24 01:17:44 +08:00
|
|
|
post "login" => "static#enter"
|
|
|
|
get "login" => "static#show", id: "login"
|
|
|
|
get "password-reset" => "static#show", id: "password_reset"
|
|
|
|
get "faq" => "static#show", id: "faq"
|
|
|
|
get "tos" => "static#show", id: "tos", as: 'tos'
|
|
|
|
get "privacy" => "static#show", id: "privacy", as: 'privacy'
|
|
|
|
get "signup" => "static#show", id: "signup"
|
|
|
|
get "login-preferences" => "static#show", id: "login"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2018-12-19 05:40:05 +08:00
|
|
|
%w{guidelines rules conduct}.each do |faq_alias|
|
2019-07-24 01:17:44 +08:00
|
|
|
get faq_alias => "static#show", id: "guidelines", as: faq_alias
|
2018-12-19 05:40:05 +08:00
|
|
|
end
|
|
|
|
|
2017-03-28 03:34:54 +08:00
|
|
|
get "my/*path", to: 'users#my_redirect'
|
2021-10-09 01:53:14 +08:00
|
|
|
get ".well-known/change-password", to: redirect(relative_url_root + 'my/preferences/security', status: 302)
|
2017-03-28 03:34:54 +08:00
|
|
|
|
2020-03-06 20:23:22 +08:00
|
|
|
get "user-cards" => "users#cards", format: :json
|
2021-06-08 01:34:01 +08:00
|
|
|
get "directory-columns" => "directory_columns#index", format: :json
|
2021-06-23 02:00:04 +08:00
|
|
|
get "edit-directory-columns" => "edit_directory_columns#index", format: :json
|
|
|
|
put "edit-directory-columns" => "edit_directory_columns#update", format: :json
|
2020-03-06 20:23:22 +08:00
|
|
|
|
2017-03-31 10:04:00 +08:00
|
|
|
%w{users u}.each_with_index do |root_path, index|
|
2017-06-27 09:03:00 +08:00
|
|
|
get "#{root_path}" => "users#index", constraints: { format: 'html' }
|
|
|
|
|
|
|
|
resources :users, except: [:index, :new, :show, :update, :destroy], path: root_path do
|
2017-03-31 10:04:00 +08:00
|
|
|
collection do
|
|
|
|
get "check_username"
|
2021-03-22 23:46:03 +08:00
|
|
|
get "check_email"
|
2017-03-31 10:04:00 +08:00
|
|
|
get "is_local_username"
|
2020-06-05 10:49:31 +08:00
|
|
|
end
|
2017-03-31 10:04:00 +08:00
|
|
|
end
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2019-06-27 07:58:06 +08:00
|
|
|
post "#{root_path}/second_factors" => "users#list_second_factors"
|
2018-02-20 14:44:51 +08:00
|
|
|
put "#{root_path}/second_factor" => "users#update_second_factor"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2019-10-02 10:08:41 +08:00
|
|
|
post "#{root_path}/create_second_factor_security_key" => "users#create_second_factor_security_key"
|
|
|
|
post "#{root_path}/register_second_factor_security_key" => "users#register_second_factor_security_key"
|
|
|
|
put "#{root_path}/security_key" => "users#update_security_key"
|
2019-06-27 07:58:06 +08:00
|
|
|
post "#{root_path}/create_second_factor_totp" => "users#create_second_factor_totp"
|
|
|
|
post "#{root_path}/enable_second_factor_totp" => "users#enable_second_factor_totp"
|
|
|
|
put "#{root_path}/disable_second_factor" => "users#disable_second_factor"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2018-06-28 16:12:32 +08:00
|
|
|
put "#{root_path}/second_factors_backup" => "users#create_second_factor_backup"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2017-04-06 04:14:22 +08:00
|
|
|
put "#{root_path}/update-activation-email" => "users#update_activation_email"
|
2017-04-20 23:17:24 +08:00
|
|
|
post "#{root_path}/email-login" => "users#email_login"
|
2017-03-31 10:04:00 +08:00
|
|
|
get "#{root_path}/admin-login" => "users#admin_login"
|
|
|
|
put "#{root_path}/admin-login" => "users#admin_login"
|
|
|
|
post "#{root_path}/toggle-anon" => "users#toggle_anon"
|
|
|
|
post "#{root_path}/read-faq" => "users#read_faq"
|
2021-11-26 04:44:15 +08:00
|
|
|
get "#{root_path}/recent-searches" => "users#recent_searches", constraints: { format: 'json' }
|
|
|
|
delete "#{root_path}/recent-searches" => "users#reset_recent_searches", constraints: { format: 'json' }
|
2017-03-31 10:04:00 +08:00
|
|
|
get "#{root_path}/search/users" => "users#search_users"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2017-05-11 23:18:06 +08:00
|
|
|
get({ "#{root_path}/account-created/" => "users#account_created" }.merge(index == 1 ? { as: :users_account_created } : { as: :old_account_created }))
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2017-05-03 03:57:55 +08:00
|
|
|
get "#{root_path}/account-created/resent" => "users#account_created"
|
|
|
|
get "#{root_path}/account-created/edit-email" => "users#account_created"
|
2020-01-15 18:27:12 +08:00
|
|
|
get({ "#{root_path}/password-reset/:token" => "users#password_reset_show" }.merge(index == 1 ? { as: :password_reset_token } : {}))
|
2017-03-31 10:04:00 +08:00
|
|
|
get "#{root_path}/confirm-email-token/:token" => "users#confirm_email_token", constraints: { format: 'json' }
|
2020-01-15 18:27:12 +08:00
|
|
|
put "#{root_path}/password-reset/:token" => "users#password_reset_update"
|
2017-03-31 10:04:00 +08:00
|
|
|
get "#{root_path}/activate-account/:token" => "users#activate_account"
|
|
|
|
put({ "#{root_path}/activate-account/:token" => "users#perform_account_activation" }.merge(index == 1 ? { as: 'perform_activate_account' } : {}))
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2019-11-20 15:31:25 +08:00
|
|
|
get "#{root_path}/confirm-old-email/:token" => "users_email#show_confirm_old_email"
|
|
|
|
put "#{root_path}/confirm-old-email" => "users_email#confirm_old_email"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2019-11-20 15:31:25 +08:00
|
|
|
get "#{root_path}/confirm-new-email/:token" => "users_email#show_confirm_new_email"
|
|
|
|
put "#{root_path}/confirm-new-email" => "users_email#confirm_new_email"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
|
|
|
get({
|
2017-04-05 01:59:22 +08:00
|
|
|
"#{root_path}/confirm-admin/:token" => "users#confirm_admin",
|
|
|
|
constraints: { token: /[0-9a-f]+/ }
|
|
|
|
}.merge(index == 1 ? { as: 'confirm_admin' } : {}))
|
|
|
|
post "#{root_path}/confirm-admin/:token" => "users#confirm_admin", constraints: { token: /[0-9a-f]+/ }
|
2017-12-22 04:30:32 +08:00
|
|
|
get "#{root_path}/:username/private-messages" => "user_actions#private_messages", constraints: { username: RouteFormat.username }
|
|
|
|
get "#{root_path}/:username/private-messages/:filter" => "user_actions#private_messages", constraints: { username: RouteFormat.username }
|
|
|
|
get "#{root_path}/:username/messages" => "user_actions#private_messages", constraints: { username: RouteFormat.username }
|
|
|
|
get "#{root_path}/:username/messages/:filter" => "user_actions#private_messages", constraints: { username: RouteFormat.username }
|
|
|
|
get "#{root_path}/:username/messages/group/:group_name" => "user_actions#private_messages", constraints: { username: RouteFormat.username, group_name: RouteFormat.username }
|
2021-08-02 12:41:41 +08:00
|
|
|
get "#{root_path}/:username/messages/group/:group_name/:filter" => "user_actions#private_messages", constraints: { username: RouteFormat.username, group_name: RouteFormat.username }
|
2021-08-05 13:01:01 +08:00
|
|
|
get "#{root_path}/:username/messages/tags/:tag_id" => "user_actions#private_messages", constraints: StaffConstraint.new
|
2017-12-22 04:30:32 +08:00
|
|
|
get "#{root_path}/:username.json" => "users#show", constraints: { username: RouteFormat.username }, defaults: { format: :json }
|
2019-07-24 01:17:44 +08:00
|
|
|
get({ "#{root_path}/:username" => "users#show", constraints: { username: RouteFormat.username } }.merge(index == 1 ? { as: 'user' } : {}))
|
|
|
|
put "#{root_path}/:username" => "users#update", constraints: { username: RouteFormat.username }, defaults: { format: :json }
|
2017-12-22 04:30:32 +08:00
|
|
|
get "#{root_path}/:username/emails" => "users#check_emails", constraints: { username: RouteFormat.username }
|
2020-11-11 03:12:44 +08:00
|
|
|
get "#{root_path}/:username/sso-email" => "users#check_sso_email", constraints: { username: RouteFormat.username }
|
2021-02-17 23:57:51 +08:00
|
|
|
get "#{root_path}/:username/sso-payload" => "users#check_sso_payload", constraints: { username: RouteFormat.username }
|
2020-06-06 00:42:12 +08:00
|
|
|
get "#{root_path}/:username/preferences" => "users#preferences", constraints: { username: RouteFormat.username }
|
2017-12-22 04:30:32 +08:00
|
|
|
get "#{root_path}/:username/preferences/email" => "users_email#index", constraints: { username: RouteFormat.username }
|
|
|
|
get "#{root_path}/:username/preferences/account" => "users#preferences", constraints: { username: RouteFormat.username }
|
2021-03-03 17:09:22 +08:00
|
|
|
get "#{root_path}/:username/preferences/security" => "users#preferences", constraints: { username: RouteFormat.username }
|
2017-12-22 04:30:32 +08:00
|
|
|
get "#{root_path}/:username/preferences/profile" => "users#preferences", constraints: { username: RouteFormat.username }
|
|
|
|
get "#{root_path}/:username/preferences/emails" => "users#preferences", constraints: { username: RouteFormat.username }
|
2020-06-11 00:11:49 +08:00
|
|
|
put "#{root_path}/:username/preferences/primary-email" => "users#update_primary_email", format: :json, constraints: { username: RouteFormat.username }
|
|
|
|
delete "#{root_path}/:username/preferences/email" => "users#destroy_email", constraints: { username: RouteFormat.username }
|
2017-12-22 04:30:32 +08:00
|
|
|
get "#{root_path}/:username/preferences/notifications" => "users#preferences", constraints: { username: RouteFormat.username }
|
|
|
|
get "#{root_path}/:username/preferences/categories" => "users#preferences", constraints: { username: RouteFormat.username }
|
2019-03-05 22:47:51 +08:00
|
|
|
get "#{root_path}/:username/preferences/users" => "users#preferences", constraints: { username: RouteFormat.username }
|
2017-12-22 04:30:32 +08:00
|
|
|
get "#{root_path}/:username/preferences/tags" => "users#preferences", constraints: { username: RouteFormat.username }
|
|
|
|
get "#{root_path}/:username/preferences/interface" => "users#preferences", constraints: { username: RouteFormat.username }
|
|
|
|
get "#{root_path}/:username/preferences/apps" => "users#preferences", constraints: { username: RouteFormat.username }
|
2020-06-11 00:11:49 +08:00
|
|
|
post "#{root_path}/:username/preferences/email" => "users_email#create", constraints: { username: RouteFormat.username }
|
2017-12-22 04:30:32 +08:00
|
|
|
put "#{root_path}/:username/preferences/email" => "users_email#update", constraints: { username: RouteFormat.username }
|
|
|
|
get "#{root_path}/:username/preferences/badge_title" => "users#preferences", constraints: { username: RouteFormat.username }
|
|
|
|
put "#{root_path}/:username/preferences/badge_title" => "users#badge_title", constraints: { username: RouteFormat.username }
|
|
|
|
get "#{root_path}/:username/preferences/username" => "users#preferences", constraints: { username: RouteFormat.username }
|
|
|
|
put "#{root_path}/:username/preferences/username" => "users#username", constraints: { username: RouteFormat.username }
|
2017-12-22 09:18:12 +08:00
|
|
|
get "#{root_path}/:username/preferences/second-factor" => "users#preferences", constraints: { username: RouteFormat.username }
|
2018-06-28 16:12:32 +08:00
|
|
|
get "#{root_path}/:username/preferences/second-factor-backup" => "users#preferences", constraints: { username: RouteFormat.username }
|
2017-12-22 04:30:32 +08:00
|
|
|
delete "#{root_path}/:username/preferences/user_image" => "users#destroy_user_image", constraints: { username: RouteFormat.username }
|
|
|
|
put "#{root_path}/:username/preferences/avatar/pick" => "users#pick_avatar", constraints: { username: RouteFormat.username }
|
2018-07-18 18:57:43 +08:00
|
|
|
put "#{root_path}/:username/preferences/avatar/select" => "users#select_avatar", constraints: { username: RouteFormat.username }
|
2018-07-23 23:51:57 +08:00
|
|
|
post "#{root_path}/:username/preferences/revoke-account" => "users#revoke_account", constraints: { username: RouteFormat.username }
|
2018-08-31 16:18:06 +08:00
|
|
|
post "#{root_path}/:username/preferences/revoke-auth-token" => "users#revoke_auth_token", constraints: { username: RouteFormat.username }
|
2017-12-22 04:30:32 +08:00
|
|
|
get "#{root_path}/:username/staff-info" => "users#staff_info", constraints: { username: RouteFormat.username }
|
|
|
|
get "#{root_path}/:username/summary" => "users#summary", constraints: { username: RouteFormat.username }
|
2019-03-27 17:41:50 +08:00
|
|
|
put "#{root_path}/:username/notification_level" => "users#notification_level", constraints: { username: RouteFormat.username }
|
2017-12-22 04:30:32 +08:00
|
|
|
get "#{root_path}/:username/invited" => "users#invited", constraints: { username: RouteFormat.username }
|
|
|
|
get "#{root_path}/:username/invited/:filter" => "users#invited", constraints: { username: RouteFormat.username }
|
2017-03-31 10:04:00 +08:00
|
|
|
post "#{root_path}/action/send_activation_email" => "users#send_activation_email"
|
2017-12-22 04:30:32 +08:00
|
|
|
get "#{root_path}/:username/summary" => "users#show", constraints: { username: RouteFormat.username }
|
|
|
|
get "#{root_path}/:username/activity/topics.rss" => "list#user_topics_feed", format: :rss, constraints: { username: RouteFormat.username }
|
|
|
|
get "#{root_path}/:username/activity.rss" => "posts#user_posts_feed", format: :rss, constraints: { username: RouteFormat.username }
|
2018-10-09 22:21:41 +08:00
|
|
|
get "#{root_path}/:username/activity.json" => "posts#user_posts_feed", format: :json, constraints: { username: RouteFormat.username }
|
2017-12-22 04:30:32 +08:00
|
|
|
get "#{root_path}/:username/activity" => "users#show", constraints: { username: RouteFormat.username }
|
|
|
|
get "#{root_path}/:username/activity/:filter" => "users#show", constraints: { username: RouteFormat.username }
|
|
|
|
get "#{root_path}/:username/badges" => "users#badges", constraints: { username: RouteFormat.username }
|
2020-03-18 06:22:41 +08:00
|
|
|
get "#{root_path}/:username/bookmarks" => "users#bookmarks", constraints: { username: RouteFormat.username, format: /(json|ics)/ }
|
2017-12-22 04:30:32 +08:00
|
|
|
get "#{root_path}/:username/notifications" => "users#show", constraints: { username: RouteFormat.username }
|
|
|
|
get "#{root_path}/:username/notifications/:filter" => "users#show", constraints: { username: RouteFormat.username }
|
2019-07-24 01:17:44 +08:00
|
|
|
delete "#{root_path}/:username" => "users#destroy", constraints: { username: RouteFormat.username }
|
2017-03-31 10:04:00 +08:00
|
|
|
get "#{root_path}/by-external/:external_id" => "users#show", constraints: { external_id: /[^\/]+/ }
|
2020-11-10 18:41:46 +08:00
|
|
|
get "#{root_path}/by-external/:external_provider/:external_id" => "users#show", constraints: { external_id: /[^\/]+/ }
|
2017-12-22 04:30:32 +08:00
|
|
|
get "#{root_path}/:username/flagged-posts" => "users#show", constraints: { username: RouteFormat.username }
|
|
|
|
get "#{root_path}/:username/deleted-posts" => "users#show", constraints: { username: RouteFormat.username }
|
|
|
|
get "#{root_path}/:username/topic-tracking-state" => "users#topic_tracking_state", constraints: { username: RouteFormat.username }
|
2021-08-25 11:17:56 +08:00
|
|
|
get "#{root_path}/:username/private-message-topic-tracking-state" => "users#private_message_topic_tracking_state", constraints: { username: RouteFormat.username }
|
2018-10-11 01:00:08 +08:00
|
|
|
get "#{root_path}/:username/profile-hidden" => "users#profile_hidden"
|
2019-12-10 03:15:47 +08:00
|
|
|
put "#{root_path}/:username/feature-topic" => "users#feature_topic", constraints: { username: RouteFormat.username }
|
|
|
|
put "#{root_path}/:username/clear-featured-topic" => "users#clear_featured_topic", constraints: { username: RouteFormat.username }
|
2020-01-28 19:55:46 +08:00
|
|
|
get "#{root_path}/:username/card.json" => "users#show_card", format: :json, constraints: { username: RouteFormat.username }
|
2017-03-31 10:04:00 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
get "user-badges/:username.json" => "user_badges#username", constraints: { username: RouteFormat.username }, defaults: { format: :json }
|
2017-12-22 04:30:32 +08:00
|
|
|
get "user-badges/:username" => "user_badges#username", constraints: { username: RouteFormat.username }
|
2016-08-12 14:35:10 +08:00
|
|
|
|
2017-12-22 04:30:32 +08:00
|
|
|
post "user_avatar/:username/refresh_gravatar" => "user_avatars#refresh_gravatar", constraints: { username: RouteFormat.username }
|
|
|
|
get "letter_avatar/:username/:size/:version.png" => "user_avatars#show_letter", constraints: { hostname: /[\w\.-]+/, size: /\d+/, username: RouteFormat.username, format: :png }
|
|
|
|
get "user_avatar/:hostname/:username/:size/:version.png" => "user_avatars#show", constraints: { hostname: /[\w\.-]+/, size: /\d+/, username: RouteFormat.username, format: :png }
|
2013-12-24 07:50:36 +08:00
|
|
|
|
2019-07-24 01:17:44 +08:00
|
|
|
get "letter_avatar_proxy/:version/letter/:letter/:color/:size.png" => "user_avatars#show_proxy_letter", constraints: { format: :png }
|
2014-05-26 17:46:43 +08:00
|
|
|
|
2021-06-15 14:57:17 +08:00
|
|
|
get "svg-sprite/:hostname/svg-:theme_id-:version.js" => "svg_sprite#show", constraints: { hostname: /[\w\.-]+/, version: /\h{40}/, theme_id: /([0-9]+)?/, format: :js }
|
2019-07-24 01:17:44 +08:00
|
|
|
get "svg-sprite/search/:keyword" => "svg_sprite#search", format: false, constraints: { keyword: /[-a-z0-9\s\%]+/ }
|
|
|
|
get "svg-sprite/picker-search" => "svg_sprite#icon_picker_search", defaults: { format: :json }
|
|
|
|
get "svg-sprite/:hostname/icon(/:color)/:name.svg" => "svg_sprite#svg_icon", constraints: { hostname: /[\w\.-]+/, name: /[-a-z0-9\s\%]+/, color: /(\h{3}{1,2})/, format: :svg }
|
2015-11-25 11:42:46 +08:00
|
|
|
|
2019-07-24 01:17:44 +08:00
|
|
|
get "highlight-js/:hostname/:version.js" => "highlight_js#show", constraints: { hostname: /[\w\.-]+/, format: :js }
|
2018-11-27 05:49:57 +08:00
|
|
|
|
2019-07-24 01:17:44 +08:00
|
|
|
get "stylesheets/:name.css.map" => "stylesheets#show_source_map", constraints: { name: /[-a-z0-9_]+/ }
|
|
|
|
get "stylesheets/:name.css" => "stylesheets#show", constraints: { name: /[-a-z0-9_]+/ }
|
2020-08-28 22:36:52 +08:00
|
|
|
get "color-scheme-stylesheet/:id(/:theme_id)" => "stylesheets#color_scheme", constraints: { format: :json }
|
2019-07-24 01:17:44 +08:00
|
|
|
get "theme-javascripts/:digest.js" => "theme_javascripts#show", constraints: { digest: /\h{40}/ }
|
2021-04-29 04:12:08 +08:00
|
|
|
get "theme-javascripts/tests/:theme_id-:digest.js" => "theme_javascripts#show_tests"
|
2015-03-13 13:15:13 +08:00
|
|
|
|
2017-04-12 22:52:52 +08:00
|
|
|
post "uploads/lookup-metadata" => "uploads#metadata"
|
2018-10-15 23:32:52 +08:00
|
|
|
post "uploads" => "uploads#create"
|
|
|
|
post "uploads/lookup-urls" => "uploads#lookup_urls"
|
2015-05-05 13:50:13 +08:00
|
|
|
|
FEATURE: Uppy direct S3 multipart uploads in composer (#14051)
This pull request introduces the endpoints required, and the JavaScript functionality in the `ComposerUppyUpload` mixin, for direct S3 multipart uploads. There are four new endpoints in the uploads controller:
* `create-multipart.json` - Creates the multipart upload in S3 along with an `ExternalUploadStub` record, storing information about the file in the same way as `generate-presigned-put.json` does for regular direct S3 uploads
* `batch-presign-multipart-parts.json` - Takes a list of part numbers and the unique identifier for an `ExternalUploadStub` record, and generates the presigned URLs for those parts if the multipart upload still exists and if the user has permission to access that upload
* `complete-multipart.json` - Completes the multipart upload in S3. Needs the full list of part numbers and their associated ETags which are returned when the part is uploaded to the presigned URL above. Only works if the user has permission to access the associated `ExternalUploadStub` record and the multipart upload still exists.
After we confirm the upload is complete in S3, we go through the regular `UploadCreator` flow, the same as `complete-external-upload.json`, and promote the temporary upload S3 into a full `Upload` record, moving it to its final destination.
* `abort-multipart.json` - Aborts the multipart upload on S3 and destroys the `ExternalUploadStub` record if the user has permission to access that upload.
Also added are a few new columns to `ExternalUploadStub`:
* multipart - Whether or not this is a multipart upload
* external_upload_identifier - The "upload ID" for an S3 multipart upload
* filesize - The size of the file when the `create-multipart.json` or `generate-presigned-put.json` is called. This is used for validation.
When the user completes a direct S3 upload, either regular or multipart, we take the `filesize` that was captured when the `ExternalUploadStub` was first created and compare it with the final `Content-Length` size of the file where it is stored in S3. Then, if the two do not match, we throw an error, delete the file on S3, and ban the user from uploading files for N (default 5) minutes. This would only happen if the user uploads a different file than what they first specified, or in the case of multipart uploads uploaded larger chunks than needed. This is done to prevent abuse of S3 storage by bad actors.
Also included in this PR is an update to vendor/uppy.js. This has been built locally from the latest uppy source at https://github.com/transloadit/uppy/commit/d613b849a6591083f8a0968aa8d66537e231bbcd. This must be done so that I can get my multipart upload changes into Discourse. When the Uppy team cuts a proper release, we can bump the package.json versions instead.
2021-08-25 06:46:54 +08:00
|
|
|
# direct to s3 uploads
|
|
|
|
post "uploads/generate-presigned-put" => "uploads#generate_presigned_put", format: :json
|
|
|
|
post "uploads/complete-external-upload" => "uploads#complete_external_upload", format: :json
|
|
|
|
|
|
|
|
# multipart uploads
|
|
|
|
post "uploads/create-multipart" => "uploads#create_multipart", format: :json
|
|
|
|
post "uploads/complete-multipart" => "uploads#complete_multipart", format: :json
|
|
|
|
post "uploads/abort-multipart" => "uploads#abort_multipart", format: :json
|
|
|
|
post "uploads/batch-presign-multipart-parts" => "uploads#batch_presign_multipart_parts", format: :json
|
2021-07-28 06:42:25 +08:00
|
|
|
|
2019-02-21 10:13:37 +08:00
|
|
|
# used to download original images
|
|
|
|
get "uploads/:site/:sha(.:extension)" => "uploads#show", constraints: { site: /\w+/, sha: /\h{40}/, extension: /[a-z0-9\._]+/i }
|
2021-03-18 02:01:29 +08:00
|
|
|
get "uploads/short-url/:base62(.:extension)" => "uploads#show_short", constraints: { site: /\w+/, base62: /[a-zA-Z0-9]+/, extension: /[a-zA-Z0-9\._-]+/i }, as: :upload_short
|
2015-12-25 04:22:14 +08:00
|
|
|
# used to download attachments
|
2020-01-03 12:39:07 +08:00
|
|
|
get "uploads/:site/original/:tree:sha(.:extension)" => "uploads#show", constraints: { site: /\w+/, tree: /([a-z0-9]+\/)+/i, sha: /\h{40}/, extension: /[a-z0-9\._]+/i }
|
2017-08-23 04:40:01 +08:00
|
|
|
if Rails.env.test?
|
2020-04-28 21:03:04 +08:00
|
|
|
get "uploads/:site/test_:index/original/:tree:sha(.:extension)" => "uploads#show", constraints: { site: /\w+/, index: /\d+/, tree: /([a-z0-9]+\/)+/i, sha: /\h{40}/, extension: /[a-z0-9\._]+/i }
|
2020-06-05 10:49:31 +08:00
|
|
|
end
|
2015-05-20 20:55:42 +08:00
|
|
|
# used to download attachments (old route)
|
2019-07-24 01:17:44 +08:00
|
|
|
get "uploads/:site/:id/:sha" => "uploads#show", constraints: { site: /\w+/, id: /\d+/, sha: /\h{16}/, format: /.*/ }
|
2017-08-23 04:40:01 +08:00
|
|
|
get "secure-media-uploads/*path(.:extension)" => "uploads#show_secure", constraints: { extension: /[a-z0-9\._]+/i }
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2017-08-23 04:40:01 +08:00
|
|
|
get "posts" => "posts#latest", id: "latest_posts", constraints: { format: /(json|rss)/ }
|
2019-07-24 01:17:44 +08:00
|
|
|
get "private-posts" => "posts#latest", id: "private_posts", constraints: { format: /(json|rss)/ }
|
2013-12-24 07:50:36 +08:00
|
|
|
get "posts/by_number/:topic_id/:post_number" => "posts#by_number"
|
2018-07-19 22:00:13 +08:00
|
|
|
get "posts/by-date/:topic_id/:date" => "posts#by_date"
|
2013-12-24 07:50:36 +08:00
|
|
|
get "posts/:id/reply-history" => "posts#reply_history"
|
2017-12-14 05:12:06 +08:00
|
|
|
get "posts/:id/reply-ids" => "posts#reply_ids"
|
2018-04-21 05:05:51 +08:00
|
|
|
get "posts/:id/reply-ids/all" => "posts#all_reply_ids"
|
2017-12-22 04:30:32 +08:00
|
|
|
get "posts/:username/deleted" => "posts#deleted_posts", constraints: { username: RouteFormat.username }
|
2021-08-13 02:54:14 +08:00
|
|
|
get "posts/:username/flagged" => "posts#flagged_posts", constraints: { username: RouteFormat.username }
|
2021-08-27 00:16:00 +08:00
|
|
|
get "posts/:username/pending" => "posts#pending", constraints: { username: RouteFormat.username }
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2017-08-23 04:40:01 +08:00
|
|
|
%w{groups g}.each do |root_path|
|
|
|
|
resources :groups, id: RouteFormat.username, path: root_path do
|
2019-02-21 13:44:25 +08:00
|
|
|
get "posts.rss" => "groups#posts_feed", format: :rss
|
|
|
|
get "mentions.rss" => "groups#mentions_feed", format: :rss
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2019-02-21 13:44:25 +08:00
|
|
|
get 'members'
|
|
|
|
get 'posts'
|
|
|
|
get 'mentions'
|
|
|
|
get 'counts'
|
|
|
|
get 'mentionable'
|
|
|
|
get 'messageable'
|
|
|
|
get 'logs' => 'groups#histories'
|
FEATURE: Improve group email settings UI (#13083)
This overhauls the user interface for the group email settings management, aiming to make it a lot easier to test the settings entered and confirm they are correct before proceeding. We do this by forcing the user to test the settings before they can be saved to the database. It also includes some quality of life improvements around setting up IMAP and SMTP for our first supported provider, GMail. This PR does not remove the old group email config, that will come in a subsequent PR. This is related to https://meta.discourse.org/t/imap-support-for-group-inboxes/160588 so read that if you would like more backstory.
### UI
Both site settings of `enable_imap` and `enable_smtp` must be true to test this. You must enable SMTP first to enable IMAP.
You can prefill the SMTP settings with GMail configuration. To proceed with saving these settings you must test them, which is handled by the EmailSettingsValidator.
If there is an issue with the configuration or credentials a meaningful error message should be shown.
IMAP settings must also be validated when IMAP is enabled, before saving.
When saving IMAP, we fetch the mailboxes for that account and populate them. This mailbox must be selected and saved for IMAP to work (the feature acts as though it is disabled until the mailbox is selected and saved):
### Database & Backend
This adds several columns to the Groups table. The purpose of this change is to make it much more explicit that SMTP/IMAP is enabled for a group, rather than relying on settings not being null. Also included is an UPDATE query to backfill these columns. These columns are automatically filled when updating the group.
For GMail, we now filter the mailboxes returned. This is so users cannot use a mailbox like Sent or Trash for syncing, which would generally be disastrous.
There is a new group endpoint for testing email settings. This may be useful in the future for other places in our UI, at which point it can be extracted to a more generic endpoint or module to be included.
2021-05-28 07:28:18 +08:00
|
|
|
post 'test_email_settings'
|
2013-12-24 07:50:36 +08:00
|
|
|
|
2020-01-03 12:39:07 +08:00
|
|
|
collection do
|
|
|
|
get "check-name" => 'groups#check_name'
|
2020-08-19 22:41:40 +08:00
|
|
|
get 'custom/new' => 'groups#new', constraints: StaffConstraint.new
|
2019-02-21 13:44:25 +08:00
|
|
|
get "search" => "groups#search"
|
|
|
|
end
|
2015-01-09 07:35:52 +08:00
|
|
|
|
2019-02-21 13:44:25 +08:00
|
|
|
member do
|
2020-06-05 10:49:31 +08:00
|
|
|
%w{
|
2019-02-21 13:44:25 +08:00
|
|
|
activity
|
|
|
|
activity/:filter
|
2020-06-05 10:49:31 +08:00
|
|
|
requests
|
2019-02-21 13:44:25 +08:00
|
|
|
messages
|
|
|
|
messages/inbox
|
|
|
|
messages/archive
|
|
|
|
manage
|
|
|
|
manage/profile
|
|
|
|
manage/members
|
|
|
|
manage/membership
|
|
|
|
manage/interaction
|
2020-07-10 17:05:55 +08:00
|
|
|
manage/email
|
2020-08-07 00:27:27 +08:00
|
|
|
manage/categories
|
|
|
|
manage/tags
|
2019-02-21 13:44:25 +08:00
|
|
|
manage/logs
|
|
|
|
}.each do |path|
|
|
|
|
get path => 'groups#show'
|
2018-03-15 16:01:40 +08:00
|
|
|
end
|
|
|
|
|
2020-08-10 22:49:05 +08:00
|
|
|
get "permissions" => "groups#permissions"
|
2019-02-21 13:44:25 +08:00
|
|
|
put "members" => "groups#add_members"
|
2021-07-22 15:11:23 +08:00
|
|
|
put "join" => "groups#join"
|
2019-02-21 13:44:25 +08:00
|
|
|
delete "members" => "groups#remove_member"
|
2021-07-23 00:14:18 +08:00
|
|
|
delete "leave" => "groups#leave"
|
2019-02-21 13:44:25 +08:00
|
|
|
post "request_membership" => "groups#request_membership"
|
|
|
|
put "handle_membership_request" => "groups#handle_membership_request"
|
|
|
|
post "notifications" => "groups#set_notifications"
|
|
|
|
end
|
|
|
|
end
|
2015-11-09 21:52:04 +08:00
|
|
|
end
|
2014-01-31 06:10:36 +08:00
|
|
|
|
2021-12-09 20:30:27 +08:00
|
|
|
resources :associated_groups, only: %i[index], constraints: AdminConstraint.new
|
|
|
|
|
2015-11-09 21:52:04 +08:00
|
|
|
# aliases so old API code works
|
|
|
|
delete "admin/groups/:id/members" => "groups#remove_member", constraints: AdminConstraint.new
|
|
|
|
put "admin/groups/:id/members" => "groups#add_members", constraints: AdminConstraint.new
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2013-02-06 03:16:51 +08:00
|
|
|
resources :posts do
|
2019-12-11 12:04:02 +08:00
|
|
|
delete "bookmark", to: "posts#destroy_bookmark"
|
2014-05-13 20:53:11 +08:00
|
|
|
put "wiki"
|
2014-09-11 05:08:33 +08:00
|
|
|
put "post_type"
|
2014-09-11 22:04:40 +08:00
|
|
|
put "rebake"
|
2014-09-23 00:55:13 +08:00
|
|
|
put "unhide"
|
2018-01-26 04:38:40 +08:00
|
|
|
put "locked"
|
2019-04-19 22:53:58 +08:00
|
|
|
put "notice"
|
2013-12-24 07:50:36 +08:00
|
|
|
get "replies"
|
2014-10-28 05:06:43 +08:00
|
|
|
get "revisions/latest" => "posts#latest_revision"
|
|
|
|
get "revisions/:revision" => "posts#revisions", constraints: { revision: /\d+/ }
|
|
|
|
put "revisions/:revision/hide" => "posts#hide_revision", constraints: { revision: /\d+/ }
|
|
|
|
put "revisions/:revision/show" => "posts#show_revision", constraints: { revision: /\d+/ }
|
2016-03-09 23:40:49 +08:00
|
|
|
put "revisions/:revision/revert" => "posts#revert", constraints: { revision: /\d+/ }
|
2013-12-24 07:50:36 +08:00
|
|
|
put "recover"
|
2013-02-06 03:16:51 +08:00
|
|
|
collection do
|
2013-12-24 07:50:36 +08:00
|
|
|
delete "destroy_many"
|
2016-03-22 07:31:56 +08:00
|
|
|
put "merge_posts"
|
2020-06-05 10:49:31 +08:00
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
end
|
|
|
|
|
2021-03-22 07:50:22 +08:00
|
|
|
resources :bookmarks, only: %i[create destroy update] do
|
|
|
|
put "toggle_pin"
|
|
|
|
end
|
2019-12-11 12:04:02 +08:00
|
|
|
|
2018-02-13 14:38:26 +08:00
|
|
|
resources :notifications, except: :show do
|
|
|
|
collection do
|
|
|
|
put 'mark-read' => 'notifications#mark_read'
|
|
|
|
# creating an alias cause the api was extended to mark a single notification
|
|
|
|
# this allows us to cleanly target it
|
|
|
|
put 'read' => 'notifications#mark_read'
|
2020-06-05 10:49:31 +08:00
|
|
|
end
|
2018-02-13 14:38:26 +08:00
|
|
|
end
|
2013-02-12 14:51:44 +08:00
|
|
|
|
2019-08-08 22:49:09 +08:00
|
|
|
match "/auth/failure", to: "users/omniauth_callbacks#failure", via: [:get, :post]
|
2019-08-08 18:57:28 +08:00
|
|
|
get "/auth/:provider", to: "users/omniauth_callbacks#confirm_request"
|
2013-07-02 02:00:06 +08:00
|
|
|
match "/auth/:provider/callback", to: "users/omniauth_callbacks#complete", via: [:get, :post]
|
2019-07-17 19:34:02 +08:00
|
|
|
get "/associate/:token", to: "users/associate_accounts#connect_info", constraints: { token: /\h{32}/ }
|
|
|
|
post "/associate/:token", to: "users/associate_accounts#connect", constraints: { token: /\h{32}/ }
|
2013-02-06 03:16:51 +08:00
|
|
|
|
|
|
|
resources :clicks do
|
|
|
|
collection do
|
2019-05-07 14:37:43 +08:00
|
|
|
post "track"
|
2020-06-05 10:49:31 +08:00
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
end
|
|
|
|
|
2013-12-24 07:50:36 +08:00
|
|
|
get "excerpt" => "excerpt#show"
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2015-10-01 00:28:13 +08:00
|
|
|
resources :post_action_users
|
2019-08-27 20:09:00 +08:00
|
|
|
resources :post_readers, only: %i[index]
|
2013-02-06 03:16:51 +08:00
|
|
|
resources :post_actions do
|
|
|
|
collection do
|
2013-12-24 07:50:36 +08:00
|
|
|
get "users"
|
2014-07-29 01:17:37 +08:00
|
|
|
post "defer_flags"
|
2020-06-05 10:49:31 +08:00
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
end
|
|
|
|
resources :user_actions
|
|
|
|
|
2014-04-16 21:12:06 +08:00
|
|
|
resources :badges, only: [:index]
|
2019-07-24 01:17:44 +08:00
|
|
|
get "/badges/:id(/:slug)" => "badges#show", constraints: { format: /(json|html|rss)/ }
|
2021-06-01 16:33:40 +08:00
|
|
|
resources :user_badges, only: [:index, :create, :destroy] do
|
|
|
|
put "toggle_favorite" => "user_badges#toggle_favorite", constraints: { format: :json }
|
|
|
|
end
|
2014-03-05 20:52:20 +08:00
|
|
|
|
2018-04-11 04:24:29 +08:00
|
|
|
get '/c', to: redirect(relative_url_root + 'categories')
|
2015-03-31 00:15:02 +08:00
|
|
|
|
2019-10-31 01:22:32 +08:00
|
|
|
resources :categories, except: [:show, :new, :edit]
|
2015-08-28 01:14:59 +08:00
|
|
|
post "categories/reorder" => "categories#reorder"
|
2019-10-31 01:22:32 +08:00
|
|
|
|
|
|
|
scope path: 'category/:category_id' do
|
|
|
|
post "/move" => "categories#move"
|
|
|
|
post "/notifications" => "categories#set_notifications"
|
|
|
|
put "/slug" => "categories#update_slug"
|
|
|
|
end
|
|
|
|
|
|
|
|
get "category/*path" => "categories#redirect"
|
2014-01-18 06:52:06 +08:00
|
|
|
|
2016-08-30 04:47:44 +08:00
|
|
|
get "categories_and_latest" => "categories#categories_and_latest"
|
2018-03-03 10:53:29 +08:00
|
|
|
get "categories_and_top" => "categories#categories_and_top"
|
2016-08-30 04:47:44 +08:00
|
|
|
|
2014-10-17 00:15:31 +08:00
|
|
|
get "c/:id/show" => "categories#show"
|
2019-10-31 01:22:32 +08:00
|
|
|
|
2020-12-09 05:50:26 +08:00
|
|
|
get "c/*category_slug/find_by_slug" => "categories#find_by_slug"
|
|
|
|
get "c/*category_slug/edit(/:tab)" => "categories#find_by_slug", constraints: { format: 'html' }
|
2020-10-24 00:49:02 +08:00
|
|
|
get "/new-category" => "categories#show", constraints: { format: 'html' }
|
2019-10-31 01:22:32 +08:00
|
|
|
|
|
|
|
get "c/*category_slug_path_with_id.rss" => "list#category_feed", format: :rss
|
|
|
|
scope path: 'c/*category_slug_path_with_id' do
|
|
|
|
get "/none" => "list#category_none_latest"
|
|
|
|
|
|
|
|
TopTopic.periods.each do |period|
|
2021-07-06 17:55:11 +08:00
|
|
|
get "/none/l/top/#{period}", to: redirect("/none/l/top?period=#{period}", status: 301)
|
|
|
|
get "/l/top/#{period}", to: redirect("/l/top?period=#{period}", status: 301)
|
2019-10-31 01:22:32 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
Discourse.filters.each do |filter|
|
|
|
|
get "/none/l/#{filter}" => "list#category_none_#{filter}", as: "category_none_#{filter}"
|
|
|
|
get "/l/#{filter}" => "list#category_#{filter}", as: "category_#{filter}"
|
|
|
|
end
|
|
|
|
|
2020-06-04 03:26:56 +08:00
|
|
|
get "/all" => "list#category_default", as: "category_all", constraints: { format: 'html' }
|
2019-10-31 01:22:32 +08:00
|
|
|
get "/" => "list#category_default", as: "category_default"
|
|
|
|
end
|
2014-10-17 00:15:31 +08:00
|
|
|
|
2020-07-14 00:13:17 +08:00
|
|
|
get "hashtags" => "hashtags#show"
|
2014-01-14 08:02:14 +08:00
|
|
|
|
|
|
|
TopTopic.periods.each do |period|
|
2021-07-06 17:55:11 +08:00
|
|
|
get "top/#{period}.rss", to: redirect("top.rss?period=#{period}", status: 301)
|
|
|
|
get "top/#{period}.json", to: redirect("top.json?period=#{period}", status: 301)
|
|
|
|
get "top/#{period}", to: redirect("top?period=#{period}", status: 301)
|
2013-07-06 04:49:06 +08:00
|
|
|
end
|
|
|
|
|
2014-10-16 11:52:21 +08:00
|
|
|
Discourse.anonymous_filters.each do |filter|
|
|
|
|
get "#{filter}.rss" => "list##{filter}_feed", format: :rss
|
|
|
|
end
|
|
|
|
|
2013-12-24 07:50:36 +08:00
|
|
|
Discourse.filters.each do |filter|
|
2019-07-24 01:17:44 +08:00
|
|
|
get "#{filter}" => "list##{filter}"
|
2013-03-28 21:01:13 +08:00
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2015-04-04 04:55:32 +08:00
|
|
|
get "search/query" => "search#query"
|
2015-07-27 14:46:50 +08:00
|
|
|
get "search" => "search#show"
|
2017-07-18 03:42:32 +08:00
|
|
|
post "search/click" => "search#click"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2013-02-06 03:16:51 +08:00
|
|
|
# Topics resource
|
2013-12-24 07:50:36 +08:00
|
|
|
get "t/:id" => "topics#show"
|
2022-03-12 02:01:08 +08:00
|
|
|
put "t/:topic_id" => "topics#update", constraints: { topic_id: /\d+/ }
|
2014-01-14 08:02:14 +08:00
|
|
|
delete "t/:id" => "topics#destroy"
|
2015-12-30 10:26:21 +08:00
|
|
|
put "t/:id/archive-message" => "topics#archive_message"
|
|
|
|
put "t/:id/move-to-inbox" => "topics#move_to_inbox"
|
2016-05-01 19:48:43 +08:00
|
|
|
put "t/:id/convert-topic/:type" => "topics#convert_topic"
|
2018-03-14 03:59:12 +08:00
|
|
|
put "t/:id/publish" => "topics#publish"
|
2018-03-23 23:12:22 +08:00
|
|
|
put "t/:id/shared-draft" => "topics#update_shared_draft"
|
2018-08-10 08:51:03 +08:00
|
|
|
put "t/:id/reset-bump-date" => "topics#reset_bump_date"
|
2014-01-30 04:48:52 +08:00
|
|
|
put "topics/bulk"
|
2014-03-04 04:46:38 +08:00
|
|
|
put "topics/reset-new" => 'topics#reset_new'
|
2021-07-30 17:00:48 +08:00
|
|
|
put "topics/pm-reset-new" => 'topics#private_message_reset_new'
|
2013-12-24 07:50:36 +08:00
|
|
|
post "topics/timings"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2015-06-25 03:08:22 +08:00
|
|
|
get 'topics/similar_to' => 'similar_topics#index'
|
|
|
|
resources :similar_topics
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2015-03-14 08:18:05 +08:00
|
|
|
get "topics/feature_stats"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2018-02-14 04:46:25 +08:00
|
|
|
scope "/topics", username: RouteFormat.username do
|
2019-07-24 01:17:44 +08:00
|
|
|
get "created-by/:username" => "list#topics_by", as: "topics_by", defaults: { format: :json }
|
|
|
|
get "private-messages/:username" => "list#private_messages", as: "topics_private_messages", defaults: { format: :json }
|
|
|
|
get "private-messages-sent/:username" => "list#private_messages_sent", as: "topics_private_messages_sent", defaults: { format: :json }
|
|
|
|
get "private-messages-archive/:username" => "list#private_messages_archive", as: "topics_private_messages_archive", defaults: { format: :json }
|
|
|
|
get "private-messages-unread/:username" => "list#private_messages_unread", as: "topics_private_messages_unread", defaults: { format: :json }
|
2020-06-22 21:48:24 +08:00
|
|
|
get "private-messages-tags/:username/:tag_id.json" => "list#private_messages_tag", as: "topics_private_messages_tag", defaults: { format: :json }
|
2021-08-02 12:41:41 +08:00
|
|
|
get "private-messages-new/:username" => "list#private_messages_new", as: "topics_private_messages_new", defaults: { format: :json }
|
2021-06-15 05:01:17 +08:00
|
|
|
get "private-messages-warnings/:username" => "list#private_messages_warnings", as: "topics_private_messages_warnings", defaults: { format: :json }
|
2018-04-12 17:10:53 +08:00
|
|
|
get "groups/:group_name" => "list#group_topics", as: "group_topics", group_name: RouteFormat.username
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2018-02-14 04:46:25 +08:00
|
|
|
scope "/private-messages-group/:username", group_name: RouteFormat.username do
|
|
|
|
get ":group_name.json" => "list#private_messages_group", as: "topics_private_messages_group"
|
|
|
|
get ":group_name/archive.json" => "list#private_messages_group_archive", as: "topics_private_messages_group_archive"
|
2021-08-02 12:41:41 +08:00
|
|
|
get ":group_name/new.json" => "list#private_messages_group_new", as: "topics_private_messages_group_new"
|
|
|
|
get ":group_name/unread.json" => "list#private_messages_group_unread", as: "topics_private_messages_group_unread"
|
2020-06-05 10:49:31 +08:00
|
|
|
end
|
2018-02-14 04:46:25 +08:00
|
|
|
end
|
2015-12-23 08:09:17 +08:00
|
|
|
|
2019-08-16 01:41:06 +08:00
|
|
|
get 'embed/topics' => 'embed#topics'
|
2014-01-04 01:52:24 +08:00
|
|
|
get 'embed/comments' => 'embed#comments'
|
2014-01-14 01:47:24 +08:00
|
|
|
get 'embed/count' => 'embed#count'
|
2015-05-06 09:00:31 +08:00
|
|
|
get 'embed/info' => 'embed#info'
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2015-04-28 02:36:02 +08:00
|
|
|
get "new-topic" => "list#latest"
|
2015-11-24 20:46:42 +08:00
|
|
|
get "new-message" => "list#latest"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2013-02-15 03:11:13 +08:00
|
|
|
# Topic routes
|
2014-09-17 23:18:41 +08:00
|
|
|
get "t/id_for/:slug" => "topics#id_for_slug"
|
2022-02-09 11:55:32 +08:00
|
|
|
get "t/external_id/:external_id" => "topics#show_by_external_id", format: :json, constrains: { external_id: /\A[\w-]+\z/ }
|
2016-08-01 12:45:05 +08:00
|
|
|
get "t/:slug/:topic_id/print" => "topics#show", format: :html, print: true, constraints: { topic_id: /\d+/ }
|
2013-12-24 07:50:36 +08:00
|
|
|
get "t/:slug/:topic_id/wordpress" => "topics#wordpress", constraints: { topic_id: /\d+/ }
|
|
|
|
get "t/:topic_id/wordpress" => "topics#wordpress", constraints: { topic_id: /\d+/ }
|
2015-08-13 05:00:16 +08:00
|
|
|
get "t/:slug/:topic_id/moderator-liked" => "topics#moderator_liked", constraints: { topic_id: /\d+/ }
|
|
|
|
get "t/:slug/:topic_id/summary" => "topics#show", defaults: { summary: true }, constraints: { topic_id: /\d+/ }
|
|
|
|
get "t/:topic_id/summary" => "topics#show", constraints: { topic_id: /\d+/ }
|
2013-12-24 07:50:36 +08:00
|
|
|
put "t/:slug/:topic_id" => "topics#update", constraints: { topic_id: /\d+/ }
|
|
|
|
put "t/:slug/:topic_id/star" => "topics#star", constraints: { topic_id: /\d+/ }
|
|
|
|
put "t/:topic_id/star" => "topics#star", constraints: { topic_id: /\d+/ }
|
|
|
|
put "t/:slug/:topic_id/status" => "topics#status", constraints: { topic_id: /\d+/ }
|
|
|
|
put "t/:topic_id/status" => "topics#status", constraints: { topic_id: /\d+/ }
|
|
|
|
put "t/:topic_id/clear-pin" => "topics#clear_pin", constraints: { topic_id: /\d+/ }
|
2014-04-10 08:56:56 +08:00
|
|
|
put "t/:topic_id/re-pin" => "topics#re_pin", constraints: { topic_id: /\d+/ }
|
2013-12-24 07:50:36 +08:00
|
|
|
put "t/:topic_id/mute" => "topics#mute", constraints: { topic_id: /\d+/ }
|
|
|
|
put "t/:topic_id/unmute" => "topics#unmute", constraints: { topic_id: /\d+/ }
|
2017-05-12 06:23:18 +08:00
|
|
|
post "t/:topic_id/timer" => "topics#timer", constraints: { topic_id: /\d+/ }
|
2014-06-17 00:28:07 +08:00
|
|
|
put "t/:topic_id/make-banner" => "topics#make_banner", constraints: { topic_id: /\d+/ }
|
|
|
|
put "t/:topic_id/remove-banner" => "topics#remove_banner", constraints: { topic_id: /\d+/ }
|
2013-12-24 07:50:36 +08:00
|
|
|
put "t/:topic_id/remove-allowed-user" => "topics#remove_allowed_user", constraints: { topic_id: /\d+/ }
|
2016-06-20 14:29:11 +08:00
|
|
|
put "t/:topic_id/remove-allowed-group" => "topics#remove_allowed_group", constraints: { topic_id: /\d+/ }
|
2013-12-24 07:50:36 +08:00
|
|
|
put "t/:topic_id/recover" => "topics#recover", constraints: { topic_id: /\d+/ }
|
|
|
|
get "t/:topic_id/:post_number" => "topics#show", constraints: { topic_id: /\d+/, post_number: /\d+/ }
|
2014-01-31 04:23:42 +08:00
|
|
|
get "t/:topic_id/last" => "topics#show", post_number: 99999999, constraints: { topic_id: /\d+/ }
|
2013-12-24 07:50:36 +08:00
|
|
|
get "t/:slug/:topic_id.rss" => "topics#feed", format: :rss, constraints: { topic_id: /\d+/ }
|
|
|
|
get "t/:slug/:topic_id" => "topics#show", constraints: { topic_id: /\d+/ }
|
|
|
|
get "t/:slug/:topic_id/:post_number" => "topics#show", constraints: { topic_id: /\d+/, post_number: /\d+/ }
|
2014-01-31 04:23:42 +08:00
|
|
|
get "t/:slug/:topic_id/last" => "topics#show", post_number: 99999999, constraints: { topic_id: /\d+/ }
|
2016-04-06 01:12:14 +08:00
|
|
|
get "t/:topic_id/posts" => "topics#posts", constraints: { topic_id: /\d+/ }, format: :json
|
2018-06-28 14:54:54 +08:00
|
|
|
get "t/:topic_id/post_ids" => "topics#post_ids", constraints: { topic_id: /\d+/ }, format: :json
|
2016-11-25 08:34:43 +08:00
|
|
|
get "t/:topic_id/excerpts" => "topics#excerpts", constraints: { topic_id: /\d+/ }, format: :json
|
2013-12-24 07:50:36 +08:00
|
|
|
post "t/:topic_id/timings" => "topics#timings", constraints: { topic_id: /\d+/ }
|
|
|
|
post "t/:topic_id/invite" => "topics#invite", constraints: { topic_id: /\d+/ }
|
2016-06-20 14:29:11 +08:00
|
|
|
post "t/:topic_id/invite-group" => "topics#invite_group", constraints: { topic_id: /\d+/ }
|
2013-12-24 07:50:36 +08:00
|
|
|
post "t/:topic_id/move-posts" => "topics#move_posts", constraints: { topic_id: /\d+/ }
|
|
|
|
post "t/:topic_id/merge-topic" => "topics#merge_topic", constraints: { topic_id: /\d+/ }
|
2014-03-28 09:28:14 +08:00
|
|
|
post "t/:topic_id/change-owner" => "topics#change_post_owners", constraints: { topic_id: /\d+/ }
|
2015-07-26 00:06:49 +08:00
|
|
|
put "t/:topic_id/change-timestamp" => "topics#change_timestamps", constraints: { topic_id: /\d+/ }
|
2013-12-24 07:50:36 +08:00
|
|
|
delete "t/:topic_id/timings" => "topics#destroy_timings", constraints: { topic_id: /\d+/ }
|
2015-01-12 19:10:15 +08:00
|
|
|
put "t/:topic_id/bookmark" => "topics#bookmark", constraints: { topic_id: /\d+/ }
|
2015-02-19 07:58:57 +08:00
|
|
|
put "t/:topic_id/remove_bookmarks" => "topics#remove_bookmarks", constraints: { topic_id: /\d+/ }
|
2019-10-24 02:05:38 +08:00
|
|
|
put "t/:topic_id/tags" => "topics#update_tags", constraints: { topic_id: /\d+/ }
|
2020-10-17 03:24:38 +08:00
|
|
|
put "t/:topic_id/slow_mode" => "topics#set_slow_mode", constraints: { topic_id: /\d+/ }
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2013-12-24 07:50:36 +08:00
|
|
|
post "t/:topic_id/notifications" => "topics#set_notifications" , constraints: { topic_id: /\d+/ }
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2014-07-12 05:34:26 +08:00
|
|
|
get "p/:post_id(/:user_id)" => "posts#short_link"
|
2014-06-21 05:06:44 +08:00
|
|
|
get "/posts/:id/cooked" => "posts#cooked"
|
2014-04-02 05:45:16 +08:00
|
|
|
get "/posts/:id/expand-embed" => "posts#expand_embed"
|
2014-07-12 05:34:26 +08:00
|
|
|
get "/posts/:id/raw" => "posts#markdown_id"
|
2014-10-18 03:18:29 +08:00
|
|
|
get "/posts/:id/raw-email" => "posts#raw_email"
|
2014-07-12 05:34:26 +08:00
|
|
|
get "raw/:topic_id(/:post_number)" => "posts#markdown_num"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
UX: invites#show can't be requested with json and is not configured properly (#8570)
Currently at
tempting to access an invite via json will result in the following error:
```
HTTP_ACCEPT application/json, text/javascript, */*; q=0.01
GET /invites/xxxxxxx
ActionView::MissingTemplate (Missing template invites/show, application/show with {:locale=>[:en_US, :en], :formats=>[:json], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby]}. Searched in:
* "/var/www/discourse/app/views"
)
```
2019-12-20 16:24:55 +08:00
|
|
|
resources :invites, except: [:show]
|
|
|
|
get "/invites/:id" => "invites#show", constraints: { format: :html }
|
2021-03-03 17:45:29 +08:00
|
|
|
put "/invites/:id" => "invites#update"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2016-12-05 00:06:35 +08:00
|
|
|
post "invites/upload_csv" => "invites#upload_csv"
|
2021-03-03 17:45:29 +08:00
|
|
|
post "invites/destroy-all-expired" => "invites#destroy_all_expired"
|
2014-10-07 02:48:56 +08:00
|
|
|
post "invites/reinvite" => "invites#resend_invite"
|
2016-06-03 03:09:02 +08:00
|
|
|
post "invites/reinvite-all" => "invites#resend_all_invites"
|
2013-12-24 07:50:36 +08:00
|
|
|
delete "invites" => "invites#destroy"
|
2017-01-25 04:15:29 +08:00
|
|
|
put "invites/show/:id" => "invites#perform_accept_invitation", as: 'perform_accept_invite'
|
2021-04-06 23:01:07 +08:00
|
|
|
get "invites/retrieve" => "invites#retrieve"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2014-12-23 00:17:04 +08:00
|
|
|
resources :export_csv do
|
|
|
|
collection do
|
2015-07-25 00:33:53 +08:00
|
|
|
post "export_entity" => "export_csv#export_entity"
|
2020-06-05 10:49:31 +08:00
|
|
|
end
|
2014-12-23 00:17:04 +08:00
|
|
|
end
|
|
|
|
|
2013-12-24 07:50:36 +08:00
|
|
|
get "onebox" => "onebox#show"
|
2017-07-20 03:08:54 +08:00
|
|
|
get "inline-onebox" => "inline_onebox#show"
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2014-06-17 02:25:33 +08:00
|
|
|
get "exception" => "list#latest"
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2013-12-24 07:50:36 +08:00
|
|
|
get "message-bus/poll" => "message_bus#poll"
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2021-09-14 20:18:01 +08:00
|
|
|
resources :drafts, only: [:index, :create, :show, :destroy]
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2022-02-14 20:47:56 +08:00
|
|
|
get "/service-worker.js" => "static#service_worker_asset", format: :js
|
2018-02-15 10:24:39 +08:00
|
|
|
if service_worker_asset = Rails.application.assets_manifest.assets['service-worker.js']
|
2018-02-19 08:04:15 +08:00
|
|
|
# https://developers.google.com/web/fundamentals/codelabs/debugging-service-workers/
|
|
|
|
# Normally the browser will wait until a user closes all tabs that contain the
|
|
|
|
# current site before updating to a new Service Worker.
|
|
|
|
# Support the old Service Worker path to avoid routing error filling up the
|
|
|
|
# logs.
|
2018-02-15 10:24:39 +08:00
|
|
|
get service_worker_asset => "static#service_worker_asset", format: :js
|
|
|
|
end
|
2017-11-23 09:02:01 +08:00
|
|
|
|
2019-07-24 01:17:44 +08:00
|
|
|
get "cdn_asset/:site/*path" => "static#cdn_asset", format: false, constraints: { format: /.*/ }
|
|
|
|
get "brotli_asset/*path" => "static#brotli_asset", format: false, constraints: { format: /.*/ }
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2015-08-25 09:54:23 +08:00
|
|
|
get "favicon/proxied" => "static#favicon", format: false
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2013-12-24 07:50:36 +08:00
|
|
|
get "robots.txt" => "robots_txt#index"
|
2018-04-17 03:43:20 +08:00
|
|
|
get "robots-builder.json" => "robots_txt#builder"
|
2017-10-31 07:46:48 +08:00
|
|
|
get "offline.html" => "offline#index"
|
2018-06-14 11:13:28 +08:00
|
|
|
get "manifest.webmanifest" => "metadata#manifest", as: :manifest
|
|
|
|
get "manifest.json" => "metadata#manifest"
|
2019-08-28 02:05:37 +08:00
|
|
|
get ".well-known/assetlinks.json" => "metadata#app_association_android"
|
|
|
|
get "apple-app-site-association" => "metadata#app_association_ios", format: false
|
2019-07-24 01:17:44 +08:00
|
|
|
get "opensearch" => "metadata#opensearch", constraints: { format: :xml }
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2019-12-13 19:05:23 +08:00
|
|
|
scope '/tag/:tag_id' do
|
|
|
|
constraints format: :json do
|
2020-01-22 01:23:08 +08:00
|
|
|
get '/' => 'tags#show', as: 'tag_show'
|
2019-12-13 19:05:23 +08:00
|
|
|
get '/info' => 'tags#info'
|
|
|
|
get '/notifications' => 'tags#notifications'
|
|
|
|
put '/notifications' => 'tags#update_notifications'
|
|
|
|
put '/' => 'tags#update'
|
|
|
|
delete '/' => 'tags#destroy'
|
|
|
|
post '/synonyms' => 'tags#create_synonyms'
|
|
|
|
delete '/synonyms/:synonym_id' => 'tags#destroy_synonym'
|
|
|
|
|
|
|
|
Discourse.filters.each do |filter|
|
2020-01-22 01:23:08 +08:00
|
|
|
get "/l/#{filter}" => "tags#show_#{filter}", as: "tag_show_#{filter}"
|
2020-06-05 10:49:31 +08:00
|
|
|
end
|
2019-12-13 19:05:23 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
constraints format: :rss do
|
|
|
|
get '/' => 'tags#tag_feed'
|
2020-06-05 10:49:31 +08:00
|
|
|
end
|
2019-12-13 19:05:23 +08:00
|
|
|
end
|
|
|
|
|
2016-04-26 03:55:15 +08:00
|
|
|
scope "/tags" do
|
|
|
|
get '/' => 'tags#index'
|
|
|
|
get '/filter/list' => 'tags#index'
|
|
|
|
get '/filter/search' => 'tags#search'
|
2021-07-29 13:43:25 +08:00
|
|
|
get '/personal_messages/:username' => 'tags#personal_messages', constraints: { username: RouteFormat.username }
|
2018-10-15 16:12:54 +08:00
|
|
|
post '/upload' => 'tags#upload'
|
2018-11-13 00:24:34 +08:00
|
|
|
get '/unused' => 'tags#list_unused'
|
|
|
|
delete '/unused' => 'tags#destroy_unused'
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2016-04-26 03:55:15 +08:00
|
|
|
constraints(tag_id: /[^\/]+?/, format: /json|rss/) do
|
2019-11-04 00:32:31 +08:00
|
|
|
scope path: '/c/*category_slug_path_with_id' do
|
|
|
|
Discourse.filters.each do |filter|
|
|
|
|
get "/none/:tag_id/l/#{filter}" => "tags#show_#{filter}", as: "tag_category_none_show_#{filter}", defaults: { no_subcategories: true }
|
|
|
|
end
|
|
|
|
|
|
|
|
get '/none/:tag_id' => 'tags#show', as: 'tag_category_none_show', defaults: { no_subcategories: true }
|
|
|
|
|
|
|
|
Discourse.filters.each do |filter|
|
|
|
|
get "/:tag_id/l/#{filter}" => "tags#show_#{filter}", as: "tag_category_show_#{filter}"
|
2020-06-05 10:49:31 +08:00
|
|
|
end
|
|
|
|
|
2019-11-04 00:32:31 +08:00
|
|
|
get '/:tag_id' => 'tags#show', as: 'tag_category_show'
|
|
|
|
end
|
|
|
|
|
|
|
|
get '/intersection/:tag_id/*additional_tag_ids' => 'tags#show', as: 'tag_intersection'
|
|
|
|
end
|
2020-11-10 01:01:33 +08:00
|
|
|
|
|
|
|
get '*tag_id', to: redirect(relative_url_root + 'tag/%{tag_id}')
|
2016-04-26 03:55:15 +08:00
|
|
|
end
|
2016-06-08 01:08:59 +08:00
|
|
|
|
2021-04-09 01:23:13 +08:00
|
|
|
resources :tag_groups, constraints: StaffConstraint.new, except: [:edit]
|
|
|
|
get '/tag_groups/filter/search' => 'tag_groups#search', format: :json
|
2016-04-26 03:55:15 +08:00
|
|
|
|
2013-12-24 07:50:36 +08:00
|
|
|
Discourse.filters.each do |filter|
|
2013-07-02 02:00:06 +08:00
|
|
|
root to: "list##{filter}", constraints: HomePageConstraint.new("#{filter}"), as: "list_#{filter}"
|
2013-03-28 21:01:13 +08:00
|
|
|
end
|
|
|
|
# special case for categories
|
2013-07-02 02:00:06 +08:00
|
|
|
root to: "categories#index", constraints: HomePageConstraint.new("categories"), as: "categories_index"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2016-10-18 23:44:25 +08:00
|
|
|
root to: 'finish_installation#index', constraints: HomePageConstraint.new("finish_installation"), as: 'installation_redirect'
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2016-08-15 15:58:33 +08:00
|
|
|
get "/user-api-key/new" => "user_api_keys#new"
|
2016-08-16 13:10:32 +08:00
|
|
|
post "/user-api-key" => "user_api_keys#create"
|
2016-08-16 15:06:33 +08:00
|
|
|
post "/user-api-key/revoke" => "user_api_keys#revoke"
|
|
|
|
post "/user-api-key/undo-revoke" => "user_api_keys#undo_revoke"
|
2019-04-02 01:18:53 +08:00
|
|
|
get "/user-api-key/otp" => "user_api_keys#otp"
|
|
|
|
post "/user-api-key/otp" => "user_api_keys#create_otp"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2016-11-21 13:46:02 +08:00
|
|
|
get "/safe-mode" => "safe_mode#index"
|
|
|
|
post "/safe-mode" => "safe_mode#enter", as: "safe_mode_enter"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2018-11-23 13:15:40 +08:00
|
|
|
unless Rails.env.production?
|
2018-04-23 14:42:25 +08:00
|
|
|
get "/qunit" => "qunit#index"
|
2018-11-23 13:15:40 +08:00
|
|
|
get "/wizard/qunit" => "wizard#qunit"
|
2018-04-23 14:42:25 +08:00
|
|
|
end
|
2021-04-29 04:12:08 +08:00
|
|
|
get "/theme-qunit" => "qunit#theme"
|
2018-04-23 14:42:25 +08:00
|
|
|
|
2018-05-05 06:31:48 +08:00
|
|
|
post "/push_notifications/subscribe" => "push_notification#subscribe"
|
|
|
|
post "/push_notifications/unsubscribe" => "push_notification#unsubscribe"
|
|
|
|
|
2018-10-23 01:22:23 +08:00
|
|
|
resources :csp_reports, only: [:create]
|
|
|
|
|
2020-03-06 09:33:25 +08:00
|
|
|
get "/permalink-check", to: 'permalinks#check'
|
|
|
|
|
2020-12-18 23:03:51 +08:00
|
|
|
post "/do-not-disturb" => "do_not_disturb#create"
|
|
|
|
delete "/do-not-disturb" => "do_not_disturb#destroy"
|
|
|
|
|
DEV: Introduce PresenceChannel API for core and plugin use
PresenceChannel aims to be a generic system for allow the server, and end-users, to track the number and identity of users performing a specific task on the site. For example, it might be used to track who is currently 'replying' to a specific topic, editing a specific wiki post, etc.
A few key pieces of information about the system:
- PresenceChannels are identified by a name of the format `/prefix/blah`, where `prefix` has been configured by some core/plugin implementation, and `blah` can be any string the implementation wants to use.
- Presence is a boolean thing - each user is either present, or not present. If a user has multiple clients 'present' in a channel, they will be deduplicated so that the user is only counted once
- Developers can configure the existence and configuration of channels 'just in time' using a callback. The result of this is cached for 2 minutes.
- Configuration of a channel can specify permissions in a similar way to MessageBus (public boolean, a list of allowed_user_ids, and a list of allowed_group_ids). A channel can also be placed in 'count_only' mode, where the identity of present users is not revealed to end-users.
- The backend implementation uses redis lua scripts, and is designed to scale well. In the future, hard limits may be introduced on the maximum number of users that can be present in a channel.
- Clients can enter/leave at will. If a client has not marked itself 'present' in the last 60 seconds, they will automatically 'leave' the channel. The JS implementation takes care of this regular check-in.
- On the client-side, PresenceChannel instances can be fetched from the `presence` ember service. Each PresenceChannel can be used entered/left/subscribed/unsubscribed, and the service will automatically deduplicate information before interacting with the server.
- When a client joins a PresenceChannel, the JS implementation will automatically make a GET request for the current channel state. To avoid this, the channel state can be serialized into one of your existing endpoints, and then passed to the `subscribe` method on the channel.
- The PresenceChannel JS object is an ember object. The `users` and `count` property can be used directly in ember templates, and in computed properties.
- It is important to make sure that you `unsubscribe()` and `leave()` any PresenceChannel objects after use
An example implementation may look something like this. On the server:
```ruby
register_presence_channel_prefix("site") do |channel|
next nil unless channel == "/site/online"
PresenceChannel::Config.new(public: true)
end
```
And on the client, a component could be implemented like this:
```javascript
import Component from "@ember/component";
import { inject as service } from "@ember/service";
export default Component.extend({
presence: service(),
init() {
this._super(...arguments);
this.set("presenceChannel", this.presence.getChannel("/site/online"));
},
didInsertElement() {
this.presenceChannel.enter();
this.presenceChannel.subscribe();
},
willDestroyElement() {
this.presenceChannel.leave();
this.presenceChannel.unsubscribe();
},
});
```
With this template:
```handlebars
Online: {{presenceChannel.count}}
<ul>
{{#each presenceChannel.users as |user|}}
<li>{{avatar user imageSize="tiny"}} {{user.username}}</li>
{{/each}}
</ul>
```
2021-08-27 21:43:39 +08:00
|
|
|
post "/presence/update" => "presence#update"
|
|
|
|
get "/presence/get" => "presence#get"
|
|
|
|
|
2018-06-06 14:55:22 +08:00
|
|
|
get "*url", to: 'permalinks#show', constraints: PermalinkConstraint.new
|
2019-07-24 01:17:44 +08:00
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
end
|