feature/tagging #16
60
src/main.rs
60
src/main.rs
@@ -8,15 +8,15 @@ use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
|
|||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use prometheus::{self, IntGauge};
|
use prometheus::{self, IntGauge};
|
||||||
|
use std::error::Error;
|
||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
|
use std::sync::Mutex;
|
||||||
use std::{collections::HashMap, io::prelude::*};
|
use std::{collections::HashMap, io::prelude::*};
|
||||||
use std::{env, fs::File};
|
use std::{env, fs::File};
|
||||||
use std::{
|
use std::{
|
||||||
io::ErrorKind,
|
io::ErrorKind,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
use std::error::Error;
|
|
||||||
use std::sync::Mutex;
|
|
||||||
use walkdir::{DirEntry, WalkDir};
|
use walkdir::{DirEntry, WalkDir};
|
||||||
|
|
||||||
use actix_files::NamedFile;
|
use actix_files::NamedFile;
|
||||||
@@ -155,7 +155,7 @@ async fn upload_image(
|
|||||||
if !file_content.is_empty() {
|
if !file_content.is_empty() {
|
||||||
let full_path = PathBuf::from(&path).join(file_name.unwrap());
|
let full_path = PathBuf::from(&path).join(file_name.unwrap());
|
||||||
if let Some(full_path) =
|
if let Some(full_path) =
|
||||||
is_valid_full_path(&app_state.base_path, full_path.to_str().unwrap_or(""))
|
is_valid_full_path(&app_state.base_path, full_path.to_str().unwrap_or(""))
|
||||||
{
|
{
|
||||||
if !full_path.is_file() && is_image_or_video(&full_path) {
|
if !full_path.is_file() && is_image_or_video(&full_path) {
|
||||||
let mut file = File::create(full_path).unwrap();
|
let mut file = File::create(full_path).unwrap();
|
||||||
@@ -244,8 +244,13 @@ async fn favorites(
|
|||||||
claims: Claims,
|
claims: Claims,
|
||||||
favorites_dao: Data<Mutex<Box<dyn FavoriteDao>>>,
|
favorites_dao: Data<Mutex<Box<dyn FavoriteDao>>>,
|
||||||
) -> impl Responder {
|
) -> impl Responder {
|
||||||
match web::block(move || favorites_dao.lock()
|
match web::block(move || {
|
||||||
.expect("Unable to get FavoritesDao").get_favorites(claims.sub.parse::<i32>().unwrap())).await
|
favorites_dao
|
||||||
|
.lock()
|
||||||
|
.expect("Unable to get FavoritesDao")
|
||||||
|
.get_favorites(claims.sub.parse::<i32>().unwrap())
|
||||||
|
})
|
||||||
|
.await
|
||||||
{
|
{
|
||||||
Ok(Ok(favorites)) => {
|
Ok(Ok(favorites)) => {
|
||||||
let favorites = favorites
|
let favorites = favorites
|
||||||
@@ -275,9 +280,12 @@ async fn put_add_favorite(
|
|||||||
if let Ok(user_id) = claims.sub.parse::<i32>() {
|
if let Ok(user_id) = claims.sub.parse::<i32>() {
|
||||||
let path = body.path.clone();
|
let path = body.path.clone();
|
||||||
match web::block::<_, Result<usize, DbError>>(move || {
|
match web::block::<_, Result<usize, DbError>>(move || {
|
||||||
favorites_dao.lock().expect("Unable to get FavoritesDao").add_favorite(user_id, &path)
|
favorites_dao
|
||||||
|
.lock()
|
||||||
|
.expect("Unable to get FavoritesDao")
|
||||||
|
.add_favorite(user_id, &path)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(Err(e)) if e.kind == DbErrorKind::AlreadyExists => {
|
Ok(Err(e)) if e.kind == DbErrorKind::AlreadyExists => {
|
||||||
debug!("Favorite: {} exists for user: {}", &body.path, user_id);
|
debug!("Favorite: {} exists for user: {}", &body.path, user_id);
|
||||||
@@ -311,10 +319,13 @@ async fn delete_favorite(
|
|||||||
if let Ok(user_id) = claims.sub.parse::<i32>() {
|
if let Ok(user_id) = claims.sub.parse::<i32>() {
|
||||||
let path = body.path.clone();
|
let path = body.path.clone();
|
||||||
web::block(move || {
|
web::block(move || {
|
||||||
favorites_dao.lock().expect("Unable to get favorites dao").remove_favorite(user_id, path);
|
favorites_dao
|
||||||
|
.lock()
|
||||||
|
.expect("Unable to get favorites dao")
|
||||||
|
.remove_favorite(user_id, path);
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Removing favorite \"{}\" for userid: {}",
|
"Removing favorite \"{}\" for userid: {}",
|
||||||
@@ -430,8 +441,7 @@ fn main() -> std::io::Result<()> {
|
|||||||
dotenv::dotenv().ok();
|
dotenv::dotenv().ok();
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
run_migrations(&mut connect())
|
run_migrations(&mut connect()).expect("Failed to run migrations");
|
||||||
.expect("Failed to run migrations");
|
|
||||||
|
|
||||||
create_thumbnails();
|
create_thumbnails();
|
||||||
watch_files();
|
watch_files();
|
||||||
@@ -475,24 +485,28 @@ fn main() -> std::io::Result<()> {
|
|||||||
.service(
|
.service(
|
||||||
web::resource("image/tags")
|
web::resource("image/tags")
|
||||||
.route(web::post().to(add_tag::<SqliteTagDao>))
|
.route(web::post().to(add_tag::<SqliteTagDao>))
|
||||||
.route(web::get().to(get_all_tags::<SqliteTagDao>))
|
|
||||||
.route(web::get().to(get_tags::<SqliteTagDao>))
|
.route(web::get().to(get_tags::<SqliteTagDao>))
|
||||||
.route(web::delete().to(remove_tagged_photo::<SqliteTagDao>)),
|
.route(web::delete().to(remove_tagged_photo::<SqliteTagDao>)),
|
||||||
)
|
)
|
||||||
|
.service(web::resource("image/tags/all").route(web::get().to(get_all_tags::<SqliteTagDao>)))
|
||||||
.app_data(app_data.clone())
|
.app_data(app_data.clone())
|
||||||
.app_data::<Data<Mutex<SqliteUserDao>>>(Data::new(Mutex::new(user_dao)))
|
.app_data::<Data<Mutex<SqliteUserDao>>>(Data::new(Mutex::new(user_dao)))
|
||||||
.app_data::<Data<Mutex<Box<dyn FavoriteDao>>>>(Data::new(Mutex::new(Box::new(favorites_dao))))
|
.app_data::<Data<Mutex<Box<dyn FavoriteDao>>>>(Data::new(Mutex::new(Box::new(
|
||||||
|
favorites_dao,
|
||||||
|
))))
|
||||||
.app_data::<Data<Mutex<SqliteTagDao>>>(Data::new(Mutex::new(tag_dao)))
|
.app_data::<Data<Mutex<SqliteTagDao>>>(Data::new(Mutex::new(tag_dao)))
|
||||||
.wrap(prometheus.clone())
|
.wrap(prometheus.clone())
|
||||||
})
|
})
|
||||||
.bind(dotenv::var("BIND_URL").unwrap())?
|
.bind(dotenv::var("BIND_URL").unwrap())?
|
||||||
.bind("localhost:8088")?
|
.bind("localhost:8088")?
|
||||||
.run()
|
.run()
|
||||||
.await
|
.await
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_migrations(connection: &mut impl MigrationHarness<Sqlite>) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
fn run_migrations(
|
||||||
|
connection: &mut impl MigrationHarness<Sqlite>,
|
||||||
|
) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
||||||
connection.run_pending_migrations(MIGRATIONS)?;
|
connection.run_pending_migrations(MIGRATIONS)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -515,10 +529,10 @@ fn watch_files() {
|
|||||||
let image_base_path = PathBuf::from(env::var("BASE_PATH").unwrap());
|
let image_base_path = PathBuf::from(env::var("BASE_PATH").unwrap());
|
||||||
let image_relative = orig.strip_prefix(&image_base_path).unwrap();
|
let image_relative = orig.strip_prefix(&image_base_path).unwrap();
|
||||||
if let Ok(old_thumbnail) =
|
if let Ok(old_thumbnail) =
|
||||||
env::var("THUMBNAILS").map(PathBuf::from).map(|mut base| {
|
env::var("THUMBNAILS").map(PathBuf::from).map(|mut base| {
|
||||||
base.push(image_relative);
|
base.push(image_relative);
|
||||||
base
|
base
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
if let Err(e) = std::fs::remove_file(&old_thumbnail) {
|
if let Err(e) = std::fs::remove_file(&old_thumbnail) {
|
||||||
error!(
|
error!(
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use actix_web::{web, HttpResponse, Responder};
|
|||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use log::info;
|
use log::{debug, info};
|
||||||
use schema::{tagged_photo, tags};
|
use schema::{tagged_photo, tags};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::borrow::BorrowMut;
|
use std::borrow::BorrowMut;
|
||||||
@@ -132,6 +132,7 @@ impl TagDao for SqliteTagDao {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_tags_for_path(&mut self, path: &str) -> anyhow::Result<Vec<Tag>> {
|
fn get_tags_for_path(&mut self, path: &str) -> anyhow::Result<Vec<Tag>> {
|
||||||
|
debug!("Getting Tags for path: {:?}", path);
|
||||||
tags::table
|
tags::table
|
||||||
.left_join(tagged_photo::table)
|
.left_join(tagged_photo::table)
|
||||||
.filter(tagged_photo::photo_name.eq(&path))
|
.filter(tagged_photo::photo_name.eq(&path))
|
||||||
@@ -149,6 +150,7 @@ impl TagDao for SqliteTagDao {
|
|||||||
.execute(&mut self.connection)
|
.execute(&mut self.connection)
|
||||||
.with_context(|| "Unable to insert tag in Sqlite")
|
.with_context(|| "Unable to insert tag in Sqlite")
|
||||||
.and_then(|_| {
|
.and_then(|_| {
|
||||||
|
debug!("Inserted tag: {:?}", name);
|
||||||
no_arg_sql_function!(
|
no_arg_sql_function!(
|
||||||
last_insert_rowid,
|
last_insert_rowid,
|
||||||
diesel::sql_types::Integer,
|
diesel::sql_types::Integer,
|
||||||
@@ -159,6 +161,7 @@ impl TagDao for SqliteTagDao {
|
|||||||
.with_context(|| "Unable to get last inserted tag from Sqlite")
|
.with_context(|| "Unable to get last inserted tag from Sqlite")
|
||||||
})
|
})
|
||||||
.and_then(|id| {
|
.and_then(|id| {
|
||||||
|
debug!("Got id: {:?} for inserted tag: {:?}", id, name);
|
||||||
tags::table
|
tags::table
|
||||||
.left_join(tagged_photo::table)
|
.left_join(tagged_photo::table)
|
||||||
.filter(tagged_photo::id.eq(id))
|
.filter(tagged_photo::id.eq(id))
|
||||||
@@ -199,8 +202,9 @@ impl TagDao for SqliteTagDao {
|
|||||||
created_time: Utc::now().timestamp(),
|
created_time: Utc::now().timestamp(),
|
||||||
})
|
})
|
||||||
.execute(self.connection.borrow_mut())
|
.execute(self.connection.borrow_mut())
|
||||||
.with_context(|| "Unable to insert tag into sqlite")
|
.with_context(|| "Unable to tag file in sqlite")
|
||||||
.and_then(|tagged_id| {
|
.and_then(|tagged_id| {
|
||||||
|
debug!("Inserted tagged photo: {:?}", tagged_id);
|
||||||
tagged_photo::table
|
tagged_photo::table
|
||||||
.find(tagged_id as i32)
|
.find(tagged_id as i32)
|
||||||
.first(self.connection.borrow_mut())
|
.first(self.connection.borrow_mut())
|
||||||
|
|||||||
Reference in New Issue
Block a user