From 3e6a2aa550db443000043728ce0f4b43cb1a5a7f Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 11 Feb 2025 02:59:34 +0100 Subject: [PATCH] vks: add simple updated-prefixes route --- database/src/fs.rs | 5 +++++ database/src/lib.rs | 3 ++- database/src/sqlite.rs | 24 ++++++++++++++++++++++++ database/src/types.rs | 12 ++++++++++++ src/web/mod.rs | 1 + src/web/vks_api.rs | 21 +++++++++++++++++++++ 6 files changed, 65 insertions(+), 1 deletion(-) diff --git a/database/src/fs.rs b/database/src/fs.rs index dfc8821..f8c55a5 100644 --- a/database/src/fs.rs +++ b/database/src/fs.rs @@ -7,6 +7,7 @@ use std::io::Write; use std::os::unix::fs::PermissionsExt; use std::path::{Path, PathBuf}; +use chrono::NaiveDate; use pathdiff::diff_paths; use std::time::SystemTime; use tempfile; @@ -617,6 +618,10 @@ impl<'a> Database<'a> for Filesystem { Fingerprint::from_str(last_entry) } + fn get_updates_since(&self, _day: NaiveDate) -> Vec { + vec![] + } + fn check_link_fpr( &self, fpr: &Fingerprint, diff --git a/database/src/lib.rs b/database/src/lib.rs index 6bb1d55..2fc407d 100644 --- a/database/src/lib.rs +++ b/database/src/lib.rs @@ -5,7 +5,7 @@ use std::str::FromStr; use openpgp::serialize::SerializeInto; -use chrono::prelude::Utc; +use chrono::{prelude::Utc, NaiveDate}; #[macro_use] extern crate anyhow; @@ -176,6 +176,7 @@ pub trait Database<'a>: Sync + Send { fn by_primary_fpr(&self, fpr: &Fingerprint) -> Option; fn get_last_log_entry(&self) -> Result; + fn get_updates_since(&self, day: NaiveDate) -> Vec; fn write_log_append(&self, filename: &str, fpr_primary: &Fingerprint) -> Result<()>; diff --git a/database/src/sqlite.rs b/database/src/sqlite.rs index e6d70e6..7bd3448 100644 --- a/database/src/sqlite.rs +++ b/database/src/sqlite.rs @@ -1,3 +1,5 @@ +use chrono::{NaiveDate, NaiveDateTime, NaiveTime, Utc}; + use self_cell::self_cell; use std::convert::TryFrom; @@ -473,6 +475,28 @@ impl<'a> Database<'a> for Sqlite { Ok(()) } + fn get_updates_since(&self, since_day: NaiveDate) -> Vec { + let conn = self.pool.get().unwrap(); + + let midnight = NaiveTime::from_hms_opt(0, 0, 0).expect("noon exists"); + let until_day_midnight = Utc::now().with_time(midnight).unwrap(); + let since_day_midnight = NaiveDateTime::new(since_day, midnight); + + let fprs: Vec = conn + .prepare( + "SELECT primary_fingerprint FROM certs WHERE updated_at > ?1 AND updated_at < ?2", + ) + .expect("query must be correct") + .query_map( + params![since_day_midnight.and_utc().timestamp_millis(), until_day_midnight.timestamp_millis()], + |row| row.get::<_, Fingerprint>(0), + ) + .expect("query must not fail") + .flatten() + .collect(); + fprs + } + fn get_last_log_entry(&self) -> Result { let conn = self.pool.get().unwrap(); Ok(conn.query_row( diff --git a/database/src/types.rs b/database/src/types.rs index 3bf6c2f..955da7c 100644 --- a/database/src/types.rs +++ b/database/src/types.rs @@ -102,6 +102,18 @@ impl FromStr for Email { #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct Fingerprint([u8; 20]); +impl Fingerprint { + pub fn to_prefix(&self) -> String { + let mut prefix_bytes = [0u8; 8]; + prefix_bytes.copy_from_slice(&self.0[0..8]); + + let mut result = String::new(); + prefix_bytes.write_hex_upper(&mut result).expect("writing bytes as hex cannot fail"); + + result + } +} + impl FromSql for Fingerprint { fn column_result(value: ValueRef<'_>) -> FromSqlResult { value diff --git a/src/web/mod.rs b/src/web/mod.rs index 4400567..727f4bb 100644 --- a/src/web/mod.rs +++ b/src/web/mod.rs @@ -428,6 +428,7 @@ fn rocket_factory( vks_api::vks_v1_by_email, vks_api::vks_v1_by_fingerprint, vks_api::vks_v1_by_keyid, + vks_api::vks_v1_updated_prefixes, vks_api::upload_json, vks_api::upload_fallback, vks_api::request_verify_json, diff --git a/src/web/vks_api.rs b/src/web/vks_api.rs index ac3478a..f5a978f 100644 --- a/src/web/vks_api.rs +++ b/src/web/vks_api.rs @@ -1,3 +1,5 @@ +use chrono::NaiveDate; +use database::Database; use rocket::http::{ContentType, Status}; use rocket::request::Request; use rocket::response::{self, Responder, Response}; @@ -5,6 +7,7 @@ use rocket::serde::json::Json; use rocket_i18n::{I18n, Translations}; use serde_json::json; use std::io::Cursor; +use std::str::FromStr; use crate::database::types::{Email, Fingerprint, KeyID}; use crate::database::{KeyDatabase, Query, StatefulTokens}; @@ -200,3 +203,21 @@ pub fn vks_v1_by_keyid(db: &rocket::State, i18n: I18n, kid: String) web::key_to_response_plain(db, i18n, query) } + +#[get("/vks/v1/updated-prefixes/since/")] +pub fn vks_v1_updated_prefixes( + db: &rocket::State, + day: &str, +) -> MyResponse { + let day = match NaiveDate::from_str(day) { + Ok(day) => day, + Err(_) => return MyResponse::bad_request_plain("malformed iso8601 timestamp"), + }; + + let updates = db.get_updates_since(day); + let prefixes: Vec = updates.iter().map(|fpr| fpr.to_prefix()).collect(); + let prefix_text = prefixes.join("\n"); + + MyResponse::Plain(prefix_text) +} +