feat: category crud
This commit is contained in:
124
src/category/handler.rs
Normal file
124
src/category/handler.rs
Normal file
@@ -0,0 +1,124 @@
|
||||
// use std::sync::Arc;
|
||||
use axum::{http::StatusCode, extract::{State,Path}, Json, Router};
|
||||
use axum::routing::{get, post};
|
||||
use diesel::prelude::*;
|
||||
// use diesel::update;
|
||||
use serde::{Deserialize, Serialize};
|
||||
// use serde_json::to_string;
|
||||
use crate::util;
|
||||
use crate::model::schema;
|
||||
use crate::model::db_model;
|
||||
// use crate::model::schema::categories::dsl::categories;
|
||||
use crate::util::req::CommonResp;
|
||||
use chrono::prelude::*;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct CreateCategoryResponse {
|
||||
id: i64,
|
||||
name: String,
|
||||
}
|
||||
|
||||
pub fn get_nest_handlers() -> Router<crate::AppState> {
|
||||
Router::new()
|
||||
.route("/", post(create_category).get(get_all_categories))
|
||||
.route("/:id", post(update_category).get(get_category))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CreateCategoryRequest {
|
||||
name: String,
|
||||
}
|
||||
|
||||
pub async fn create_category(
|
||||
State(app_state): State<crate::AppState>,
|
||||
Json(Payload): Json<CreateCategoryRequest>,
|
||||
) -> Result<Json<db_model::Category>, (StatusCode, String)> {
|
||||
let uid: i64 = 123214124; // TODO replace with actual user id.
|
||||
// let ret = CreateCategoryResponse{id: 134132413541, name: "24532452".to_string()};
|
||||
let conn = app_state.db.get().await.map_err(util::req::internal_error)?;
|
||||
let new_category = db_model::CategoryForm{
|
||||
name: Payload.name,
|
||||
uid: uid,
|
||||
};
|
||||
let res = conn
|
||||
.interact(move |conn| {
|
||||
diesel::insert_into(schema::categories::table)
|
||||
.values(&new_category)
|
||||
.returning(db_model::Category::as_returning())
|
||||
.get_result(conn)
|
||||
})
|
||||
.await
|
||||
.map_err(util::req::internal_error)?
|
||||
.map_err(util::req::internal_error)?;
|
||||
// let ret = CreateCategoryResponse{id: res.id, name: res.name};
|
||||
Ok(Json(res))
|
||||
}
|
||||
|
||||
pub async fn update_category(
|
||||
Path(id): Path<i64>,
|
||||
State(app_state): State<crate::AppState>,
|
||||
Json(Payload): Json<CreateCategoryRequest>,
|
||||
) -> Result<Json<CommonResp>, (StatusCode, String)> {
|
||||
let uid: i64 = 123214124; // TODO replace with actual user id.
|
||||
// let ret = CreateCategoryResponse{id: 134132413541, name: "24532452".to_string()};
|
||||
let conn = app_state.db.get().await.map_err(util::req::internal_error)?;
|
||||
let now = Utc::now().naive_utc();
|
||||
let res = conn
|
||||
.interact(move |conn| {
|
||||
diesel::update(schema::categories::table)
|
||||
.filter(schema::categories::id.eq(id))
|
||||
.filter(schema::categories::uid.eq(uid))
|
||||
.set((
|
||||
schema::categories::name.eq(Payload.name),
|
||||
schema::categories::update_at.eq(now),
|
||||
))
|
||||
.execute(conn)
|
||||
})
|
||||
.await
|
||||
.map_err(util::req::internal_error)?
|
||||
.map_err(util::req::internal_error)?;
|
||||
// let ret = CreateCategoryResponse{id: res.id, name: res.name};
|
||||
let resp = util::req::CommonResp{
|
||||
code: 0,
|
||||
};
|
||||
Ok(Json(resp))
|
||||
}
|
||||
|
||||
pub async fn get_category(
|
||||
Path(id): Path<i64>,
|
||||
State(app_state): State<crate::AppState>,
|
||||
) -> Result<Json<db_model::Category>, (StatusCode, String)>{
|
||||
let uid: i64 = 123214124; // TODO replace with actual user id.
|
||||
let conn = app_state.db.get().await.map_err(util::req::internal_error)?;
|
||||
let res = conn
|
||||
.interact(move |conn| {
|
||||
schema::categories::table
|
||||
.filter(schema::categories::id.eq(id))
|
||||
.filter(schema::categories::uid.eq(uid))
|
||||
.select(db_model::Category::as_select())
|
||||
.limit(1)
|
||||
.get_result(conn)
|
||||
})
|
||||
.await
|
||||
.map_err(util::req::internal_error)?
|
||||
.map_err(util::req::internal_error)?;
|
||||
Ok(Json(res))
|
||||
}
|
||||
|
||||
pub async fn get_all_categories(
|
||||
State(app_state): State<crate::AppState>,
|
||||
) -> Result<Json<Vec<db_model::Category>>, (StatusCode, String)>{
|
||||
let uid: i64 = 123214124; // TODO replace with actual user id.
|
||||
let conn = app_state.db.get().await.map_err(util::req::internal_error)?;
|
||||
let res = conn
|
||||
.interact(move |conn| {
|
||||
schema::categories::table
|
||||
.filter(schema::categories::uid.eq(uid))
|
||||
.select(db_model::Category::as_select())
|
||||
.load(conn)
|
||||
})
|
||||
.await
|
||||
.map_err(util::req::internal_error)?
|
||||
.map_err(util::req::internal_error)?;
|
||||
Ok(Json(res))
|
||||
}
|
||||
1
src/category/mod.rs
Normal file
1
src/category/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod handler;
|
||||
47
src/main.rs
47
src/main.rs
@@ -1,3 +1,46 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
use axum::{
|
||||
// http::StatusCode,
|
||||
// routing::{get, post},
|
||||
// Json,
|
||||
Router,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::info;
|
||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||
|
||||
// Project modules
|
||||
mod category;
|
||||
mod model;
|
||||
mod util;
|
||||
|
||||
// Passed App State
|
||||
#[derive(Clone)]
|
||||
pub struct AppState{
|
||||
db: deadpool_diesel::postgres::Pool,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
dotenvy::dotenv().unwrap();
|
||||
tracing_subscriber::registry().with(tracing_subscriber::fmt::layer()).init();
|
||||
// initialize db connection
|
||||
let db_url = std::env::var("DATABASE_URL").unwrap();
|
||||
|
||||
let manager = deadpool_diesel::postgres::Manager::new(db_url, deadpool_diesel::Runtime::Tokio1);
|
||||
let pool = deadpool_diesel::postgres::Pool::builder(manager)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
|
||||
let shared_state = AppState {db: pool,};
|
||||
|
||||
// Register routers
|
||||
let app = Router::new()
|
||||
// V1 apis
|
||||
.nest("/api/v1/category", category::handler::get_nest_handlers())
|
||||
.with_state(shared_state);
|
||||
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:8987").await.unwrap();
|
||||
info!("starting server on 0.0.0.0:8987");
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
}
|
||||
|
||||
22
src/model/db_model.rs
Normal file
22
src/model/db_model.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use diesel::prelude::*;
|
||||
use crate::model::schema;
|
||||
|
||||
#[derive(Queryable, Selectable)]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[diesel(table_name = schema::categories)]
|
||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||
pub struct Category {
|
||||
id: i64,
|
||||
uid: i64,
|
||||
name: String,
|
||||
is_delete: bool,
|
||||
create_at: chrono::NaiveDateTime,
|
||||
update_at: chrono::NaiveDateTime,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Insertable)]
|
||||
#[diesel(table_name = schema::categories)]
|
||||
pub struct CategoryForm {
|
||||
pub uid: i64,
|
||||
pub name: String,
|
||||
}
|
||||
2
src/model/mod.rs
Normal file
2
src/model/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod schema;
|
||||
pub mod db_model;
|
||||
109
src/model/schema.rs
Normal file
109
src/model/schema.rs
Normal file
@@ -0,0 +1,109 @@
|
||||
// @generated automatically by Diesel CLI.
|
||||
|
||||
diesel::table! {
|
||||
accounts (id) {
|
||||
id -> Int8,
|
||||
uid -> Int8,
|
||||
name -> Text,
|
||||
#[sql_name = "type"]
|
||||
type_ -> Int8,
|
||||
is_delete -> Bool,
|
||||
create_at -> Timestamp,
|
||||
update_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
amounts (id) {
|
||||
id -> Int8,
|
||||
uid -> Int8,
|
||||
transaction_id -> Int8,
|
||||
value -> Int8,
|
||||
expo -> Int8,
|
||||
is_delete -> Bool,
|
||||
create_at -> Timestamp,
|
||||
update_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
books (id) {
|
||||
id -> Int8,
|
||||
uid -> Int8,
|
||||
name -> Text,
|
||||
is_delete -> Bool,
|
||||
create_at -> Timestamp,
|
||||
update_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
categories (id) {
|
||||
id -> Int8,
|
||||
uid -> Int8,
|
||||
name -> Text,
|
||||
is_delete -> Bool,
|
||||
create_at -> Timestamp,
|
||||
update_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
tags (id) {
|
||||
id -> Int8,
|
||||
uid -> Int8,
|
||||
name -> Text,
|
||||
is_delete -> Bool,
|
||||
create_at -> Timestamp,
|
||||
update_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
transaction_tag_rels (id) {
|
||||
id -> Int8,
|
||||
uid -> Int8,
|
||||
transaction_id -> Int8,
|
||||
tag_id -> Int8,
|
||||
is_delete -> Bool,
|
||||
create_at -> Timestamp,
|
||||
update_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
transactions (id) {
|
||||
id -> Int8,
|
||||
uid -> Int8,
|
||||
book_id -> Int8,
|
||||
description -> Text,
|
||||
category_id -> Int8,
|
||||
is_delete -> Bool,
|
||||
time -> Timestamptz,
|
||||
create_at -> Timestamp,
|
||||
update_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
users (id) {
|
||||
id -> Int8,
|
||||
username -> Text,
|
||||
password -> Text,
|
||||
mail -> Text,
|
||||
is_delete -> Bool,
|
||||
create_at -> Timestamp,
|
||||
update_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::allow_tables_to_appear_in_same_query!(
|
||||
accounts,
|
||||
amounts,
|
||||
books,
|
||||
categories,
|
||||
tags,
|
||||
transaction_tag_rels,
|
||||
transactions,
|
||||
users,
|
||||
);
|
||||
109
src/schema.rs
Normal file
109
src/schema.rs
Normal file
@@ -0,0 +1,109 @@
|
||||
// @generated automatically by Diesel CLI.
|
||||
|
||||
diesel::table! {
|
||||
accounts (id) {
|
||||
id -> Int8,
|
||||
uid -> Int8,
|
||||
name -> Text,
|
||||
#[sql_name = "type"]
|
||||
type_ -> Int8,
|
||||
is_delete -> Bool,
|
||||
create_at -> Timestamp,
|
||||
update_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
amounts (id) {
|
||||
id -> Int8,
|
||||
uid -> Int8,
|
||||
transaction_id -> Int8,
|
||||
value -> Int8,
|
||||
expo -> Int8,
|
||||
is_delete -> Bool,
|
||||
create_at -> Timestamp,
|
||||
update_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
books (id) {
|
||||
id -> Int8,
|
||||
uid -> Int8,
|
||||
name -> Text,
|
||||
is_delete -> Bool,
|
||||
create_at -> Timestamp,
|
||||
update_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
categories (id) {
|
||||
id -> Int8,
|
||||
uid -> Int8,
|
||||
name -> Text,
|
||||
is_delete -> Bool,
|
||||
create_at -> Timestamp,
|
||||
update_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
tags (id) {
|
||||
id -> Int8,
|
||||
uid -> Int8,
|
||||
name -> Text,
|
||||
is_delete -> Bool,
|
||||
create_at -> Timestamp,
|
||||
update_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
transaction_tag_rels (id) {
|
||||
id -> Int8,
|
||||
uid -> Int8,
|
||||
transaction_id -> Int8,
|
||||
tag_id -> Int8,
|
||||
is_delete -> Bool,
|
||||
create_at -> Timestamp,
|
||||
update_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
transactions (id) {
|
||||
id -> Int8,
|
||||
uid -> Int8,
|
||||
book_id -> Int8,
|
||||
description -> Text,
|
||||
category_id -> Int8,
|
||||
is_delete -> Bool,
|
||||
time -> Timestamptz,
|
||||
create_at -> Timestamp,
|
||||
update_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
users (id) {
|
||||
id -> Int8,
|
||||
username -> Text,
|
||||
password -> Text,
|
||||
mail -> Text,
|
||||
is_delete -> Bool,
|
||||
create_at -> Timestamp,
|
||||
update_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::allow_tables_to_appear_in_same_query!(
|
||||
accounts,
|
||||
amounts,
|
||||
books,
|
||||
categories,
|
||||
tags,
|
||||
transaction_tag_rels,
|
||||
transactions,
|
||||
users,
|
||||
);
|
||||
1
src/util/mod.rs
Normal file
1
src/util/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod req;
|
||||
14
src/util/req.rs
Normal file
14
src/util/req.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
use axum::http::StatusCode;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct CommonResp {
|
||||
pub code: i64,
|
||||
}
|
||||
|
||||
pub fn internal_error<E>(err: E) -> (StatusCode, String)
|
||||
where
|
||||
E: std::error::Error,
|
||||
{
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
|
||||
}
|
||||
Reference in New Issue
Block a user