feat: include private repo contributions in graph metrics

Aggregate graph endpoints (daily counts, language daily counts, source
summaries, OG image) now include private repository activity. These
endpoints only expose numeric counts — no commit messages, repo names,
or other metadata — so private details remain hidden. The activity
timeline continues to serve only public events.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-11 15:35:22 +03:00
parent f386e0b574
commit b41e8c330a
3 changed files with 13 additions and 11 deletions

View File

@@ -130,7 +130,7 @@ async fn list_sources(
) -> Result<Json<Vec<SourceSummary>>, ApiError> { ) -> Result<Json<Vec<SourceSummary>>, ApiError> {
let summaries = state let summaries = state
.store .store
.source_summaries(/* include_private */ false) .source_summaries(/* include_private */ true)
.await .await
.map_err(internal)?; .map_err(internal)?;
Ok(Json(summaries)) Ok(Json(summaries))
@@ -155,7 +155,7 @@ async fn daily_counts(
) -> Result<Json<Vec<DailyCount>>, ApiError> { ) -> Result<Json<Vec<DailyCount>>, ApiError> {
let to = params.to.unwrap_or_else(|| Utc::now().date_naive()); let to = params.to.unwrap_or_else(|| Utc::now().date_naive());
let from = params.from.unwrap_or_else(|| to - chrono::Duration::days(365)); let from = params.from.unwrap_or_else(|| to - chrono::Duration::days(365));
let counts = state.store.daily_counts(from, to).await.map_err(internal)?; let counts = state.store.daily_counts(from, to, /* include_private */ true).await.map_err(internal)?;
Ok(Json(counts)) Ok(Json(counts))
} }
@@ -165,7 +165,7 @@ async fn language_daily_counts(
) -> Result<Json<Vec<LanguageDailyCount>>, ApiError> { ) -> Result<Json<Vec<LanguageDailyCount>>, ApiError> {
let to = params.to.unwrap_or_else(|| Utc::now().date_naive()); let to = params.to.unwrap_or_else(|| Utc::now().date_naive());
let from = params.from.unwrap_or_else(|| to - chrono::Duration::days(365)); let from = params.from.unwrap_or_else(|| to - chrono::Duration::days(365));
let counts = state.store.language_daily_counts(from, to).await.map_err(internal)?; let counts = state.store.language_daily_counts(from, to, /* include_private */ true).await.map_err(internal)?;
Ok(Json(counts)) Ok(Json(counts))
} }
@@ -182,7 +182,7 @@ async fn og_contributions(
// Get date range from source summaries // Get date range from source summaries
let summaries = state let summaries = state
.store .store
.source_summaries(false) .source_summaries(/* include_private */ true)
.await .await
.map_err(internal)?; .map_err(internal)?;
let earliest = summaries let earliest = summaries
@@ -195,7 +195,7 @@ async fn og_contributions(
let counts = state let counts = state
.store .store
.daily_counts(earliest, today) .daily_counts(earliest, today, /* include_private */ true)
.await .await
.map_err(internal)?; .map_err(internal)?;

View File

@@ -20,8 +20,8 @@ pub trait EventReader: Send + Sync {
async fn list_events(&self, query: &EventQuery) -> Result<Vec<Event>, StoreError>; async fn list_events(&self, query: &EventQuery) -> Result<Vec<Event>, StoreError>;
async fn source_summaries(&self, include_private: bool) -> Result<Vec<SourceSummary>, StoreError>; async fn source_summaries(&self, include_private: bool) -> Result<Vec<SourceSummary>, StoreError>;
async fn list_projects(&self) -> Result<Vec<ProjectSummary>, StoreError>; async fn list_projects(&self) -> Result<Vec<ProjectSummary>, StoreError>;
async fn daily_counts(&self, from: NaiveDate, to: NaiveDate) -> Result<Vec<DailyCount>, StoreError>; async fn daily_counts(&self, from: NaiveDate, to: NaiveDate, include_private: bool) -> Result<Vec<DailyCount>, StoreError>;
async fn language_daily_counts(&self, from: NaiveDate, to: NaiveDate) -> Result<Vec<LanguageDailyCount>, StoreError>; async fn language_daily_counts(&self, from: NaiveDate, to: NaiveDate, include_private: bool) -> Result<Vec<LanguageDailyCount>, StoreError>;
async fn repo_languages(&self) -> Result<Vec<RepoLanguage>, StoreError>; async fn repo_languages(&self) -> Result<Vec<RepoLanguage>, StoreError>;
} }

View File

@@ -196,7 +196,7 @@ impl EventReader for PgStore {
.collect() .collect()
} }
async fn daily_counts(&self, from: NaiveDate, to: NaiveDate) -> Result<Vec<DailyCount>, StoreError> { async fn daily_counts(&self, from: NaiveDate, to: NaiveDate, include_private: bool) -> Result<Vec<DailyCount>, StoreError> {
let rows = sqlx::query( let rows = sqlx::query(
r#" r#"
SELECT d::date AS date, SELECT d::date AS date,
@@ -205,13 +205,14 @@ impl EventReader for PgStore {
LEFT JOIN events e LEFT JOIN events e
ON e.occurred_at >= (d::date || 'T00:00:00Z')::timestamptz ON e.occurred_at >= (d::date || 'T00:00:00Z')::timestamptz
AND e.occurred_at < ((d::date + 1) || 'T00:00:00Z')::timestamptz AND e.occurred_at < ((d::date + 1) || 'T00:00:00Z')::timestamptz
AND e.public = true AND ($3::bool OR e.public = true)
GROUP BY d::date GROUP BY d::date
ORDER BY d::date ORDER BY d::date
"#, "#,
) )
.bind(from) .bind(from)
.bind(to) .bind(to)
.bind(include_private)
.fetch_all(&self.pool) .fetch_all(&self.pool)
.await .await
.map_err(map_err)?; .map_err(map_err)?;
@@ -226,7 +227,7 @@ impl EventReader for PgStore {
.collect() .collect()
} }
async fn language_daily_counts(&self, from: NaiveDate, to: NaiveDate) -> Result<Vec<LanguageDailyCount>, StoreError> { async fn language_daily_counts(&self, from: NaiveDate, to: NaiveDate, include_private: bool) -> Result<Vec<LanguageDailyCount>, StoreError> {
let rows = sqlx::query( let rows = sqlx::query(
r#" r#"
SELECT date, language, color, SELECT date, language, color,
@@ -244,7 +245,7 @@ impl EventReader for PgStore {
JOIN events e JOIN events e
ON e.occurred_at >= (d::date || 'T00:00:00Z')::timestamptz ON e.occurred_at >= (d::date || 'T00:00:00Z')::timestamptz
AND e.occurred_at < ((d::date + 1) || 'T00:00:00Z')::timestamptz AND e.occurred_at < ((d::date + 1) || 'T00:00:00Z')::timestamptz
AND e.public = true AND ($3::bool OR e.public = true)
AND e.action IN ('Commit', 'PushEvent', 'commit_repo') AND e.action IN ('Commit', 'PushEvent', 'commit_repo')
JOIN repo_languages rl JOIN repo_languages rl
ON rl.source = e.source ON rl.source = e.source
@@ -273,6 +274,7 @@ impl EventReader for PgStore {
) )
.bind(from) .bind(from)
.bind(to) .bind(to)
.bind(include_private)
.fetch_all(&self.pool) .fetch_all(&self.pool)
.await .await
.map_err(map_err)?; .map_err(map_err)?;