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