control systemd service
This commit is contained in:
parent
a8578a10cc
commit
3bd18b3fa6
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1027,7 +1027,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ffplayout-api"
|
name = "ffplayout-api"
|
||||||
version = "0.3.0"
|
version = "0.3.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-multipart",
|
"actix-multipart",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
|
3
assets/11-ffplayout
Normal file
3
assets/11-ffplayout
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# give user www-data permission to control the ffplayout systemd service
|
||||||
|
|
||||||
|
www-data ALL = NOPASSWD: /bin/systemctl start ffplayout.service, /bin/systemctl stop ffplayout.service, /bin/systemctl reload ffplayout.service, /bin/systemctl restart ffplayout.service, /bin/systemctl status ffplayout.service, /bin/systemctl is-active ffplayout.service
|
@ -138,6 +138,10 @@ Response is in JSON format
|
|||||||
- **GET** `/api/control/{id}/media/last/`\
|
- **GET** `/api/control/{id}/media/last/`\
|
||||||
Response is in JSON format
|
Response is in JSON format
|
||||||
|
|
||||||
|
- **POST** `/api/control/{id}/process/`\
|
||||||
|
JSON Data: `{"command": "<start/stop/restart/status>"}`
|
||||||
|
Response is in TEXT format
|
||||||
|
|
||||||
#### Playlist Operations
|
#### Playlist Operations
|
||||||
|
|
||||||
- **GET** `/api/playlist/{id}/2022-06-20`\
|
- **GET** `/api/playlist/{id}/2022-06-20`\
|
||||||
|
@ -4,7 +4,7 @@ description = "Rest API for ffplayout"
|
|||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
authors = ["Jonathan Baecker jonbae77@gmail.com"]
|
authors = ["Jonathan Baecker jonbae77@gmail.com"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
version = "0.3.0"
|
version = "0.3.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -17,9 +17,9 @@ use utils::{
|
|||||||
routes::{
|
routes::{
|
||||||
add_preset, add_user, del_playlist, file_browser, gen_playlist, get_playlist,
|
add_preset, add_user, del_playlist, file_browser, gen_playlist, get_playlist,
|
||||||
get_playout_config, get_presets, get_settings, jump_to_last, jump_to_next, login,
|
get_playout_config, get_presets, get_settings, jump_to_last, jump_to_next, login,
|
||||||
media_current, media_last, media_next, move_rename, patch_settings, remove, reset_playout,
|
media_current, media_last, media_next, move_rename, patch_settings, process_control,
|
||||||
save_file, save_playlist, send_text_message, update_playout_config, update_preset,
|
remove, reset_playout, save_file, save_playlist, send_text_message, update_playout_config,
|
||||||
update_user,
|
update_preset, update_user,
|
||||||
},
|
},
|
||||||
run_args, Role,
|
run_args, Role,
|
||||||
};
|
};
|
||||||
@ -92,6 +92,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
.service(media_current)
|
.service(media_current)
|
||||||
.service(media_next)
|
.service(media_next)
|
||||||
.service(media_last)
|
.service(media_last)
|
||||||
|
.service(process_control)
|
||||||
.service(get_playlist)
|
.service(get_playlist)
|
||||||
.service(save_playlist)
|
.service(save_playlist)
|
||||||
.service(gen_playlist)
|
.service(gen_playlist)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, process::Command};
|
||||||
|
|
||||||
use reqwest::{
|
use reqwest::{
|
||||||
header::{HeaderMap, AUTHORIZATION, CONTENT_TYPE},
|
header::{HeaderMap, AUTHORIZATION, CONTENT_TYPE},
|
||||||
@ -7,7 +7,8 @@ use reqwest::{
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use simplelog::*;
|
use simplelog::*;
|
||||||
|
|
||||||
use crate::utils::{errors::ServiceError, playout_config};
|
use crate::utils::{errors::ServiceError, handles::db_get_settings, playout_config};
|
||||||
|
use ffplayout_lib::vec_strings;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
struct RpcObj<T> {
|
struct RpcObj<T> {
|
||||||
@ -44,6 +45,62 @@ impl<T> RpcObj<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
pub struct Process {
|
||||||
|
pub command: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SystemD {
|
||||||
|
service: String,
|
||||||
|
cmd: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SystemD {
|
||||||
|
async fn new(id: i64) -> Result<Self, ServiceError> {
|
||||||
|
let settings = db_get_settings(&id).await?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
service: settings.service,
|
||||||
|
cmd: vec_strings!["systemctl"],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start(mut self) -> Result<String, ServiceError> {
|
||||||
|
self.cmd
|
||||||
|
.append(&mut vec!["start".to_string(), self.service]);
|
||||||
|
|
||||||
|
Command::new("sudo").args(self.cmd).spawn()?;
|
||||||
|
|
||||||
|
Ok("Success".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stop(mut self) -> Result<String, ServiceError> {
|
||||||
|
self.cmd.append(&mut vec!["stop".to_string(), self.service]);
|
||||||
|
|
||||||
|
Command::new("sudo").args(self.cmd).spawn()?;
|
||||||
|
|
||||||
|
Ok("Success".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn restart(mut self) -> Result<String, ServiceError> {
|
||||||
|
self.cmd
|
||||||
|
.append(&mut vec!["restart".to_string(), self.service]);
|
||||||
|
|
||||||
|
Command::new("sudo").args(self.cmd).spawn()?;
|
||||||
|
|
||||||
|
Ok("Success".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn status(mut self) -> Result<String, ServiceError> {
|
||||||
|
self.cmd
|
||||||
|
.append(&mut vec!["is-active".to_string(), self.service]);
|
||||||
|
|
||||||
|
let output = Command::new("sudo").args(self.cmd).output()?;
|
||||||
|
|
||||||
|
Ok(String::from_utf8_lossy(&output.stdout).to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn create_header(auth: &str) -> HeaderMap {
|
fn create_header(auth: &str) -> HeaderMap {
|
||||||
let mut headers = HeaderMap::new();
|
let mut headers = HeaderMap::new();
|
||||||
headers.insert(
|
headers.insert(
|
||||||
@ -105,3 +162,15 @@ pub async fn media_info(id: i64, command: String) -> Result<Response, ServiceErr
|
|||||||
|
|
||||||
post_request(id, json_obj).await
|
post_request(id, json_obj).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn control_service(id: i64, command: &str) -> Result<String, ServiceError> {
|
||||||
|
let system_d = SystemD::new(id).await?;
|
||||||
|
|
||||||
|
match command {
|
||||||
|
"start" => system_d.start(),
|
||||||
|
"stop" => system_d.stop(),
|
||||||
|
"restart" => system_d.restart(),
|
||||||
|
"status" => system_d.status(),
|
||||||
|
_ => Err(ServiceError::BadRequest("Command not found!".to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -59,3 +59,9 @@ impl From<actix_web::error::BlockingError> for ServiceError {
|
|||||||
ServiceError::BadRequest(err.to_string())
|
ServiceError::BadRequest(err.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<sqlx::Error> for ServiceError {
|
||||||
|
fn from(err: sqlx::Error) -> ServiceError {
|
||||||
|
ServiceError::BadRequest(err.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -56,6 +56,7 @@ async fn create_schema() -> Result<SqliteQueryResult, sqlx::Error> {
|
|||||||
preview_url TEXT NOT NULL,
|
preview_url TEXT NOT NULL,
|
||||||
config_path TEXT NOT NULL,
|
config_path TEXT NOT NULL,
|
||||||
extra_extensions TEXT NOT NULL,
|
extra_extensions TEXT NOT NULL,
|
||||||
|
service TEXT NOT NULL,
|
||||||
UNIQUE(channel_name)
|
UNIQUE(channel_name)
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS user
|
CREATE TABLE IF NOT EXISTS user
|
||||||
@ -108,9 +109,9 @@ pub async fn db_init() -> Result<&'static str, Box<dyn std::error::Error>> {
|
|||||||
('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',
|
('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.0', '1', '#000000@0x80', '4');
|
'24', '4', '#ffffff', '1.0', '1', '#000000@0x80', '4');
|
||||||
INSERT INTO roles(name) VALUES('admin'), ('user'), ('guest');
|
INSERT INTO roles(name) VALUES('admin'), ('user'), ('guest');
|
||||||
INSERT INTO settings(channel_name, preview_url, config_path, extra_extensions)
|
INSERT INTO settings(channel_name, preview_url, config_path, extra_extensions, service)
|
||||||
VALUES('Channel 1', 'http://localhost/live/preview.m3u8',
|
VALUES('Channel 1', 'http://localhost/live/preview.m3u8',
|
||||||
'/etc/ffplayout/ffplayout.yml', '.jpg,.jpeg,.png');";
|
'/etc/ffplayout/ffplayout.yml', '.jpg,.jpeg,.png', 'ffplayout.service');";
|
||||||
sqlx::query(query).bind(secret).execute(&instances).await?;
|
sqlx::query(query).bind(secret).execute(&instances).await?;
|
||||||
instances.close().await;
|
instances.close().await;
|
||||||
|
|
||||||
|
@ -66,4 +66,5 @@ pub struct Settings {
|
|||||||
#[sqlx(default)]
|
#[sqlx(default)]
|
||||||
#[serde(skip_serializing, skip_deserializing)]
|
#[serde(skip_serializing, skip_deserializing)]
|
||||||
pub secret: String,
|
pub secret: String,
|
||||||
|
pub service: String,
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ use simplelog::*;
|
|||||||
|
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
auth::{create_jwt, Claims},
|
auth::{create_jwt, Claims},
|
||||||
control::{control_state, media_info, send_message},
|
control::{control_service, control_state, media_info, send_message, Process},
|
||||||
errors::ServiceError,
|
errors::ServiceError,
|
||||||
files::{browser, remove_file_or_folder, rename_file, upload, MoveObject, PathObject},
|
files::{browser, remove_file_or_folder, rename_file, upload, MoveObject, PathObject},
|
||||||
handles::{
|
handles::{
|
||||||
@ -352,6 +352,18 @@ pub async fn media_last(id: web::Path<i64>) -> Result<impl Responder, ServiceErr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// curl -X GET http://localhost:8080/api/control/1/process/
|
||||||
|
/// --header 'Content-Type: application/json' --header 'Authorization: <TOKEN>'
|
||||||
|
/// -d '{"command": "start"}'
|
||||||
|
#[post("/control/{id}/process/")]
|
||||||
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
|
pub async fn process_control(
|
||||||
|
id: web::Path<i64>,
|
||||||
|
proc: web::Json<Process>,
|
||||||
|
) -> Result<impl Responder, ServiceError> {
|
||||||
|
control_service(*id, &proc.command).await
|
||||||
|
}
|
||||||
|
|
||||||
/// ----------------------------------------------------------------------------
|
/// ----------------------------------------------------------------------------
|
||||||
/// ffplayout playlist operations
|
/// ffplayout playlist operations
|
||||||
///
|
///
|
||||||
|
@ -52,6 +52,7 @@ assets = [
|
|||||||
"755"
|
"755"
|
||||||
],
|
],
|
||||||
["../assets/ffpapi.service", "/lib/systemd/system/ffpapi.service", "644"],
|
["../assets/ffpapi.service", "/lib/systemd/system/ffpapi.service", "644"],
|
||||||
|
["../assets/11-ffplayout", "/etc/sudoers.d/11-ffplayout", "644"],
|
||||||
["../assets/ffplayout.yml", "/etc/ffplayout/ffplayout.yml", "644"],
|
["../assets/ffplayout.yml", "/etc/ffplayout/ffplayout.yml", "644"],
|
||||||
["../assets/logo.png", "/usr/share/ffplayout/logo.png", "644"],
|
["../assets/logo.png", "/usr/share/ffplayout/logo.png", "644"],
|
||||||
["../README.md", "/usr/share/doc/ffplayout/README", "644"],
|
["../README.md", "/usr/share/doc/ffplayout/README", "644"],
|
||||||
@ -69,6 +70,7 @@ assets = [
|
|||||||
{ source = "../assets/ffplayout.yml", dest = "/etc/ffplayout/ffplayout.yml", mode = "644", config = true },
|
{ source = "../assets/ffplayout.yml", dest = "/etc/ffplayout/ffplayout.yml", mode = "644", config = true },
|
||||||
{ source = "../assets/ffpapi.service", dest = "/lib/systemd/system/ffpapi.service", mode = "644" },
|
{ source = "../assets/ffpapi.service", dest = "/lib/systemd/system/ffpapi.service", mode = "644" },
|
||||||
{ source = "../assets/ffplayout.service", dest = "/lib/systemd/system/ffplayout.service", mode = "644" },
|
{ source = "../assets/ffplayout.service", dest = "/lib/systemd/system/ffplayout.service", mode = "644" },
|
||||||
|
{ source = "../assets/11-ffplayout", dest = "/etc/sudoers.d/11-ffplayout", mode = "644" },
|
||||||
{ source = "../README.md", dest = "/usr/share/doc/ffplayout/README", mode = "644", doc = true },
|
{ source = "../README.md", dest = "/usr/share/doc/ffplayout/README", mode = "644", doc = true },
|
||||||
{ source = "../LICENSE", dest = "/usr/share/doc/ffplayout/LICENSE", mode = "644" },
|
{ source = "../LICENSE", dest = "/usr/share/doc/ffplayout/LICENSE", mode = "644" },
|
||||||
{ source = "../assets/logo.png", dest = "/usr/share/ffplayout/logo.png", mode = "644" },
|
{ source = "../assets/logo.png", dest = "/usr/share/ffplayout/logo.png", mode = "644" },
|
||||||
|
Loading…
Reference in New Issue
Block a user