restructure api, import playlist from text file #195
This commit is contained in:
parent
dcc4616421
commit
ec4f5d2ac2
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -949,7 +949,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ffplayout-api"
|
||||
version = "0.6.3"
|
||||
version = "0.7.0"
|
||||
dependencies = [
|
||||
"actix-files",
|
||||
"actix-multipart",
|
||||
|
@ -4,7 +4,7 @@ description = "Rest API for ffplayout"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Jonathan Baecker jonbae77@gmail.com"]
|
||||
readme = "README.md"
|
||||
version = "0.6.3"
|
||||
version = "0.7.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
2
ffplayout-api/src/api/mod.rs
Normal file
2
ffplayout-api/src/api/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod auth;
|
||||
pub mod routes;
|
@ -8,7 +8,7 @@
|
||||
///
|
||||
/// For all endpoints an (Bearer) authentication is required.\
|
||||
/// `{id}` represent the channel id, and at default is 1.
|
||||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, env, fs, path::Path};
|
||||
|
||||
use actix_multipart::Multipart;
|
||||
use actix_web::{delete, get, http::StatusCode, patch, post, put, web, HttpResponse, Responder};
|
||||
@ -20,8 +20,12 @@ use argon2::{
|
||||
use serde::{Deserialize, Serialize};
|
||||
use simplelog::*;
|
||||
|
||||
use crate::auth::{create_jwt, Claims};
|
||||
use crate::db::{
|
||||
handles,
|
||||
models::{Channel, LoginUser, TextPreset, User},
|
||||
};
|
||||
use crate::utils::{
|
||||
auth::{create_jwt, Claims},
|
||||
channels::{create_channel, delete_channel},
|
||||
control::{control_service, control_state, media_info, send_message, Process},
|
||||
errors::ServiceError,
|
||||
@ -29,16 +33,10 @@ use crate::utils::{
|
||||
browser, create_directory, remove_file_or_folder, rename_file, upload, MoveObject,
|
||||
PathObject,
|
||||
},
|
||||
handles::{
|
||||
db_add_preset, db_add_user, db_delete_preset, db_get_all_channels, db_get_channel,
|
||||
db_get_presets, db_get_user, db_login, db_role, db_update_channel, db_update_preset,
|
||||
db_update_user,
|
||||
},
|
||||
models::{Channel, LoginUser, TextPreset, User},
|
||||
playlist::{delete_playlist, generate_playlist, read_playlist, write_playlist},
|
||||
read_log_file, read_playout_config, Role,
|
||||
playout_config, read_log_file, read_playout_config, Role,
|
||||
};
|
||||
use ffplayout_lib::utils::{JsonPlaylist, PlayoutConfig};
|
||||
use ffplayout_lib::utils::{import::import_file, JsonPlaylist, PlayoutConfig};
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ResponseObj<T> {
|
||||
@ -60,11 +58,19 @@ pub struct DateObj {
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct FileObj {
|
||||
struct FileObj {
|
||||
#[serde(default)]
|
||||
path: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct ImportObj {
|
||||
#[serde(default)]
|
||||
file: String,
|
||||
#[serde(default)]
|
||||
date: String,
|
||||
}
|
||||
|
||||
/// #### User Handling
|
||||
///
|
||||
/// **Login**
|
||||
@ -85,7 +91,7 @@ pub struct FileObj {
|
||||
/// ```
|
||||
#[post("/auth/login/")]
|
||||
pub async fn login(credentials: web::Json<User>) -> impl Responder {
|
||||
match db_login(&credentials.username).await {
|
||||
match handles::select_login(&credentials.username).await {
|
||||
Ok(mut user) => {
|
||||
let pass = user.password.clone();
|
||||
let hash = PasswordHash::new(&pass).unwrap();
|
||||
@ -96,7 +102,7 @@ pub async fn login(credentials: web::Json<User>) -> impl Responder {
|
||||
.verify_password(credentials.password.as_bytes(), &hash)
|
||||
.is_ok()
|
||||
{
|
||||
let role = db_role(&user.role_id.unwrap_or_default())
|
||||
let role = handles::select_role(&user.role_id.unwrap_or_default())
|
||||
.await
|
||||
.unwrap_or_else(|_| "guest".to_string());
|
||||
let claims = Claims::new(user.id, user.username.clone(), role.clone());
|
||||
@ -147,7 +153,7 @@ pub async fn login(credentials: web::Json<User>) -> impl Responder {
|
||||
#[get("/user")]
|
||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||
async fn get_user(user: web::ReqData<LoginUser>) -> Result<impl Responder, ServiceError> {
|
||||
match db_get_user(&user.username).await {
|
||||
match handles::select_user(&user.username).await {
|
||||
Ok(user) => Ok(web::Json(user)),
|
||||
Err(e) => {
|
||||
error!("{e}");
|
||||
@ -189,7 +195,7 @@ async fn update_user(
|
||||
fields.push_str(format!("password = '{}', salt = '{salt}'", password_hash).as_str());
|
||||
}
|
||||
|
||||
if db_update_user(user.id, fields).await.is_ok() {
|
||||
if handles::update_user(user.id, fields).await.is_ok() {
|
||||
return Ok("Update Success");
|
||||
};
|
||||
|
||||
@ -209,7 +215,7 @@ async fn update_user(
|
||||
#[post("/user/")]
|
||||
#[has_any_role("Role::Admin", type = "Role")]
|
||||
async fn add_user(data: web::Json<User>) -> Result<impl Responder, ServiceError> {
|
||||
match db_add_user(data.into_inner()).await {
|
||||
match handles::insert_user(data.into_inner()).await {
|
||||
Ok(_) => Ok("Add User Success"),
|
||||
Err(e) => {
|
||||
error!("{e}");
|
||||
@ -242,7 +248,7 @@ async fn add_user(data: web::Json<User>) -> Result<impl Responder, ServiceError>
|
||||
#[get("/channel/{id}")]
|
||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||
async fn get_channel(id: web::Path<i64>) -> Result<impl Responder, ServiceError> {
|
||||
if let Ok(channel) = db_get_channel(&id).await {
|
||||
if let Ok(channel) = handles::select_channel(&id).await {
|
||||
return Ok(web::Json(channel));
|
||||
}
|
||||
|
||||
@ -257,7 +263,7 @@ async fn get_channel(id: web::Path<i64>) -> Result<impl Responder, ServiceError>
|
||||
#[get("/channels")]
|
||||
#[has_any_role("Role::Admin", type = "Role")]
|
||||
async fn get_all_channels() -> Result<impl Responder, ServiceError> {
|
||||
if let Ok(channel) = db_get_all_channels().await {
|
||||
if let Ok(channel) = handles::select_all_channels().await {
|
||||
return Ok(web::Json(channel));
|
||||
}
|
||||
|
||||
@ -278,7 +284,10 @@ async fn patch_channel(
|
||||
id: web::Path<i64>,
|
||||
data: web::Json<Channel>,
|
||||
) -> Result<impl Responder, ServiceError> {
|
||||
if db_update_channel(*id, data.into_inner()).await.is_ok() {
|
||||
if handles::update_channel(*id, data.into_inner())
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
return Ok("Update Success");
|
||||
};
|
||||
|
||||
@ -333,7 +342,7 @@ async fn get_playout_config(
|
||||
id: web::Path<i64>,
|
||||
_details: AuthDetails<Role>,
|
||||
) -> Result<impl Responder, ServiceError> {
|
||||
if let Ok(channel) = db_get_channel(&id).await {
|
||||
if let Ok(channel) = handles::select_channel(&id).await {
|
||||
if let Ok(config) = read_playout_config(&channel.config_path) {
|
||||
return Ok(web::Json(config));
|
||||
}
|
||||
@ -354,7 +363,7 @@ async fn update_playout_config(
|
||||
id: web::Path<i64>,
|
||||
data: web::Json<PlayoutConfig>,
|
||||
) -> Result<impl Responder, ServiceError> {
|
||||
if let Ok(channel) = db_get_channel(&id).await {
|
||||
if let Ok(channel) = handles::select_channel(&id).await {
|
||||
if let Ok(f) = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
@ -384,7 +393,7 @@ async fn update_playout_config(
|
||||
#[get("/presets/{id}")]
|
||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||
async fn get_presets(id: web::Path<i64>) -> Result<impl Responder, ServiceError> {
|
||||
if let Ok(presets) = db_get_presets(*id).await {
|
||||
if let Ok(presets) = handles::select_presets(*id).await {
|
||||
return Ok(web::Json(presets));
|
||||
}
|
||||
|
||||
@ -405,7 +414,7 @@ async fn update_preset(
|
||||
id: web::Path<i64>,
|
||||
data: web::Json<TextPreset>,
|
||||
) -> Result<impl Responder, ServiceError> {
|
||||
if db_update_preset(&id, data.into_inner()).await.is_ok() {
|
||||
if handles::update_preset(&id, data.into_inner()).await.is_ok() {
|
||||
return Ok("Update Success");
|
||||
}
|
||||
|
||||
@ -423,7 +432,7 @@ async fn update_preset(
|
||||
#[post("/presets/")]
|
||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||
async fn add_preset(data: web::Json<TextPreset>) -> Result<impl Responder, ServiceError> {
|
||||
if db_add_preset(data.into_inner()).await.is_ok() {
|
||||
if handles::insert_preset(data.into_inner()).await.is_ok() {
|
||||
return Ok("Add preset Success");
|
||||
}
|
||||
|
||||
@ -439,7 +448,7 @@ async fn add_preset(data: web::Json<TextPreset>) -> Result<impl Responder, Servi
|
||||
#[delete("/presets/{id}")]
|
||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||
async fn delete_preset(id: web::Path<i64>) -> Result<impl Responder, ServiceError> {
|
||||
if db_delete_preset(&id).await.is_ok() {
|
||||
if handles::delete_preset(&id).await.is_ok() {
|
||||
return Ok("Delete preset Success");
|
||||
}
|
||||
|
||||
@ -763,5 +772,34 @@ async fn save_file(
|
||||
payload: Multipart,
|
||||
obj: web::Query<FileObj>,
|
||||
) -> Result<HttpResponse, ServiceError> {
|
||||
upload(*id, payload, &obj.path).await
|
||||
upload(*id, payload, &obj.path, false).await
|
||||
}
|
||||
|
||||
/// **Import playlist**
|
||||
///
|
||||
/// Import text/m3u file and convert it to a playlist
|
||||
/// lines with leading "#" will be ignore
|
||||
///
|
||||
/// ```BASH
|
||||
/// curl -X POST http://127.0.0.1:8787/api/file/1/import/ -H 'Authorization: <TOKEN>'
|
||||
/// -F "file=@list.m3u"
|
||||
/// ```
|
||||
#[put("/file/{id}/import/")]
|
||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||
async fn import_playlist(
|
||||
id: web::Path<i64>,
|
||||
payload: Multipart,
|
||||
obj: web::Query<ImportObj>,
|
||||
) -> Result<HttpResponse, ServiceError> {
|
||||
let file = Path::new(&obj.file).file_name().unwrap_or_default();
|
||||
let path = env::temp_dir().join(&file).to_string_lossy().to_string();
|
||||
let (config, _) = playout_config(&id).await?;
|
||||
let channel = handles::select_channel(&id).await?;
|
||||
|
||||
upload(*id, payload, &path, true).await?;
|
||||
import_file(&config, &obj.date, Some(channel.name), &path)?;
|
||||
|
||||
fs::remove_file(path)?;
|
||||
|
||||
Ok(HttpResponse::Ok().into())
|
||||
}
|
@ -7,11 +7,8 @@ use rand::{distributions::Alphanumeric, Rng};
|
||||
use simplelog::*;
|
||||
use sqlx::{migrate::MigrateDatabase, sqlite::SqliteQueryResult, Pool, Sqlite, SqlitePool};
|
||||
|
||||
use crate::utils::{
|
||||
db_path, local_utc_offset,
|
||||
models::{Channel, TextPreset, User},
|
||||
GlobalSettings,
|
||||
};
|
||||
use crate::db::models::{Channel, TextPreset, User};
|
||||
use crate::utils::{db_path, local_utc_offset, GlobalSettings};
|
||||
|
||||
#[derive(Debug, sqlx::FromRow)]
|
||||
struct Role {
|
||||
@ -19,7 +16,7 @@ struct Role {
|
||||
}
|
||||
|
||||
async fn create_schema() -> Result<SqliteQueryResult, sqlx::Error> {
|
||||
let conn = db_connection().await?;
|
||||
let conn = connection().await?;
|
||||
let query = "PRAGMA foreign_keys = ON;
|
||||
CREATE TABLE IF NOT EXISTS global
|
||||
(
|
||||
@ -96,7 +93,7 @@ pub async fn db_init(domain: Option<String>) -> Result<&'static str, Box<dyn std
|
||||
.map(char::from)
|
||||
.collect();
|
||||
|
||||
let instances = db_connection().await?;
|
||||
let instances = connection().await?;
|
||||
|
||||
let url = match domain {
|
||||
Some(d) => format!("http://{d}/live/stream.m3u8"),
|
||||
@ -130,15 +127,15 @@ pub async fn db_init(domain: Option<String>) -> Result<&'static str, Box<dyn std
|
||||
Ok("Database initialized!")
|
||||
}
|
||||
|
||||
pub async fn db_connection() -> Result<Pool<Sqlite>, sqlx::Error> {
|
||||
pub async fn connection() -> Result<Pool<Sqlite>, sqlx::Error> {
|
||||
let db_path = db_path().unwrap();
|
||||
let conn = SqlitePool::connect(&db_path).await?;
|
||||
|
||||
Ok(conn)
|
||||
}
|
||||
|
||||
pub async fn db_global() -> Result<GlobalSettings, sqlx::Error> {
|
||||
let conn = db_connection().await?;
|
||||
pub async fn select_global() -> Result<GlobalSettings, sqlx::Error> {
|
||||
let conn = connection().await?;
|
||||
let query = "SELECT secret FROM global WHERE id = 1";
|
||||
let result: GlobalSettings = sqlx::query_as(query).fetch_one(&conn).await?;
|
||||
conn.close().await;
|
||||
@ -146,8 +143,8 @@ pub async fn db_global() -> Result<GlobalSettings, sqlx::Error> {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn db_get_channel(id: &i64) -> Result<Channel, sqlx::Error> {
|
||||
let conn = db_connection().await?;
|
||||
pub async fn select_channel(id: &i64) -> Result<Channel, sqlx::Error> {
|
||||
let conn = connection().await?;
|
||||
let query = "SELECT * FROM channels WHERE id = $1";
|
||||
let mut result: Channel = sqlx::query_as(query).bind(id).fetch_one(&conn).await?;
|
||||
conn.close().await;
|
||||
@ -157,8 +154,8 @@ pub async fn db_get_channel(id: &i64) -> Result<Channel, sqlx::Error> {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn db_get_all_channels() -> Result<Vec<Channel>, sqlx::Error> {
|
||||
let conn = db_connection().await?;
|
||||
pub async fn select_all_channels() -> Result<Vec<Channel>, sqlx::Error> {
|
||||
let conn = connection().await?;
|
||||
let query = "SELECT * FROM channels";
|
||||
let mut results: Vec<Channel> = sqlx::query_as(query).fetch_all(&conn).await?;
|
||||
conn.close().await;
|
||||
@ -170,11 +167,8 @@ pub async fn db_get_all_channels() -> Result<Vec<Channel>, sqlx::Error> {
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
pub async fn db_update_channel(
|
||||
id: i64,
|
||||
channel: Channel,
|
||||
) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||
let conn = db_connection().await?;
|
||||
pub async fn update_channel(id: i64, channel: Channel) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||
let conn = connection().await?;
|
||||
|
||||
let query = "UPDATE channels SET name = $2, preview_url = $3, config_path = $4, extra_extensions = $5 WHERE id = $1";
|
||||
let result: SqliteQueryResult = sqlx::query(query)
|
||||
@ -190,8 +184,8 @@ pub async fn db_update_channel(
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn db_add_channel(channel: Channel) -> Result<Channel, sqlx::Error> {
|
||||
let conn = db_connection().await?;
|
||||
pub async fn insert_channel(channel: Channel) -> Result<Channel, sqlx::Error> {
|
||||
let conn = connection().await?;
|
||||
|
||||
let query = "INSERT INTO channels (name, preview_url, config_path, extra_extensions, service) VALUES($1, $2, $3, $4, $5)";
|
||||
let result = sqlx::query(query)
|
||||
@ -211,8 +205,8 @@ pub async fn db_add_channel(channel: Channel) -> Result<Channel, sqlx::Error> {
|
||||
Ok(new_channel)
|
||||
}
|
||||
|
||||
pub async fn db_delete_channel(id: &i64) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||
let conn = db_connection().await?;
|
||||
pub async fn delete_channel(id: &i64) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||
let conn = connection().await?;
|
||||
|
||||
let query = "DELETE FROM channels WHERE id = $1";
|
||||
let result: SqliteQueryResult = sqlx::query(query).bind(id).execute(&conn).await?;
|
||||
@ -221,8 +215,8 @@ pub async fn db_delete_channel(id: &i64) -> Result<SqliteQueryResult, sqlx::Erro
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn db_role(id: &i64) -> Result<String, sqlx::Error> {
|
||||
let conn = db_connection().await?;
|
||||
pub async fn select_role(id: &i64) -> Result<String, sqlx::Error> {
|
||||
let conn = connection().await?;
|
||||
let query = "SELECT name FROM roles WHERE id = $1";
|
||||
let result: Role = sqlx::query_as(query).bind(id).fetch_one(&conn).await?;
|
||||
conn.close().await;
|
||||
@ -230,8 +224,8 @@ pub async fn db_role(id: &i64) -> Result<String, sqlx::Error> {
|
||||
Ok(result.name)
|
||||
}
|
||||
|
||||
pub async fn db_login(user: &str) -> Result<User, sqlx::Error> {
|
||||
let conn = db_connection().await?;
|
||||
pub async fn select_login(user: &str) -> Result<User, sqlx::Error> {
|
||||
let conn = connection().await?;
|
||||
let query = "SELECT id, mail, username, password, salt, role_id FROM user WHERE username = $1";
|
||||
let result: User = sqlx::query_as(query).bind(user).fetch_one(&conn).await?;
|
||||
conn.close().await;
|
||||
@ -239,8 +233,8 @@ pub async fn db_login(user: &str) -> Result<User, sqlx::Error> {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn db_get_user(user: &str) -> Result<User, sqlx::Error> {
|
||||
let conn = db_connection().await?;
|
||||
pub async fn select_user(user: &str) -> Result<User, sqlx::Error> {
|
||||
let conn = connection().await?;
|
||||
let query = "SELECT id, mail, username, role_id FROM user WHERE username = $1";
|
||||
let result: User = sqlx::query_as(query).bind(user).fetch_one(&conn).await?;
|
||||
conn.close().await;
|
||||
@ -248,8 +242,8 @@ pub async fn db_get_user(user: &str) -> Result<User, sqlx::Error> {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn db_add_user(user: User) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||
let conn = db_connection().await?;
|
||||
pub async fn insert_user(user: User) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||
let conn = connection().await?;
|
||||
let salt = SaltString::generate(&mut OsRng);
|
||||
let password_hash = Argon2::default()
|
||||
.hash_password(user.password.clone().as_bytes(), &salt)
|
||||
@ -270,8 +264,8 @@ pub async fn db_add_user(user: User) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn db_update_user(id: i64, fields: String) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||
let conn = db_connection().await?;
|
||||
pub async fn update_user(id: i64, fields: String) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||
let conn = connection().await?;
|
||||
let query = format!("UPDATE user SET {fields} WHERE id = $1");
|
||||
let result: SqliteQueryResult = sqlx::query(&query).bind(id).execute(&conn).await?;
|
||||
conn.close().await;
|
||||
@ -279,8 +273,8 @@ pub async fn db_update_user(id: i64, fields: String) -> Result<SqliteQueryResult
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn db_get_presets(id: i64) -> Result<Vec<TextPreset>, sqlx::Error> {
|
||||
let conn = db_connection().await?;
|
||||
pub async fn select_presets(id: i64) -> Result<Vec<TextPreset>, sqlx::Error> {
|
||||
let conn = connection().await?;
|
||||
let query = "SELECT * FROM presets WHERE channel_id = $1";
|
||||
let result: Vec<TextPreset> = sqlx::query_as(query).bind(id).fetch_all(&conn).await?;
|
||||
conn.close().await;
|
||||
@ -288,11 +282,8 @@ pub async fn db_get_presets(id: i64) -> Result<Vec<TextPreset>, sqlx::Error> {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn db_update_preset(
|
||||
id: &i64,
|
||||
preset: TextPreset,
|
||||
) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||
let conn = db_connection().await?;
|
||||
pub async fn update_preset(id: &i64, preset: TextPreset) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||
let conn = connection().await?;
|
||||
let query =
|
||||
"UPDATE presets SET name = $1, text = $2, x = $3, y = $4, fontsize = $5, line_spacing = $6,
|
||||
fontcolor = $7, alpha = $8, box = $9, boxcolor = $10, boxborderw = 11 WHERE id = $12";
|
||||
@ -316,8 +307,8 @@ pub async fn db_update_preset(
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn db_add_preset(preset: TextPreset) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||
let conn = db_connection().await?;
|
||||
pub async fn insert_preset(preset: TextPreset) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||
let conn = connection().await?;
|
||||
let query =
|
||||
"INSERT INTO presets (channel_id, name, text, x, y, fontsize, line_spacing, fontcolor, alpha, box, boxcolor, boxborderw)
|
||||
VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)";
|
||||
@ -341,8 +332,8 @@ pub async fn db_add_preset(preset: TextPreset) -> Result<SqliteQueryResult, sqlx
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn db_delete_preset(id: &i64) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||
let conn = db_connection().await?;
|
||||
pub async fn delete_preset(id: &i64) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||
let conn = connection().await?;
|
||||
let query = "DELETE FROM presets WHERE id = $1;";
|
||||
let result: SqliteQueryResult = sqlx::query(query).bind(id).execute(&conn).await?;
|
||||
conn.close().await;
|
2
ffplayout-api/src/db/mod.rs
Normal file
2
ffplayout-api/src/db/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod handles;
|
||||
pub mod models;
|
@ -9,21 +9,23 @@ use actix_web_httpauth::middleware::HttpAuthentication;
|
||||
use clap::Parser;
|
||||
use simplelog::*;
|
||||
|
||||
pub mod api;
|
||||
pub mod db;
|
||||
pub mod utils;
|
||||
|
||||
use utils::{
|
||||
args_parse::Args,
|
||||
auth, db_path, init_config,
|
||||
models::LoginUser,
|
||||
use api::{
|
||||
auth,
|
||||
routes::{
|
||||
add_channel, add_dir, add_preset, add_user, control_playout, del_playlist, delete_preset,
|
||||
file_browser, gen_playlist, get_all_channels, get_channel, get_log, get_playlist,
|
||||
get_playout_config, get_presets, get_user, login, media_current, media_last, media_next,
|
||||
move_rename, patch_channel, process_control, remove, remove_channel, save_file,
|
||||
save_playlist, send_text_message, update_playout_config, update_preset, update_user,
|
||||
get_playout_config, get_presets, get_user, import_playlist, login, media_current,
|
||||
media_last, media_next, move_rename, patch_channel, process_control, remove,
|
||||
remove_channel, save_file, save_playlist, send_text_message, update_playout_config,
|
||||
update_preset, update_user,
|
||||
},
|
||||
run_args, Role,
|
||||
};
|
||||
use db::models::LoginUser;
|
||||
use utils::{args_parse::Args, db_path, init_config, run_args, Role};
|
||||
|
||||
use ffplayout_lib::utils::{init_logging, PlayoutConfig};
|
||||
|
||||
@ -118,7 +120,8 @@ async fn main() -> std::io::Result<()> {
|
||||
.service(add_dir)
|
||||
.service(move_rename)
|
||||
.service(remove)
|
||||
.service(save_file),
|
||||
.service(save_file)
|
||||
.service(import_playlist),
|
||||
)
|
||||
.service(Files::new("/", public_path()).index_file("index.html"))
|
||||
})
|
||||
|
@ -2,12 +2,9 @@ use std::fs;
|
||||
|
||||
use simplelog::*;
|
||||
|
||||
use crate::utils::{
|
||||
control::control_service,
|
||||
errors::ServiceError,
|
||||
handles::{db_add_channel, db_delete_channel, db_get_channel},
|
||||
models::Channel,
|
||||
};
|
||||
use crate::utils::{control::control_service, errors::ServiceError};
|
||||
|
||||
use crate::db::{handles, models::Channel};
|
||||
|
||||
pub async fn create_channel(target_channel: Channel) -> Result<Channel, ServiceError> {
|
||||
if !target_channel.service.starts_with("ffplayout@") {
|
||||
@ -23,14 +20,14 @@ pub async fn create_channel(target_channel: Channel) -> Result<Channel, ServiceE
|
||||
&target_channel.config_path,
|
||||
)?;
|
||||
|
||||
let new_channel = db_add_channel(target_channel).await?;
|
||||
let new_channel = handles::insert_channel(target_channel).await?;
|
||||
control_service(new_channel.id, "enable").await?;
|
||||
|
||||
Ok(new_channel)
|
||||
}
|
||||
|
||||
pub async fn delete_channel(id: i64) -> Result<(), ServiceError> {
|
||||
let channel = db_get_channel(&id).await?;
|
||||
let channel = handles::select_channel(&id).await?;
|
||||
control_service(channel.id, "stop").await?;
|
||||
control_service(channel.id, "disable").await?;
|
||||
|
||||
@ -38,7 +35,7 @@ pub async fn delete_channel(id: i64) -> Result<(), ServiceError> {
|
||||
error!("{e}");
|
||||
};
|
||||
|
||||
db_delete_channel(&id).await?;
|
||||
handles::delete_channel(&id).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -6,7 +6,8 @@ use reqwest::{
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::utils::{errors::ServiceError, handles::db_get_channel, playout_config};
|
||||
use crate::db::handles::select_channel;
|
||||
use crate::utils::{errors::ServiceError, playout_config};
|
||||
use ffplayout_lib::vec_strings;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
@ -56,7 +57,7 @@ struct SystemD {
|
||||
|
||||
impl SystemD {
|
||||
async fn new(id: i64) -> Result<Self, ServiceError> {
|
||||
let channel = db_get_channel(&id).await?;
|
||||
let channel = select_channel(&id).await?;
|
||||
|
||||
Ok(Self {
|
||||
service: channel.service,
|
||||
|
@ -47,9 +47,9 @@ pub struct VideoFile {
|
||||
///
|
||||
/// This function takes care, that it is not possible to break out from root_path.
|
||||
/// It also gives alway a relative path back.
|
||||
fn norm_abs_path(root_path: &String, input_path: &String) -> (PathBuf, String, String) {
|
||||
let mut path = PathBuf::from(root_path.clone());
|
||||
let path_relative = RelativePath::new(&root_path)
|
||||
fn norm_abs_path(root_path: &str, input_path: &str) -> (PathBuf, String, String) {
|
||||
let mut path = PathBuf::from(root_path);
|
||||
let path_relative = RelativePath::new(root_path)
|
||||
.normalize()
|
||||
.to_string()
|
||||
.replace("../", "");
|
||||
@ -57,7 +57,11 @@ fn norm_abs_path(root_path: &String, input_path: &String) -> (PathBuf, String, S
|
||||
.normalize()
|
||||
.to_string()
|
||||
.replace("../", "");
|
||||
let path_suffix = path.file_name().unwrap().to_string_lossy().to_string();
|
||||
let path_suffix = path
|
||||
.file_name()
|
||||
.unwrap_or_default()
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
if input_path.starts_with(root_path) || source_relative.starts_with(&path_relative) {
|
||||
source_relative = source_relative
|
||||
@ -225,7 +229,7 @@ pub async fn rename_file(id: i64, move_object: &MoveObject) -> Result<MoveObject
|
||||
Err(ServiceError::InternalServerError)
|
||||
}
|
||||
|
||||
pub async fn remove_file_or_folder(id: i64, source_path: &String) -> Result<(), ServiceError> {
|
||||
pub async fn remove_file_or_folder(id: i64, source_path: &str) -> Result<(), ServiceError> {
|
||||
let (config, _) = playout_config(&id).await?;
|
||||
let (source, _, _) = norm_abs_path(&config.storage.path, source_path);
|
||||
|
||||
@ -258,7 +262,7 @@ pub async fn remove_file_or_folder(id: i64, source_path: &String) -> Result<(),
|
||||
Err(ServiceError::InternalServerError)
|
||||
}
|
||||
|
||||
async fn valid_path(id: i64, path: &String) -> Result<PathBuf, ServiceError> {
|
||||
async fn valid_path(id: i64, path: &str) -> Result<PathBuf, ServiceError> {
|
||||
let (config, _) = playout_config(&id).await?;
|
||||
let (test_path, _, _) = norm_abs_path(&config.storage.path, path);
|
||||
|
||||
@ -272,7 +276,8 @@ async fn valid_path(id: i64, path: &String) -> Result<PathBuf, ServiceError> {
|
||||
pub async fn upload(
|
||||
id: i64,
|
||||
mut payload: Multipart,
|
||||
path: &String,
|
||||
path: &str,
|
||||
abs_path: bool,
|
||||
) -> Result<HttpResponse, ServiceError> {
|
||||
while let Some(mut field) = payload.try_next().await? {
|
||||
let content_disposition = field.content_disposition();
|
||||
@ -286,8 +291,14 @@ pub async fn upload(
|
||||
.get_filename()
|
||||
.map_or_else(|| rand_string.to_string(), sanitize_filename::sanitize);
|
||||
|
||||
let target_path = valid_path(id, path).await?;
|
||||
let filepath = target_path.join(filename);
|
||||
let filepath;
|
||||
|
||||
if abs_path {
|
||||
filepath = PathBuf::from(path);
|
||||
} else {
|
||||
let target_path = valid_path(id, path).await?;
|
||||
filepath = target_path.join(filename);
|
||||
}
|
||||
|
||||
if filepath.is_file() {
|
||||
return Err(ServiceError::BadRequest("Target already exists!".into()));
|
||||
|
@ -12,22 +12,17 @@ use rpassword::read_password;
|
||||
use simplelog::*;
|
||||
|
||||
pub mod args_parse;
|
||||
pub mod auth;
|
||||
pub mod channels;
|
||||
pub mod control;
|
||||
pub mod errors;
|
||||
pub mod files;
|
||||
pub mod handles;
|
||||
pub mod models;
|
||||
pub mod playlist;
|
||||
pub mod routes;
|
||||
|
||||
use crate::utils::{
|
||||
args_parse::Args,
|
||||
errors::ServiceError,
|
||||
handles::{db_add_user, db_get_channel, db_global, db_init},
|
||||
use crate::db::{
|
||||
handles::{db_init, insert_user, select_channel, select_global},
|
||||
models::{Channel, User},
|
||||
};
|
||||
use crate::utils::{args_parse::Args, errors::ServiceError};
|
||||
use ffplayout_lib::utils::PlayoutConfig;
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
@ -54,7 +49,7 @@ pub struct GlobalSettings {
|
||||
|
||||
impl GlobalSettings {
|
||||
async fn new() -> Self {
|
||||
let global_settings = db_global();
|
||||
let global_settings = select_global();
|
||||
|
||||
match global_settings.await {
|
||||
Ok(g) => g,
|
||||
@ -165,7 +160,7 @@ pub async fn run_args(mut args: Args) -> Result<(), i32> {
|
||||
token: None,
|
||||
};
|
||||
|
||||
if let Err(e) = db_add_user(user).await {
|
||||
if let Err(e) = insert_user(user).await {
|
||||
error!("{e}");
|
||||
return Err(1);
|
||||
};
|
||||
@ -186,7 +181,7 @@ pub fn read_playout_config(path: &str) -> Result<PlayoutConfig, Box<dyn Error>>
|
||||
}
|
||||
|
||||
pub async fn playout_config(channel_id: &i64) -> Result<(PlayoutConfig, Channel), ServiceError> {
|
||||
if let Ok(channel) = db_get_channel(channel_id).await {
|
||||
if let Ok(channel) = select_channel(channel_id).await {
|
||||
if let Ok(config) = read_playout_config(&channel.config_path.clone()) {
|
||||
return Ok((config, channel));
|
||||
}
|
||||
@ -198,7 +193,7 @@ pub async fn playout_config(channel_id: &i64) -> Result<(PlayoutConfig, Channel)
|
||||
}
|
||||
|
||||
pub async fn read_log_file(channel_id: &i64, date: &str) -> Result<String, ServiceError> {
|
||||
if let Ok(channel) = db_get_channel(channel_id).await {
|
||||
if let Ok(channel) = select_channel(channel_id).await {
|
||||
let mut date_str = "".to_string();
|
||||
|
||||
if !date.is_empty() {
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 51719eb9c03d26da8ffac6a677d1c41b756bdf83
|
||||
Subproject commit 0060d40b597a8e595a2dbef6e493821be2e7d12f
|
Loading…
x
Reference in New Issue
Block a user