feat: Add ALLOW_REGISTRATION configuration to enable/disable user registration and introduce a Forbidden API error type.
This commit is contained in:
10
README.md
10
README.md
@@ -60,8 +60,18 @@ The frontend is automatically configured to talk to the backend.
|
|||||||
cargo run -p notes-api
|
cargo run -p notes-api
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
By default, this uses the **SQLite** backend.
|
By default, this uses the **SQLite** backend.
|
||||||
|
|
||||||
|
#### Configuration
|
||||||
|
|
||||||
|
The application is configured via environment variables (or `.env` file):
|
||||||
|
|
||||||
|
- `ALLOW_REGISTRATION`: Set to `false` to disable new user registration (default: `true`).
|
||||||
|
- `DATABASE_URL`: Connection string for the database.
|
||||||
|
- `SESSION_SECRET`: Secret key for session encryption.
|
||||||
|
- `CORS_ALLOWED_ORIGINS`: Comma-separated list of allowed origins.
|
||||||
|
|
||||||
**Running with Postgres:**
|
**Running with Postgres:**
|
||||||
|
|
||||||
To use PostgreSQL, build with the `postgres` feature:
|
To use PostgreSQL, build with the `postgres` feature:
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ services:
|
|||||||
- CORS_ALLOWED_ORIGINS=http://localhost:8080,http://localhost:5173
|
- CORS_ALLOWED_ORIGINS=http://localhost:8080,http://localhost:5173
|
||||||
- HOST=0.0.0.0
|
- HOST=0.0.0.0
|
||||||
- PORT=3000
|
- PORT=3000
|
||||||
|
- ALLOW_REGISTRATION=true
|
||||||
volumes:
|
volumes:
|
||||||
- ./data:/app/data
|
- ./data:/app/data
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ pub struct Config {
|
|||||||
pub database_url: String,
|
pub database_url: String,
|
||||||
pub session_secret: String,
|
pub session_secret: String,
|
||||||
pub cors_allowed_origins: Vec<String>,
|
pub cors_allowed_origins: Vec<String>,
|
||||||
|
pub allow_registration: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
@@ -19,6 +20,7 @@ impl Default for Config {
|
|||||||
session_secret: "k-notes-super-secret-key-must-be-at-least-64-bytes-long!!!!"
|
session_secret: "k-notes-super-secret-key-must-be-at-least-64-bytes-long!!!!"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
cors_allowed_origins: vec!["http://localhost:5173".to_string()],
|
cors_allowed_origins: vec!["http://localhost:5173".to_string()],
|
||||||
|
allow_registration: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,12 +52,17 @@ impl Config {
|
|||||||
.filter(|s| !s.is_empty())
|
.filter(|s| !s.is_empty())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let allow_registration = env::var("ALLOW_REGISTRATION")
|
||||||
|
.map(|s| s.to_lowercase() == "true")
|
||||||
|
.unwrap_or(true);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
host,
|
host,
|
||||||
port,
|
port,
|
||||||
database_url,
|
database_url,
|
||||||
session_secret,
|
session_secret,
|
||||||
cors_allowed_origins,
|
cors_allowed_origins,
|
||||||
|
allow_registration,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ pub enum ApiError {
|
|||||||
|
|
||||||
#[error("Internal server error")]
|
#[error("Internal server error")]
|
||||||
Internal(String),
|
Internal(String),
|
||||||
|
|
||||||
|
#[error("Forbidden: {0}")]
|
||||||
|
Forbidden(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error response body
|
/// Error response body
|
||||||
@@ -83,6 +86,14 @@ impl IntoResponse for ApiError {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ApiError::Forbidden(msg) => (
|
||||||
|
StatusCode::FORBIDDEN,
|
||||||
|
ErrorResponse {
|
||||||
|
error: "Forbidden".to_string(),
|
||||||
|
details: Some(msg.clone()),
|
||||||
|
},
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
(status, Json(error_response)).into_response()
|
(status, Json(error_response)).into_response()
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
note_service,
|
note_service,
|
||||||
tag_service,
|
tag_service,
|
||||||
user_service,
|
user_service,
|
||||||
|
config.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Auth backend
|
// Auth backend
|
||||||
|
|||||||
@@ -22,6 +22,11 @@ pub async fn register(
|
|||||||
.validate()
|
.validate()
|
||||||
.map_err(|e| ApiError::validation(e.to_string()))?;
|
.map_err(|e| ApiError::validation(e.to_string()))?;
|
||||||
|
|
||||||
|
// Check if registration is allowed
|
||||||
|
if !state.config.allow_registration {
|
||||||
|
return Err(ApiError::Forbidden("Registration is disabled".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
// Check if user exists
|
// Check if user exists
|
||||||
if state
|
if state
|
||||||
.user_repo
|
.user_repo
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::config::Config;
|
||||||
use notes_domain::{
|
use notes_domain::{
|
||||||
NoteRepository, NoteService, TagRepository, TagService, UserRepository, UserService,
|
NoteRepository, NoteService, TagRepository, TagService, UserRepository, UserService,
|
||||||
};
|
};
|
||||||
@@ -13,6 +14,7 @@ pub struct AppState {
|
|||||||
pub note_service: Arc<NoteService>,
|
pub note_service: Arc<NoteService>,
|
||||||
pub tag_service: Arc<TagService>,
|
pub tag_service: Arc<TagService>,
|
||||||
pub user_service: Arc<UserService>,
|
pub user_service: Arc<UserService>,
|
||||||
|
pub config: Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppState {
|
impl AppState {
|
||||||
@@ -23,6 +25,7 @@ impl AppState {
|
|||||||
note_service: Arc<NoteService>,
|
note_service: Arc<NoteService>,
|
||||||
tag_service: Arc<TagService>,
|
tag_service: Arc<TagService>,
|
||||||
user_service: Arc<UserService>,
|
user_service: Arc<UserService>,
|
||||||
|
config: Config,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
note_repo,
|
note_repo,
|
||||||
@@ -31,6 +34,7 @@ impl AppState {
|
|||||||
note_service,
|
note_service,
|
||||||
tag_service,
|
tag_service,
|
||||||
user_service,
|
user_service,
|
||||||
|
config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user