d6dc02f
pub mod account_deletion;
pub mod assets;
pub mod cleanup;
pub mod middleware;
pub mod openapi;
use actix_cors::Cors;
use actix_web::http::Method;
use actix_web::{Scope, web};
use export::AppState;
use export::http::{Guard, Router};
pub const MAX_REQUEST_BODY_BYTES: usize = 2 * 1024 * 1024;
pub fn build_cors(web: &config::web::WebConfig) -> Cors {
let mut cors = Cors::default();
for origin in &web.cors_allow_origins {
cors = cors.allowed_origin(origin);
}
let mut cors = cors
.allowed_methods([
Method::GET,
Method::POST,
Method::PUT,
Method::PATCH,
Method::DELETE,
Method::OPTIONS,
])
.allowed_headers(["content-type", "authorization", "x-request-id"]);
if web.cors_allow_credentials {
cors = cors.supports_credentials();
}
cors
}
pub fn scope(state: AppState) -> Scope {
let accounts = auth::Accounts::new(state.database.connection().clone(), state.cache.clone());
let access = space::Access::new(state.database.connection().clone(), state.cache.clone());
let notifications =
export::notify::Notifications::new(state.database.clone(), state.redis.clone());
let auth = auth::AuthService::new(
accounts.clone(),
auth::services::AuthSettings::from_config(&state.config),
state.mail.clone(),
);
let chat = build_chat_service(&state, accounts.clone());
let issue_audience = issues::services::audience::Audience::new(
accounts.clone(),
notifications,
state.mail.clone(),
i18n::config_fallback(state.config.email.default_locale()),
);
let issues = issues::services::service::IssueService::new(
state.database.clone(),
state.storage.clone(),
issue_audience,
issues::services::service::IssueSettings::from_config(&state.config),
issues::services::admission::Admission::new(
access.clone(),
state.redis.clone(),
state.config.quota.clone(),
),
);
let space = space::services::service::SpaceService::new(
access.clone(),
space::services::images::SpaceImages::new(
access.clone(),
state.storage.clone(),
state.redis.clone(),
),
space::services::policy::SpacePolicy::new(
space::services::service::SpaceSettings::from_config(&state.config),
accounts.clone(),
state.redis.clone(),
),
export::notify::Notifications::new(state.database.clone(), state.redis.clone()),
);
let user = user::services::service::UserService::new(
state.database.clone(),
accounts.clone(),
access.clone(),
user::services::avatar::AvatarService::new(
state.database.clone(),
state.storage.clone(),
state.redis.clone(),
),
);
let git_config = state.config.git.clone();
let ks = proto::KenShellClient::connect_with_key(
&git_config.kenshell_endpoint,
&git_config.kenshell_rpc_key,
std::time::Duration::from_secs(git_config.kenshell_timeout_secs.max(1)),
)
.unwrap_or_else(|error| {
panic!(
"invalid git.kenshell_endpoint `{}`: {error}",
git_config.kenshell_endpoint
)
});
let git = git::service::GitService::new(
state.database.clone(),
accounts.clone(),
access,
git::service::GitBackend::new(state.cache.clone(), git_config, std::sync::Arc::new(ks)),
);
let pulls = pulls::services::service::PullService::new(
state.database.clone(),
git.clone(),
issues.clone(),
pulls::services::actors::Actors::new(
accounts.clone(),
state.redis.clone(),
state.config.quota.clone(),
),
);
let internal_service_key = state.config.internal.service_key.clone();
let cleanup_router = cleanup::router(
state.database.clone(),
space.clone(),
issues.clone(),
git.clone(),
);
let account_deletion = Router::<AppState>::new()
.route(
"/me/deletion",
export::http::get(account_deletion::account_deletion_status)
.post(account_deletion::request_account_deletion)
.delete(account_deletion::cancel_account_deletion),
)
.wrap(Guard::Session)
.with_state(state.clone());
let accounts_for_http = accounts.clone();
let push_config = state.config.push.clone();
let public = Router::<()>::new()
.setup(move |scope| {
scope
.app_data(web::Data::new(accounts_for_http))
.app_data(web::Data::new(push_config))
})
.merge(account_deletion)
.merge(::auth::routes::router(auth.clone()).with_state(auth.clone()))
.merge(chat::routes::router(chat.clone(), space.clone()).with_state(chat.clone()))
.merge(issues::routes::router(issues.clone()).with_state(issues))
.merge(space::routes::router(space.clone()).with_state(space))
.merge(user::routes::router(user.clone()).with_state(user))
.merge(git::routes::router(git.clone()).with_state(git.clone()))
.merge(pulls::routes::router().with_state(pulls))
.merge(cleanup_router);
let internal = Router::<()>::new()
.merge(git::routes::internal_router(git.clone()).with_state(git))
.merge(chat::routes::internal_router(chat.clone()).with_state(chat))
.with_state(internal_service_key)
.wrap(Guard::ServiceKey);
Router::<()>::new()
.nest("/api/v1/internal", internal)
.nest("/api/v1", public)
.nest("/pages", ::auth::routes::pages_router(auth.clone()))
.merge(export::health::router(state))
.into_scope(web::scope(""))
.default_service(web::to(assets::serve))
}
pub fn build_chat_service(
state: &AppState,
accounts: auth::Accounts,
) -> chat::services::service::ChatService {
let audience = chat::services::audience::Audience::new(
accounts,
export::notify::Notifications::new(state.database.clone(), state.redis.clone()),
state.mail.clone(),
chat::share::realtime::Realtime::new(state.database.clone()),
i18n::config_fallback(state.config.email.default_locale()),
);
let participants = chat::services::presence::Participants::new(
state.database.clone(),
state.redis.clone(),
space::Access::new(state.database.connection().clone(), state.cache.clone()),
audience.clone(),
chat::services::presence::ParticipantSettings::from_config(&state.config),
);
chat::services::service::ChatService::new(
state.database.clone(),
chat::services::media::Media::new(
state.storage.clone(),
state.redis.clone(),
state.database.clone(),
),
audience,
chat::services::service::ChatSettings::from_config(&state.config),
participants,
)
}