Update and Migrate Diesel to 2.0
Almost have tag support working, still figuring out how to get photo tags.
This commit is contained in:
83
src/main.rs
83
src/main.rs
@@ -4,6 +4,7 @@ extern crate rayon;
|
||||
|
||||
use actix_web::web::Data;
|
||||
use actix_web_prom::PrometheusMetricsBuilder;
|
||||
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
|
||||
use futures::stream::StreamExt;
|
||||
use lazy_static::lazy_static;
|
||||
use prometheus::{self, IntGauge};
|
||||
@@ -14,6 +15,8 @@ use std::{
|
||||
io::ErrorKind,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use std::error::Error;
|
||||
use std::sync::Mutex;
|
||||
use walkdir::{DirEntry, WalkDir};
|
||||
|
||||
use actix_files::NamedFile;
|
||||
@@ -23,6 +26,7 @@ use actix_web::{
|
||||
web::{self, BufMut, BytesMut},
|
||||
App, HttpRequest, HttpResponse, HttpServer, Responder,
|
||||
};
|
||||
use diesel::sqlite::Sqlite;
|
||||
use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
|
||||
use rayon::prelude::*;
|
||||
|
||||
@@ -39,6 +43,7 @@ use crate::video::*;
|
||||
mod auth;
|
||||
mod data;
|
||||
mod database;
|
||||
mod error;
|
||||
mod files;
|
||||
mod state;
|
||||
mod tags;
|
||||
@@ -60,12 +65,14 @@ lazy_static! {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!();
|
||||
|
||||
#[get("/image")]
|
||||
async fn get_image(
|
||||
_claims: Claims,
|
||||
request: HttpRequest,
|
||||
req: web::Query<ThumbnailRequest>,
|
||||
app_state: web::Data<AppState>,
|
||||
app_state: Data<AppState>,
|
||||
) -> impl Responder {
|
||||
if let Some(path) = is_valid_full_path(&app_state.base_path, &req.path) {
|
||||
if req.size.is_some() {
|
||||
@@ -97,7 +104,7 @@ async fn get_image(
|
||||
async fn get_file_metadata(
|
||||
_: Claims,
|
||||
path: web::Query<ThumbnailRequest>,
|
||||
app_state: web::Data<AppState>,
|
||||
app_state: Data<AppState>,
|
||||
) -> impl Responder {
|
||||
match is_valid_full_path(&app_state.base_path, &path.path)
|
||||
.ok_or_else(|| ErrorKind::InvalidData.into())
|
||||
@@ -119,7 +126,7 @@ async fn get_file_metadata(
|
||||
async fn upload_image(
|
||||
_: Claims,
|
||||
mut payload: mp::Multipart,
|
||||
app_state: web::Data<AppState>,
|
||||
app_state: Data<AppState>,
|
||||
) -> impl Responder {
|
||||
let mut file_content: BytesMut = BytesMut::new();
|
||||
let mut file_name: Option<String> = None;
|
||||
@@ -148,7 +155,7 @@ async fn upload_image(
|
||||
if !file_content.is_empty() {
|
||||
let full_path = PathBuf::from(&path).join(file_name.unwrap());
|
||||
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) {
|
||||
let mut file = File::create(full_path).unwrap();
|
||||
@@ -170,7 +177,7 @@ async fn upload_image(
|
||||
#[post("/video/generate")]
|
||||
async fn generate_video(
|
||||
_claims: Claims,
|
||||
app_state: web::Data<AppState>,
|
||||
app_state: Data<AppState>,
|
||||
body: web::Json<ThumbnailRequest>,
|
||||
) -> impl Responder {
|
||||
let filename = PathBuf::from(&body.path);
|
||||
@@ -200,7 +207,7 @@ async fn stream_video(
|
||||
request: HttpRequest,
|
||||
_: Claims,
|
||||
path: web::Query<ThumbnailRequest>,
|
||||
app_state: web::Data<AppState>,
|
||||
app_state: Data<AppState>,
|
||||
) -> impl Responder {
|
||||
let playlist = &path.path;
|
||||
debug!("Playlist: {}", playlist);
|
||||
@@ -235,9 +242,10 @@ async fn get_video_part(
|
||||
#[get("image/favorites")]
|
||||
async fn favorites(
|
||||
claims: Claims,
|
||||
favorites_dao: web::Data<Box<dyn FavoriteDao>>,
|
||||
favorites_dao: Data<Mutex<Box<dyn FavoriteDao>>>,
|
||||
) -> impl Responder {
|
||||
match web::block(move || favorites_dao.get_favorites(claims.sub.parse::<i32>().unwrap())).await
|
||||
match web::block(move || favorites_dao.lock()
|
||||
.expect("Unable to get FavoritesDao").get_favorites(claims.sub.parse::<i32>().unwrap())).await
|
||||
{
|
||||
Ok(Ok(favorites)) => {
|
||||
let favorites = favorites
|
||||
@@ -262,14 +270,14 @@ async fn favorites(
|
||||
async fn put_add_favorite(
|
||||
claims: Claims,
|
||||
body: web::Json<AddFavoriteRequest>,
|
||||
favorites_dao: web::Data<Box<dyn FavoriteDao>>,
|
||||
favorites_dao: Data<Mutex<Box<dyn FavoriteDao>>>,
|
||||
) -> impl Responder {
|
||||
if let Ok(user_id) = claims.sub.parse::<i32>() {
|
||||
let path = body.path.clone();
|
||||
match web::block::<_, Result<usize, DbError>>(move || {
|
||||
favorites_dao.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 => {
|
||||
debug!("Favorite: {} exists for user: {}", &body.path, user_id);
|
||||
@@ -298,15 +306,15 @@ async fn put_add_favorite(
|
||||
async fn delete_favorite(
|
||||
claims: Claims,
|
||||
body: web::Query<AddFavoriteRequest>,
|
||||
favorites_dao: web::Data<Box<dyn FavoriteDao>>,
|
||||
favorites_dao: Data<Mutex<Box<dyn FavoriteDao>>>,
|
||||
) -> impl Responder {
|
||||
if let Ok(user_id) = claims.sub.parse::<i32>() {
|
||||
let path = body.path.clone();
|
||||
web::block(move || {
|
||||
favorites_dao.remove_favorite(user_id, path);
|
||||
favorites_dao.lock().expect("Unable to get favorites dao").remove_favorite(user_id, path);
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
info!(
|
||||
"Removing favorite \"{}\" for userid: {}",
|
||||
@@ -325,7 +333,7 @@ fn create_thumbnails() {
|
||||
|
||||
let images = PathBuf::from(dotenv::var("BASE_PATH").unwrap());
|
||||
|
||||
walkdir::WalkDir::new(&images)
|
||||
WalkDir::new(&images)
|
||||
.into_iter()
|
||||
.collect::<Vec<Result<_, _>>>()
|
||||
.into_par_iter()
|
||||
@@ -422,12 +430,15 @@ fn main() -> std::io::Result<()> {
|
||||
dotenv::dotenv().ok();
|
||||
env_logger::init();
|
||||
|
||||
run_migrations(&mut connect())
|
||||
.expect("Failed to run migrations");
|
||||
|
||||
create_thumbnails();
|
||||
watch_files();
|
||||
|
||||
let system = actix::System::new();
|
||||
system.block_on(async {
|
||||
let app_data = web::Data::new(AppState::default());
|
||||
let app_data = Data::new(AppState::default());
|
||||
|
||||
let labels = HashMap::new();
|
||||
let prometheus = PrometheusMetricsBuilder::new("api")
|
||||
@@ -447,6 +458,7 @@ fn main() -> std::io::Result<()> {
|
||||
HttpServer::new(move || {
|
||||
let user_dao = SqliteUserDao::new();
|
||||
let favorites_dao = SqliteFavoriteDao::new();
|
||||
let tag_dao = SqliteTagDao::default();
|
||||
App::new()
|
||||
.wrap(middleware::Logger::default())
|
||||
.service(web::resource("/login").route(web::post().to(login::<SqliteUserDao>)))
|
||||
@@ -460,21 +472,32 @@ fn main() -> std::io::Result<()> {
|
||||
.service(put_add_favorite)
|
||||
.service(delete_favorite)
|
||||
.service(get_file_metadata)
|
||||
.service(add_tag)
|
||||
.service(get_tags)
|
||||
.service(remove_tagged_photo)
|
||||
.service(
|
||||
web::resource("image/tags")
|
||||
.route(web::post().to(add_tag::<SqliteTagDao>))
|
||||
.route(web::get().to(get_all_tags::<SqliteTagDao>))
|
||||
.route(web::get().to(get_tags::<SqliteTagDao>))
|
||||
.route(web::delete().to(remove_tagged_photo::<SqliteTagDao>)),
|
||||
)
|
||||
.app_data(app_data.clone())
|
||||
.app_data::<Data<SqliteUserDao>>(Data::new(user_dao))
|
||||
.app_data::<Data<Box<dyn FavoriteDao>>>(Data::new(Box::new(favorites_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<SqliteTagDao>>>(Data::new(Mutex::new(tag_dao)))
|
||||
.wrap(prometheus.clone())
|
||||
})
|
||||
.bind(dotenv::var("BIND_URL").unwrap())?
|
||||
.bind("localhost:8088")?
|
||||
.run()
|
||||
.await
|
||||
.bind(dotenv::var("BIND_URL").unwrap())?
|
||||
.bind("localhost:8088")?
|
||||
.run()
|
||||
.await
|
||||
})
|
||||
}
|
||||
|
||||
fn run_migrations(connection: &mut impl MigrationHarness<Sqlite>) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
||||
connection.run_pending_migrations(MIGRATIONS)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn watch_files() {
|
||||
std::thread::spawn(|| {
|
||||
let (wtx, wrx) = channel();
|
||||
@@ -492,10 +515,10 @@ fn watch_files() {
|
||||
let image_base_path = PathBuf::from(env::var("BASE_PATH").unwrap());
|
||||
let image_relative = orig.strip_prefix(&image_base_path).unwrap();
|
||||
if let Ok(old_thumbnail) =
|
||||
env::var("THUMBNAILS").map(PathBuf::from).map(|mut base| {
|
||||
base.push(image_relative);
|
||||
base
|
||||
})
|
||||
env::var("THUMBNAILS").map(PathBuf::from).map(|mut base| {
|
||||
base.push(image_relative);
|
||||
base
|
||||
})
|
||||
{
|
||||
if let Err(e) = std::fs::remove_file(&old_thumbnail) {
|
||||
error!(
|
||||
|
||||
Reference in New Issue
Block a user