Compare commits
3 Commits
dev/v1-s
...
952a37892d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
952a37892d | ||
|
|
7270399f35 | ||
|
|
bddf92686c |
8
.editorconfig
Normal file
8
.editorconfig
Normal file
@@ -0,0 +1,8 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -2,4 +2,4 @@
|
||||
.idea
|
||||
.vscode
|
||||
.DS_Store
|
||||
|
||||
.env
|
||||
|
||||
1654
Cargo.lock
generated
Normal file
1654
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
17
Cargo.toml
17
Cargo.toml
@@ -6,3 +6,20 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
async-trait = "0.1.81"
|
||||
axum = {version = "0.7.5", features = ["macros"]}
|
||||
axum-extra = { version = "0.9.3", features = ["typed-header"] }
|
||||
chrono = {version = "0.4", features = ["serde"]}
|
||||
deadpool-diesel = {version ="0.6.1", features = ["postgres"]}
|
||||
diesel = { version = "2", features = ["postgres", "chrono"] }
|
||||
dotenvy = "0.15"
|
||||
jsonwebtoken = "9.3.0"
|
||||
serde = { version = "1.0.202", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
tokio = { version = "1.37.0", features = ["full"] }
|
||||
tower = "0.4.13"
|
||||
tower-http = {version= "0.5.2", features=["trace", "cors"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
once_cell = "1.19.0"
|
||||
axum-macros = "0.4.1"
|
||||
|
||||
2
Makefile
Normal file
2
Makefile
Normal file
@@ -0,0 +1,2 @@
|
||||
build-schema:
|
||||
diesel print-schema > src/model/schema.rs
|
||||
9
diesel.toml
Normal file
9
diesel.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
# For documentation on how to configure this file,
|
||||
# see https://diesel.rs/guides/configuring-diesel-cli
|
||||
|
||||
[print_schema]
|
||||
file = "src/schema.rs"
|
||||
custom_type_derives = ["diesel::query_builder::QueryId", "Clone"]
|
||||
|
||||
[migrations_directory]
|
||||
dir = "/data/codes/helios-server-rs/migrations"
|
||||
0
migrations/.keep
Normal file
0
migrations/.keep
Normal file
6
migrations/00000000000000_diesel_initial_setup/down.sql
Normal file
6
migrations/00000000000000_diesel_initial_setup/down.sql
Normal file
@@ -0,0 +1,6 @@
|
||||
-- This file was automatically created by Diesel to setup helper functions
|
||||
-- and other internal bookkeeping. This file is safe to edit, any future
|
||||
-- changes will be added to existing projects as new migrations.
|
||||
|
||||
DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass);
|
||||
DROP FUNCTION IF EXISTS diesel_set_updated_at();
|
||||
36
migrations/00000000000000_diesel_initial_setup/up.sql
Normal file
36
migrations/00000000000000_diesel_initial_setup/up.sql
Normal file
@@ -0,0 +1,36 @@
|
||||
-- This file was automatically created by Diesel to setup helper functions
|
||||
-- and other internal bookkeeping. This file is safe to edit, any future
|
||||
-- changes will be added to existing projects as new migrations.
|
||||
|
||||
|
||||
|
||||
|
||||
-- Sets up a trigger for the given table to automatically set a column called
|
||||
-- `updated_at` whenever the row is modified (unless `updated_at` was included
|
||||
-- in the modified columns)
|
||||
--
|
||||
-- # Example
|
||||
--
|
||||
-- ```sql
|
||||
-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW());
|
||||
--
|
||||
-- SELECT diesel_manage_updated_at('users');
|
||||
-- ```
|
||||
CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$
|
||||
BEGIN
|
||||
EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s
|
||||
FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$
|
||||
BEGIN
|
||||
IF (
|
||||
NEW IS DISTINCT FROM OLD AND
|
||||
NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at
|
||||
) THEN
|
||||
NEW.updated_at := current_timestamp;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
11
migrations/2024-07-07-151037_base_schema/down.sql
Normal file
11
migrations/2024-07-07-151037_base_schema/down.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
-- This file should undo anything in `up.sql`
|
||||
-- This file should undo anything in `up.sql`
|
||||
DROP TABLE IF EXISTS "categories";
|
||||
DROP TABLE IF EXISTS "tags";
|
||||
DROP TABLE IF EXISTS "books";
|
||||
DROP TABLE IF EXISTS "transactions";
|
||||
DROP TABLE IF EXISTS "transaction_tag_rels";
|
||||
DROP TABLE IF EXISTS "accounts";
|
||||
DROP TABLE IF EXISTS "amounts";
|
||||
|
||||
DROP TABLE IF EXISTS "users";
|
||||
81
migrations/2024-07-07-151037_base_schema/up.sql
Normal file
81
migrations/2024-07-07-151037_base_schema/up.sql
Normal file
@@ -0,0 +1,81 @@
|
||||
-- Your SQL goes here
|
||||
-- Your SQL goes here
|
||||
CREATE TABLE "categories" (
|
||||
"id" BIGSERIAL PRIMARY KEY,
|
||||
"uid" BIGINT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"is_delete" BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
"create_at" TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
"update_at" TIMESTAMP NOT NULL DEFAULT current_timestamp
|
||||
);
|
||||
|
||||
CREATE TABLE "tags" (
|
||||
"id" BIGSERIAL PRIMARY KEY,
|
||||
"uid" BIGINT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"is_delete" BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
"create_at" TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
"update_at" TIMESTAMP NOT NULL DEFAULT current_timestamp
|
||||
);
|
||||
|
||||
CREATE TABLE "books" (
|
||||
"id" BIGSERIAL PRIMARY KEY,
|
||||
"uid" BIGINT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"is_delete" BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
"create_at" TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
"update_at" TIMESTAMP NOT NULL DEFAULT current_timestamp
|
||||
);
|
||||
|
||||
CREATE TABLE "transactions" (
|
||||
"id" BIGSERIAL PRIMARY KEY,
|
||||
"uid" BIGINT NOT NULL,
|
||||
"book_id" BIGINT NOT NULL,
|
||||
"description" TEXT NOT NULL,
|
||||
"category_id" BIGINT NOT NULL,
|
||||
"is_delete" BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
"time" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT current_timestamp,
|
||||
"create_at" TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
"update_at" TIMESTAMP NOT NULL DEFAULT current_timestamp
|
||||
);
|
||||
|
||||
CREATE TABLE "transaction_tag_rels" (
|
||||
"id" BIGSERIAL PRIMARY KEY,
|
||||
"uid" BIGINT NOT NULL,
|
||||
"transaction_id" BIGINT NOT NULL,
|
||||
"tag_id" BIGINT NOT NULL,
|
||||
"is_delete" BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
"create_at" TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
"update_at" TIMESTAMP NOT NULL DEFAULT current_timestamp
|
||||
);
|
||||
|
||||
CREATE TABLE "accounts" (
|
||||
"id" BIGSERIAL PRIMARY KEY,
|
||||
"uid" BIGINT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"type" BIGINT NOT NULL DEFAULT 0,
|
||||
"is_delete" BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
"create_at" TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
"update_at" TIMESTAMP NOT NULL DEFAULT current_timestamp
|
||||
);
|
||||
|
||||
CREATE TABLE "amounts" (
|
||||
"id" BIGSERIAL PRIMARY KEY,
|
||||
"uid" BIGINT NOT NULL,
|
||||
"transaction_id" BIGINT NOT NULL,
|
||||
"value" BIGINT NOT NULL DEFAULT 0,
|
||||
"expo" BIGINT NOT NULL DEFAULT 5,
|
||||
"is_delete" BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
"create_at" TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
"update_at" TIMESTAMP NOT NULL DEFAULT current_timestamp
|
||||
);
|
||||
|
||||
CREATE TABLE "users" (
|
||||
"id" BIGSERIAL PRIMARY KEY,
|
||||
"username" TEXT NOT NULL,
|
||||
"password" TEXT NOT NULL,
|
||||
"mail" TEXT NOT NULL,
|
||||
"is_delete" BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
"create_at" TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
"update_at" TIMESTAMP NOT NULL DEFAULT current_timestamp
|
||||
);
|
||||
151
src/category/handler.rs
Normal file
151
src/category/handler.rs
Normal file
@@ -0,0 +1,151 @@
|
||||
// use std::sync::Arc;
|
||||
use axum::routing::{get, post};
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
http::StatusCode,
|
||||
Json, Router,
|
||||
};
|
||||
use axum_macros::debug_handler;
|
||||
use diesel::prelude::*;
|
||||
// use diesel::update;
|
||||
use serde::{Deserialize, Serialize};
|
||||
// use serde_json::to_string;
|
||||
use crate::model::db_model;
|
||||
use crate::model::schema;
|
||||
use crate::util;
|
||||
// use crate::model::schema::categories::dsl::categories;
|
||||
use crate::util::req::CommonResp;
|
||||
use chrono::prelude::*;
|
||||
use tracing::info;
|
||||
use crate::middleware::auth;
|
||||
use crate::middleware::auth::Claims;
|
||||
|
||||
#[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,
|
||||
}
|
||||
|
||||
#[debug_handler]
|
||||
pub async fn create_category(
|
||||
State(app_state): State<crate::AppState>,
|
||||
claims: Claims,
|
||||
Json(payload): Json<CreateCategoryRequest>,
|
||||
) -> Result<Json<db_model::Category>, (StatusCode, String)> {
|
||||
let uid: i64 = claims.uid.clone(); // 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,
|
||||
};
|
||||
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>,
|
||||
claims: Claims,
|
||||
Json(payload): Json<CreateCategoryRequest>,
|
||||
) -> Result<Json<CommonResp>, (StatusCode, String)> {
|
||||
let uid: i64 = claims.uid.clone(); // 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>,
|
||||
claims: Claims,
|
||||
) -> Result<Json<db_model::Category>, (StatusCode, String)> {
|
||||
let uid: i64 = claims.uid.clone();
|
||||
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>,
|
||||
claims: Claims,
|
||||
) -> Result<Json<Vec<db_model::Category>>, (StatusCode, String)> {
|
||||
let uid: i64 = claims.uid.clone();
|
||||
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;
|
||||
62
src/main.rs
62
src/main.rs
@@ -1,3 +1,61 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
use axum::{
|
||||
// http::StatusCode,
|
||||
// routing::{get, post},
|
||||
// Json,
|
||||
Router,
|
||||
};
|
||||
use axum::http::Method;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tower::ServiceBuilder;
|
||||
use tower_http::cors::{Any, CorsLayer};
|
||||
use tower_http::trace::TraceLayer;
|
||||
use tracing::info;
|
||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||
|
||||
// Project modules
|
||||
mod category;
|
||||
mod middleware;
|
||||
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 cors_layer = CorsLayer::new()
|
||||
.allow_methods([Method::GET, Method::POST])
|
||||
.allow_origin(Any);
|
||||
let global_layer = ServiceBuilder::new()
|
||||
.layer(TraceLayer::new_for_http())
|
||||
.layer(cors_layer);
|
||||
|
||||
let app = Router::new()
|
||||
// V1 apis
|
||||
.nest("/api/v1/category", category::handler::get_nest_handlers())
|
||||
.nest("/api/v1/v2", category::handler::get_nest_handlers())
|
||||
.with_state(shared_state)
|
||||
.layer(global_layer);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
118
src/middleware/auth.rs
Normal file
118
src/middleware/auth.rs
Normal file
@@ -0,0 +1,118 @@
|
||||
use axum::{
|
||||
async_trait,
|
||||
extract::FromRequestParts,
|
||||
http::{
|
||||
request::Parts,
|
||||
StatusCode,
|
||||
},
|
||||
Json, RequestPartsExt,
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use axum_extra::{
|
||||
headers::{authorization::Bearer, Authorization},
|
||||
TypedHeader,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use jsonwebtoken::{decode,encode, DecodingKey, EncodingKey, Header, Validation};
|
||||
use std::fmt::Display;
|
||||
use once_cell::sync::Lazy;
|
||||
use crate::util;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Claims {
|
||||
sub: String,
|
||||
company: String,
|
||||
exp: usize,
|
||||
pub uid: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct AuthBody {
|
||||
access_token: String,
|
||||
token_type: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct AuthPayload {
|
||||
client_id: String,
|
||||
client_secret: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AuthError {
|
||||
WrongCredentials,
|
||||
MissingCredentials,
|
||||
TokenCreation,
|
||||
InvalidToken,
|
||||
}
|
||||
|
||||
static KEYS: Lazy<Keys> = Lazy::new(|| {
|
||||
let secret = std::env::var("JWT_SECRET").expect("JWT_SECRET must be set");
|
||||
Keys::new(secret.as_bytes())
|
||||
});
|
||||
|
||||
struct Keys {
|
||||
encoding: EncodingKey,
|
||||
decoding: DecodingKey,
|
||||
}
|
||||
|
||||
impl Keys {
|
||||
fn new(secret: &[u8]) -> Self {
|
||||
Self {
|
||||
encoding: EncodingKey::from_secret(secret),
|
||||
decoding: DecodingKey::from_secret(secret),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Claims {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Email: {}\nCompany: {}", self.sub, self.company)
|
||||
}
|
||||
}
|
||||
|
||||
impl AuthBody {
|
||||
fn new(access_token: String) -> Self {
|
||||
Self {
|
||||
access_token,
|
||||
token_type: "Bearer".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<S> FromRequestParts<S> for Claims
|
||||
where
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = (StatusCode, String);
|
||||
|
||||
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
|
||||
// Extract the token from the authorization header
|
||||
let TypedHeader(Authorization(bearer)) = parts
|
||||
.extract::<TypedHeader<Authorization<Bearer>>>()
|
||||
.await
|
||||
.map_err(util::req::internal_error)?;
|
||||
// Decode the user data
|
||||
let token_data = decode::<Claims>(bearer.token(), &KEYS.decoding, &Validation::default())
|
||||
.map_err(util::req::internal_error)?;
|
||||
|
||||
Ok(token_data.claims)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoResponse for AuthError {
|
||||
fn into_response(self) -> Response {
|
||||
let (status, error_message) = match self {
|
||||
AuthError::WrongCredentials => (StatusCode::UNAUTHORIZED, "Wrong credentials"),
|
||||
AuthError::MissingCredentials => (StatusCode::BAD_REQUEST, "Missing credentials"),
|
||||
AuthError::TokenCreation => (StatusCode::INTERNAL_SERVER_ERROR, "Token creation error"),
|
||||
AuthError::InvalidToken => (StatusCode::BAD_REQUEST, "Invalid token"),
|
||||
};
|
||||
let body = Json(json!({
|
||||
"error": error_message,
|
||||
}));
|
||||
(status, body).into_response()
|
||||
}
|
||||
}
|
||||
1
src/middleware/mod.rs
Normal file
1
src/middleware/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod auth;
|
||||
21
src/model/db_model.rs
Normal file
21
src/model/db_model.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
use crate::model::schema;
|
||||
use diesel::prelude::*;
|
||||
|
||||
#[derive(Queryable, Selectable, 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 db_model;
|
||||
pub mod schema;
|
||||
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