feat: Refactor user and thought models to use UUIDs instead of integers
- Updated user and thought models to utilize UUIDs for primary keys. - Modified persistence functions to accommodate UUIDs for user and thought IDs. - Implemented tag functionality with new Tag and ThoughtTag models. - Added migration scripts to create new tables for tags and thought-tag relationships. - Enhanced thought creation to parse hashtags and link them to thoughts. - Updated tests to reflect changes in user and thought ID types.
This commit is contained in:
@@ -5,13 +5,14 @@ use axum::{
|
||||
|
||||
use jsonwebtoken::{decode, DecodingKey, Validation};
|
||||
use once_cell::sync::Lazy;
|
||||
use sea_orm::prelude::Uuid;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use app::state::AppState;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Claims {
|
||||
pub sub: i32,
|
||||
pub sub: Uuid,
|
||||
pub exp: usize,
|
||||
}
|
||||
|
||||
@@ -19,7 +20,7 @@ static JWT_SECRET: Lazy<String> =
|
||||
Lazy::new(|| std::env::var("AUTH_SECRET").expect("AUTH_SECRET must be set"));
|
||||
|
||||
pub struct AuthUser {
|
||||
pub id: i32,
|
||||
pub id: Uuid,
|
||||
}
|
||||
|
||||
impl FromRequestParts<AppState> for AuthUser {
|
||||
@@ -31,7 +32,7 @@ impl FromRequestParts<AppState> for AuthUser {
|
||||
) -> Result<Self, Self::Rejection> {
|
||||
if let Some(user_id_header) = parts.headers.get("x-test-user-id") {
|
||||
let user_id_str = user_id_header.to_str().unwrap_or("0");
|
||||
let user_id = user_id_str.parse::<i32>().unwrap_or(0);
|
||||
let user_id = user_id_str.parse::<Uuid>().unwrap_or(Uuid::nil());
|
||||
return Ok(AuthUser { id: user_id });
|
||||
}
|
||||
|
||||
|
@@ -3,6 +3,7 @@ use axum::Router;
|
||||
pub mod auth;
|
||||
pub mod feed;
|
||||
pub mod root;
|
||||
pub mod tag;
|
||||
pub mod thought;
|
||||
pub mod user;
|
||||
pub mod well_known;
|
||||
@@ -25,6 +26,7 @@ pub fn create_router(state: AppState) -> Router {
|
||||
.nest("/users", create_user_router())
|
||||
.nest("/thoughts", create_thought_router())
|
||||
.nest("/feed", create_feed_router())
|
||||
.nest("/tags", tag::create_tag_router())
|
||||
.with_state(state)
|
||||
.layer(cors)
|
||||
}
|
||||
|
38
thoughts-backend/api/src/routers/tag.rs
Normal file
38
thoughts-backend/api/src/routers/tag.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use crate::error::ApiError;
|
||||
use app::{persistence::thought::get_thoughts_by_tag_name, state::AppState};
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
response::IntoResponse,
|
||||
routing::get,
|
||||
Json, Router,
|
||||
};
|
||||
use models::schemas::thought::{ThoughtListSchema, ThoughtSchema};
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "{tagName}",
|
||||
params(("tagName" = String, Path, description = "Tag name")),
|
||||
responses((status = 200, description = "List of thoughts with a specific tag", body = ThoughtListSchema))
|
||||
)]
|
||||
async fn get_thoughts_by_tag(
|
||||
State(state): State<AppState>,
|
||||
Path(tag_name): Path<String>,
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
let thoughts_with_authors = get_thoughts_by_tag_name(&state.conn, &tag_name).await;
|
||||
println!(
|
||||
"Result from get_thoughts_by_tag_name: {:?}",
|
||||
thoughts_with_authors
|
||||
);
|
||||
let thoughts_with_authors = thoughts_with_authors?;
|
||||
println!("Thoughts with authors: {:?}", thoughts_with_authors);
|
||||
let thoughts_schema: Vec<ThoughtSchema> = thoughts_with_authors
|
||||
.into_iter()
|
||||
.map(ThoughtSchema::from)
|
||||
.collect();
|
||||
println!("Thoughts schema: {:?}", thoughts_schema);
|
||||
Ok(Json(ThoughtListSchema::from(thoughts_schema)))
|
||||
}
|
||||
|
||||
pub fn create_tag_router() -> Router<AppState> {
|
||||
Router::new().route("/{tag_name}", get(get_thoughts_by_tag))
|
||||
}
|
@@ -12,6 +12,7 @@ use app::{
|
||||
state::AppState,
|
||||
};
|
||||
use models::{params::thought::CreateThoughtParams, schemas::thought::ThoughtSchema};
|
||||
use sea_orm::prelude::Uuid;
|
||||
|
||||
use crate::{
|
||||
error::ApiError,
|
||||
@@ -74,7 +75,7 @@ async fn thoughts_post(
|
||||
async fn thoughts_delete(
|
||||
State(state): State<AppState>,
|
||||
auth_user: AuthUser,
|
||||
Path(id): Path<i32>,
|
||||
Path(id): Path<Uuid>,
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
let thought = get_thought(&state.conn, id)
|
||||
.await?
|
||||
|
@@ -5,6 +5,7 @@ use axum::{
|
||||
routing::{get, post},
|
||||
Router,
|
||||
};
|
||||
use sea_orm::prelude::Uuid;
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use app::persistence::{
|
||||
@@ -201,7 +202,7 @@ async fn get_user_by_param(
|
||||
Path(param): Path<String>,
|
||||
) -> Response {
|
||||
// First, try to handle it as a numeric ID.
|
||||
if let Ok(id) = param.parse::<i32>() {
|
||||
if let Ok(id) = param.parse::<Uuid>() {
|
||||
return match get_user(&state.conn, id).await {
|
||||
Ok(Some(user)) => Json(UserSchema::from(user)).into_response(),
|
||||
Ok(None) => ApiError::from(UserError::NotFound).into_response(),
|
||||
|
Reference in New Issue
Block a user