use super::{write_file, Scaffold, ScaffoldArgs, ScaffoldResult};
use anyhow::Result;
pub struct ActivityLogScaffold;
impl Scaffold for ActivityLogScaffold {
fn name(&self) -> &'static str {
"activity-log"
}
fn description(&self) -> &'static str {
"Activity log: event-driven logging, feed endpoint, actor/target filtering, retention"
}
fn generate(&self, args: &ScaffoldArgs) -> Result<ScaffoldResult> {
let mut r = ScaffoldResult::default();
let d = args.dry_run;
write_file(
&mut r,
"src/app/controllers/activity_controller.rs",
CONTROLLER,
d,
)?;
write_file(&mut r, "src/app/listeners/activity_logger.rs", LISTENER, d)?;
write_file(
&mut r,
"migrations/create_activities_table.sql",
MIGRATION,
d,
)?;
r.warnings
.push("Register the ActivityLogger listener in your EventDispatcher".into());
r.warnings.push("Register GET /activities route".into());
Ok(r)
}
}
const CONTROLLER: &str = r#"use axum::{extract::{Query, State}, response::IntoResponse};
use rok_auth::axum::{Ctx, Response};
use serde::Deserialize;
#[derive(Deserialize)]
pub struct ActivityQuery {
pub actor_id: Option<i64>,
pub target_type: Option<String>,
pub target_id: Option<i64>,
pub page: Option<i64>,
pub per_page: Option<i64>,
}
pub async fn index(Query(q): Query<ActivityQuery>, State(pool): State<sqlx::PgPool>) -> impl IntoResponse {
// TODO: paginated activity feed with actor/target filters
Response::json(serde_json::json!({ "data": [], "meta": { "total": 0 } }))
}
"#;
const LISTENER: &str = r#"use rok_events::Listener;
pub struct ActivityLogger {
pub pool: sqlx::PgPool,
}
#[async_trait::async_trait]
impl Listener for ActivityLogger {
async fn handle(&self, event: &rok_events::Event) -> anyhow::Result<()> {
// TODO: INSERT INTO activities (actor_id, action, target_type, target_id, metadata)
Ok(())
}
}
"#;
const MIGRATION: &str = r#"CREATE TABLE activities (
id BIGSERIAL PRIMARY KEY,
actor_id BIGINT,
action TEXT NOT NULL,
target_type TEXT,
target_id BIGINT,
metadata JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ON activities (actor_id, created_at DESC);
CREATE INDEX ON activities (target_type, target_id, created_at DESC);
-- Auto-prune job: delete activities older than 90 days
-- Add to your cron scheduler: DELETE FROM activities WHERE created_at < now() - interval '90 days'
"#;