forked from
smokesignal.events/smokesignal
i18n+filtering fork - fluent-templates v2
1use std::time::Duration;
2
3use axum::{
4 http::HeaderValue,
5 middleware::{from_fn, from_fn_with_state},
6 routing::{get, post},
7 Router,
8};
9use axum_htmx::AutoVaryLayer;
10use http::{
11 header::{ACCEPT, ACCEPT_LANGUAGE},
12 Method,
13};
14use tower_http::trace::TraceLayer;
15use tower_http::{classify::ServerErrorsFailureClass, timeout::TimeoutLayer};
16use tower_http::{cors::CorsLayer, services::ServeDir};
17use tracing::Span;
18
19use crate::http::{
20 context::WebContext,
21 handle_admin_denylist::{
22 handle_admin_denylist, handle_admin_denylist_add, handle_admin_denylist_remove,
23 },
24 handle_admin_event::handle_admin_event,
25 handle_admin_events::handle_admin_events,
26 handle_admin_handles::{handle_admin_handles, handle_admin_nuke_identity},
27 handle_admin_import_event::handle_admin_import_event,
28 handle_admin_import_rsvp::handle_admin_import_rsvp,
29 handle_admin_index::handle_admin_index,
30 handle_admin_rsvp::handle_admin_rsvp,
31 handle_admin_rsvps::handle_admin_rsvps,
32 handle_create_event::{
33 handle_create_event, handle_link_at_builder, handle_location_at_builder,
34 handle_location_datalist, handle_starts_at_builder,
35 },
36 handle_create_rsvp::handle_create_rsvp,
37 handle_edit_event::handle_edit_event,
38 handle_filter_events::handle_filter_events,
39 handle_import::{handle_import, handle_import_submit},
40 handle_index::handle_index,
41 handle_migrate_event::handle_migrate_event,
42 handle_migrate_rsvp::handle_migrate_rsvp,
43 handle_oauth_callback::handle_oauth_callback,
44 handle_oauth_jwks::handle_oauth_jwks,
45 handle_oauth_login::handle_oauth_login,
46 handle_oauth_logout::handle_logout,
47 handle_oauth_metadata::handle_oauth_metadata,
48 handle_policy::{
49 handle_acknowledgement, handle_cookie_policy, handle_privacy_policy,
50 handle_terms_of_service,
51 },
52 handle_profile::handle_profile_view,
53 handle_set_language::handle_set_language,
54 handle_settings::{handle_language_update, handle_settings, handle_timezone_update},
55 handle_view_event::handle_view_event,
56 handle_ical_event::handle_ical_event,
57 handle_view_feed::handle_view_feed,
58 handle_view_rsvp::handle_view_rsvp,
59 middleware_filter,
60 middleware_timezone,
61};
62
63pub fn build_router(web_context: WebContext) -> Router {
64 let serve_dir = ServeDir::new(web_context.config.http_static_path.clone());
65
66 Router::new()
67 .route("/", get(handle_index))
68 .route(
69 "/events",
70 get(handle_filter_events)
71 .layer(from_fn(middleware_filter::extract_filter_params))
72 )
73 .route(
74 "/events/filter",
75 get(handle_filter_events)
76 .layer(from_fn(middleware_filter::extract_filter_params))
77 )
78 .route("/privacy-policy", get(handle_privacy_policy))
79 .route("/terms-of-service", get(handle_terms_of_service))
80 .route("/cookie-policy", get(handle_cookie_policy))
81 .route("/acknowledgement", get(handle_acknowledgement))
82 .route("/admin", get(handle_admin_index))
83 .route("/admin/handles", get(handle_admin_handles))
84 .route(
85 "/admin/handles/nuke/{did}",
86 post(handle_admin_nuke_identity),
87 )
88 .route("/admin/denylist", get(handle_admin_denylist))
89 .route("/admin/denylist/add", post(handle_admin_denylist_add))
90 .route("/admin/denylist/remove", post(handle_admin_denylist_remove))
91 .route("/admin/events", get(handle_admin_events))
92 .route("/admin/events/import", post(handle_admin_import_event))
93 .route("/admin/event", get(handle_admin_event))
94 .route("/admin/rsvps", get(handle_admin_rsvps))
95 .route("/admin/rsvp", get(handle_admin_rsvp))
96 .route("/admin/rsvps/import", post(handle_admin_import_rsvp))
97 .route("/oauth/client-metadata.json", get(handle_oauth_metadata))
98 .route("/.well-known/jwks.json", get(handle_oauth_jwks))
99 .route("/oauth/login", get(handle_oauth_login))
100 .route("/oauth/login", post(handle_oauth_login))
101 .route("/oauth/callback", get(handle_oauth_callback))
102 .route("/logout", get(handle_logout))
103 .route("/language", post(handle_set_language))
104 .route("/settings", get(handle_settings))
105 .route("/settings/timezone", post(handle_timezone_update))
106 .route("/settings/language", post(handle_language_update))
107 .route("/import", get(handle_import))
108 .route("/import", post(handle_import_submit))
109 .route("/event", get(handle_create_event))
110 .route("/event", post(handle_create_event))
111 .route("/rsvp", get(handle_create_rsvp))
112 .route("/rsvp", post(handle_create_rsvp))
113 .route("/rsvps", get(handle_view_rsvp))
114 .route("/event/starts", get(handle_starts_at_builder))
115 .route("/event/starts", post(handle_starts_at_builder))
116 .route("/event/location", get(handle_location_at_builder))
117 .route("/event/location", post(handle_location_at_builder))
118 .route("/event/location/datalist", get(handle_location_datalist))
119 .route("/event/links", get(handle_link_at_builder))
120 .route("/event/links", post(handle_link_at_builder))
121 .route("/{handle_slug}/{event_rkey}/edit", get(handle_edit_event))
122 .route("/{handle_slug}/{event_rkey}/edit", post(handle_edit_event))
123 .route(
124 "/{handle_slug}/{event_rkey}/migrate",
125 get(handle_migrate_event),
126 )
127 .route(
128 "/{handle_slug}/{event_rkey}/migrate-rsvp",
129 get(handle_migrate_rsvp),
130 )
131 .route("/feed/{handle_slug}/{feed_rkey}", get(handle_view_feed))
132 .route("/rsvp/{handle_slug}/{rsvp_rkey}", get(handle_view_rsvp))
133 .route("/{handle_slug}/{event_rkey}/ical", get(handle_ical_event))
134 .route("/{handle_slug}/{event_rkey}", get(handle_view_event))
135 .route("/favicon.ico", get(|| async { axum::response::Redirect::permanent("/static/favicon.ico") }))
136 .route("/{handle_slug}", get(handle_profile_view))
137 .nest_service("/static", serve_dir.clone())
138 .fallback_service(serve_dir)
139 .layer((
140 TraceLayer::new_for_http().on_failure(
141 |err: ServerErrorsFailureClass, _latency: Duration, _span: &Span| {
142 tracing::error!(error = ?err, "Unhandled error: {err}");
143 },
144 ),
145 TimeoutLayer::new(Duration::from_secs(10)),
146 ))
147 .layer(
148 CorsLayer::new()
149 .allow_origin(
150 web_context
151 .config
152 .external_base
153 .parse::<HeaderValue>()
154 .unwrap(),
155 )
156 .allow_methods([Method::GET, Method::POST])
157 .allow_headers([ACCEPT_LANGUAGE, ACCEPT]),
158 )
159 .layer(AutoVaryLayer)
160 .layer(from_fn_with_state(web_context.clone(), middleware_timezone::timezone_middleware))
161 .with_state(web_context.clone())
162}