2024-01-21 19:53:30 +00:00
|
|
|
mod api;
|
|
|
|
mod config;
|
2024-02-02 02:34:38 +00:00
|
|
|
mod model;
|
2024-01-21 19:53:30 +00:00
|
|
|
mod state;
|
2024-02-02 02:34:38 +00:00
|
|
|
mod storage_manager;
|
2024-01-21 19:53:30 +00:00
|
|
|
|
2024-02-02 02:34:38 +00:00
|
|
|
use crate::config::PicOxConfig;
|
|
|
|
use crate::model::album::Album;
|
|
|
|
use crate::model::image::ImageData;
|
|
|
|
use crate::state::Context;
|
|
|
|
use crate::storage_manager::StorageManager;
|
|
|
|
use axum::extract::{Multipart, Path, Query, State};
|
2024-01-21 19:53:30 +00:00
|
|
|
use axum::http::StatusCode;
|
|
|
|
use axum::response::IntoResponse;
|
|
|
|
use axum::routing::{get, post};
|
2024-02-02 02:34:38 +00:00
|
|
|
use axum::{Json, Router};
|
2024-01-21 19:53:30 +00:00
|
|
|
use j_db::database::Database;
|
2024-02-02 02:34:38 +00:00
|
|
|
use j_db::model::JdbModel;
|
|
|
|
use log::info;
|
2024-01-21 19:53:30 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
2024-02-02 02:34:38 +00:00
|
|
|
use std::path::PathBuf;
|
|
|
|
use std::sync::Arc;
|
2024-01-21 19:53:30 +00:00
|
|
|
use structopt::StructOpt;
|
2024-02-02 02:34:38 +00:00
|
|
|
use tokio::sync::RwLock;
|
|
|
|
|
|
|
|
type PicContext = Arc<Context>;
|
2024-01-21 19:53:30 +00:00
|
|
|
|
|
|
|
#[derive(Debug, Clone, StructOpt)]
|
|
|
|
struct Args {
|
2024-02-02 02:34:38 +00:00
|
|
|
pub config: PathBuf,
|
2024-01-21 19:53:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
struct CreateAlbum {
|
2024-02-02 02:34:38 +00:00
|
|
|
pub album_name: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
struct AlbumQuery {
|
|
|
|
pub album_name: Option<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
struct AddImage {
|
|
|
|
pub album: AlbumQuery,
|
|
|
|
pub image_name: String,
|
2024-01-21 19:53:30 +00:00
|
|
|
}
|
|
|
|
|
2024-02-02 02:34:38 +00:00
|
|
|
async fn create_album(
|
|
|
|
State(context): State<PicContext>,
|
|
|
|
Json(album): Json<CreateAlbum>,
|
|
|
|
) -> impl IntoResponse {
|
2024-01-21 19:53:30 +00:00
|
|
|
info!("Creating new album '{}'", album.album_name);
|
|
|
|
|
|
|
|
let new_album = Album::new(&album.album_name, Vec::new(), 0);
|
|
|
|
|
|
|
|
let new_album = context.db.insert(new_album).unwrap();
|
|
|
|
|
|
|
|
(StatusCode::OK, Json(new_album))
|
|
|
|
}
|
|
|
|
|
2024-02-02 02:34:38 +00:00
|
|
|
async fn get_album(album_id: Path<u64>, State(context): State<PicContext>) -> impl IntoResponse {
|
2024-01-21 19:53:30 +00:00
|
|
|
let album = context.db.get::<Album>(*album_id).unwrap();
|
|
|
|
|
|
|
|
(StatusCode::OK, Json(album))
|
|
|
|
}
|
|
|
|
|
2024-02-02 02:34:38 +00:00
|
|
|
async fn add_image(
|
|
|
|
State(context): State<PicContext>,
|
|
|
|
mut img_data: Multipart,
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
let mut data: Vec<u8> = Vec::new();
|
|
|
|
let mut metadata: Option<AddImage> = None;
|
|
|
|
while let Some(field) = img_data.next_field().await.unwrap() {
|
|
|
|
let field_name = field.name().clone();
|
|
|
|
if let Some(field_name) = field_name {
|
|
|
|
if field_name == "metadata" {
|
|
|
|
let metadata_json = field.text().await.unwrap();
|
|
|
|
metadata = Some(serde_json::from_str(&metadata_json).unwrap())
|
|
|
|
} else if field_name == "img_data" {
|
|
|
|
data.extend_from_slice(field.bytes().await.unwrap().as_ref());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut album =
|
|
|
|
Album::find_album_by_query(&context.db, metadata.clone().unwrap().album).unwrap();
|
|
|
|
|
|
|
|
let mut store_manager = context.store_manager.write().await;
|
|
|
|
let img = store_manager
|
|
|
|
.store_img(
|
|
|
|
&context.db,
|
|
|
|
None,
|
|
|
|
ImageData::Bytes(data),
|
|
|
|
&metadata.unwrap().image_name,
|
|
|
|
0,
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
album.images.push(img.id().unwrap());
|
|
|
|
|
|
|
|
context.db.insert::<Album>(album).unwrap();
|
|
|
|
|
|
|
|
StatusCode::OK
|
2024-01-21 19:53:30 +00:00
|
|
|
}
|
|
|
|
|
2024-02-02 02:34:38 +00:00
|
|
|
async fn query_album(
|
|
|
|
album_query: Query<AlbumQuery>,
|
|
|
|
State(context): State<PicContext>,
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
let resp = Album::find_album_by_query(&context.db, album_query.0);
|
2024-01-21 19:53:30 +00:00
|
|
|
(StatusCode::OK, Json(resp))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::main]
|
|
|
|
async fn main() {
|
|
|
|
let args = Args::from_args();
|
|
|
|
|
|
|
|
let config = PicOxConfig::new(args.config);
|
|
|
|
|
|
|
|
let db = Database::new(&config.db_path).unwrap();
|
|
|
|
|
2024-02-02 02:34:38 +00:00
|
|
|
let store_manager = StorageManager::new(config.storage_config.clone());
|
|
|
|
|
2024-01-21 19:53:30 +00:00
|
|
|
let context = Context {
|
|
|
|
db,
|
2024-02-02 02:34:38 +00:00
|
|
|
config: config.clone(),
|
|
|
|
store_manager: RwLock::new(store_manager),
|
2024-01-21 19:53:30 +00:00
|
|
|
};
|
|
|
|
|
2024-02-02 02:34:38 +00:00
|
|
|
let context = Arc::new(context);
|
2024-01-21 19:53:30 +00:00
|
|
|
|
|
|
|
// initialize tracing
|
|
|
|
tracing_subscriber::fmt::init();
|
|
|
|
// build our application with a route
|
|
|
|
let app = Router::new()
|
|
|
|
// `GET /` goes to `root`
|
|
|
|
.route("/api/album/create", post(create_album))
|
|
|
|
.route("/api/album/:id", get(get_album))
|
|
|
|
.route("/api/album/", get(query_album))
|
2024-02-02 02:34:38 +00:00
|
|
|
.route("/api/image/", post(add_image))
|
|
|
|
.with_state(context);
|
2024-01-21 19:53:30 +00:00
|
|
|
|
|
|
|
// run our app with hyper, listening globally on port 3000
|
2024-02-02 02:34:38 +00:00
|
|
|
let listener = tokio::net::TcpListener::bind(&config.host).await.unwrap();
|
|
|
|
info!("Serving at {}", config.host);
|
2024-01-21 19:53:30 +00:00
|
|
|
axum::serve(listener, app).await.unwrap();
|
2024-02-02 02:34:38 +00:00
|
|
|
}
|