Skip to content
This repository was archived by the owner on Mar 18, 2023. It is now read-only.

Commit dfed1dd

Browse files
committed
✨ Add /api/:realm/accounts/:since/active-since
1 parent a8344e4 commit dfed1dd

File tree

6 files changed

+72
-8
lines changed

6 files changed

+72
-8
lines changed

src/database/mongodb/models/account/id_projection.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ impl TypedDocument for AccountIdProjection {
1919
}
2020

2121
impl AccountIdProjection {
22-
pub async fn retrieve_page(
22+
#[instrument(skip_all, level = "info", fields(realm = realm, limit = limit))]
23+
pub async fn retrieve_recently_active(
2324
from: &Database,
2425
realm: &str,
2526
limit: i64,
@@ -32,4 +33,17 @@ impl AccountIdProjection {
3233
.build();
3334
Ok(Self::collection(from).find(filter, options).await?)
3435
}
36+
37+
#[instrument(skip_all, level = "info", fields(realm = ?realm, since = ?since))]
38+
pub async fn retrieve_active_since(
39+
from: &Database,
40+
realm: wargaming::Realm,
41+
since: DateTime,
42+
) -> Result<impl Stream<Item = Result<Self, mongodb::error::Error>>> {
43+
let filter = doc! { "rlm": realm.to_str(), "lbts": { "$gte": since } };
44+
let options = FindOptions::builder()
45+
.projection(doc! { "_id": 0, "aid": 1 })
46+
.build();
47+
Ok(Self::collection(from).find(filter, options).await?)
48+
}
3549
}

src/web.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::opts::WebOpts;
1212
use crate::prelude::*;
1313
use crate::wargaming::cache::account::{AccountInfoCache, AccountTanksCache};
1414
use crate::wargaming::WargamingApi;
15+
use crate::web::middleware::timeit::TimeItMiddleware;
1516
use crate::web::middleware::{ErrorMiddleware, SecurityHeadersMiddleware, SentryMiddleware};
1617
use crate::web::tracking_code::TrackingCode;
1718

@@ -120,6 +121,10 @@ async fn create_standalone_app() -> Result<impl Endpoint> {
120121
.at("/random", get(views::random::get_random))
121122
.at("/sitemaps/:realm/sitemap.txt", get(views::sitemaps::get_sitemap))
122123
.at("/api/health", get(views::api::get_health))
124+
.at(
125+
"/api/:realm/accounts/:since/active-since",
126+
get(views::api::get_active_since).with(TimeItMiddleware),
127+
)
123128
.data(i18n::build_resources()?)
124129
.with(Tracing)
125130
.with(CatchPanic::new())

src/web/middleware.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
pub mod error;
22
pub mod security_headers;
33
pub mod sentry;
4+
pub mod timeit;
45

56
pub use self::error::*;
67
pub use self::security_headers::*;

src/web/middleware/timeit.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use poem::{Endpoint, Middleware, Request, Response, Result};
2+
3+
use crate::prelude::*;
4+
5+
pub struct TimeItMiddleware;
6+
7+
impl<E: Endpoint<Output = Response>> Middleware<E> for TimeItMiddleware {
8+
type Output = TimeItMiddlewareImpl<E>;
9+
10+
fn transform(&self, ep: E) -> Self::Output {
11+
TimeItMiddlewareImpl { ep }
12+
}
13+
}
14+
15+
pub struct TimeItMiddlewareImpl<E> {
16+
ep: E,
17+
}
18+
19+
#[poem::async_trait]
20+
impl<E: Endpoint<Output = Response>> Endpoint for TimeItMiddlewareImpl<E> {
21+
type Output = Response;
22+
23+
async fn call(&self, request: Request) -> Result<Self::Output> {
24+
let method = request.method().clone();
25+
let uri = request.uri().clone();
26+
let start_instant = Instant::now();
27+
let response = self.ep.call(request).await;
28+
info!(elapsed = ?start_instant.elapsed(), ?method, ?uri);
29+
response
30+
}
31+
}

src/web/views/api.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
use futures::StreamExt;
12
use poem::http::StatusCode;
2-
use poem::{handler, IntoResponse, Response};
3+
use poem::web::{Data, Path};
4+
use poem::{handler, Body, IntoResponse, Response};
35

6+
use crate::database::AccountIdProjection;
47
use crate::prelude::*;
58

69
const CACHE_CONTROL: &str = "no-cache";
@@ -10,3 +13,17 @@ const CACHE_CONTROL: &str = "no-cache";
1013
pub async fn get_health() -> Result<impl IntoResponse> {
1114
Ok(Response::from(StatusCode::NO_CONTENT).with_header("Cache-Control", CACHE_CONTROL))
1215
}
16+
17+
#[handler]
18+
#[instrument(skip_all, level = "info")]
19+
pub async fn get_active_since(
20+
db: Data<&mongodb::Database>,
21+
Path((realm, since)): Path<(wargaming::Realm, DateTime)>,
22+
) -> Result<impl IntoResponse> {
23+
let stream = AccountIdProjection::retrieve_active_since(&db, realm, since)
24+
.await?
25+
.map(move |account| account.map(|account| format!("{}\n", account.id).into_bytes()));
26+
Ok(Response::from(Body::from_bytes_stream(stream))
27+
.with_header("Cache-Control", CACHE_CONTROL)
28+
.with_content_type("text/plain"))
29+
}

src/web/views/sitemaps.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,10 @@ pub async fn get_sitemap(
1414
Path(realm): Path<String>,
1515
) -> Result<impl IntoResponse> {
1616
let start_instant = Instant::now();
17-
let stream = database::AccountIdProjection::retrieve_page(&db, &realm, 1000).await?;
17+
let stream = database::AccountIdProjection::retrieve_recently_active(&db, &realm, 1000).await?;
1818
info!(elapsed_secs = ?start_instant.elapsed(), "stream ready");
1919
let stream = stream.map(move |account| {
20-
account.map(|account| {
21-
format!("https://yastati.st/{}/{}\n", realm, account.id)
22-
.as_bytes()
23-
.to_vec()
24-
})
20+
account.map(|account| format!("https://yastati.st/{}/{}\n", realm, account.id).into_bytes())
2521
});
2622

2723
Ok(Response::from(Body::from_bytes_stream(stream))

0 commit comments

Comments
 (0)