1use serde::{Deserialize, Serialize};
9use ulid::Ulid;
10
11use crate::UrlBuilder;
12pub use crate::traits::*;
13
14#[derive(Deserialize, Serialize, Clone, Debug)]
15#[serde(rename_all = "snake_case", tag = "kind")]
16pub enum PostAuthAction {
17 ContinueAuthorizationGrant {
18 id: Ulid,
19 },
20 ContinueDeviceCodeGrant {
21 id: Ulid,
22 },
23 ContinueCompatSsoLogin {
24 id: Ulid,
25 },
26 ChangePassword,
27 LinkUpstream {
28 id: Ulid,
29 },
30 ManageAccount {
31 #[serde(flatten)]
32 action: Option<AccountAction>,
33 },
34}
35
36impl PostAuthAction {
37 #[must_use]
38 pub const fn continue_grant(id: Ulid) -> Self {
39 PostAuthAction::ContinueAuthorizationGrant { id }
40 }
41
42 #[must_use]
43 pub const fn continue_device_code_grant(id: Ulid) -> Self {
44 PostAuthAction::ContinueDeviceCodeGrant { id }
45 }
46
47 #[must_use]
48 pub const fn continue_compat_sso_login(id: Ulid) -> Self {
49 PostAuthAction::ContinueCompatSsoLogin { id }
50 }
51
52 #[must_use]
53 pub const fn link_upstream(id: Ulid) -> Self {
54 PostAuthAction::LinkUpstream { id }
55 }
56
57 #[must_use]
58 pub const fn manage_account(action: Option<AccountAction>) -> Self {
59 PostAuthAction::ManageAccount { action }
60 }
61
62 pub fn go_next(&self, url_builder: &UrlBuilder) -> axum::response::Redirect {
63 match self {
64 Self::ContinueAuthorizationGrant { id } => url_builder.redirect(&Consent(*id)),
65 Self::ContinueDeviceCodeGrant { id } => {
66 url_builder.redirect(&DeviceCodeConsent::new(*id))
67 }
68 Self::ContinueCompatSsoLogin { id } => {
69 url_builder.redirect(&CompatLoginSsoComplete::new(*id, None))
70 }
71 Self::ChangePassword => url_builder.redirect(&AccountPasswordChange),
72 Self::LinkUpstream { id } => url_builder.redirect(&UpstreamOAuth2Link::new(*id)),
73 Self::ManageAccount { action } => url_builder.redirect(&Account {
74 action: action.clone(),
75 }),
76 }
77 }
78}
79
80#[derive(Default, Debug, Clone)]
82pub struct OidcConfiguration;
83
84impl SimpleRoute for OidcConfiguration {
85 const PATH: &'static str = "/.well-known/openid-configuration";
86}
87
88#[derive(Default, Debug, Clone)]
90pub struct Webfinger;
91
92impl SimpleRoute for Webfinger {
93 const PATH: &'static str = "/.well-known/webfinger";
94}
95
96pub struct ChangePasswordDiscovery;
98
99impl SimpleRoute for ChangePasswordDiscovery {
100 const PATH: &'static str = "/.well-known/change-password";
101}
102
103#[derive(Default, Debug, Clone)]
105pub struct OAuth2Keys;
106
107impl SimpleRoute for OAuth2Keys {
108 const PATH: &'static str = "/oauth2/keys.json";
109}
110
111#[derive(Default, Debug, Clone)]
113pub struct OidcUserinfo;
114
115impl SimpleRoute for OidcUserinfo {
116 const PATH: &'static str = "/oauth2/userinfo";
117}
118
119#[derive(Default, Debug, Clone)]
121pub struct OAuth2Introspection;
122
123impl SimpleRoute for OAuth2Introspection {
124 const PATH: &'static str = "/oauth2/introspect";
125}
126
127#[derive(Default, Debug, Clone)]
129pub struct OAuth2Revocation;
130
131impl SimpleRoute for OAuth2Revocation {
132 const PATH: &'static str = "/oauth2/revoke";
133}
134
135#[derive(Default, Debug, Clone)]
137pub struct OAuth2TokenEndpoint;
138
139impl SimpleRoute for OAuth2TokenEndpoint {
140 const PATH: &'static str = "/oauth2/token";
141}
142
143#[derive(Default, Debug, Clone)]
145pub struct OAuth2RegistrationEndpoint;
146
147impl SimpleRoute for OAuth2RegistrationEndpoint {
148 const PATH: &'static str = "/oauth2/registration";
149}
150
151#[derive(Default, Debug, Clone)]
153pub struct OAuth2AuthorizationEndpoint;
154
155impl SimpleRoute for OAuth2AuthorizationEndpoint {
156 const PATH: &'static str = "/authorize";
157}
158
159#[derive(Default, Debug, Clone)]
161pub struct Index;
162
163impl SimpleRoute for Index {
164 const PATH: &'static str = "/";
165}
166
167#[derive(Default, Debug, Clone)]
169pub struct Healthcheck;
170
171impl SimpleRoute for Healthcheck {
172 const PATH: &'static str = "/health";
173}
174
175#[derive(Default, Debug, Clone, Serialize, Deserialize)]
177pub struct Login {
178 #[serde(flatten)]
179 post_auth_action: Option<PostAuthAction>,
180
181 login_hint: Option<String>,
182}
183
184impl Route for Login {
185 type Query = Self;
186
187 fn route() -> &'static str {
188 "/login"
189 }
190
191 fn query(&self) -> Option<&Self::Query> {
192 Some(self)
193 }
194}
195
196impl Login {
197 #[must_use]
198 pub const fn and_then(action: PostAuthAction) -> Self {
199 Self {
200 post_auth_action: Some(action),
201 login_hint: None,
202 }
203 }
204
205 #[must_use]
206 pub const fn and_continue_grant(id: Ulid) -> Self {
207 Self {
208 post_auth_action: Some(PostAuthAction::continue_grant(id)),
209 login_hint: None,
210 }
211 }
212
213 #[must_use]
214 pub const fn and_continue_device_code_grant(id: Ulid) -> Self {
215 Self {
216 post_auth_action: Some(PostAuthAction::continue_device_code_grant(id)),
217 login_hint: None,
218 }
219 }
220
221 #[must_use]
222 pub const fn and_continue_compat_sso_login(id: Ulid) -> Self {
223 Self {
224 post_auth_action: Some(PostAuthAction::continue_compat_sso_login(id)),
225 login_hint: None,
226 }
227 }
228
229 #[must_use]
230 pub const fn and_link_upstream(id: Ulid) -> Self {
231 Self {
232 post_auth_action: Some(PostAuthAction::link_upstream(id)),
233 login_hint: None,
234 }
235 }
236
237 #[must_use]
238 pub fn with_login_hint(mut self, login_hint: String) -> Self {
239 self.login_hint = Some(login_hint);
240 self
241 }
242
243 #[must_use]
245 pub fn post_auth_action(&self) -> Option<&PostAuthAction> {
246 self.post_auth_action.as_ref()
247 }
248
249 pub fn go_next(&self, url_builder: &UrlBuilder) -> axum::response::Redirect {
250 match &self.post_auth_action {
251 Some(action) => action.go_next(url_builder),
252 None => url_builder.redirect(&Index),
253 }
254 }
255}
256
257impl From<Option<PostAuthAction>> for Login {
258 fn from(post_auth_action: Option<PostAuthAction>) -> Self {
259 Self {
260 post_auth_action,
261 login_hint: None,
262 }
263 }
264}
265
266#[derive(Default, Debug, Clone)]
268pub struct Logout;
269
270impl SimpleRoute for Logout {
271 const PATH: &'static str = "/logout";
272}
273
274#[derive(Default, Debug, Clone)]
276pub struct Register {
277 post_auth_action: Option<PostAuthAction>,
278}
279
280impl Register {
281 #[must_use]
282 pub fn and_then(action: PostAuthAction) -> Self {
283 Self {
284 post_auth_action: Some(action),
285 }
286 }
287
288 #[must_use]
289 pub fn and_continue_grant(data: Ulid) -> Self {
290 Self {
291 post_auth_action: Some(PostAuthAction::continue_grant(data)),
292 }
293 }
294
295 #[must_use]
296 pub fn and_continue_compat_sso_login(data: Ulid) -> Self {
297 Self {
298 post_auth_action: Some(PostAuthAction::continue_compat_sso_login(data)),
299 }
300 }
301
302 #[must_use]
304 pub fn post_auth_action(&self) -> Option<&PostAuthAction> {
305 self.post_auth_action.as_ref()
306 }
307
308 pub fn go_next(&self, url_builder: &UrlBuilder) -> axum::response::Redirect {
309 match &self.post_auth_action {
310 Some(action) => action.go_next(url_builder),
311 None => url_builder.redirect(&Index),
312 }
313 }
314}
315
316impl Route for Register {
317 type Query = PostAuthAction;
318
319 fn route() -> &'static str {
320 "/register"
321 }
322
323 fn query(&self) -> Option<&Self::Query> {
324 self.post_auth_action.as_ref()
325 }
326}
327
328impl From<Option<PostAuthAction>> for Register {
329 fn from(post_auth_action: Option<PostAuthAction>) -> Self {
330 Self { post_auth_action }
331 }
332}
333
334#[derive(Default, Debug, Clone, Serialize, Deserialize)]
336pub struct PasswordRegister {
337 username: Option<String>,
338
339 #[serde(flatten)]
340 post_auth_action: Option<PostAuthAction>,
341}
342
343impl PasswordRegister {
344 #[must_use]
345 pub fn and_then(mut self, action: PostAuthAction) -> Self {
346 self.post_auth_action = Some(action);
347 self
348 }
349
350 #[must_use]
352 pub fn with_username(mut self, username: String) -> Self {
353 self.username = Some(username);
354 self
355 }
356
357 #[must_use]
358 pub fn and_continue_grant(mut self, data: Ulid) -> Self {
359 self.post_auth_action = Some(PostAuthAction::continue_grant(data));
360 self
361 }
362
363 #[must_use]
364 pub fn and_continue_compat_sso_login(mut self, data: Ulid) -> Self {
365 self.post_auth_action = Some(PostAuthAction::continue_compat_sso_login(data));
366 self
367 }
368
369 #[must_use]
371 pub fn post_auth_action(&self) -> Option<&PostAuthAction> {
372 self.post_auth_action.as_ref()
373 }
374
375 #[must_use]
377 pub fn username(&self) -> Option<&str> {
378 self.username.as_deref()
379 }
380
381 pub fn go_next(&self, url_builder: &UrlBuilder) -> axum::response::Redirect {
382 match &self.post_auth_action {
383 Some(action) => action.go_next(url_builder),
384 None => url_builder.redirect(&Index),
385 }
386 }
387}
388
389impl Route for PasswordRegister {
390 type Query = Self;
391
392 fn route() -> &'static str {
393 "/register/password"
394 }
395
396 fn query(&self) -> Option<&Self::Query> {
397 Some(self)
398 }
399}
400
401impl From<Option<PostAuthAction>> for PasswordRegister {
402 fn from(post_auth_action: Option<PostAuthAction>) -> Self {
403 Self {
404 username: None,
405 post_auth_action,
406 }
407 }
408}
409
410#[derive(Debug, Clone)]
412pub struct RegisterToken {
413 id: Ulid,
414}
415
416impl RegisterToken {
417 #[must_use]
418 pub fn new(id: Ulid) -> Self {
419 Self { id }
420 }
421}
422
423impl Route for RegisterToken {
424 type Query = ();
425 fn route() -> &'static str {
426 "/register/steps/{id}/token"
427 }
428
429 fn path(&self) -> std::borrow::Cow<'static, str> {
430 format!("/register/steps/{}/token", self.id).into()
431 }
432}
433
434#[derive(Debug, Clone)]
436pub struct RegisterDisplayName {
437 id: Ulid,
438}
439
440impl RegisterDisplayName {
441 #[must_use]
442 pub fn new(id: Ulid) -> Self {
443 Self { id }
444 }
445}
446
447impl Route for RegisterDisplayName {
448 type Query = ();
449 fn route() -> &'static str {
450 "/register/steps/{id}/display-name"
451 }
452
453 fn path(&self) -> std::borrow::Cow<'static, str> {
454 format!("/register/steps/{}/display-name", self.id).into()
455 }
456}
457
458#[derive(Debug, Clone)]
460pub struct RegisterVerifyEmail {
461 id: Ulid,
462}
463
464impl RegisterVerifyEmail {
465 #[must_use]
466 pub fn new(id: Ulid) -> Self {
467 Self { id }
468 }
469}
470
471impl Route for RegisterVerifyEmail {
472 type Query = ();
473 fn route() -> &'static str {
474 "/register/steps/{id}/verify-email"
475 }
476
477 fn path(&self) -> std::borrow::Cow<'static, str> {
478 format!("/register/steps/{}/verify-email", self.id).into()
479 }
480}
481
482#[derive(Debug, Clone)]
484pub struct RegisterFinish {
485 id: Ulid,
486}
487
488impl RegisterFinish {
489 #[must_use]
490 pub const fn new(id: Ulid) -> Self {
491 Self { id }
492 }
493}
494
495impl Route for RegisterFinish {
496 type Query = ();
497 fn route() -> &'static str {
498 "/register/steps/{id}/finish"
499 }
500
501 fn path(&self) -> std::borrow::Cow<'static, str> {
502 format!("/register/steps/{}/finish", self.id).into()
503 }
504}
505
506#[derive(Debug, Clone, Serialize, Deserialize)]
508#[serde(tag = "action")]
509pub enum AccountAction {
510 #[serde(rename = "org.matrix.profile")]
511 OrgMatrixProfile,
512 #[serde(rename = "profile")]
514 Profile,
515
516 #[serde(rename = "org.matrix.devices_list")]
517 OrgMatrixDevicesList,
518 #[serde(rename = "org.matrix.sessions_list")]
520 OrgMatrixSessionsList,
521 #[serde(rename = "sessions_list")]
523 SessionsList,
524
525 #[serde(rename = "org.matrix.device_view")]
526 OrgMatrixDeviceView { device_id: String },
527 #[serde(rename = "org.matrix.session_view")]
529 OrgMatrixSessionView { device_id: String },
530 #[serde(rename = "session_view")]
532 SessionView { device_id: String },
533
534 #[serde(rename = "org.matrix.device_delete")]
535 OrgMatrixDeviceDelete { device_id: String },
536 #[serde(rename = "org.matrix.session_end")]
538 OrgMatrixSessionEnd { device_id: String },
539 #[serde(rename = "session_end")]
541 SessionEnd { device_id: String },
542
543 #[serde(rename = "org.matrix.cross_signing_reset")]
544 OrgMatrixCrossSigningReset,
545}
546
547#[derive(Default, Debug, Clone)]
549pub struct Account {
550 action: Option<AccountAction>,
551}
552
553impl Route for Account {
554 type Query = AccountAction;
555
556 fn route() -> &'static str {
557 "/account/"
558 }
559
560 fn query(&self) -> Option<&Self::Query> {
561 self.action.as_ref()
562 }
563}
564
565#[derive(Default, Debug, Clone)]
567pub struct AccountWildcard;
568
569impl SimpleRoute for AccountWildcard {
570 const PATH: &'static str = "/account/{*rest}";
571}
572
573#[derive(Default, Debug, Clone)]
578pub struct AccountPasswordChange;
579
580impl SimpleRoute for AccountPasswordChange {
581 const PATH: &'static str = "/account/password/change";
582}
583
584#[derive(Debug, Clone)]
586pub struct Consent(pub Ulid);
587
588impl Route for Consent {
589 type Query = ();
590 fn route() -> &'static str {
591 "/consent/{grant_id}"
592 }
593
594 fn path(&self) -> std::borrow::Cow<'static, str> {
595 format!("/consent/{}", self.0).into()
596 }
597}
598
599pub struct CompatLogin;
601
602impl SimpleRoute for CompatLogin {
603 const PATH: &'static str = "/_matrix/client/{version}/login";
604}
605
606pub struct CompatLogout;
608
609impl SimpleRoute for CompatLogout {
610 const PATH: &'static str = "/_matrix/client/{version}/logout";
611}
612
613pub struct CompatLogoutAll;
615
616impl SimpleRoute for CompatLogoutAll {
617 const PATH: &'static str = "/_matrix/client/{version}/logout/all";
618}
619
620pub struct CompatRefresh;
622
623impl SimpleRoute for CompatRefresh {
624 const PATH: &'static str = "/_matrix/client/{version}/refresh";
625}
626
627pub struct CompatLoginSsoRedirect;
629
630impl SimpleRoute for CompatLoginSsoRedirect {
631 const PATH: &'static str = "/_matrix/client/{version}/login/sso/redirect";
632}
633
634pub struct CompatLoginSsoRedirectSlash;
639
640impl SimpleRoute for CompatLoginSsoRedirectSlash {
641 const PATH: &'static str = "/_matrix/client/{version}/login/sso/redirect/";
642}
643
644pub struct CompatLoginSsoRedirectIdp;
646
647impl SimpleRoute for CompatLoginSsoRedirectIdp {
648 const PATH: &'static str = "/_matrix/client/{version}/login/sso/redirect/{idp}";
649}
650
651#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
652#[serde(rename_all = "lowercase")]
653pub enum CompatLoginSsoAction {
654 Login,
655 Register,
656 #[serde(other)]
657 Unknown,
658}
659
660impl CompatLoginSsoAction {
661 #[must_use]
663 pub fn is_known(&self) -> bool {
664 !matches!(self, Self::Unknown)
665 }
666}
667
668#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
669pub struct CompatLoginSsoActionParams {
670 action: CompatLoginSsoAction,
671 #[serde(rename = "org.matrix.msc3824.action")]
674 unstable_action: CompatLoginSsoAction,
675}
676
677pub struct CompatLoginSsoComplete {
679 id: Ulid,
680 query: Option<CompatLoginSsoActionParams>,
681}
682
683impl CompatLoginSsoComplete {
684 #[must_use]
685 pub fn new(id: Ulid, action: Option<CompatLoginSsoAction>) -> Self {
686 Self {
687 id,
688 query: action.map(|action| CompatLoginSsoActionParams {
689 action,
690 unstable_action: action,
691 }),
692 }
693 }
694}
695
696impl Route for CompatLoginSsoComplete {
697 type Query = CompatLoginSsoActionParams;
698
699 fn query(&self) -> Option<&Self::Query> {
700 self.query.as_ref()
701 }
702
703 fn route() -> &'static str {
704 "/complete-compat-sso/{grant_id}"
705 }
706
707 fn path(&self) -> std::borrow::Cow<'static, str> {
708 format!("/complete-compat-sso/{}", self.id).into()
709 }
710}
711
712pub struct UpstreamOAuth2Authorize {
714 id: Ulid,
715 post_auth_action: Option<PostAuthAction>,
716}
717
718impl UpstreamOAuth2Authorize {
719 #[must_use]
720 pub const fn new(id: Ulid) -> Self {
721 Self {
722 id,
723 post_auth_action: None,
724 }
725 }
726
727 #[must_use]
728 pub fn and_then(mut self, action: PostAuthAction) -> Self {
729 self.post_auth_action = Some(action);
730 self
731 }
732}
733
734impl Route for UpstreamOAuth2Authorize {
735 type Query = PostAuthAction;
736 fn route() -> &'static str {
737 "/upstream/authorize/{provider_id}"
738 }
739
740 fn path(&self) -> std::borrow::Cow<'static, str> {
741 format!("/upstream/authorize/{}", self.id).into()
742 }
743
744 fn query(&self) -> Option<&Self::Query> {
745 self.post_auth_action.as_ref()
746 }
747}
748
749pub struct UpstreamOAuth2Callback {
751 id: Ulid,
752}
753
754impl UpstreamOAuth2Callback {
755 #[must_use]
756 pub const fn new(id: Ulid) -> Self {
757 Self { id }
758 }
759}
760
761impl Route for UpstreamOAuth2Callback {
762 type Query = ();
763 fn route() -> &'static str {
764 "/upstream/callback/{provider_id}"
765 }
766
767 fn path(&self) -> std::borrow::Cow<'static, str> {
768 format!("/upstream/callback/{}", self.id).into()
769 }
770}
771
772pub struct UpstreamOAuth2Link {
774 id: Ulid,
775}
776
777impl UpstreamOAuth2Link {
778 #[must_use]
779 pub const fn new(id: Ulid) -> Self {
780 Self { id }
781 }
782}
783
784impl Route for UpstreamOAuth2Link {
785 type Query = ();
786 fn route() -> &'static str {
787 "/upstream/link/{link_id}"
788 }
789
790 fn path(&self) -> std::borrow::Cow<'static, str> {
791 format!("/upstream/link/{}", self.id).into()
792 }
793}
794
795pub struct UpstreamOAuth2BackchannelLogout {
797 id: Ulid,
798}
799
800impl UpstreamOAuth2BackchannelLogout {
801 #[must_use]
802 pub const fn new(id: Ulid) -> Self {
803 Self { id }
804 }
805}
806
807impl Route for UpstreamOAuth2BackchannelLogout {
808 type Query = ();
809 fn route() -> &'static str {
810 "/upstream/backchannel-logout/{provider_id}"
811 }
812
813 fn path(&self) -> std::borrow::Cow<'static, str> {
814 format!("/upstream/backchannel-logout/{}", self.id).into()
815 }
816}
817
818#[derive(Default, Serialize, Deserialize, Debug, Clone)]
820pub struct DeviceCodeLink {
821 code: Option<String>,
822}
823
824impl DeviceCodeLink {
825 #[must_use]
826 pub fn with_code(code: String) -> Self {
827 Self { code: Some(code) }
828 }
829}
830
831impl Route for DeviceCodeLink {
832 type Query = DeviceCodeLink;
833 fn route() -> &'static str {
834 "/link"
835 }
836
837 fn query(&self) -> Option<&Self::Query> {
838 Some(self)
839 }
840}
841
842#[derive(Default, Serialize, Deserialize, Debug, Clone)]
844pub struct DeviceCodeConsent {
845 id: Ulid,
846}
847
848impl Route for DeviceCodeConsent {
849 type Query = ();
850 fn route() -> &'static str {
851 "/device/{device_code_id}"
852 }
853
854 fn path(&self) -> std::borrow::Cow<'static, str> {
855 format!("/device/{}", self.id).into()
856 }
857}
858
859impl DeviceCodeConsent {
860 #[must_use]
861 pub fn new(id: Ulid) -> Self {
862 Self { id }
863 }
864}
865
866#[derive(Default, Serialize, Deserialize, Debug, Clone)]
868pub struct OAuth2DeviceAuthorizationEndpoint;
869
870impl SimpleRoute for OAuth2DeviceAuthorizationEndpoint {
871 const PATH: &'static str = "/oauth2/device";
872}
873
874#[derive(Default, Serialize, Deserialize, Debug, Clone)]
876pub struct AccountRecoveryStart;
877
878impl SimpleRoute for AccountRecoveryStart {
879 const PATH: &'static str = "/recover";
880}
881
882#[derive(Default, Serialize, Deserialize, Debug, Clone)]
884pub struct AccountRecoveryProgress {
885 session_id: Ulid,
886}
887
888impl AccountRecoveryProgress {
889 #[must_use]
890 pub fn new(session_id: Ulid) -> Self {
891 Self { session_id }
892 }
893}
894
895impl Route for AccountRecoveryProgress {
896 type Query = ();
897 fn route() -> &'static str {
898 "/recover/progress/{session_id}"
899 }
900
901 fn path(&self) -> std::borrow::Cow<'static, str> {
902 format!("/recover/progress/{}", self.session_id).into()
903 }
904}
905
906#[derive(Default, Serialize, Deserialize, Debug, Clone)]
909pub struct AccountRecoveryFinish {
910 ticket: String,
911}
912
913impl AccountRecoveryFinish {
914 #[must_use]
915 pub fn new(ticket: String) -> Self {
916 Self { ticket }
917 }
918}
919
920impl Route for AccountRecoveryFinish {
921 type Query = AccountRecoveryFinish;
922
923 fn route() -> &'static str {
924 "/account/password/recovery"
925 }
926
927 fn query(&self) -> Option<&Self::Query> {
928 Some(self)
929 }
930}
931
932pub struct StaticAsset {
934 path: String,
935}
936
937impl StaticAsset {
938 #[must_use]
939 pub fn new(path: String) -> Self {
940 Self { path }
941 }
942}
943
944impl Route for StaticAsset {
945 type Query = ();
946 fn route() -> &'static str {
947 "/assets/"
948 }
949
950 fn path(&self) -> std::borrow::Cow<'static, str> {
951 format!("/assets/{}", self.path).into()
952 }
953}
954
955pub struct GraphQL;
957
958impl SimpleRoute for GraphQL {
959 const PATH: &'static str = "/graphql";
960}
961
962pub struct ApiSpec;
964
965impl SimpleRoute for ApiSpec {
966 const PATH: &'static str = "/api/spec.json";
967}
968
969pub struct ApiDoc;
971
972impl SimpleRoute for ApiDoc {
973 const PATH: &'static str = "/api/doc/";
974}
975
976pub struct ApiDocCallback;
978
979impl SimpleRoute for ApiDocCallback {
980 const PATH: &'static str = "/api/doc/oauth2-callback";
981}