feat: initialize thoughts-frontend with Next.js, TypeScript, and ESLint
- Add ESLint configuration for Next.js and TypeScript support. - Create Next.js configuration file with standalone output option. - Initialize package.json with scripts for development, build, and linting. - Set up PostCSS configuration for Tailwind CSS. - Add SVG assets for UI components. - Create TypeScript configuration for strict type checking and module resolution.
This commit is contained in:
68
thoughts-backend/api/src/routers/blog.rs
Normal file
68
thoughts-backend/api/src/routers/blog.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use axum::{
|
||||
extract::{Query, State},
|
||||
http::StatusCode,
|
||||
response::IntoResponse,
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use sea_orm::TryIntoModel;
|
||||
|
||||
use app::persistence::blog::{create_blog, search_blogs};
|
||||
use app::state::AppState;
|
||||
use models::params::blog::CreateBlogParams;
|
||||
use models::queries::blog::BlogQuery;
|
||||
use models::schemas::blog::{BlogListSchema, BlogSchema};
|
||||
|
||||
use crate::error::ApiError;
|
||||
use crate::extractor::{Json, Valid};
|
||||
use crate::models::{ApiErrorResponse, ParamsErrorResponse};
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "",
|
||||
request_body = CreateBlogParams,
|
||||
responses(
|
||||
(status = 201, description = "Blog created", body = BlogSchema),
|
||||
(status = 400, description = "Bad request", body = ApiErrorResponse),
|
||||
(status = 422, description = "Validation error", body = ParamsErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
)
|
||||
)]
|
||||
async fn blogs_post(
|
||||
state: State<AppState>,
|
||||
Valid(Json(params)): Valid<Json<CreateBlogParams>>,
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
let blog = create_blog(&state.conn, params)
|
||||
.await
|
||||
.map_err(ApiError::from)?;
|
||||
|
||||
let blog = blog.try_into_model().unwrap();
|
||||
Ok((StatusCode::CREATED, Json(BlogSchema::from(blog))))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "",
|
||||
params(
|
||||
BlogQuery
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "List blogs", body = BlogListSchema),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
)
|
||||
)]
|
||||
async fn blogs_get(
|
||||
state: State<AppState>,
|
||||
query: Query<BlogQuery>,
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
let Query(query) = query;
|
||||
|
||||
let blogs = search_blogs(&state.conn, query)
|
||||
.await
|
||||
.map_err(ApiError::from)?;
|
||||
Ok(Json(BlogListSchema::from(blogs)))
|
||||
}
|
||||
|
||||
pub fn create_blog_router() -> Router<AppState> {
|
||||
Router::new().route("/", get(blogs_get).post(blogs_post))
|
||||
}
|
||||
18
thoughts-backend/api/src/routers/mod.rs
Normal file
18
thoughts-backend/api/src/routers/mod.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use axum::Router;
|
||||
|
||||
pub mod blog;
|
||||
pub mod root;
|
||||
pub mod user;
|
||||
|
||||
use app::state::AppState;
|
||||
use blog::create_blog_router;
|
||||
use root::create_root_router;
|
||||
use user::create_user_router;
|
||||
|
||||
pub fn create_router(state: AppState) -> Router {
|
||||
Router::new()
|
||||
.merge(create_root_router())
|
||||
.nest("/users", create_user_router())
|
||||
.nest("/blogs", create_blog_router())
|
||||
.with_state(state)
|
||||
}
|
||||
30
thoughts-backend/api/src/routers/root.rs
Normal file
30
thoughts-backend/api/src/routers/root.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use axum::{extract::State, routing::get, Router};
|
||||
use sea_orm::{ConnectionTrait, Statement};
|
||||
|
||||
use app::state::AppState;
|
||||
|
||||
use crate::error::ApiError;
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "",
|
||||
responses(
|
||||
(status = 200, description = "Hello world", body = String)
|
||||
)
|
||||
)]
|
||||
async fn root_get(state: State<AppState>) -> Result<String, ApiError> {
|
||||
let result = state
|
||||
.conn
|
||||
.query_one(Statement::from_string(
|
||||
state.conn.get_database_backend(),
|
||||
"SELECT 'Hello, World from DB!'",
|
||||
))
|
||||
.await
|
||||
.map_err(ApiError::from)?;
|
||||
|
||||
result.unwrap().try_get_by(0).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
pub fn create_root_router() -> Router<AppState> {
|
||||
Router::new().route("/", get(root_get))
|
||||
}
|
||||
93
thoughts-backend/api/src/routers/user.rs
Normal file
93
thoughts-backend/api/src/routers/user.rs
Normal file
@@ -0,0 +1,93 @@
|
||||
use axum::{
|
||||
extract::{Path, Query, State},
|
||||
http::StatusCode,
|
||||
response::IntoResponse,
|
||||
routing::{get, post},
|
||||
Router,
|
||||
};
|
||||
use sea_orm::TryIntoModel;
|
||||
|
||||
use app::error::UserError;
|
||||
use app::persistence::user::{create_user, get_user, search_users};
|
||||
use app::state::AppState;
|
||||
use models::params::user::CreateUserParams;
|
||||
use models::queries::user::UserQuery;
|
||||
use models::schemas::user::{UserListSchema, UserSchema};
|
||||
|
||||
use crate::error::ApiError;
|
||||
use crate::extractor::{Json, Valid};
|
||||
use crate::models::{ApiErrorResponse, ParamsErrorResponse};
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "",
|
||||
request_body = CreateUserParams,
|
||||
responses(
|
||||
(status = 201, description = "User created", body = UserSchema),
|
||||
(status = 400, description = "Bad request", body = ApiErrorResponse),
|
||||
(status = 422, description = "Validation error", body = ParamsErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
)
|
||||
)]
|
||||
async fn users_post(
|
||||
state: State<AppState>,
|
||||
Valid(Json(params)): Valid<Json<CreateUserParams>>,
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
let user = create_user(&state.conn, params)
|
||||
.await
|
||||
.map_err(ApiError::from)?;
|
||||
|
||||
let user = user.try_into_model().unwrap();
|
||||
Ok((StatusCode::CREATED, Json(UserSchema::from(user))))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "",
|
||||
params(
|
||||
UserQuery
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "List users", body = UserListSchema),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
)
|
||||
)]
|
||||
async fn users_get(
|
||||
state: State<AppState>,
|
||||
query: Query<UserQuery>,
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
let Query(query) = query;
|
||||
|
||||
let users = search_users(&state.conn, query)
|
||||
.await
|
||||
.map_err(ApiError::from)?;
|
||||
Ok(Json(UserListSchema::from(users)))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/{id}",
|
||||
params(
|
||||
("id" = i32, Path, description = "User id")
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Get user", body = UserSchema),
|
||||
(status = 404, description = "Not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
)
|
||||
)]
|
||||
async fn users_id_get(
|
||||
state: State<AppState>,
|
||||
Path(id): Path<i32>,
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
let user = get_user(&state.conn, id).await.map_err(ApiError::from)?;
|
||||
|
||||
user.map(|user| Json(UserSchema::from(user)))
|
||||
.ok_or_else(|| UserError::NotFound.into())
|
||||
}
|
||||
|
||||
pub fn create_user_router() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route("/", post(users_post).get(users_get))
|
||||
.route("/{id}", get(users_id_get))
|
||||
}
|
||||
Reference in New Issue
Block a user