This commit is contained in:
Brandon Thomas committed 2025-09-24 16:46:11 -04:00
1 parent 528677cd47
commit 054eaba609
6 files changed
+168 -2

No files matched your search

@@ -0,0 +1,5 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
drop table analytics_totals;
@@ -0,0 +1,37 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
CREATE TABLE analytics_active_users (
-- Not used for anything except replication.
id BIGINT(20) NOT NULL AUTO_INCREMENT,
-- If we host multiple sites with distinct categories, this will enable us to
-- segregate analytics.
app_namespace VARCHAR(32) NOT NULL,
-- Who sent the event.
user_token VARCHAR(32) NOT NULL,
-- IP address is updated on every write.
ip_address VARCHAR(40) NOT NULL,
-- Incrementing count of events.
-- NB: This is kind of an "hours spent" measure, but will lose information if we ever
-- change the ping cadence (eg. 1 minute pings --> 5 minute pings).
ping_count BIGINT UNSIGNED NOT NULL DEFAULT 0,
-- ========== RECORD TIMESTAMPS ==========
-- Written at create only.
first_active_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
-- Updated on every ping.
last_active_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
-- ========== INDICES ==========
PRIMARY KEY (id),
UNIQUE KEY (app_namespace, user_token), -- For now
INDEX idx_last_active_at (last_active_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
@@ -0,0 +1,29 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
CREATE TABLE analytics_totals (
-- Not used for anything except replication.
id BIGINT(20) NOT NULL AUTO_INCREMENT,
-- If we host multiple sites with distinct categories, this will enable us to
-- segregate analytics.
app_namespace VARCHAR(32) NOT NULL,
-- Who sent the event.
user_token VARCHAR(32) NOT NULL,
-- Incrementing count of events.
event_count BIGINT UNSIGNED NOT NULL DEFAULT 0,
-- ========== RECORD TIMESTAMPS ==========
first_event_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
last_event_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
-- ========== INDICES ==========
PRIMARY KEY (id),
UNIQUE KEY (app_namespace, user_token), -- For now
INDEX idx_last_event_at (last_event_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
@@ -0,0 +1 @@
mod upsert_analytics_totals;
@@ -0,0 +1,92 @@
use anyhow::anyhow;
use log::error;
use sqlx::{MySql, MySqlPool, QueryBuilder};
use sqlx::mysql::MySqlArguments;
use sqlx::pool::PoolConnection;
use sqlx::query::Query;
use enums::common::payments_namespace::PaymentsNamespace;
use errors::AnyhowResult;
use tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken;
use tokens::tokens::browser_session_logs::BrowserSessionLogToken;
use tokens::tokens::users::UserToken;
pub struct UpsertAnalyticsTotals {
pub user_token: UserToken,
pub ip_address: String,
// TODO: Swap PaymentsNamespace (artcraft,fakeyou) for an AnalyticsNamespace enum.
pub namespace: PaymentsNamespace,
}
impl UpsertAnalyticsTotals {
fn query(&self) -> Query<MySql, MySqlArguments> {
sqlx::query!(
r#"
INSERT INTO analytics_totals
SET
app_namespace = ?,
user_token = ?,
ip_address = ?,
event_count = event_count + 1,
last_event_at = NOW()
ON DUPLICATE KEY UPDATE
ip_address = ?,
event_count = event_count + 1,
last_event_at = NOW()
"#,
&self.namespace.as_str(),
&self.user_token,
&self.ip_address,
self.namespace.as_str(),
&self.maybe_user_token,
self.action_was_taken,
self.should_ignore_retry,
)
}
}
pub async fn upsert_browser_session_log<'a>(args: UpsertAnalyticsTotalsArgs<'a>) -> AnyhowResult<BrowserSessionLogToken> {
let token = args.maybe_log_token
.map(|token| token.clone())
.unwrap_or_else(|| BrowserSessionLogToken::generate());
let maybe_last_action = args.maybe_last_action
.map(|action| {
let mut action = action.trim().to_string();
action.truncate(32); // NB: Field is 32 wide.
action
});
let query_result = sqlx::query!(
r#"
INSERT INTO browser_session_logs
SET
token = ?,
ip_address = ?,
maybe_user_token = ?,
maybe_anonymous_visitor_token = ?,
maybe_last_action = ?
ON DUPLICATE KEY UPDATE
maybe_last_action = ?,
update_count = update_count + 1,
maybe_last_updated_at = NOW()
"#,
token.as_str(),
args.ip_address,
args.maybe_user_token.map(|t| t.as_str()),
args.maybe_visitor_token.map(|t| t.as_str()),
maybe_last_action.as_deref(),
maybe_last_action.as_deref(),
)
.execute(args.mysql_pool)
.await;
match query_result {
Ok(_) => Ok(token),
Err(err) => {
error!("Error with query: {:?}", &err);
Err(anyhow!("query error: {:?}", &err))
},
}
}
@@ -1,3 +1,5 @@
pub mod analytics_totals;
pub mod api_tokens;
pub mod audit_logs;
pub mod batch_generations;
@@ -36,5 +38,5 @@ pub mod voice_clone_requests;
pub mod voice_conversion;
pub mod voice_designer;
pub mod w2l;
pub mod wallets;
mod wallet_ledger_entries;
pub mod wallet_ledger_entries;
pub mod wallets;