Skip to main content

systemprompt_cli/session/
api.rs

1use anyhow::{Context, Result};
2use chrono::Duration;
3use systemprompt_analytics::{CreateSessionParams, SessionRepository};
4use systemprompt_database::DbPool;
5use systemprompt_identifiers::{SessionId, SessionSource, UserId};
6use uuid::Uuid;
7
8// Why: the public `POST /oauth/session` endpoint must not accept a
9// caller-supplied `user_id` — doing so allows arbitrary admin-JWT issuance
10// against any known user UUID on a public route. The CLI is colocated with
11// the database and holds the JWT signing secret, so it mints session rows
12// (and the JWTs above) locally instead of round-tripping through the public
13// HTTP endpoint.
14pub async fn create_local_session_row(db_pool: &DbPool, user: &UserId) -> Result<SessionId> {
15    let session_repo =
16        SessionRepository::new(db_pool).context("Failed to construct session repository")?;
17
18    let session_id = SessionId::new(format!("sess_{}", Uuid::new_v4()));
19    let expires_at = chrono::Utc::now() + Duration::hours(24);
20
21    let params = CreateSessionParams {
22        session_id: &session_id,
23        user_id: Some(user),
24        session_source: SessionSource::Cli,
25        fingerprint_hash: None,
26        ip_address: None,
27        user_agent: None,
28        device_type: None,
29        browser: None,
30        os: None,
31        country: None,
32        region: None,
33        city: None,
34        preferred_locale: None,
35        referrer_source: None,
36        referrer_url: None,
37        landing_page: None,
38        entry_url: None,
39        utm_source: None,
40        utm_medium: None,
41        utm_campaign: None,
42        utm_content: None,
43        utm_term: None,
44        is_bot: false,
45        is_ai_crawler: false,
46        expires_at,
47    };
48
49    session_repo
50        .create_session(&params)
51        .await
52        .context("Failed to insert CLI session row")?;
53
54    Ok(session_id)
55}