feat: GuiPresence (chat-service presence backend, gateway route, mod module); restore TODO history

This commit is contained in:
loki5512344 2026-10-02 13:41:49 +02:00
parent 8d9a2ad0d6
commit e82efb74e7
Signed by: boba
GPG key ID: 253067914055423B
44 changed files with 2202 additions and 19 deletions

View file

@ -8,6 +8,7 @@ pub struct Config {
pub accounts_http_url: String,
pub accounts_grpc_url: String,
pub configs_http_url: String,
pub chat_http_url: String,
pub site_origin: String,
/// Directory served read-only under `GET /downloads/*` — today just
/// `lovisual.jar`, the mod's direct download (see TODO.md «Скачивание»).
@ -33,6 +34,7 @@ impl Config {
accounts_http_url: var("ACCOUNTS_HTTP_URL")?,
accounts_grpc_url: var("ACCOUNTS_GRPC_URL")?,
configs_http_url: var("CONFIGS_HTTP_URL")?,
chat_http_url: var("CHAT_HTTP_URL")?,
site_origin: var("SITE_ORIGIN")?,
downloads_dir: std::env::var("DOWNLOADS_DIR").unwrap_or_else(|_| "downloads".into()),
trust_proxy: std::env::var("TRUST_PROXY").is_ok_and(|v| v == "true"),
@ -71,6 +73,7 @@ mod tests {
accounts_http_url: String::new(),
accounts_grpc_url: String::new(),
configs_http_url: String::new(),
chat_http_url: String::new(),
site_origin: "http://localhost:5173".into(),
downloads_dir: "downloads".into(),
trust_proxy: false,

View file

@ -31,6 +31,7 @@ pub struct Upstreams {
pub client: reqwest::Client,
pub accounts: String,
pub configs: String,
pub chat: String,
pub internal_key: HeaderValue,
}
@ -44,6 +45,7 @@ impl Upstreams {
.build()?,
accounts: cfg.accounts_http_url.trim_end_matches('/').to_owned(),
configs: cfg.configs_http_url.trim_end_matches('/').to_owned(),
chat: cfg.chat_http_url.trim_end_matches('/').to_owned(),
internal_key: HeaderValue::from_str(&cfg.internal_key)?,
})
}
@ -67,6 +69,7 @@ pub async fn proxy(State(up): State<Arc<Upstreams>>, req: Request) -> Response {
let base = match target {
Upstream::Accounts => &up.accounts,
Upstream::Configs => &up.configs,
Upstream::Chat => &up.chat,
};
let path_and_query = req.uri().path_and_query().map_or("/", |p| p.as_str());
let url = format!("{base}{path_and_query}");

View file

@ -2,12 +2,14 @@
pub enum Upstream {
Accounts,
Configs,
Chat,
}
pub fn upstream_for(path: &str) -> Option<Upstream> {
match path.trim_start_matches('/').split('/').next()? {
"auth" | "device" | "avatars" | "media" | "me" | "users" => Some(Upstream::Accounts),
"configs" | "showcase" => Some(Upstream::Configs),
"presence" => Some(Upstream::Chat),
_ => None,
}
}
@ -26,6 +28,7 @@ mod tests {
);
assert_eq!(upstream_for("/configs/shared/ABC"), Some(Upstream::Configs));
assert_eq!(upstream_for("/showcase"), Some(Upstream::Configs));
assert_eq!(upstream_for("/presence/gui"), Some(Upstream::Chat));
assert_eq!(upstream_for("/authx"), None);
assert_eq!(upstream_for("/"), None);
assert_eq!(upstream_for("/health"), None);

View file

@ -60,6 +60,8 @@ pub fn rules() -> Vec<Rule> {
),
rule(Method::POST, "/avatars", Quota::per_hour(n(5)), Account),
rule(Method::GET, "/showcase*", Quota::per_minute(n(60)), Ip),
// The mod syncs menu presence about 3 times a second while a menu is open.
rule(Method::POST, "/presence/*", Quota::per_second(n(6)), Account),
]
}