Lines
0 %
Functions
Branches
100 %
use askama::Template;
use axum::{
Extension, Json,
extract::{Multipart, Path, State},
http::StatusCode,
response::IntoResponse,
};
use serde::Deserialize;
use std::sync::Arc;
use uuid::Uuid;
use crate::{AppState, jwt_auth::JWTAuthMiddleware, pages::HtmlTemplate};
#[derive(Template)]
#[template(path = "pages/script/edit.html")]
struct ScriptEditPage {
id: Uuid,
name: Option<String>,
size: usize,
enabled: bool,
}
pub async fn script_edit_page(
State(_data): State<Arc<AppState>>,
Extension(jwt_auth): Extension<JWTAuthMiddleware>,
Path(id): Path<Uuid>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
let user = &jwt_auth.user;
let server_user = server::user::User { id: user.id };
let script = server_user
.get_script(id)
.await
.map_err(|e| (StatusCode::NOT_FOUND, format!("Script not found: {e:?}")))?;
let template = ScriptEditPage {
id: script.id,
name: script.name,
size: script.bytecode.len(),
enabled: script.enabled.as_deref() != Some("false"),
Ok(HtmlTemplate(template))
fn deserialize_checkbox<'de, D>(deserializer: D) -> Result<Option<bool>, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::Error;
let value: Option<serde_json::Value> = Option::deserialize(deserializer)?;
match value {
None => Ok(None),
Some(serde_json::Value::Bool(b)) => Ok(Some(b)),
Some(serde_json::Value::String(s)) => match s.as_str() {
"on" | "true" | "1" => Ok(Some(true)),
"off" | "false" | "0" | "" => Ok(Some(false)),
_ => Err(D::Error::custom(format!("invalid checkbox value: {s}"))),
},
Some(v) => Err(D::Error::custom(format!("invalid checkbox type: {v}"))),
#[derive(Deserialize)]
pub struct ScriptEditForm {
#[serde(default, deserialize_with = "deserialize_checkbox")]
enabled: Option<bool>,
pub async fn edit_script(
Json(form): Json<ScriptEditForm>,
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
server_user
.update_script_name(form.id, form.name)
.map_err(|e| {
let error_response = serde_json::json!({
"status": "fail",
"message": t!("Failed to update script name"),
});
log::error!("Failed to update script name: {e:?}");
(StatusCode::INTERNAL_SERVER_ERROR, Json(error_response))
})?;
let enabled = form.enabled.unwrap_or(false);
.set_script_enabled(form.id, enabled)
"message": t!("Failed to update script enabled state"),
log::error!("Failed to update script enabled state: {e:?}");
Ok(format!("{}: {}", t!("Script updated"), form.id))
pub async fn upload_script_bytecode(
mut multipart: Multipart,
while let Some(field) = multipart.next_field().await.map_err(|e| {
"message": "Failed to read multipart data",
log::error!("Multipart error: {e:?}");
(StatusCode::BAD_REQUEST, Json(error_response))
})? {
if field.name() == Some("bytecode") {
let data = field.bytes().await.map_err(|e| {
"message": "Failed to read file data",
log::error!("File read error: {e:?}");
.update_script_bytecode(id, data.to_vec())
"message": t!("Failed to update script bytecode"),
log::error!("Failed to update script bytecode: {e:?}");
return Ok(format!("{}: {}", t!("Script bytecode updated"), id));
"message": "No bytecode field found in upload",
Err((StatusCode::BAD_REQUEST, Json(error_response)))
pub async fn delete_script(
server_user.delete_script(id).await.map_err(|e| {
"message": t!("Failed to delete script"),
log::error!("Failed to delete script: {e:?}");
Ok(t!("Script deleted"))
pub async fn create_script(
let mut bytecode: Option<Vec<u8>> = None;
let mut name: Option<String> = None;
match field.name() {
Some("bytecode") => {
bytecode = Some(data.to_vec());
Some("name") => {
let text = field.text().await.map_err(|e| {
"message": "Failed to read name field",
log::error!("Field read error: {e:?}");
if !text.is_empty() {
name = Some(text);
_ => {}
let bytecode = bytecode.ok_or_else(|| {
"message": "No bytecode file provided",
let id = server_user
.create_script(bytecode, name)
"message": t!("Failed to create script"),
log::error!("Failed to create script: {e:?}");
Ok(format!("{}: {}", t!("Script created"), id))
#[template(path = "pages/script/create.html")]
struct ScriptCreatePage;
pub async fn script_create_page() -> impl IntoResponse {
HtmlTemplate(ScriptCreatePage {})