161 lines
4.9 KiB
Rust
161 lines
4.9 KiB
Rust
use axum::{
|
|
extract::State,
|
|
response::Json,
|
|
routing::post,
|
|
Router,
|
|
};
|
|
use validator::Validate;
|
|
|
|
use crate::{
|
|
auth::AtprotoAuthService,
|
|
error::{AppError, AppResult},
|
|
models::*,
|
|
AppState,
|
|
};
|
|
|
|
pub fn create_routes() -> Router<AppState> {
|
|
Router::new()
|
|
.route("/login", post(login))
|
|
.route("/verify", post(verify_token))
|
|
}
|
|
|
|
/// Authenticate user with atproto credentials
|
|
async fn login(
|
|
State(state): State<AppState>,
|
|
Json(request): Json<LoginRequest>,
|
|
) -> AppResult<Json<LoginResponse>> {
|
|
// Validate request
|
|
request.validate().map_err(|e| AppError::validation(e.to_string()))?;
|
|
|
|
// Create auth service
|
|
let auth_service = AtprotoAuthService::new(&state.settings.secret_key);
|
|
|
|
// Authenticate user
|
|
let user = auth_service
|
|
.authenticate(&request.identifier, &request.password)
|
|
.await?;
|
|
|
|
// Create access token
|
|
let access_token = auth_service
|
|
.create_access_token(&user, state.settings.access_token_expire_minutes)?;
|
|
|
|
// Create or update user in database
|
|
let _db_user = create_or_update_user(&state, &user.did, &user.handle).await?;
|
|
|
|
Ok(Json(LoginResponse {
|
|
access_token,
|
|
token_type: "Bearer".to_string(),
|
|
expires_in: state.settings.access_token_expire_minutes * 60, // Convert to seconds
|
|
user: UserInfo {
|
|
did: user.did,
|
|
handle: user.handle,
|
|
},
|
|
}))
|
|
}
|
|
|
|
/// Verify JWT token
|
|
async fn verify_token(
|
|
State(state): State<AppState>,
|
|
Json(token): Json<serde_json::Value>,
|
|
) -> AppResult<Json<serde_json::Value>> {
|
|
let token_str = token["token"]
|
|
.as_str()
|
|
.ok_or_else(|| AppError::validation("Token is required"))?;
|
|
|
|
let auth_service = AtprotoAuthService::new(&state.settings.secret_key);
|
|
let claims = auth_service.verify_access_token(token_str)?;
|
|
|
|
Ok(Json(serde_json::json!({
|
|
"valid": true,
|
|
"did": claims.did,
|
|
"handle": claims.handle,
|
|
"exp": claims.exp
|
|
})))
|
|
}
|
|
|
|
/// Create or update user in database
|
|
async fn create_or_update_user(
|
|
state: &AppState,
|
|
did: &str,
|
|
handle: &str,
|
|
) -> AppResult<User> {
|
|
let now = chrono::Utc::now();
|
|
|
|
// Try to get existing user
|
|
let existing_user = match &state.db {
|
|
crate::database::Database::Postgres(pool) => {
|
|
sqlx::query_as::<_, User>("SELECT * FROM users WHERE did = $1")
|
|
.bind(did)
|
|
.fetch_optional(pool)
|
|
.await
|
|
.map_err(AppError::Database)?
|
|
}
|
|
crate::database::Database::Sqlite(pool) => {
|
|
sqlx::query_as::<_, User>("SELECT * FROM users WHERE did = ?")
|
|
.bind(did)
|
|
.fetch_optional(pool)
|
|
.await
|
|
.map_err(AppError::Database)?
|
|
}
|
|
};
|
|
|
|
if let Some(mut user) = existing_user {
|
|
// Update handle if changed
|
|
if user.handle != handle {
|
|
user = match &state.db {
|
|
crate::database::Database::Postgres(pool) => {
|
|
sqlx::query_as::<_, User>(
|
|
"UPDATE users SET handle = $1, updated_at = $2 WHERE did = $3 RETURNING *"
|
|
)
|
|
.bind(handle)
|
|
.bind(now)
|
|
.bind(did)
|
|
.fetch_one(pool)
|
|
.await
|
|
.map_err(AppError::Database)?
|
|
}
|
|
crate::database::Database::Sqlite(pool) => {
|
|
sqlx::query_as::<_, User>(
|
|
"UPDATE users SET handle = ?, updated_at = ? WHERE did = ? RETURNING *"
|
|
)
|
|
.bind(handle)
|
|
.bind(now)
|
|
.bind(did)
|
|
.fetch_one(pool)
|
|
.await
|
|
.map_err(AppError::Database)?
|
|
}
|
|
};
|
|
}
|
|
Ok(user)
|
|
} else {
|
|
// Create new user
|
|
let user = match &state.db {
|
|
crate::database::Database::Postgres(pool) => {
|
|
sqlx::query_as::<_, User>(
|
|
"INSERT INTO users (did, handle, created_at, updated_at) VALUES ($1, $2, $3, $4) RETURNING *"
|
|
)
|
|
.bind(did)
|
|
.bind(handle)
|
|
.bind(now)
|
|
.bind(now)
|
|
.fetch_one(pool)
|
|
.await
|
|
.map_err(AppError::Database)?
|
|
}
|
|
crate::database::Database::Sqlite(pool) => {
|
|
sqlx::query_as::<_, User>(
|
|
"INSERT INTO users (did, handle, created_at, updated_at) VALUES (?, ?, ?, ?) RETURNING *"
|
|
)
|
|
.bind(did)
|
|
.bind(handle)
|
|
.bind(now)
|
|
.bind(now)
|
|
.fetch_one(pool)
|
|
.await
|
|
.map_err(AppError::Database)?
|
|
}
|
|
};
|
|
Ok(user)
|
|
}
|
|
} |