//! Per-workspace Tailscale connection (BYO tailnet): the API key + tailnet we //! use to read fleet network metrics. Used server-side only. use cm_domain::WorkspaceId; use sqlx::{PgPool, Row}; use crate::DbError; /// Store (or replace) a workspace's Tailscale API key + tailnet. pub async fn set( pool: &PgPool, workspace_id: WorkspaceId, api_key: &str, tailnet: &str, ) -> Result<(), DbError> { sqlx::query( "INSERT INTO workspace_tailscale (workspace_id, api_key, tailnet, connected_at) VALUES ($1, $2, $3, now()) ON CONFLICT (workspace_id) DO UPDATE SET api_key = excluded.api_key, tailnet = excluded.tailnet, connected_at = now()", ) .bind(workspace_id.as_uuid()) .bind(api_key) .bind(tailnet) .execute(pool) .await?; Ok(()) } /// Get a workspace's stored (api_key, tailnet), if connected. pub async fn get( pool: &PgPool, workspace_id: WorkspaceId, ) -> Result, DbError> { let row = sqlx::query("SELECT api_key, tailnet FROM workspace_tailscale WHERE workspace_id = $1") .bind(workspace_id.as_uuid()) .fetch_optional(pool) .await?; Ok(row.map(|r| (r.get("api_key"), r.get("tailnet")))) } /// Disconnect a workspace's Tailscale. pub async fn delete(pool: &PgPool, workspace_id: WorkspaceId) -> Result<(), DbError> { sqlx::query("DELETE FROM workspace_tailscale WHERE workspace_id = $1") .bind(workspace_id.as_uuid()) .execute(pool) .await?; Ok(()) }