Skip to content
23 changes: 23 additions & 0 deletions src/db/main/user/blocking_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,29 @@ pub fn insert(name: &str, password: &str, conn: &Connection) -> Result<User> {
.map_err(Into::into)
}

pub fn insert_with_npub(
name: &str,
password: &str,
npub: &str,
roles: &[Role],
conn: &Connection,
) -> Result<User> {
let roles_json: Vec<String> = roles.iter().map(|r| r.to_string()).collect();
conn.query_row(
&format!(
r#"
INSERT INTO {TABLE} ({Name}, {Password}, {Npub}, {Roles})
VALUES (?1, ?2, ?3, json(?4))
RETURNING {projection}
"#,
projection = User::projection(),
),
params![name, password, npub, serde_json::to_string(&roles_json)?],
User::mapper(),
)
.map_err(Into::into)
}

#[allow(dead_code)]
pub fn select_all(conn: &Connection) -> Result<Vec<User>> {
conn.prepare(&format!(
Expand Down
27 changes: 26 additions & 1 deletion src/db/main/user/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,32 @@ pub async fn insert(
.await?
}

/// Insert a user with `npub` and `roles` set in a single statement.
/// Errors with a SQLite UNIQUE-violation if another row already has the
/// same `npub` (see migration 101). Used by the NIP-98 sign-in endpoint to
/// make auto-creation race-safe and atomic: a concurrent first-time login
/// for the same pubkey will lose this insert and fall back to
/// `select_by_npub`, and there is no window where a row exists with empty
/// roles.
pub async fn insert_with_npub(
name: impl Into<String>,
password: impl Into<String>,
npub: impl Into<String>,
roles: &[Role],
pool: &Pool,
) -> Result<User> {
let name = name.into();
let password = password.into();
let npub = npub.into();
let roles = roles.to_vec();
pool.get()
.await?
.interact(move |conn| {
blocking_queries::insert_with_npub(&name, &password, &npub, &roles, conn)
})
.await?
}

pub async fn select_by_id(id: i64, pool: &Pool) -> Result<User> {
pool.get()
.await?
Expand All @@ -30,7 +56,6 @@ pub async fn select_by_name(name: impl Into<String>, pool: &Pool) -> Result<User
.await?
}

#[allow(dead_code)]
pub async fn select_by_npub(npub: impl Into<String>, pool: &Pool) -> Result<Option<User>> {
let npub = npub.into();
pool.get()
Expand Down
12 changes: 12 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ async fn main() -> Result<()> {

let conf = db::main::conf::queries::select(&main_pool).await?;

// Trusted external base URL of this API. Used by the NIP-98 NostrAuth
// extractor to reconstruct the URL the signed event must bind to.
// Per-deployment infrastructure value, so it lives in env, not in Conf
// (which is DB-backed and meant for runtime-tunable values shared across
// deployments). Production must set this to the public URL.
let api_base_url =
env::var("BTCMAP_API_BASE_URL").unwrap_or_else(|_| "http://127.0.0.1:8000".to_string());

check_areas_without_icon_square(&main_pool).await;

service::matrix::init(&main_pool);
Expand All @@ -45,6 +53,9 @@ async fn main() -> Result<()> {
.app_data(Data::new(image_pool.clone()))
.app_data(Data::new(log_pool.clone()))
.app_data(Data::new(conf.clone()))
.app_data(Data::new(rest::nostr_auth::ApiBaseUrl(
api_base_url.clone(),
)))
.service(og::element::get_element)
.service(
scope("rpc")
Expand Down Expand Up @@ -188,6 +199,7 @@ async fn main() -> Result<()> {
.service(rest::v4::areas::get_by_id)
.service(rest::v4::areas::get),
)
.service(scope("auth").service(rest::v4::nostr::auth_nostr))
.service(scope("dashboard").service(rest::v4::dashboard::get))
.service(scope("top-editors").service(rest::v4::top_editors::get))
.service(scope("communities").service(rest::v4::communities::get_top))
Expand Down
4 changes: 0 additions & 4 deletions src/rest/nostr_auth.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
// See the note in `src/service/nip98.rs` — this extractor is infrastructure
// ahead of the endpoints that will consume it in a follow-up PR.
#![allow(dead_code)]

use actix_web::web::Data;
use actix_web::{dev::Payload, http::header, FromRequest, HttpRequest};
use std::future::Future;
Expand Down
1 change: 1 addition & 0 deletions src/rest/v4/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod dashboard;
pub mod element_issues;
pub mod events;
pub mod invoices;
pub mod nostr;
pub mod place_boosts;
pub mod place_comments;
pub mod places;
Expand Down
Loading