Merge pull request #428 from jb-alvarado/master
embed static files from frontend in ffpapi, add db path argument
This commit is contained in:
commit
809b649226
9
.github/workflows/rust.yml
vendored
9
.github/workflows/rust.yml
vendored
@ -8,12 +8,19 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: On all Systems
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Set Build Tools and update Rust
|
||||
run: |
|
||||
rustup update stable
|
||||
rustup component add rustfmt
|
||||
rustup component add clippy
|
||||
|
||||
- name: Init Submodules
|
||||
run: |
|
||||
git submodule update --init --recursive
|
||||
|
||||
- name: Use ffmpeg on Linux
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
uses: FedericoCarboni/setup-ffmpeg@v2
|
||||
|
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -1,3 +1,3 @@
|
||||
[submodule "ffplayout-frontend"]
|
||||
path = ffplayout-frontend
|
||||
url = git@github.com:ffplayout/ffplayout-frontend.git
|
||||
url = https://github.com/ffplayout/ffplayout-frontend.git
|
||||
|
63
Cargo.lock
generated
63
Cargo.lock
generated
@ -278,6 +278,18 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-web-static-files"
|
||||
version = "4.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adf6d1ef6d7a60e084f9e0595e2a5234abda14e76c105ecf8e2d0e8800c41a1f"
|
||||
dependencies = [
|
||||
"actix-web",
|
||||
"derive_more",
|
||||
"futures-util",
|
||||
"static-files",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.21.0"
|
||||
@ -731,6 +743,16 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "change-detection"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "159fa412eae48a1d94d0b9ecdb85c97ce56eb2a347c62394d3fdbf221adabc1a"
|
||||
dependencies = [
|
||||
"path-matchers",
|
||||
"path-slash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.31"
|
||||
@ -1141,6 +1163,7 @@ dependencies = [
|
||||
"actix-web",
|
||||
"actix-web-grants",
|
||||
"actix-web-httpauth",
|
||||
"actix-web-static-files",
|
||||
"argon2",
|
||||
"chrono",
|
||||
"clap",
|
||||
@ -1151,6 +1174,7 @@ dependencies = [
|
||||
"jsonwebtoken",
|
||||
"lexical-sort",
|
||||
"once_cell",
|
||||
"path-clean",
|
||||
"rand",
|
||||
"regex",
|
||||
"relative-path",
|
||||
@ -1162,6 +1186,7 @@ dependencies = [
|
||||
"serde_yaml",
|
||||
"simplelog",
|
||||
"sqlx",
|
||||
"static-files",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
@ -1424,6 +1449,12 @@ version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||
|
||||
[[package]]
|
||||
name = "gloo-timers"
|
||||
version = "0.2.6"
|
||||
@ -2167,6 +2198,27 @@ version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
|
||||
|
||||
[[package]]
|
||||
name = "path-clean"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17359afc20d7ab31fdb42bb844c8b3bb1dabd7dcf7e68428492da7f16966fcef"
|
||||
|
||||
[[package]]
|
||||
name = "path-matchers"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36cd9b72a47679ec193a5f0229d9ab686b7bd45e1fbc59ccf953c9f3d83f7b2b"
|
||||
dependencies = [
|
||||
"glob",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "path-slash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "498a099351efa4becc6a19c72aa9270598e8fd274ca47052e37455241c88b696"
|
||||
|
||||
[[package]]
|
||||
name = "pem"
|
||||
version = "1.1.1"
|
||||
@ -3036,6 +3088,17 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static-files"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64712ea1e3e140010e1d9605872ba205afa2ab5bd38191cc6ebd248ae1f6a06b"
|
||||
dependencies = [
|
||||
"change-detection",
|
||||
"mime_guess",
|
||||
"path-slash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stringprep"
|
||||
version = "0.1.4"
|
||||
|
2
debian/postinst
vendored
2
debian/postinst
vendored
@ -23,8 +23,6 @@ if [ ! -d "/usr/share/ffplayout/db" ]; then
|
||||
chown -R ${sysUser}: "/usr/share/ffplayout"
|
||||
chown -R ${sysUser}: "/var/lib/ffplayout"
|
||||
chown -R ${sysUser}: "/etc/ffplayout"
|
||||
|
||||
ln -s "/var/lib/ffplayout/tv-media" "/usr/share/ffplayout/public/"
|
||||
fi
|
||||
|
||||
if [ ! -d "/var/log/ffplayout" ]; then
|
||||
|
@ -13,7 +13,7 @@ ffplayout provides ***.deb** and ***.rpm** packages, which makes it more easy to
|
||||
6. use a revers proxy for SSL, Port is **8787**.
|
||||
7. login with your browser, address without proxy would be: **http://[IP ADDRESS]:8787**
|
||||
|
||||
Default location for playlists and media files are: **/var/lib/ffplayout/**. If you need to change them, the media storage folder needs a symlink to **/usr/share/ffplayout/public/**.
|
||||
Default location for playlists and media files are: **/var/lib/ffplayout/**.
|
||||
|
||||
When you don't need the frontend and API, skip enable the systemd service **ffpapi**.
|
||||
|
||||
|
@ -15,6 +15,7 @@ actix-multipart = "0.6"
|
||||
actix-web = "4"
|
||||
actix-web-grants = "3"
|
||||
actix-web-httpauth = "0.8"
|
||||
actix-web-static-files = "4.0"
|
||||
argon2 = "0.5"
|
||||
chrono = { version = "0.4", default-features = false, features = ["clock", "std"] }
|
||||
clap = { version = "4.3", features = ["derive"] }
|
||||
@ -24,6 +25,7 @@ futures-util = { version = "0.3", default-features = false, features = ["std"] }
|
||||
jsonwebtoken = "8"
|
||||
lexical-sort = "0.3"
|
||||
once_cell = "1.18"
|
||||
path-clean = "1.0"
|
||||
rand = "0.8"
|
||||
regex = "1"
|
||||
relative-path = "1.8"
|
||||
@ -34,9 +36,13 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde_yaml = "0.9"
|
||||
simplelog = { version = "0.12", features = ["paris"] }
|
||||
static-files = "0.2"
|
||||
sqlx = { version = "0.7", features = ["runtime-tokio", "sqlite"] }
|
||||
tokio = { version = "1.29", features = ["full"] }
|
||||
|
||||
[build-dependencies]
|
||||
static-files = "0.2"
|
||||
|
||||
[[bin]]
|
||||
name = "ffpapi"
|
||||
path = "src/main.rs"
|
||||
|
11
ffplayout-api/build.rs
Normal file
11
ffplayout-api/build.rs
Normal file
@ -0,0 +1,11 @@
|
||||
use static_files::NpmBuild;
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
NpmBuild::new("../ffplayout-frontend")
|
||||
.install()?
|
||||
.run("generate")?
|
||||
.target("../ffplayout-frontend/.output/public")
|
||||
.change_detection()
|
||||
.to_resource_dir()
|
||||
.build()
|
||||
}
|
@ -79,15 +79,16 @@ async fn create_schema(conn: &Pool<Sqlite>) -> Result<SqliteQueryResult, sqlx::E
|
||||
sqlx::query(query).execute(conn).await
|
||||
}
|
||||
|
||||
pub async fn db_init(domain: Option<String>) -> Result<&'static str, Box<dyn std::error::Error>> {
|
||||
let db_path = db_path()?;
|
||||
|
||||
println!("--- db_path: {db_path:?}");
|
||||
pub async fn db_init(
|
||||
domain: Option<String>,
|
||||
db: &Option<String>,
|
||||
) -> Result<&'static str, Box<dyn std::error::Error>> {
|
||||
let db_path = db_path(db.clone())?;
|
||||
|
||||
if !Sqlite::database_exists(db_path).await.unwrap_or(false) {
|
||||
Sqlite::create_database(db_path).await.unwrap();
|
||||
|
||||
let pool = db_pool().await?;
|
||||
let pool = db_pool(db).await?;
|
||||
|
||||
match create_schema(&pool).await {
|
||||
Ok(_) => info!("Database created Successfully"),
|
||||
@ -129,7 +130,7 @@ pub async fn db_init(domain: Option<String>) -> Result<&'static str, Box<dyn std
|
||||
('Scrolling Text', 'We have a very important announcement to make.', 'ifnot(ld(1),st(1,t));if(lt(t,ld(1)+1),w+4,w-w/12*mod(t-ld(1),12*(w+tw)/w))', '(h-line_h)*0.9',
|
||||
'24', '4', '#ffffff', '1', '#000000@0x80', '4', '1.0', '1');";
|
||||
|
||||
let pool = db_pool().await?;
|
||||
let pool = db_pool(db).await?;
|
||||
|
||||
sqlx::query(query)
|
||||
.bind(secret)
|
||||
|
@ -5,8 +5,8 @@ pub mod models;
|
||||
|
||||
use crate::utils::db_path;
|
||||
|
||||
pub async fn db_pool() -> Result<Pool<Sqlite>, sqlx::Error> {
|
||||
let db_path = db_path().unwrap();
|
||||
pub async fn db_pool(db: &Option<String>) -> Result<Pool<Sqlite>, sqlx::Error> {
|
||||
let db_path = db_path(db.clone()).unwrap();
|
||||
let conn = SqlitePool::connect(db_path).await?;
|
||||
|
||||
Ok(conn)
|
||||
|
@ -1,9 +1,11 @@
|
||||
use std::{path::Path, process::exit};
|
||||
use std::process::exit;
|
||||
|
||||
use actix_files::Files;
|
||||
use actix_web::{dev::ServiceRequest, middleware, web, App, Error, HttpMessage, HttpServer};
|
||||
use actix_web::{
|
||||
dev::ServiceRequest, middleware::Logger, web, App, Error, HttpMessage, HttpServer,
|
||||
};
|
||||
use actix_web_grants::permissions::AttachPermissions;
|
||||
use actix_web_httpauth::{extractors::bearer::BearerAuth, middleware::HttpAuthentication};
|
||||
use actix_web_static_files::ResourceFiles;
|
||||
|
||||
use clap::Parser;
|
||||
use simplelog::*;
|
||||
@ -18,6 +20,8 @@ use utils::{args_parse::Args, control::ProcessControl, db_path, init_config, run
|
||||
|
||||
use ffplayout_lib::utils::{init_logging, PlayoutConfig};
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
||||
|
||||
async fn validator(
|
||||
req: ServiceRequest,
|
||||
credentials: BearerAuth,
|
||||
@ -36,18 +40,6 @@ async fn validator(
|
||||
}
|
||||
}
|
||||
|
||||
fn public_path() -> &'static str {
|
||||
if Path::new("/usr/share/ffplayout/public/").is_dir() {
|
||||
return "/usr/share/ffplayout/public/";
|
||||
}
|
||||
|
||||
if Path::new("./public/").is_dir() {
|
||||
return "./public/";
|
||||
}
|
||||
|
||||
"./ffplayout-frontend/.output/public/"
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
let args = Args::parse();
|
||||
@ -64,7 +56,7 @@ async fn main() -> std::io::Result<()> {
|
||||
exit(c);
|
||||
}
|
||||
|
||||
let pool = match db_pool().await {
|
||||
let pool = match db_pool(&args.db).await {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
error!("{e}");
|
||||
@ -73,7 +65,7 @@ async fn main() -> std::io::Result<()> {
|
||||
};
|
||||
|
||||
if let Some(conn) = args.listen {
|
||||
if db_path().is_err() {
|
||||
if db_path(args.db).is_err() {
|
||||
error!("Database is not initialized! Init DB first and add admin user.");
|
||||
exit(1);
|
||||
}
|
||||
@ -89,11 +81,15 @@ async fn main() -> std::io::Result<()> {
|
||||
HttpServer::new(move || {
|
||||
let auth = HttpAuthentication::bearer(validator);
|
||||
let db_pool = web::Data::new(pool.clone());
|
||||
let generated = generate();
|
||||
|
||||
let logger = Logger::new("%{r}a \"%r\" %s %b \"%{Referer}i\" \"%{User-Agent}i\" %T")
|
||||
.exclude_regex(r"/_nuxt/*");
|
||||
|
||||
App::new()
|
||||
.app_data(db_pool)
|
||||
.app_data(engine_process.clone())
|
||||
.wrap(middleware::Logger::default())
|
||||
.wrap(logger)
|
||||
.service(login)
|
||||
.service(
|
||||
web::scope("/api")
|
||||
@ -132,7 +128,7 @@ async fn main() -> std::io::Result<()> {
|
||||
.service(get_program),
|
||||
)
|
||||
.service(get_file)
|
||||
.service(Files::new("/", public_path()).index_file("index.html"))
|
||||
.service(ResourceFiles::new("/", generated))
|
||||
})
|
||||
.bind((addr, port))?
|
||||
.run()
|
||||
|
@ -8,6 +8,9 @@ pub struct Args {
|
||||
#[clap(short, long, help = "ask for user credentials")]
|
||||
pub ask: bool,
|
||||
|
||||
#[clap(long, help = "path to database file")]
|
||||
pub db: Option<String>,
|
||||
|
||||
#[clap(short, long, help = "Listen on IP:PORT, like: 127.0.0.1:8787")]
|
||||
pub listen: Option<String>,
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
use std::{
|
||||
env,
|
||||
error::Error,
|
||||
fs::{self, File},
|
||||
io::{stdin, stdout, Write},
|
||||
@ -8,6 +9,7 @@ use std::{
|
||||
use chrono::{format::ParseErrorKind, prelude::*};
|
||||
use faccess::PathExt;
|
||||
use once_cell::sync::OnceCell;
|
||||
use path_clean::PathClean;
|
||||
use rpassword::read_password;
|
||||
use serde::{de, Deserialize, Deserializer};
|
||||
use simplelog::*;
|
||||
@ -74,7 +76,27 @@ pub async fn init_config(conn: &Pool<Sqlite>) {
|
||||
INSTANCE.set(config).unwrap();
|
||||
}
|
||||
|
||||
pub fn db_path() -> Result<&'static str, Box<dyn std::error::Error>> {
|
||||
pub fn db_path(db: Option<String>) -> Result<&'static str, Box<dyn std::error::Error>> {
|
||||
if let Some(path) = db {
|
||||
let path_buf = Path::new(&path);
|
||||
let absolute_path = if path_buf.is_absolute() {
|
||||
path_buf.to_path_buf()
|
||||
} else {
|
||||
env::current_dir()?.join(path_buf)
|
||||
}
|
||||
.clean();
|
||||
|
||||
if let Some(abs_path) = absolute_path.parent() {
|
||||
if abs_path.writable() {
|
||||
return Ok(Box::leak(
|
||||
absolute_path.to_string_lossy().to_string().into_boxed_str(),
|
||||
));
|
||||
} else {
|
||||
error!("Given database path is not writable!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let sys_path = Path::new("/usr/share/ffplayout/db");
|
||||
let mut db_path = "./ffplayout.db";
|
||||
|
||||
@ -99,7 +121,7 @@ pub async fn run_args(mut args: Args) -> Result<(), i32> {
|
||||
}
|
||||
|
||||
if args.init {
|
||||
if let Err(e) = db_init(args.domain).await {
|
||||
if let Err(e) = db_init(args.domain, &args.db).await {
|
||||
panic!("{e}");
|
||||
};
|
||||
|
||||
@ -163,7 +185,7 @@ pub async fn run_args(mut args: Args) -> Result<(), i32> {
|
||||
token: None,
|
||||
};
|
||||
|
||||
match db_pool().await {
|
||||
match db_pool(&args.db).await {
|
||||
Ok(conn) => {
|
||||
if let Err(e) = insert_user(&conn, user).await {
|
||||
error!("{e}");
|
||||
|
@ -112,11 +112,6 @@ assets = [
|
||||
"/usr/share/man/man1/",
|
||||
"644",
|
||||
],
|
||||
[
|
||||
"../public/**/*",
|
||||
"/usr/share/ffplayout/public/",
|
||||
"644",
|
||||
],
|
||||
]
|
||||
maintainer-scripts = "../debian/"
|
||||
systemd-units = { enable = false, unit-scripts = "../assets" }
|
||||
@ -188,11 +183,6 @@ assets = [
|
||||
"/usr/share/man/man1/",
|
||||
"644",
|
||||
],
|
||||
[
|
||||
"../public/**/*",
|
||||
"/usr/share/ffplayout/public/",
|
||||
"644",
|
||||
],
|
||||
]
|
||||
|
||||
# REHL RPM PACKAGE
|
||||
@ -214,7 +204,6 @@ assets = [
|
||||
{ source = "../assets/logo.png", dest = "/usr/share/ffplayout/logo.png", mode = "644" },
|
||||
{ source = "../assets/ffplayout.yml", dest = "/usr/share/ffplayout/ffplayout.yml.orig", mode = "644" },
|
||||
{ source = "../assets/ffplayout.conf", dest = "/usr/share/ffplayout/ffplayout.conf.example", mode = "644" },
|
||||
{ source = "../public/**/*", dest = "/usr/share/ffplayout/public/", mode = "644" },
|
||||
{ source = "../debian/postinst", dest = "/usr/share/ffplayout/postinst", mode = "755" },
|
||||
]
|
||||
auto-req = "no"
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit d00ffcabeaafc98169e6fe3ad163e226460048f4
|
||||
Subproject commit 0898bcb2413408cca47707db36f00c74ce55c456
|
@ -3,22 +3,10 @@
|
||||
source $(dirname "$0")/man_create.sh
|
||||
target=$1
|
||||
|
||||
echo "build frontend"
|
||||
echo
|
||||
|
||||
if [ ! -f 'ffplayout-frontend/package.json' ]; then
|
||||
git submodule update --init
|
||||
fi
|
||||
|
||||
yes | rm -rf public
|
||||
cd ffplayout-frontend
|
||||
|
||||
npm install
|
||||
npm run generate
|
||||
cp -r .output/public ../public
|
||||
|
||||
cd ..
|
||||
|
||||
if [[ -n $target ]]; then
|
||||
targets=($target)
|
||||
else
|
||||
|
Loading…
Reference in New Issue
Block a user