diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..a56f5fe --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +POSTGRES_USER=thoughts_user +POSTGRES_PASSWORD=postgres +POSTGRES_DB=thoughts_db \ No newline at end of file diff --git a/.gitignore b/.gitignore index df25325..392d20e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ backend-codebase.txt -frontend-codebase.txt \ No newline at end of file +frontend-codebase.txt +.env \ No newline at end of file diff --git a/thoughts-backend/.env.example b/thoughts-backend/.env.example index 5109a05..1ac0b90 100644 --- a/thoughts-backend/.env.example +++ b/thoughts-backend/.env.example @@ -3,3 +3,5 @@ PORT=3000 DATABASE_URL="sqlite://dev.db" # DATABASE_URL="postgresql://postgres:postgres@localhost/clean-axum" PREFORK=1 +AUTH_SECRET=your_secret_key_here +BASE_URL=http://localhost:3000 \ No newline at end of file diff --git a/thoughts-backend/.gitignore b/thoughts-backend/.gitignore index ea8c4bf..0b745e2 100644 --- a/thoughts-backend/.gitignore +++ b/thoughts-backend/.gitignore @@ -1 +1,2 @@ /target +.env \ No newline at end of file diff --git a/thoughts-backend/api/src/federation.rs b/thoughts-backend/api/src/federation.rs index a85a4ce..120f2c2 100644 --- a/thoughts-backend/api/src/federation.rs +++ b/thoughts-backend/api/src/federation.rs @@ -25,9 +25,8 @@ pub async fn federate_thought( return; } - let base_url = "http://localhost:3000"; // Replace in production - let thought_url = format!("{}/thoughts/{}", base_url, thought.id); - let author_url = format!("{}/users/{}", base_url, author.username); + let thought_url = format!("{}/thoughts/{}", &state.base_url, thought.id); + let author_url = format!("{}/users/{}", &state.base_url, author.username); // Construct the "Create" activity containing the "Note" object let activity = json!({ @@ -59,7 +58,7 @@ pub async fn federate_thought( let client = reqwest::Client::new(); for follower in followers { - let inbox_url = format!("{}/users/{}/inbox", base_url, follower.username); + let inbox_url = format!("{}/users/{}/inbox", &state.base_url, follower.username); tracing::info!("Federating post {} to {}", thought.id, inbox_url); let res = client.post(&inbox_url).json(&activity).send().await; diff --git a/thoughts-backend/api/src/init.rs b/thoughts-backend/api/src/init.rs index d3d500c..4b0184d 100644 --- a/thoughts-backend/api/src/init.rs +++ b/thoughts-backend/api/src/init.rs @@ -9,8 +9,11 @@ use app::state::AppState; use crate::routers::create_router; // TODO: middleware, logging, authentication -pub fn setup_router(conn: DatabaseConnection) -> Router { - create_router(AppState { conn }) +pub fn setup_router(conn: DatabaseConnection, config: &Config) -> Router { + create_router(AppState { + conn, + base_url: config.base_url.clone(), + }) } pub fn setup_config() -> Config { diff --git a/thoughts-backend/api/src/routers/user.rs b/thoughts-backend/api/src/routers/user.rs index 431d502..e8dee54 100644 --- a/thoughts-backend/api/src/routers/user.rs +++ b/thoughts-backend/api/src/routers/user.rs @@ -220,8 +220,7 @@ async fn get_user_by_param( // This is the logic from `user_actor_get`. match get_user_by_username(&state.conn, &username).await { Ok(Some(user)) => { - let base_url = "http://localhost:3000"; - let user_url = format!("{}/users/{}", base_url, user.username); + let user_url = format!("{}/users/{}", &state.base_url, user.username); let actor = json!({ "@context": [ "https://www.w3.org/ns/activitystreams", @@ -272,13 +271,12 @@ async fn user_outbox_get( let thoughts = get_thoughts_by_user(&state.conn, user.id).await?; // Format the outbox as an ActivityPub OrderedCollection - let base_url = "http://localhost:3000"; - let outbox_url = format!("{}/users/{}/outbox", base_url, username); + let outbox_url = format!("{}/users/{}/outbox", &state.base_url, username); let items: Vec = thoughts .into_iter() .map(|thought| { - let thought_url = format!("{}/thoughts/{}", base_url, thought.id); - let author_url = format!("{}/users/{}", base_url, thought.author_username); + let thought_url = format!("{}/thoughts/{}", &state.base_url, thought.id); + let author_url = format!("{}/users/{}", &state.base_url, thought.author_username); json!({ "id": format!("{}/activity", thought_url), "type": "Create", diff --git a/thoughts-backend/api/src/routers/well_known.rs b/thoughts-backend/api/src/routers/well_known.rs index 65d8dc1..c92adb7 100644 --- a/thoughts-backend/api/src/routers/well_known.rs +++ b/thoughts-backend/api/src/routers/well_known.rs @@ -45,8 +45,7 @@ pub async fn webfinger( _ => return Err((axum::http::StatusCode::NOT_FOUND, "User not found")), }; - let base_url = "http://localhost:3000"; - let user_url = Url::parse(&format!("{}/users/{}", base_url, user.username)).unwrap(); + let user_url = Url::parse(&format!("{}/users/{}", &state.base_url, user.username)).unwrap(); let response = WebFingerResponse { subject: query.resource, diff --git a/thoughts-backend/app/src/config.rs b/thoughts-backend/app/src/config.rs index 25d3c6a..102edea 100644 --- a/thoughts-backend/app/src/config.rs +++ b/thoughts-backend/app/src/config.rs @@ -4,6 +4,7 @@ pub struct Config { pub port: u32, pub prefork: bool, pub auth_secret: String, + pub base_url: String, } impl Config { @@ -16,7 +17,8 @@ impl Config { .parse() .expect("PORT is not a number"), prefork: std::env::var("PREFORK").is_ok_and(|v| v == "1"), - auth_secret: std::env::var("AUTH_SECRET").unwrap_or_else(|_| "secret".into()), + auth_secret: std::env::var("AUTH_SECRET").expect("AUTH_SECRET is not set in .env file"), + base_url: std::env::var("BASE_URL").expect("BASE_URL is not set in .env file"), } } diff --git a/thoughts-backend/app/src/state.rs b/thoughts-backend/app/src/state.rs index db67554..26af328 100644 --- a/thoughts-backend/app/src/state.rs +++ b/thoughts-backend/app/src/state.rs @@ -3,4 +3,5 @@ use sea_orm::DatabaseConnection; #[derive(Clone)] pub struct AppState { pub conn: DatabaseConnection, + pub base_url: String, } diff --git a/thoughts-backend/src/tokio.rs b/thoughts-backend/src/tokio.rs index e838704..e9376e9 100644 --- a/thoughts-backend/src/tokio.rs +++ b/thoughts-backend/src/tokio.rs @@ -11,7 +11,9 @@ async fn worker(child_num: u32, db_url: &str, prefork: bool, listener: std::net: migrate(&conn).await.expect("Migration failed!"); } - let router = setup_router(conn).attach_doc(); + let config = setup_config(); + + let router = setup_router(conn, &config).attach_doc(); let listener = tokio::net::TcpListener::from_std(listener).expect("bind to port"); axum::serve(listener, router).await.expect("start server"); diff --git a/thoughts-backend/tests/api/main.rs b/thoughts-backend/tests/api/main.rs index 86d5fc7..008b25c 100644 --- a/thoughts-backend/tests/api/main.rs +++ b/thoughts-backend/tests/api/main.rs @@ -13,10 +13,17 @@ pub struct TestApp { } pub async fn setup() -> TestApp { + std::env::set_var("DATABASE_URL", "sqlite::memory:"); + std::env::set_var("AUTH_SECRET", "test_secret"); + std::env::set_var("BASE_URL", "http://localhost:3000"); + std::env::set_var("HOST", "localhost"); + std::env::set_var("PORT", "3000"); + std::env::set_var("LOG_LEVEL", "debug"); + let db = setup_test_db("sqlite::memory:") .await .expect("Failed to set up test db"); - let router = setup_router(db.clone()); + let router = setup_router(db.clone(), &app::config::Config::from_env()); TestApp { router, db } }