this repo has no description
1pub mod api;
2pub mod appview;
3pub mod auth;
4pub mod cache;
5pub mod circuit_breaker;
6pub mod comms;
7pub mod config;
8pub mod crawlers;
9pub mod delegation;
10pub mod handle;
11pub mod image;
12pub mod metrics;
13pub mod moderation;
14pub mod oauth;
15pub mod plc;
16pub mod rate_limit;
17pub mod repo;
18pub mod scheduled;
19pub mod state;
20pub mod storage;
21pub mod sync;
22pub mod types;
23pub mod util;
24pub mod validation;
25
26use api::proxy::XrpcProxyLayer;
27use axum::{
28 Json, Router,
29 extract::DefaultBodyLimit,
30 http::Method,
31 middleware,
32 routing::{get, post},
33};
34use http::StatusCode;
35use serde_json::json;
36use state::AppState;
37pub use sync::util::AccountStatus;
38use tower::ServiceBuilder;
39use tower_http::cors::{Any, CorsLayer};
40use tower_http::services::{ServeDir, ServeFile};
41pub use types::{AccountState, AtIdentifier, AtUri, Did, Handle, Nsid, Rkey};
42
43pub fn app(state: AppState) -> Router {
44 let xrpc_router = Router::new()
45 .route("/_health", get(api::server::health))
46 .route(
47 "/com.atproto.server.describeServer",
48 get(api::server::describe_server),
49 )
50 .route(
51 "/com.atproto.server.createAccount",
52 post(api::identity::create_account),
53 )
54 .route(
55 "/com.atproto.server.createSession",
56 post(api::server::create_session),
57 )
58 .route(
59 "/com.atproto.server.getSession",
60 get(api::server::get_session),
61 )
62 .route("/_account.listSessions", get(api::server::list_sessions))
63 .route("/_account.revokeSession", post(api::server::revoke_session))
64 .route(
65 "/_account.revokeAllSessions",
66 post(api::server::revoke_all_sessions),
67 )
68 .route(
69 "/com.atproto.server.deleteSession",
70 post(api::server::delete_session),
71 )
72 .route(
73 "/com.atproto.server.refreshSession",
74 post(api::server::refresh_session),
75 )
76 .route(
77 "/com.atproto.server.confirmSignup",
78 post(api::server::confirm_signup),
79 )
80 .route(
81 "/com.atproto.server.resendVerification",
82 post(api::server::resend_verification),
83 )
84 .route(
85 "/com.atproto.server.getServiceAuth",
86 get(api::server::get_service_auth),
87 )
88 .route(
89 "/com.atproto.identity.resolveHandle",
90 get(api::identity::resolve_handle),
91 )
92 .route(
93 "/com.atproto.repo.createRecord",
94 post(api::repo::create_record),
95 )
96 .route("/com.atproto.repo.putRecord", post(api::repo::put_record))
97 .route("/com.atproto.repo.getRecord", get(api::repo::get_record))
98 .route(
99 "/com.atproto.repo.deleteRecord",
100 post(api::repo::delete_record),
101 )
102 .route(
103 "/com.atproto.repo.listRecords",
104 get(api::repo::list_records),
105 )
106 .route(
107 "/com.atproto.repo.describeRepo",
108 get(api::repo::describe_repo),
109 )
110 .route("/com.atproto.repo.uploadBlob", post(api::repo::upload_blob))
111 .route(
112 "/com.atproto.repo.applyWrites",
113 post(api::repo::apply_writes),
114 )
115 .route(
116 "/com.atproto.sync.getLatestCommit",
117 get(sync::get_latest_commit),
118 )
119 .route("/com.atproto.sync.listRepos", get(sync::list_repos))
120 .route("/com.atproto.sync.getBlob", get(sync::get_blob))
121 .route("/com.atproto.sync.listBlobs", get(sync::list_blobs))
122 .route(
123 "/com.atproto.sync.getRepoStatus",
124 get(sync::get_repo_status),
125 )
126 .route(
127 "/com.atproto.server.checkAccountStatus",
128 get(api::server::check_account_status),
129 )
130 .route(
131 "/com.atproto.identity.getRecommendedDidCredentials",
132 get(api::identity::get_recommended_did_credentials),
133 )
134 .route(
135 "/com.atproto.repo.listMissingBlobs",
136 get(api::repo::list_missing_blobs),
137 )
138 .route(
139 "/com.atproto.sync.notifyOfUpdate",
140 post(sync::notify_of_update),
141 )
142 .route("/com.atproto.sync.requestCrawl", post(sync::request_crawl))
143 .route("/com.atproto.sync.getBlocks", get(sync::get_blocks))
144 .route("/com.atproto.sync.getRepo", get(sync::get_repo))
145 .route("/com.atproto.sync.getRecord", get(sync::get_record))
146 .route(
147 "/com.atproto.sync.subscribeRepos",
148 get(sync::subscribe_repos),
149 )
150 .route("/com.atproto.sync.getHead", get(sync::get_head))
151 .route("/com.atproto.sync.getCheckout", get(sync::get_checkout))
152 .route(
153 "/com.atproto.moderation.createReport",
154 post(api::moderation::create_report),
155 )
156 .route(
157 "/com.atproto.admin.getAccountInfo",
158 get(api::admin::get_account_info),
159 )
160 .route(
161 "/com.atproto.admin.getAccountInfos",
162 get(api::admin::get_account_infos),
163 )
164 .route(
165 "/com.atproto.admin.searchAccounts",
166 get(api::admin::search_accounts),
167 )
168 .route(
169 "/com.atproto.server.activateAccount",
170 post(api::server::activate_account),
171 )
172 .route(
173 "/com.atproto.server.deactivateAccount",
174 post(api::server::deactivate_account),
175 )
176 .route(
177 "/com.atproto.server.requestAccountDelete",
178 post(api::server::request_account_delete),
179 )
180 .route(
181 "/com.atproto.server.deleteAccount",
182 post(api::server::delete_account),
183 )
184 .route(
185 "/com.atproto.server.requestPasswordReset",
186 post(api::server::request_password_reset),
187 )
188 .route(
189 "/com.atproto.server.resetPassword",
190 post(api::server::reset_password),
191 )
192 .route(
193 "/_account.changePassword",
194 post(api::server::change_password),
195 )
196 .route(
197 "/_account.removePassword",
198 post(api::server::remove_password),
199 )
200 .route(
201 "/_account.setPassword",
202 post(api::server::set_password),
203 )
204 .route(
205 "/_account.getPasswordStatus",
206 get(api::server::get_password_status),
207 )
208 .route(
209 "/_account.getReauthStatus",
210 get(api::server::get_reauth_status),
211 )
212 .route(
213 "/_account.reauthPassword",
214 post(api::server::reauth_password),
215 )
216 .route("/_account.reauthTotp", post(api::server::reauth_totp))
217 .route(
218 "/_account.reauthPasskeyStart",
219 post(api::server::reauth_passkey_start),
220 )
221 .route(
222 "/_account.reauthPasskeyFinish",
223 post(api::server::reauth_passkey_finish),
224 )
225 .route(
226 "/_account.getLegacyLoginPreference",
227 get(api::server::get_legacy_login_preference),
228 )
229 .route(
230 "/_account.updateLegacyLoginPreference",
231 post(api::server::update_legacy_login_preference),
232 )
233 .route("/_account.updateLocale", post(api::server::update_locale))
234 .route(
235 "/_account.listTrustedDevices",
236 get(api::server::list_trusted_devices),
237 )
238 .route(
239 "/_account.revokeTrustedDevice",
240 post(api::server::revoke_trusted_device),
241 )
242 .route(
243 "/_account.updateTrustedDevice",
244 post(api::server::update_trusted_device),
245 )
246 .route(
247 "/_account.createPasskeyAccount",
248 post(api::server::create_passkey_account),
249 )
250 .route(
251 "/_account.startPasskeyRegistrationForSetup",
252 post(api::server::start_passkey_registration_for_setup),
253 )
254 .route(
255 "/_account.completePasskeySetup",
256 post(api::server::complete_passkey_setup),
257 )
258 .route(
259 "/_account.requestPasskeyRecovery",
260 post(api::server::request_passkey_recovery),
261 )
262 .route(
263 "/_account.recoverPasskeyAccount",
264 post(api::server::recover_passkey_account),
265 )
266 .route(
267 "/_account.updateDidDocument",
268 post(api::server::update_did_document),
269 )
270 .route(
271 "/_account.getDidDocument",
272 get(api::server::get_did_document),
273 )
274 .route(
275 "/com.atproto.server.requestEmailUpdate",
276 post(api::server::request_email_update),
277 )
278 .route(
279 "/_checkEmailVerified",
280 post(api::server::check_email_verified),
281 )
282 .route(
283 "/com.atproto.server.confirmEmail",
284 post(api::server::confirm_email),
285 )
286 .route(
287 "/com.atproto.server.updateEmail",
288 post(api::server::update_email),
289 )
290 .route(
291 "/com.atproto.server.reserveSigningKey",
292 post(api::server::reserve_signing_key),
293 )
294 .route(
295 "/com.atproto.server.verifyMigrationEmail",
296 post(api::server::verify_migration_email),
297 )
298 .route(
299 "/com.atproto.server.resendMigrationVerification",
300 post(api::server::resend_migration_verification),
301 )
302 .route(
303 "/com.atproto.identity.updateHandle",
304 post(api::identity::update_handle),
305 )
306 .route(
307 "/com.atproto.identity.requestPlcOperationSignature",
308 post(api::identity::request_plc_operation_signature),
309 )
310 .route(
311 "/com.atproto.identity.signPlcOperation",
312 post(api::identity::sign_plc_operation),
313 )
314 .route(
315 "/com.atproto.identity.submitPlcOperation",
316 post(api::identity::submit_plc_operation),
317 )
318 .route("/com.atproto.repo.importRepo", post(api::repo::import_repo))
319 .route(
320 "/com.atproto.admin.deleteAccount",
321 post(api::admin::delete_account),
322 )
323 .route(
324 "/com.atproto.admin.updateAccountEmail",
325 post(api::admin::update_account_email),
326 )
327 .route(
328 "/com.atproto.admin.updateAccountHandle",
329 post(api::admin::update_account_handle),
330 )
331 .route(
332 "/com.atproto.admin.updateAccountPassword",
333 post(api::admin::update_account_password),
334 )
335 .route(
336 "/com.atproto.server.listAppPasswords",
337 get(api::server::list_app_passwords),
338 )
339 .route(
340 "/com.atproto.server.createAppPassword",
341 post(api::server::create_app_password),
342 )
343 .route(
344 "/com.atproto.server.revokeAppPassword",
345 post(api::server::revoke_app_password),
346 )
347 .route(
348 "/com.atproto.server.createInviteCode",
349 post(api::server::create_invite_code),
350 )
351 .route(
352 "/com.atproto.server.createInviteCodes",
353 post(api::server::create_invite_codes),
354 )
355 .route(
356 "/com.atproto.server.getAccountInviteCodes",
357 get(api::server::get_account_invite_codes),
358 )
359 .route(
360 "/com.atproto.server.createTotpSecret",
361 post(api::server::create_totp_secret),
362 )
363 .route(
364 "/com.atproto.server.enableTotp",
365 post(api::server::enable_totp),
366 )
367 .route(
368 "/com.atproto.server.disableTotp",
369 post(api::server::disable_totp),
370 )
371 .route(
372 "/com.atproto.server.getTotpStatus",
373 get(api::server::get_totp_status),
374 )
375 .route(
376 "/com.atproto.server.regenerateBackupCodes",
377 post(api::server::regenerate_backup_codes),
378 )
379 .route(
380 "/com.atproto.server.startPasskeyRegistration",
381 post(api::server::start_passkey_registration),
382 )
383 .route(
384 "/com.atproto.server.finishPasskeyRegistration",
385 post(api::server::finish_passkey_registration),
386 )
387 .route(
388 "/com.atproto.server.listPasskeys",
389 get(api::server::list_passkeys),
390 )
391 .route(
392 "/com.atproto.server.deletePasskey",
393 post(api::server::delete_passkey),
394 )
395 .route(
396 "/com.atproto.server.updatePasskey",
397 post(api::server::update_passkey),
398 )
399 .route(
400 "/com.atproto.admin.getInviteCodes",
401 get(api::admin::get_invite_codes),
402 )
403 .route("/_admin.getServerStats", get(api::admin::get_server_stats))
404 .route("/_server.getConfig", get(api::admin::get_server_config))
405 .route(
406 "/_admin.updateServerConfig",
407 post(api::admin::update_server_config),
408 )
409 .route(
410 "/com.atproto.admin.disableAccountInvites",
411 post(api::admin::disable_account_invites),
412 )
413 .route(
414 "/com.atproto.admin.enableAccountInvites",
415 post(api::admin::enable_account_invites),
416 )
417 .route(
418 "/com.atproto.admin.disableInviteCodes",
419 post(api::admin::disable_invite_codes),
420 )
421 .route(
422 "/com.atproto.admin.getSubjectStatus",
423 get(api::admin::get_subject_status),
424 )
425 .route(
426 "/com.atproto.admin.updateSubjectStatus",
427 post(api::admin::update_subject_status),
428 )
429 .route("/com.atproto.admin.sendEmail", post(api::admin::send_email))
430 .route(
431 "/app.bsky.actor.getPreferences",
432 get(api::actor::get_preferences),
433 )
434 .route(
435 "/app.bsky.actor.putPreferences",
436 post(api::actor::put_preferences),
437 )
438 .route(
439 "/com.atproto.temp.checkSignupQueue",
440 get(api::temp::check_signup_queue),
441 )
442 .route(
443 "/com.atproto.temp.dereferenceScope",
444 post(api::temp::dereference_scope),
445 )
446 .route(
447 "/_account.getNotificationPrefs",
448 get(api::notification_prefs::get_notification_prefs),
449 )
450 .route(
451 "/_account.updateNotificationPrefs",
452 post(api::notification_prefs::update_notification_prefs),
453 )
454 .route(
455 "/_account.getNotificationHistory",
456 get(api::notification_prefs::get_notification_history),
457 )
458 .route(
459 "/_account.confirmChannelVerification",
460 post(api::verification::confirm_channel_verification),
461 )
462 .route("/_account.verifyToken", post(api::server::verify_token))
463 .route(
464 "/_delegation.listControllers",
465 get(api::delegation::list_controllers),
466 )
467 .route(
468 "/_delegation.addController",
469 post(api::delegation::add_controller),
470 )
471 .route(
472 "/_delegation.removeController",
473 post(api::delegation::remove_controller),
474 )
475 .route(
476 "/_delegation.updateControllerScopes",
477 post(api::delegation::update_controller_scopes),
478 )
479 .route(
480 "/_delegation.listControlledAccounts",
481 get(api::delegation::list_controlled_accounts),
482 )
483 .route(
484 "/_delegation.getAuditLog",
485 get(api::delegation::get_audit_log),
486 )
487 .route(
488 "/_delegation.getScopePresets",
489 get(api::delegation::get_scope_presets),
490 )
491 .route(
492 "/_delegation.createDelegatedAccount",
493 post(api::delegation::create_delegated_account),
494 )
495 .route("/_backup.listBackups", get(api::backup::list_backups))
496 .route("/_backup.getBackup", get(api::backup::get_backup))
497 .route("/_backup.createBackup", post(api::backup::create_backup))
498 .route("/_backup.deleteBackup", post(api::backup::delete_backup))
499 .route("/_backup.setEnabled", post(api::backup::set_backup_enabled))
500 .route("/_backup.exportBlobs", get(api::backup::export_blobs))
501 .route(
502 "/app.bsky.ageassurance.getState",
503 get(api::age_assurance::get_state),
504 )
505 .route(
506 "/app.bsky.unspecced.getAgeAssuranceState",
507 get(api::age_assurance::get_age_assurance_state),
508 )
509 .fallback(async || (
510 StatusCode::NOT_IMPLEMENTED,
511 Json(json!({"error": "MethodNotImplemented", "message": "Method not implemented. For app.bsky.* methods, include an atproto-proxy header specifying your AppView."})),
512 ));
513 let xrpc_service = ServiceBuilder::new()
514 .layer(XrpcProxyLayer::new(state.clone()))
515 .service(xrpc_router.with_state(state.clone()));
516
517 let oauth_router = Router::new()
518 .route("/jwks", get(oauth::endpoints::oauth_jwks))
519 .route(
520 "/client-metadata.json",
521 get(oauth::endpoints::frontend_client_metadata),
522 )
523 .route("/par", post(oauth::endpoints::pushed_authorization_request))
524 .route("/authorize", get(oauth::endpoints::authorize_get))
525 .route("/authorize", post(oauth::endpoints::authorize_post))
526 .route(
527 "/authorize/accounts",
528 get(oauth::endpoints::authorize_accounts),
529 )
530 .route(
531 "/authorize/select",
532 post(oauth::endpoints::authorize_select),
533 )
534 .route("/authorize/2fa", get(oauth::endpoints::authorize_2fa_get))
535 .route("/authorize/2fa", post(oauth::endpoints::authorize_2fa_post))
536 .route(
537 "/authorize/passkey",
538 get(oauth::endpoints::authorize_passkey_start),
539 )
540 .route(
541 "/authorize/passkey",
542 post(oauth::endpoints::authorize_passkey_finish),
543 )
544 .route(
545 "/passkey/check",
546 get(oauth::endpoints::check_user_has_passkeys),
547 )
548 .route(
549 "/security-status",
550 get(oauth::endpoints::check_user_security_status),
551 )
552 .route("/passkey/start", post(oauth::endpoints::passkey_start))
553 .route("/passkey/finish", post(oauth::endpoints::passkey_finish))
554 .route("/authorize/deny", post(oauth::endpoints::authorize_deny))
555 .route("/authorize/consent", get(oauth::endpoints::consent_get))
556 .route("/authorize/consent", post(oauth::endpoints::consent_post))
557 .route(
558 "/authorize/redirect",
559 get(oauth::endpoints::authorize_redirect),
560 )
561 .route("/delegation/auth", post(oauth::endpoints::delegation_auth))
562 .route(
563 "/delegation/totp",
564 post(oauth::endpoints::delegation_totp_verify),
565 )
566 .route("/token", post(oauth::endpoints::token_endpoint))
567 .route("/revoke", post(oauth::endpoints::revoke_token))
568 .route("/introspect", post(oauth::endpoints::introspect_token));
569
570 let well_known_router = Router::new()
571 .route("/did.json", get(api::identity::well_known_did))
572 .route("/atproto-did", get(api::identity::well_known_atproto_did))
573 .route(
574 "/oauth-protected-resource",
575 get(oauth::endpoints::oauth_protected_resource),
576 )
577 .route(
578 "/oauth-authorization-server",
579 get(oauth::endpoints::oauth_authorization_server),
580 );
581
582 let router = Router::new()
583 .nest_service("/xrpc", xrpc_service)
584 .nest("/oauth", oauth_router)
585 .nest("/.well-known", well_known_router)
586 .route("/metrics", get(metrics::metrics_handler))
587 .route("/health", get(api::server::health))
588 .route("/robots.txt", get(api::server::robots_txt))
589 .route("/logo", get(api::server::get_logo))
590 .route("/u/{handle}/did.json", get(api::identity::user_did_doc))
591 .layer(DefaultBodyLimit::max(util::get_max_blob_size()))
592 .layer(middleware::from_fn(metrics::metrics_middleware))
593 .layer(
594 CorsLayer::new()
595 .allow_origin(Any)
596 .allow_methods([Method::GET, Method::POST, Method::OPTIONS])
597 .allow_headers([
598 "Authorization".parse().unwrap(),
599 "Content-Type".parse().unwrap(),
600 "Content-Encoding".parse().unwrap(),
601 "Accept-Encoding".parse().unwrap(),
602 "DPoP".parse().unwrap(),
603 "atproto-proxy".parse().unwrap(),
604 "atproto-accept-labelers".parse().unwrap(),
605 "x-bsky-topics".parse().unwrap(),
606 ])
607 .expose_headers([
608 "WWW-Authenticate".parse().unwrap(),
609 "DPoP-Nonce".parse().unwrap(),
610 "atproto-repo-rev".parse().unwrap(),
611 "atproto-content-labelers".parse().unwrap(),
612 ]),
613 )
614 .with_state(state);
615
616 let frontend_dir =
617 std::env::var("FRONTEND_DIR").unwrap_or_else(|_| "./frontend/dist".to_string());
618 if std::path::Path::new(&frontend_dir)
619 .join("index.html")
620 .exists()
621 {
622 let index_path = format!("{}/index.html", frontend_dir);
623 let homepage_path = format!("{}/homepage.html", frontend_dir);
624
625 let homepage_exists = std::path::Path::new(&homepage_path).exists();
626 let homepage_file = if homepage_exists {
627 homepage_path
628 } else {
629 index_path.clone()
630 };
631
632 let spa_router = Router::new().fallback_service(ServeFile::new(&index_path));
633
634 let serve_dir = ServeDir::new(&frontend_dir).not_found_service(ServeFile::new(&index_path));
635
636 return router
637 .route_service("/", ServeFile::new(&homepage_file))
638 .nest("/app", spa_router)
639 .fallback_service(serve_dir);
640 }
641
642 router
643}