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
|
2023-01-07 19:59:28 +08:00
|
|
|
|
2019-07-24 01:17:44 +08:00
|
|
|
scope path: nil, constraints: { format: %r{(json|html|\*/\*)} } do
|
|
|
|
relative_url_root =
|
|
|
|
(
|
2023-01-07 19:59:28 +08:00
|
|
|
if (
|
2019-07-24 01:17:44 +08:00
|
|
|
defined?(Rails.configuration.relative_url_root) &&
|
|
|
|
Rails.configuration.relative_url_root
|
|
|
|
)
|
|
|
|
Rails.configuration.relative_url_root + "/"
|
2023-01-07 19:59:28 +08:00
|
|
|
else
|
|
|
|
"/"
|
|
|
|
end
|
|
|
|
)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2013-07-02 02:00:06 +08:00
|
|
|
match "/404", to: "exceptions#not_found", via: %i[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
|
|
|
|
2022-10-20 01:10:06 +08:00
|
|
|
if Rails.env.test? || Rails.env.development?
|
|
|
|
get "/bootstrap/plugin-css-for-tests.css" => "bootstrap#plugin_css_for_tests"
|
|
|
|
end
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2023-07-05 12:11:15 +08:00
|
|
|
# This is not a valid production route and is causing routing errors to be raised in
|
|
|
|
# the test env adding noise to the logs. Just handle it here so we eliminate the noise.
|
|
|
|
get "/favicon.ico", to: proc { [200, {}, [""]] } if Rails.env.test?
|
|
|
|
|
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"
|
2023-06-09 01:06:20 +08:00
|
|
|
post "webhooks/mailpace" => "webhooks#mailpace"
|
2016-06-13 18:31:01 +08:00
|
|
|
post "webhooks/mandrill" => "webhooks#mandrill"
|
2022-07-29 14:26:31 +08:00
|
|
|
get "webhooks/mandrill" => "webhooks#mandrill_head"
|
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
|
|
|
|
2022-04-19 22:07:25 +08:00
|
|
|
scope path: nil, format: true, constraints: { format: :xml } do
|
2022-04-12 21:33:59 +08:00
|
|
|
resources :sitemap, only: [:index]
|
|
|
|
get "/sitemap_:page" => "sitemap#page", :page => /[1-9][0-9]*/
|
|
|
|
get "/sitemap_recent" => "sitemap#recent"
|
|
|
|
get "/news" => "sitemap#news"
|
|
|
|
end
|
|
|
|
|
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
|
|
|
|
2023-06-15 06:18:32 +08:00
|
|
|
resources :about, only: [:index] do
|
2015-08-28 05:28:33 +08:00
|
|
|
collection { get "live_post_counts" }
|
|
|
|
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
|
|
|
|
2023-06-15 06:18:32 +08:00
|
|
|
resources :directory_items, only: [:index]
|
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"
|
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"
|
2024-03-13 11:15:12 +08:00
|
|
|
get "plugins/:plugin_id" => "plugins#show"
|
|
|
|
get "plugins/:plugin_id/settings" => "plugins#show"
|
2015-02-07 06:32:59 +08:00
|
|
|
|
2023-06-15 06:18:32 +08:00
|
|
|
resources :site_settings, only: %i[index update], constraints: AdminConstraint.new do
|
2013-12-24 07:50:36 +08:00
|
|
|
collection { get "category/:id" => "site_settings#index" }
|
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
|
|
|
|
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
|
2023-06-15 06:18:32 +08:00
|
|
|
resources :groups, only: [:destroy], constraints: AdminConstraint.new do
|
2020-04-23 00:37:39 +08:00
|
|
|
collection { put "automatic_membership_count" => "groups#automatic_membership_count" }
|
2015-01-22 03:52:48 +08:00
|
|
|
end
|
2013-05-08 13:20:38 +08:00
|
|
|
|
2023-06-15 06:18:32 +08:00
|
|
|
resources :users, id: RouteFormat.username, only: %i[index destroy] 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"
|
2023-03-21 22:26:26 +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"
|
2024-08-20 20:27:29 +08:00
|
|
|
get "similar-users.json" => "users#similar_users"
|
2024-09-27 20:08:05 +08:00
|
|
|
put "delete_associated_accounts"
|
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
|
|
|
|
|
2023-06-15 06:18:32 +08:00
|
|
|
resources :impersonate, only: %i[index create], constraints: AdminConstraint.new
|
2013-06-04 04:12:24 +08:00
|
|
|
|
2023-06-15 06:18:32 +08:00
|
|
|
resources :email, only: [:index], 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-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"
|
2023-01-05 06:57:12 +08:00
|
|
|
post "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: %i[index destroy]
|
2022-01-11 15:16:51 +08:00
|
|
|
resources :screened_ip_addresses, only: %i[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}")
|
2023-01-07 19:59:28 +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
|
2024-04-17 09:45:59 +08:00
|
|
|
get "customize/components" => "themes#index", :constraints => AdminConstraint.new
|
2023-11-08 11:42:27 +08:00
|
|
|
get "customize/theme-components" => "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
|
|
|
|
2023-06-15 06:18:32 +08:00
|
|
|
resources :themes,
|
|
|
|
only: %i[index create show update destroy],
|
|
|
|
constraints: AdminConstraint.new do
|
2022-09-30 02:00:20 +08:00
|
|
|
member do
|
|
|
|
get "preview" => "themes#preview"
|
2024-03-19 00:00:28 +08:00
|
|
|
get "translations/:locale" => "themes#get_translations"
|
2022-09-30 02:00:20 +08:00
|
|
|
put "setting" => "themes#update_single_setting"
|
2024-03-26 14:02:05 +08:00
|
|
|
get "objects_setting_metadata/:setting_name" => "themes#objects_setting_metadata"
|
2022-09-30 02:00:20 +08:00
|
|
|
end
|
2024-03-26 14:02:05 +08:00
|
|
|
|
2022-09-30 02:00:20 +08:00
|
|
|
collection do
|
|
|
|
post "import" => "themes#import"
|
|
|
|
post "upload_asset" => "themes#upload_asset"
|
|
|
|
post "generate_key_pair" => "themes#generate_key_pair"
|
2023-10-09 05:35:53 +08:00
|
|
|
delete "bulk_destroy" => "themes#bulk_destroy"
|
2022-09-30 02:00:20 +08:00
|
|
|
end
|
|
|
|
end
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2017-01-07 03:42:36 +08:00
|
|
|
scope "/customize", constraints: AdminConstraint.new do
|
2023-06-15 06:18:32 +08:00
|
|
|
resources :user_fields,
|
|
|
|
only: %i[index create update destroy],
|
|
|
|
constraints: AdminConstraint.new
|
|
|
|
resources :emojis, only: %i[index create destroy], constraints: AdminConstraint.new
|
2023-03-02 03:07:13 +08:00
|
|
|
resources :form_templates, constraints: AdminConstraint.new, path: "/form-templates" do
|
|
|
|
collection { get "preview" => "form_templates#preview" }
|
|
|
|
end
|
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"
|
2024-04-17 09:45:59 +08:00
|
|
|
get "components/:id" => "themes#index"
|
2024-05-06 12:27:30 +08:00
|
|
|
get "components/:id/:target/:field_name/edit" => "themes#index"
|
2019-01-23 22:40:21 +08:00
|
|
|
get "themes/:id/export" => "themes#export"
|
2024-02-16 14:31:49 +08:00
|
|
|
get "themes/:id/schema/:setting_name" => "themes#schema"
|
2024-04-24 15:34:57 +08:00
|
|
|
get "components/:id/schema/:setting_name" => "themes#schema"
|
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 }
|
2023-07-19 23:06:13 +08:00
|
|
|
put "site_texts/:id/dismiss_outdated" => "site_texts#dismiss_outdated",
|
|
|
|
:constraints => {
|
|
|
|
id: /[\w.\-\+\%\&]+/i,
|
|
|
|
}
|
|
|
|
put "site_texts/:id/dismiss_outdated.json" => "site_texts#dismiss_outdated",
|
|
|
|
:constraints => {
|
|
|
|
id: /[\w.\-\+\%\&]+/i,
|
|
|
|
}
|
2023-01-07 19:59:28 +08:00
|
|
|
|
2019-03-19 04:09:13 +08:00
|
|
|
get "reseed" => "site_texts#get_reseed_options"
|
|
|
|
post "reseed" => "site_texts#reseed"
|
2023-01-07 19:59:28 +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_.]+/,
|
|
|
|
}
|
2023-01-07 19:59:28 +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"
|
2023-01-07 19:59:28 +08:00
|
|
|
|
2019-07-31 03:05:08 +08:00
|
|
|
resource :email_style, only: %i[show update]
|
|
|
|
get "email_style/:field" => "email_styles#show", :constraints => { field: /html|css/ }
|
2021-04-23 21:21:45 +08:00
|
|
|
end
|
|
|
|
|
2023-06-15 06:18:32 +08:00
|
|
|
resources :embeddable_hosts, only: %i[create update destroy], constraints: AdminConstraint.new
|
|
|
|
resources :color_schemes,
|
|
|
|
only: %i[index create update destroy],
|
|
|
|
constraints: AdminConstraint.new
|
|
|
|
resources :permalinks, only: %i[index create destroy], constraints: AdminConstraint.new
|
2021-04-01 14:14:17 +08:00
|
|
|
|
2021-04-23 21:21:45 +08:00
|
|
|
scope "/customize" do
|
2023-06-15 06:18:32 +08:00
|
|
|
resources :watched_words, only: %i[index create destroy] do
|
2021-04-01 14:14:17 +08:00
|
|
|
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"
|
2023-11-27 06:32:28 +08:00
|
|
|
get "dashboard/whats-new" => "dashboard#new_features"
|
2024-03-20 11:23:18 +08:00
|
|
|
get "/whats-new" => "dashboard#new_features"
|
2018-04-16 16:42:06 +08:00
|
|
|
|
2013-03-30 03:48:26 +08:00
|
|
|
resources :dashboard, only: [:index] do
|
2013-12-24 07:50:36 +08:00
|
|
|
collection { get "problems" }
|
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: %i[index show update create destroy] do
|
2020-07-17 02:51:24 +08:00
|
|
|
collection { get "scopes" => "api#scopes" }
|
|
|
|
|
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
|
|
|
|
2023-06-15 06:18:32 +08:00
|
|
|
resources :web_hooks, only: %i[index create show edit update destroy]
|
2016-09-19 16:43:06 +08:00
|
|
|
get "web_hook_events/:id" => "web_hooks#list_events", :as => :web_hook_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"
|
2024-07-09 04:43:16 +08:00
|
|
|
post "web_hooks/:id/events/failed_redeliver" => "web_hooks#redeliver_failed_events"
|
2016-09-19 16:43:06 +08:00
|
|
|
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: %i[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"
|
2024-08-20 07:59:43 +08:00
|
|
|
get "settings" => "backups#index"
|
2014-02-13 12:33:40 +08:00
|
|
|
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
|
|
|
|
2023-06-15 06:18:32 +08:00
|
|
|
resources :badges,
|
|
|
|
only: %i[index new show create update destroy],
|
|
|
|
constraints: AdminConstraint.new do
|
2014-03-05 20:52:20 +08:00
|
|
|
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
|
2024-05-29 12:39:58 +08:00
|
|
|
namespace :config, constraints: StaffConstraint.new do
|
2024-07-30 13:41:28 +08:00
|
|
|
resources :site_settings, only: %i[index]
|
|
|
|
|
2024-07-03 06:45:37 +08:00
|
|
|
resources :flags, only: %i[index new create update destroy] do
|
2024-05-29 12:39:58 +08:00
|
|
|
put "toggle"
|
2024-06-05 11:27:06 +08:00
|
|
|
put "reorder/:direction" => "flags#reorder"
|
2024-07-03 06:45:37 +08:00
|
|
|
member { get "/" => "flags#edit" }
|
2024-05-29 12:39:58 +08:00
|
|
|
end
|
2024-06-03 15:18:14 +08:00
|
|
|
|
|
|
|
resources :about, constraints: AdminConstraint.new, only: %i[index] do
|
|
|
|
collection { put "/" => "about#update" }
|
|
|
|
end
|
2024-05-29 12:39:58 +08:00
|
|
|
end
|
2024-09-17 14:43:34 +08:00
|
|
|
|
2024-10-02 10:19:38 +08:00
|
|
|
get "section/:section_id" => "section#show", :constraints => AdminConstraint.new
|
2024-09-17 14:43:34 +08:00
|
|
|
resources :admin_notices, only: %i[destroy], constraints: AdminConstraint.new
|
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: %i[create destroy become] do
|
2018-03-28 11:31:43 +08:00
|
|
|
get "become" if !Rails.env.production?
|
2018-03-28 11:22:43 +08:00
|
|
|
|
2013-12-24 07:50:36 +08:00
|
|
|
collection { post "forgot_password" }
|
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"
|
2022-07-28 16:16:33 +08:00
|
|
|
get "review/user-menu-list" => "reviewables#user_menu_list", :format => :json
|
2019-04-03 05:00:15 +08:00
|
|
|
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
|
|
|
|
2023-06-15 06:18:32 +08:00
|
|
|
resources :reviewable_claimed_topics, only: %i[create destroy]
|
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
|
2023-10-12 02:36:54 +08:00
|
|
|
get "session/passkey/challenge" => "session#passkey_challenge"
|
|
|
|
post "session/passkey/auth" => "session#passkey_login"
|
2022-05-02 23:15:32 +08:00
|
|
|
get "session/scopes" => "session#scopes"
|
2022-12-06 02:22:05 +08:00
|
|
|
get "composer/mentions" => "composer#mentions"
|
2016-06-07 01:52:24 +08:00
|
|
|
get "composer_messages" => "composer_messages#index"
|
2022-09-28 00:36:40 +08:00
|
|
|
get "composer_messages/user_not_seen_in_a_while" => "composer_messages#user_not_seen_in_a_while"
|
2020-06-05 10:49:31 +08:00
|
|
|
|
2019-07-24 01:17:44 +08:00
|
|
|
post "login" => "static#enter"
|
2023-05-12 01:02:11 +08:00
|
|
|
|
2019-07-24 01:17:44 +08:00
|
|
|
get "login" => "static#show", :id => "login"
|
2023-05-12 01:02:11 +08:00
|
|
|
get "login-preferences" => "static#show", :id => "login"
|
|
|
|
get "signup" => "static#show", :id => "signup"
|
2019-07-24 01:17:44 +08:00
|
|
|
get "password-reset" => "static#show", :id => "password_reset"
|
|
|
|
get "privacy" => "static#show", :id => "privacy", :as => "privacy"
|
2023-05-12 01:02:11 +08:00
|
|
|
get "tos" => "static#show", :id => "tos", :as => "tos"
|
|
|
|
get "faq" => "static#show", :id => "faq"
|
|
|
|
%w[guidelines rules conduct].each do |guidelines_alias|
|
|
|
|
get guidelines_alias => "static#show", :id => "guidelines", :as => guidelines_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" }
|
|
|
|
|
2023-06-15 06:18:32 +08:00
|
|
|
resources :users, only: %i[create], 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"
|
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
|
|
|
|
2023-10-12 02:36:54 +08:00
|
|
|
get "#{root_path}/trusted-session" => "users#trusted_session"
|
|
|
|
post "#{root_path}/confirm-session" => "users#confirm_session"
|
|
|
|
|
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
|
|
|
|
2023-10-12 02:36:54 +08:00
|
|
|
post "#{root_path}/create_passkey" => "users#create_passkey"
|
|
|
|
post "#{root_path}/register_passkey" => "users#register_passkey"
|
|
|
|
put "#{root_path}/rename_passkey/:id" => "users#rename_passkey"
|
|
|
|
delete "#{root_path}/delete_passkey/:id" => "users#delete_passkey"
|
|
|
|
|
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 },
|
2023-01-07 19:59:28 +08:00
|
|
|
),
|
2017-05-11 23:18:06 +08:00
|
|
|
)
|
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 } : {},
|
2023-01-07 19:59:28 +08:00
|
|
|
),
|
2020-01-15 18:27:12 +08:00
|
|
|
)
|
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" } : {},
|
2023-01-07 19:59:28 +08:00
|
|
|
),
|
2017-03-31 10:04:00 +08:00
|
|
|
)
|
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"
|
2024-01-30 18:32:42 +08:00
|
|
|
put "#{root_path}/confirm-old-email/:token" => "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"
|
2024-01-30 18:32:42 +08:00
|
|
|
put "#{root_path}/confirm-new-email/:token" => "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]+/,
|
|
|
|
}
|
2023-06-05 19:24:22 +08:00
|
|
|
get "#{root_path}/:username/private-messages" => "users#show",
|
2017-12-22 04:30:32 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
2023-06-05 19:24:22 +08:00
|
|
|
get "#{root_path}/:username/private-messages/:filter" => "users#show",
|
2017-12-22 04:30:32 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
2023-06-05 19:24:22 +08:00
|
|
|
get "#{root_path}/:username/messages" => "users#show",
|
2017-12-22 04:30:32 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
2023-06-05 19:24:22 +08:00
|
|
|
get "#{root_path}/:username/messages/:filter" => "users#show",
|
2017-12-22 04:30:32 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
2023-06-05 19:24:22 +08:00
|
|
|
get "#{root_path}/:username/messages/group/:group_name" => "users#show",
|
2017-12-22 04:30:32 +08:00
|
|
|
:constraints => {
|
2021-08-02 12:41:41 +08:00
|
|
|
username: RouteFormat.username,
|
|
|
|
group_name: RouteFormat.username,
|
|
|
|
}
|
2023-06-05 19:24:22 +08:00
|
|
|
get "#{root_path}/:username/messages/group/:group_name/:filter" => "users#show",
|
2021-08-02 12:41:41 +08:00
|
|
|
:constraints => {
|
2022-10-20 21:24:56 +08:00
|
|
|
username: RouteFormat.username,
|
|
|
|
group_name: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/messages/tags/:tag_id" => "list#private_messages_tag",
|
2017-12-22 04:30:32 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username.json" => "users#show",
|
2019-07-24 01:17:44 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
2023-01-07 19:59:28 +08:00
|
|
|
},
|
2019-07-24 01:17:44 +08:00
|
|
|
:defaults => {
|
|
|
|
format: :json,
|
|
|
|
}
|
|
|
|
get(
|
2023-01-07 19:59:28 +08:00
|
|
|
{
|
2019-07-24 01:17:44 +08:00
|
|
|
"#{root_path}/:username" => "users#show",
|
2022-12-07 23:46:35 +08:00
|
|
|
:constraints => {
|
2017-12-22 04:30:32 +08:00
|
|
|
username: RouteFormat.username,
|
2023-01-07 19:59:28 +08:00
|
|
|
},
|
2019-07-24 01:17:44 +08:00
|
|
|
}.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",
|
2020-11-11 03:12:44 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/sso-email" => "users#check_sso_email",
|
2021-02-17 23:57:51 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/sso-payload" => "users#check_sso_payload",
|
2020-06-06 00:42:12 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/preferences" => "users#preferences",
|
2017-12-22 04:30:32 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/preferences/email" => "users_email#index",
|
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/preferences/account" => "users#preferences",
|
2021-03-03 17:09:22 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/preferences/security" => "users#preferences",
|
2017-12-22 04:30:32 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/preferences/profile" => "users#preferences",
|
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/preferences/emails" => "users#preferences",
|
2020-06-11 00:11:49 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
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",
|
2017-12-22 04:30:32 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/preferences/notifications" => "users#preferences",
|
2022-11-18 09:09:04 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/preferences/tracking" => "users#preferences",
|
2017-12-22 04:30:32 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
2019-03-05 22:47:51 +08:00
|
|
|
get "#{root_path}/:username/preferences/users" => "users#preferences",
|
2017-12-22 04:30:32 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/preferences/tags" => "users#preferences",
|
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/preferences/interface" => "users#preferences",
|
2022-06-30 14:54:20 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
2023-06-15 07:31:28 +08:00
|
|
|
get "#{root_path}/:username/preferences/navigation-menu" => "users#preferences",
|
2017-12-22 04:30:32 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/preferences/apps" => "users#preferences",
|
2020-06-11 00:11:49 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
post "#{root_path}/:username/preferences/email" => "users_email#create",
|
2017-12-22 04:30:32 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
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",
|
2017-12-22 09:18:12 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/preferences/second-factor" => "users#preferences",
|
2018-06-28 16:12:32 +08:00
|
|
|
: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",
|
2018-07-18 18:57:43 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
put "#{root_path}/:username/preferences/avatar/select" => "users#select_avatar",
|
2018-07-23 23:51:57 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
post "#{root_path}/:username/preferences/revoke-account" => "users#revoke_account",
|
2018-08-31 16:18:06 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
post "#{root_path}/:username/preferences/revoke-auth-token" => "users#revoke_auth_token",
|
2017-12-22 04:30:32 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/staff-info" => "users#staff_info",
|
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/summary" => "users#summary",
|
2019-03-27 17:41:50 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
put "#{root_path}/:username/notification_level" => "users#notification_level",
|
2017-12-22 04:30:32 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/invited" => "users#invited",
|
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/invited/:filter" => "users#invited",
|
2022-12-07 23:46:35 +08:00
|
|
|
:constraints => {
|
2017-12-22 04:30:32 +08:00
|
|
|
username: RouteFormat.username,
|
2023-01-07 19:59:28 +08:00
|
|
|
}
|
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,
|
2018-10-09 22:21:41 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/activity.json" => "posts#user_posts_feed",
|
|
|
|
:format => :json,
|
2017-12-22 04:30:32 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
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",
|
2020-03-18 06:22:41 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/bookmarks" => "users#bookmarks",
|
2022-08-08 22:24:04 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
format: /(json|ics)/,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/user-menu-bookmarks" => "users#user_menu_bookmarks",
|
2022-08-10 13:25:39 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
format: :json,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/user-menu-private-messages" => "users#user_menu_messages",
|
2017-12-22 04:30:32 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
format: :json,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/notifications" => "users#show",
|
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/notifications/:filter" => "users#show",
|
2019-07-24 01:17:44 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
delete "#{root_path}/:username" => "users#destroy",
|
2017-03-31 10:04:00 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/by-external/:external_id" => "users#show",
|
2020-11-10 18:41:46 +08:00
|
|
|
:constraints => {
|
|
|
|
external_id: %r{[^/]+},
|
|
|
|
}
|
|
|
|
get "#{root_path}/by-external/:external_provider/:external_id" => "users#show",
|
2017-12-22 04:30:32 +08:00
|
|
|
:constraints => {
|
|
|
|
external_id: %r{[^/]+},
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/deleted-posts" => "users#show",
|
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/topic-tracking-state" => "users#topic_tracking_state",
|
2021-08-25 11:17:56 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/private-message-topic-tracking-state" =>
|
|
|
|
"users#private_message_topic_tracking_state",
|
2022-12-07 23:46:35 +08:00
|
|
|
:constraints => {
|
2021-08-25 11:17:56 +08:00
|
|
|
username: RouteFormat.username,
|
2023-01-07 19:59:28 +08:00
|
|
|
}
|
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",
|
2020-01-28 19:55:46 +08:00
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
|
|
|
}
|
|
|
|
get "#{root_path}/:username/card.json" => "users#show_card",
|
|
|
|
:format => :json,
|
2022-12-07 23:46:35 +08:00
|
|
|
:constraints => {
|
2020-01-28 19:55:46 +08:00
|
|
|
username: RouteFormat.username,
|
2023-01-07 19:59:28 +08:00
|
|
|
}
|
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,
|
|
|
|
}
|
2023-01-07 19:59:28 +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",
|
2019-07-24 01:17:44 +08:00
|
|
|
:constraints => {
|
|
|
|
hostname: /[\w\.-]+/,
|
2017-12-22 04:30:32 +08:00
|
|
|
size: /\d+/,
|
2019-07-24 01:17:44 +08:00
|
|
|
username: RouteFormat.username,
|
|
|
|
format: :png,
|
|
|
|
}
|
2023-01-07 19:59:28 +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",
|
2021-06-15 14:57:17 +08:00
|
|
|
:constraints => {
|
|
|
|
format: :png,
|
|
|
|
}
|
2023-01-07 19:59:28 +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]+)?/,
|
2019-07-24 01:17:44 +08:00
|
|
|
format: :js,
|
|
|
|
}
|
|
|
|
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,
|
|
|
|
}
|
2023-01-07 19:59:28 +08:00
|
|
|
|
2019-07-24 01:17:44 +08:00
|
|
|
get "highlight-js/:hostname/:version.js" => "highlight_js#show",
|
|
|
|
:constraints => {
|
|
|
|
hostname: /[\w\.-]+/,
|
|
|
|
format: :js,
|
2022-12-07 23:46:35 +08:00
|
|
|
}
|
2023-01-07 19:59:28 +08:00
|
|
|
|
2022-12-07 23:46:35 +08:00
|
|
|
get "stylesheets/:name" => "stylesheets#show_source_map",
|
|
|
|
:constraints => {
|
|
|
|
name: /[-a-z0-9_]+/,
|
|
|
|
format: /css\.map/,
|
|
|
|
},
|
|
|
|
:format => true
|
|
|
|
get "stylesheets/:name" => "stylesheets#show",
|
|
|
|
:constraints => {
|
|
|
|
name: /[-a-z0-9_]+/,
|
|
|
|
format: "css",
|
|
|
|
},
|
|
|
|
:format => true
|
2020-08-28 22:36:52 +08:00
|
|
|
get "color-scheme-stylesheet/:id(/:theme_id)" => "stylesheets#color_scheme",
|
2022-12-07 23:46:35 +08:00
|
|
|
:constraints => {
|
|
|
|
format: :json,
|
|
|
|
}
|
|
|
|
get "theme-javascripts/:digest" => "theme_javascripts#show",
|
|
|
|
:constraints => {
|
|
|
|
digest: /\h{40}/,
|
|
|
|
format: :js,
|
|
|
|
},
|
|
|
|
:format => true
|
|
|
|
get "theme-javascripts/:digest" => "theme_javascripts#show_map",
|
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
|
|
|
:constraints => {
|
2022-12-07 23:46:35 +08:00
|
|
|
digest: /\h{40}/,
|
|
|
|
format: :map,
|
2023-01-07 19:59:28 +08:00
|
|
|
},
|
2022-04-26 03:04:13 +08:00
|
|
|
:format => true
|
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: %r{([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: %r{([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: /.*/,
|
|
|
|
}
|
2022-09-29 07:24:33 +08:00
|
|
|
|
|
|
|
# NOTE: secure-media-uploads is the old form, all new URLs generated for
|
|
|
|
# secure uploads will be secure-uploads, this is left in for backwards
|
|
|
|
# compat without needing to rebake all posts for each site.
|
|
|
|
get "secure-media-uploads/*path(.:extension)" => "uploads#_show_secure_deprecated",
|
|
|
|
:constraints => {
|
|
|
|
extension: /[a-z0-9\._]+/i,
|
|
|
|
}
|
|
|
|
get "secure-uploads/*path(.:extension)" => "uploads#show_secure",
|
|
|
|
:constraints => {
|
|
|
|
extension: /[a-z0-9\._]+/i,
|
2023-01-07 19:59:28 +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"
|
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
|
|
|
}
|
2021-08-27 00:16:00 +08:00
|
|
|
get "posts/:username/pending" => "posts#pending",
|
|
|
|
:constraints => {
|
|
|
|
username: RouteFormat.username,
|
2023-01-07 19:59:28 +08:00
|
|
|
}
|
|
|
|
|
2017-08-23 04:40:01 +08:00
|
|
|
%w[groups g].each do |root_path|
|
2023-06-13 22:57:57 +08:00
|
|
|
resources :groups,
|
|
|
|
only: %i[index show new edit update],
|
|
|
|
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
|
2023-01-07 19:59:28 +08:00
|
|
|
|
2019-02-21 13:44:25 +08:00
|
|
|
get "members"
|
|
|
|
get "posts"
|
|
|
|
get "mentions"
|
|
|
|
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 { |path| get path => "groups#show" }
|
2018-03-15 16:01:40 +08:00
|
|
|
|
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"
|
2023-01-11 16:43:18 +08:00
|
|
|
put "owners" => "groups#add_owners"
|
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
|
|
|
|
|
2023-01-27 08:58:33 +08:00
|
|
|
post "slugs", to: "slugs#generate"
|
2023-01-23 12:48:33 +08:00
|
|
|
|
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
|
|
|
|
2024-05-22 23:50:21 +08:00
|
|
|
put "bookmarks/bulk"
|
|
|
|
|
2024-07-11 22:59:00 +08:00
|
|
|
resources :posts, only: %i[show update create destroy], defaults: { format: "json" } 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+/ }
|
2023-01-20 05:09:01 +08:00
|
|
|
delete "revisions/permanently_delete" => "posts#permanently_delete_revisions"
|
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
|
|
|
|
2023-06-15 06:18:32 +08:00
|
|
|
resources :notifications, only: %i[index create update destroy] do
|
2018-02-13 14:38:26 +08:00
|
|
|
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"
|
2024-03-16 00:08:37 +08:00
|
|
|
get "totals" => "notifications#totals"
|
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: %i[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: %i[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
|
|
|
|
2023-06-15 06:18:32 +08:00
|
|
|
post "/clicks/track" => "clicks#track", :as => "track_clicks"
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2023-06-15 06:18:32 +08:00
|
|
|
resources :post_action_users, only: %i[index]
|
2019-08-27 20:09:00 +08:00
|
|
|
resources :post_readers, only: %i[index]
|
2023-06-22 02:17:44 +08:00
|
|
|
resources :post_actions, only: %i[create destroy]
|
2023-06-15 06:18:32 +08:00
|
|
|
resources :user_actions, only: %i[index show]
|
2013-02-06 03:16:51 +08:00
|
|
|
|
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: %i[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
|
|
|
|
2024-05-16 15:45:13 +08:00
|
|
|
resources :categories, only: %i[index create update destroy]
|
2015-08-28 01:14:59 +08:00
|
|
|
post "categories/reorder" => "categories#reorder"
|
2023-11-28 23:58:47 +08:00
|
|
|
get "categories/find" => "categories#find"
|
2024-05-13 19:37:17 +08:00
|
|
|
post "categories/search" => "categories#search"
|
2024-06-15 00:37:32 +08:00
|
|
|
get "categories/hierarchical_search" => "categories#hierarchical_search"
|
2024-05-16 15:45:13 +08:00
|
|
|
get "categories/:parent_category_id" => "categories#index"
|
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"
|
2022-04-08 11:14:06 +08:00
|
|
|
get "c/:id/visible_groups" => "categories#visible_groups"
|
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" }
|
2023-01-07 19:59:28 +08:00
|
|
|
|
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",
|
|
|
|
}
|
2024-05-16 15:45:13 +08:00
|
|
|
|
|
|
|
get "/subcategories" => "categories#index"
|
|
|
|
|
2019-10-31 01:22:32 +08:00
|
|
|
get "/" => "list#category_default", :as => "category_default"
|
|
|
|
end
|
2014-10-17 00:15:31 +08:00
|
|
|
|
FEATURE: Generic hashtag autocomplete lookup and markdown cooking (#18937)
This commit fleshes out and adds functionality for the new `#hashtag` search and
lookup system, still hidden behind the `enable_experimental_hashtag_autocomplete`
feature flag.
**Serverside**
We have two plugin API registration methods that are used to define data sources
(`register_hashtag_data_source`) and hashtag result type priorities depending on
the context (`register_hashtag_type_in_context`). Reading the comments in plugin.rb
should make it clear what these are doing. Reading the `HashtagAutocompleteService`
in full will likely help a lot as well.
Each data source is responsible for providing its own **lookup** and **search**
method that returns hashtag results based on the arguments provided. For example,
the category hashtag data source has to take into account parent categories and
how they relate, and each data source has to define their own icon to use for the
hashtag, and so on.
The `Site` serializer has two new attributes that source data from `HashtagAutocompleteService`.
There is `hashtag_icons` that is just a simple array of all the different icons that
can be used for allowlisting in our markdown pipeline, and there is `hashtag_context_configurations`
that is used to store the type priority orders for each registered context.
When sending emails, we cannot render the SVG icons for hashtags, so
we need to change the HTML hashtags to the normal `#hashtag` text.
**Markdown**
The `hashtag-autocomplete.js` file is where I have added the new `hashtag-autocomplete`
markdown rule, and like all of our rules this is used to cook the raw text on both the clientside
and on the serverside using MiniRacer. Only on the server side do we actually reach out to
the database with the `hashtagLookup` function, on the clientside we just render a plainer
version of the hashtag HTML. Only in the composer preview do we do further lookups based
on this.
This rule is the first one (that I can find) that uses the `currentUser` based on a passed
in `user_id` for guardian checks in markdown rendering code. This is the `last_editor_id`
for both the post and chat message. In some cases we need to cook without a user present,
so the `Discourse.system_user` is used in this case.
**Chat Channels**
This also contains the changes required for chat so that chat channels can be used
as a data source for hashtag searches and lookups. This data source will only be
used when `enable_experimental_hashtag_autocomplete` is `true`, so we don't have
to worry about channel results suddenly turning up.
------
**Known Rough Edges**
- Onebox excerpts will not render the icon svg/use tags, I plan to address that in a follow up PR
- Selecting a hashtag + pressing the Quote button will result in weird behaviour, I plan to address that in a follow up PR
- Mixed hashtag contexts for hashtags without a type suffix will not work correctly, e.g. #ux which is both a category and a channel slug will resolve to a category when used inside a post or within a [chat] transcript in that post. Users can get around this manually by adding the correct suffix, for example ::channel. We may get to this at some point in future
- Icons will not show for the hashtags in emails since SVG support is so terrible in email (this is not likely to be resolved, but still noting for posterity)
- Additional refinements and review fixes wil
2022-11-21 06:37:06 +08:00
|
|
|
get "hashtags" => "hashtags#lookup"
|
2024-02-12 18:07:14 +08:00
|
|
|
get "hashtags/by-ids" => "hashtags#by_ids"
|
2022-10-19 12:03:57 +08:00
|
|
|
get "hashtags/search" => "hashtags#search"
|
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
|
|
|
|
|
2023-06-22 02:17:44 +08:00
|
|
|
get "latest.rss" => "list#latest_feed", :format => :rss
|
|
|
|
get "top.rss" => "list#top_feed", :format => :rss
|
2024-01-17 10:01:04 +08:00
|
|
|
get "hot.rss" => "list#hot_feed", :format => :rss
|
2014-10-16 11:52:21 +08:00
|
|
|
|
2019-07-24 01:17:44 +08:00
|
|
|
Discourse.filters.each { |filter| get "#{filter}" => "list##{filter}" }
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2023-03-03 09:46:21 +08:00
|
|
|
get "filter" => "list#filter"
|
|
|
|
|
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"
|
2024-02-15 13:42:42 +08:00
|
|
|
put "t/:id/reset-bump-date/(:post_id)" => "topics#reset_bump_date",
|
|
|
|
:constraints => {
|
|
|
|
id: /\d+/,
|
|
|
|
post_id: /\d+/,
|
|
|
|
}
|
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"
|
2023-06-15 06:18:32 +08:00
|
|
|
resources :similar_topics, only: [:index]
|
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",
|
2020-06-22 21:48:24 +08:00
|
|
|
:as => "topics_private_messages_unread",
|
|
|
|
:defaults => {
|
|
|
|
format: :json,
|
|
|
|
}
|
|
|
|
get "private-messages-tags/:username/:tag_id.json" => "list#private_messages_tag",
|
2021-08-02 12:41:41 +08:00
|
|
|
:as => "topics_private_messages_tag",
|
|
|
|
:defaults => {
|
|
|
|
format: :json,
|
|
|
|
}
|
|
|
|
get "private-messages-new/:username" => "list#private_messages_new",
|
2021-06-15 05:01:17 +08:00
|
|
|
:as => "topics_private_messages_new",
|
|
|
|
:defaults => {
|
|
|
|
format: :json,
|
|
|
|
}
|
|
|
|
get "private-messages-warnings/:username" => "list#private_messages_warnings",
|
2018-04-12 17:10:53 +08:00
|
|
|
:as => "topics_private_messages_warnings",
|
|
|
|
:defaults => {
|
|
|
|
format: :json,
|
2023-01-07 19:59:28 +08:00
|
|
|
}
|
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
|
|
|
|
2022-11-09 23:27:42 +08:00
|
|
|
get "new-topic" => "new_topic#index"
|
|
|
|
get "new-message" => "new_topic#index"
|
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-08-24 10:27:05 +08:00
|
|
|
get "t/external_id/:external_id" => "topics#show_by_external_id",
|
|
|
|
:format => :json,
|
|
|
|
:constraints => {
|
|
|
|
external_id: /[\w-]+/,
|
2022-04-26 03:04:13 +08:00
|
|
|
}
|
|
|
|
get "t/:slug/:topic_id/print" => "topics#show",
|
|
|
|
:format => :html,
|
|
|
|
:print => "true",
|
|
|
|
:constraints => {
|
|
|
|
topic_id: /\d+/,
|
2023-01-07 19:59:28 +08:00
|
|
|
}
|
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+/ }
|
2023-06-15 06:18:32 +08:00
|
|
|
|
2015-08-13 05:00:16 +08:00
|
|
|
get "t/:slug/:topic_id/summary" => "topics#show",
|
|
|
|
:defaults => {
|
|
|
|
summary: true,
|
|
|
|
},
|
2019-07-24 01:17:44 +08:00
|
|
|
:constraints => {
|
2015-08-13 05:00:16 +08:00
|
|
|
topic_id: /\d+/,
|
2023-01-07 19:59:28 +08:00
|
|
|
}
|
2015-08-13 05:00:16 +08:00
|
|
|
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/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 => 99_999_999,
|
2013-12-24 07:50:36 +08:00
|
|
|
:constraints => {
|
|
|
|
topic_id: /\d+/,
|
|
|
|
}
|
|
|
|
get "t/:slug/:topic_id.rss" => "topics#feed",
|
|
|
|
:format => :rss,
|
2019-07-24 01:17:44 +08:00
|
|
|
:constraints => {
|
2013-12-24 07:50:36 +08:00
|
|
|
topic_id: /\d+/,
|
2023-01-07 19:59:28 +08:00
|
|
|
}
|
2013-12-24 07:50:36 +08:00
|
|
|
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 => 99_999_999,
|
|
|
|
:constraints => {
|
|
|
|
topic_id: /\d+/,
|
2023-01-07 19:59:28 +08:00
|
|
|
}
|
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",
|
2016-11-25 08:34:43 +08:00
|
|
|
:constraints => {
|
|
|
|
topic_id: /\d+/,
|
|
|
|
},
|
|
|
|
:format => :json
|
|
|
|
get "t/:topic_id/excerpts" => "topics#excerpts",
|
2019-07-24 01:17:44 +08:00
|
|
|
:constraints => {
|
2013-12-24 07:50:36 +08:00
|
|
|
topic_id: /\d+/,
|
2023-01-07 19:59:28 +08:00
|
|
|
},
|
2021-04-09 01:23:13 +08:00
|
|
|
: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 => {
|
2015-07-26 00:06:49 +08:00
|
|
|
topic_id: /\d+/,
|
|
|
|
}
|
|
|
|
put "t/:topic_id/change-timestamp" => "topics#change_timestamps",
|
|
|
|
:constraints => {
|
2013-12-24 07:50:36 +08:00
|
|
|
topic_id: /\d+/,
|
2023-01-07 19:59:28 +08:00
|
|
|
}
|
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+/ }
|
2023-01-07 19:59:28 +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
|
|
|
|
2023-06-15 06:18:32 +08:00
|
|
|
resources :invites, only: %i[create update destroy]
|
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
|
|
|
get "/invites/:id" => "invites#show", :constraints => { format: :html }
|
2023-12-28 23:16:04 +08:00
|
|
|
post "invites/create-multiple" => "invites#create_multiple", :constraints => { format: :json }
|
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
|
|
|
|
2023-06-15 06:18:32 +08:00
|
|
|
post "/export_csv/export_entity" => "export_csv#export_entity",
|
|
|
|
:as => "export_entity_export_csv_index"
|
2014-12-23 00:17:04 +08:00
|
|
|
|
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: %i[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: /.*/,
|
|
|
|
}
|
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"
|
2023-07-04 11:36:39 +08:00
|
|
|
get "/list" => "tags#list"
|
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"
|
2023-01-07 19:59:28 +08:00
|
|
|
|
2016-04-26 03:55:15 +08:00
|
|
|
constraints(tag_id: %r{[^/]+?}, 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,
|
|
|
|
}
|
2022-03-22 22:59:10 +08:00
|
|
|
get "/all/:tag_id/l/#{filter}" => "tags#show_#{filter}",
|
|
|
|
:as => "tag_category_all_show_#{filter}",
|
|
|
|
:defaults => {
|
|
|
|
no_subcategories: false,
|
|
|
|
}
|
2019-11-04 00:32:31 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
get "/none/:tag_id" => "tags#show",
|
|
|
|
:as => "tag_category_none_show",
|
|
|
|
:defaults => {
|
|
|
|
no_subcategories: true,
|
|
|
|
}
|
2022-03-22 22:59:10 +08:00
|
|
|
get "/all/:tag_id" => "tags#show",
|
|
|
|
:as => "tag_category_all_show",
|
|
|
|
:defaults => {
|
|
|
|
no_subcategories: false,
|
|
|
|
}
|
2019-11-04 00:32:31 +08:00
|
|
|
|
|
|
|
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
|
2024-05-27 13:25:32 +08:00
|
|
|
|
|
|
|
get "/t/:topic_id/view-stats.json" => "topic_view_stats#index"
|
|
|
|
|
2013-03-28 21:01:13 +08:00
|
|
|
# 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
|
|
|
|
2024-06-20 23:33:46 +08:00
|
|
|
root to: "custom_homepage#index",
|
|
|
|
constraints: HomePageConstraint.new("custom"),
|
|
|
|
as: "custom_index"
|
2024-04-02 23:05:08 +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
|
|
|
|
2021-04-29 04:12:08 +08:00
|
|
|
get "/theme-qunit" => "qunit#theme"
|
2024-08-29 16:48:47 +08:00
|
|
|
get "/theme-tests", to: redirect("/theme-qunit")
|
2018-04-23 14:42:25 +08:00
|
|
|
|
2023-11-17 07:17:32 +08:00
|
|
|
# This is a special route that is used when theme QUnit tests are run through testem which appends a testem_id to the
|
|
|
|
# path. Unfortunately, testem's proxy support does not allow us to easily remove this from the path, so we have to
|
|
|
|
# handle it here.
|
|
|
|
if Rails.env.development?
|
|
|
|
get "/testem-theme-qunit/:testem_id/theme-qunit" => "qunit#theme",
|
|
|
|
:constraints => {
|
|
|
|
testem_id: /\d+/,
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
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"
|
|
|
|
|
2022-11-24 23:16:28 +08:00
|
|
|
get "user-status" => "user_status#get"
|
2022-05-27 17:15:14 +08:00
|
|
|
put "user-status" => "user_status#set"
|
|
|
|
delete "user-status" => "user_status#clear"
|
|
|
|
|
2023-02-22 05:55:44 +08:00
|
|
|
resources :sidebar_sections, only: %i[index create update destroy]
|
2023-05-23 07:53:32 +08:00
|
|
|
put "/sidebar_sections/reset/:id" => "sidebar_sections#reset"
|
2023-02-03 11:44:40 +08:00
|
|
|
|
2024-04-25 18:00:01 +08:00
|
|
|
post "/pageview" => "pageview#index"
|
|
|
|
|
2018-06-06 14:55:22 +08:00
|
|
|
get "*url", to: "permalinks#show", constraints: PermalinkConstraint.new
|
2023-05-30 05:47:18 +08:00
|
|
|
|
|
|
|
get "/form-templates/:id" => "form_templates#show"
|
|
|
|
get "/form-templates" => "form_templates#index"
|
2024-08-06 07:12:42 +08:00
|
|
|
|
|
|
|
if Rails.env.test?
|
|
|
|
# Routes that are only used for testing
|
|
|
|
get "/test_net_http_timeouts" => "test_requests#test_net_http_timeouts"
|
|
|
|
end
|
2019-07-24 01:17:44 +08:00
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
end
|