add camera edit and add functionality
This commit is contained in:
323
src/admin.rs
323
src/admin.rs
@@ -1,6 +1,6 @@
|
||||
use crate::{language::language, page::Page, AppState};
|
||||
use axum::{
|
||||
extract::State,
|
||||
extract::{Path, Query, State},
|
||||
http::HeaderMap,
|
||||
response::{IntoResponse, Redirect, Response},
|
||||
routing::{get, post},
|
||||
@@ -12,13 +12,33 @@ use axum_extra::extract::{
|
||||
};
|
||||
use maud::{html, Markup};
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use time::OffsetDateTime;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct LoginForm {
|
||||
password: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct CameraForm {
|
||||
uuid: String,
|
||||
name: String,
|
||||
desc: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct DeleteCameraForm {
|
||||
uuid: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct EditCameraForm {
|
||||
name: String,
|
||||
desc: Option<String>,
|
||||
}
|
||||
|
||||
async fn login_page(cookies: CookieJar, headers: HeaderMap) -> Markup {
|
||||
let lang = language(&cookies, &headers);
|
||||
rust_i18n::set_locale(lang.to_locale());
|
||||
@@ -94,6 +114,11 @@ async fn protected_page(
|
||||
p { "Welcome to the admin area! This is a protected route." }
|
||||
p { "Only authenticated administrators can access this page." }
|
||||
|
||||
h2 { "Camera Management" }
|
||||
p { "Manage cameras in the system." }
|
||||
a href="/admin/cameras/add" { "Add Camera" }
|
||||
" | "
|
||||
a href="/admin/cameras" { "Manage Cameras" }
|
||||
|
||||
form method="POST" action="/admin/logout" {
|
||||
input type="submit" value="Logout" class="secondary";
|
||||
@@ -103,10 +128,306 @@ async fn protected_page(
|
||||
markup.into_response()
|
||||
}
|
||||
|
||||
async fn add_camera_page(
|
||||
private_cookies: PrivateCookieJar,
|
||||
cookies: CookieJar,
|
||||
headers: HeaderMap,
|
||||
Query(params): Query<HashMap<String, String>>,
|
||||
) -> Response {
|
||||
// Check if admin is authenticated
|
||||
if private_cookies.get("admin_session").is_none() {
|
||||
return Redirect::to("/admin/login").into_response();
|
||||
}
|
||||
|
||||
let lang = language(&cookies, &headers);
|
||||
rust_i18n::set_locale(lang.to_locale());
|
||||
|
||||
// Get pre-filled UUID from query params
|
||||
let prefilled_uuid = params.get("uuid").unwrap_or(&String::new()).clone();
|
||||
|
||||
let markup = Page::new(lang).content(html! {
|
||||
h1 { "Add Camera" }
|
||||
@if !prefilled_uuid.is_empty() {
|
||||
p.text-muted { "Auto-detected missing camera with UUID: " strong { (prefilled_uuid) } }
|
||||
}
|
||||
form method="POST" action="/admin/cameras/add" {
|
||||
fieldset {
|
||||
label for="uuid" { "Camera UUID:" }
|
||||
input
|
||||
type="text"
|
||||
name="uuid"
|
||||
id="uuid"
|
||||
placeholder="e.g., 123e4567-e89b-12d3-a456-426614174000"
|
||||
value=(prefilled_uuid)
|
||||
required;
|
||||
|
||||
label for="name" { "Camera Name:" }
|
||||
input
|
||||
type="text"
|
||||
name="name"
|
||||
id="name"
|
||||
placeholder="e.g., Front Entrance Camera"
|
||||
required;
|
||||
|
||||
label for="desc" { "Description (optional):" }
|
||||
textarea
|
||||
name="desc"
|
||||
id="desc"
|
||||
placeholder="e.g., Camera monitoring the main entrance" {};
|
||||
|
||||
input type="submit" value="Add Camera";
|
||||
}
|
||||
}
|
||||
p {
|
||||
a href="/protected" { "← Back to Admin Dashboard" }
|
||||
}
|
||||
});
|
||||
|
||||
markup.into_response()
|
||||
}
|
||||
|
||||
async fn manage_cameras_page(
|
||||
State(state): State<AppState>,
|
||||
private_cookies: PrivateCookieJar,
|
||||
cookies: CookieJar,
|
||||
headers: HeaderMap,
|
||||
) -> Response {
|
||||
// Check if admin is authenticated
|
||||
if private_cookies.get("admin_session").is_none() {
|
||||
return Redirect::to("/admin/login").into_response();
|
||||
}
|
||||
|
||||
let lang = language(&cookies, &headers);
|
||||
rust_i18n::set_locale(lang.to_locale());
|
||||
|
||||
let cameras = state.backend.get_all_cameras().await;
|
||||
|
||||
let markup = Page::new(lang).content(html! {
|
||||
h1 { "Manage Cameras" }
|
||||
p { "Total cameras: " strong { (cameras.len()) } }
|
||||
|
||||
@if cameras.is_empty() {
|
||||
p.text-muted { "No cameras found in the system." }
|
||||
} @else {
|
||||
table {
|
||||
thead {
|
||||
tr {
|
||||
th { "UUID" }
|
||||
th { "Name" }
|
||||
th { "Description" }
|
||||
th { "Actions" }
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
@for camera in &cameras {
|
||||
tr {
|
||||
td {
|
||||
code { (camera.uuid) }
|
||||
}
|
||||
td { (camera.name) }
|
||||
td {
|
||||
@if let Some(desc) = &camera.desc {
|
||||
(desc)
|
||||
} @else {
|
||||
em.text-muted { "No description" }
|
||||
}
|
||||
}
|
||||
td {
|
||||
a href=(format!("/admin/cameras/{}/edit", camera.uuid)) class="secondary" style="margin-right: 0.5rem;" { "Edit" }
|
||||
form method="POST" action="/admin/cameras/delete" style="display: inline;" {
|
||||
input type="hidden" name="uuid" value=(camera.uuid);
|
||||
input
|
||||
type="submit"
|
||||
value="Delete"
|
||||
class="secondary"
|
||||
onclick="return confirm('Are you sure you want to delete this camera? This will also remove all associated sightings.')";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
a href="/admin/cameras/add" { "Add New Camera" }
|
||||
" | "
|
||||
a href="/protected" { "← Back to Admin Dashboard" }
|
||||
}
|
||||
});
|
||||
|
||||
markup.into_response()
|
||||
}
|
||||
|
||||
async fn add_camera(
|
||||
State(state): State<AppState>,
|
||||
private_cookies: PrivateCookieJar,
|
||||
Form(form): Form<CameraForm>,
|
||||
) -> Response {
|
||||
// Check if admin is authenticated
|
||||
if private_cookies.get("admin_session").is_none() {
|
||||
return Redirect::to("/admin/login").into_response();
|
||||
}
|
||||
|
||||
// Parse UUID
|
||||
let uuid = match Uuid::parse_str(&form.uuid) {
|
||||
Ok(uuid) => uuid,
|
||||
Err(_) => return Redirect::to("/admin/cameras/add?error=invalid_uuid").into_response(),
|
||||
};
|
||||
|
||||
// Check if camera already exists
|
||||
if state.backend.get_camera(&uuid).await.is_some() {
|
||||
return Redirect::to("/admin/cameras/add?error=already_exists").into_response();
|
||||
}
|
||||
|
||||
// Create the camera
|
||||
let desc = if form
|
||||
.desc
|
||||
.as_ref()
|
||||
.map(|s| s.trim().is_empty())
|
||||
.unwrap_or(true)
|
||||
{
|
||||
None
|
||||
} else {
|
||||
form.desc.as_deref()
|
||||
};
|
||||
|
||||
match state.backend.create_camera(&uuid, &form.name, desc).await {
|
||||
Ok(_) => Redirect::to("/admin/cameras?camera_added=1").into_response(),
|
||||
Err(_) => Redirect::to("/admin/cameras/add?error=creation_failed").into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn edit_camera_page(
|
||||
State(state): State<AppState>,
|
||||
private_cookies: PrivateCookieJar,
|
||||
cookies: CookieJar,
|
||||
headers: HeaderMap,
|
||||
Path(uuid_str): Path<String>,
|
||||
) -> Response {
|
||||
// Check if admin is authenticated
|
||||
if private_cookies.get("admin_session").is_none() {
|
||||
return Redirect::to("/admin/login").into_response();
|
||||
}
|
||||
|
||||
let lang = language(&cookies, &headers);
|
||||
rust_i18n::set_locale(lang.to_locale());
|
||||
|
||||
// Parse UUID
|
||||
let uuid = match Uuid::parse_str(&uuid_str) {
|
||||
Ok(uuid) => uuid,
|
||||
Err(_) => return Redirect::to("/admin/cameras?error=invalid_uuid").into_response(),
|
||||
};
|
||||
|
||||
// Get camera details
|
||||
let Some(camera) = state.backend.get_camera(&uuid).await else {
|
||||
return Redirect::to("/admin/cameras?error=not_found").into_response();
|
||||
};
|
||||
|
||||
let markup = Page::new(lang).content(html! {
|
||||
h1 { "Edit Camera" }
|
||||
p.text-muted { "UUID: " code { (camera.uuid) } }
|
||||
|
||||
form method="POST" action=(format!("/admin/cameras/{}/edit", camera.uuid)) {
|
||||
fieldset {
|
||||
label for="name" { "Camera Name:" }
|
||||
input
|
||||
type="text"
|
||||
name="name"
|
||||
id="name"
|
||||
value=(camera.name)
|
||||
required;
|
||||
|
||||
label for="desc" { "Description (optional):" }
|
||||
textarea
|
||||
name="desc"
|
||||
id="desc"
|
||||
placeholder="e.g., Camera monitoring the main entrance" {
|
||||
@if let Some(desc) = &camera.desc {
|
||||
(desc)
|
||||
}
|
||||
}
|
||||
|
||||
input type="submit" value="Update Camera";
|
||||
}
|
||||
}
|
||||
p {
|
||||
a href="/admin/cameras" { "← Back to Camera List" }
|
||||
}
|
||||
});
|
||||
|
||||
markup.into_response()
|
||||
}
|
||||
|
||||
async fn update_camera(
|
||||
State(state): State<AppState>,
|
||||
private_cookies: PrivateCookieJar,
|
||||
Path(uuid_str): Path<String>,
|
||||
Form(form): Form<EditCameraForm>,
|
||||
) -> Response {
|
||||
// Check if admin is authenticated
|
||||
if private_cookies.get("admin_session").is_none() {
|
||||
return Redirect::to("/admin/login").into_response();
|
||||
}
|
||||
|
||||
// Parse UUID
|
||||
let uuid = match Uuid::parse_str(&uuid_str) {
|
||||
Ok(uuid) => uuid,
|
||||
Err(_) => return Redirect::to("/admin/cameras?error=invalid_uuid").into_response(),
|
||||
};
|
||||
|
||||
// Process description
|
||||
let desc = if form
|
||||
.desc
|
||||
.as_ref()
|
||||
.map(|s| s.trim().is_empty())
|
||||
.unwrap_or(true)
|
||||
{
|
||||
None
|
||||
} else {
|
||||
form.desc.as_deref()
|
||||
};
|
||||
|
||||
match state.backend.update_camera(&uuid, &form.name, desc).await {
|
||||
Ok(true) => Redirect::to("/admin/cameras?camera_updated=1").into_response(),
|
||||
Ok(false) => Redirect::to("/admin/cameras?error=not_found").into_response(),
|
||||
Err(_) => Redirect::to("/admin/cameras?error=update_failed").into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn delete_camera(
|
||||
State(state): State<AppState>,
|
||||
private_cookies: PrivateCookieJar,
|
||||
Form(form): Form<DeleteCameraForm>,
|
||||
) -> Response {
|
||||
// Check if admin is authenticated
|
||||
if private_cookies.get("admin_session").is_none() {
|
||||
return Redirect::to("/admin/login").into_response();
|
||||
}
|
||||
|
||||
// Parse UUID
|
||||
let uuid = match Uuid::parse_str(&form.uuid) {
|
||||
Ok(uuid) => uuid,
|
||||
Err(_) => return Redirect::to("/admin/cameras?error=invalid_uuid").into_response(),
|
||||
};
|
||||
|
||||
match state.backend.delete_camera(&uuid).await {
|
||||
Ok(true) => Redirect::to("/admin/cameras?camera_deleted=1").into_response(),
|
||||
Ok(false) => Redirect::to("/admin/cameras?error=not_found").into_response(),
|
||||
Err(_) => Redirect::to("/admin/cameras?error=deletion_failed").into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn routes() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route("/admin/login", get(login_page))
|
||||
.route("/admin/login", post(login))
|
||||
.route("/admin/logout", post(logout))
|
||||
.route("/protected", get(protected_page))
|
||||
.route("/admin/cameras", get(manage_cameras_page))
|
||||
.route("/admin/cameras/add", get(add_camera_page))
|
||||
.route("/admin/cameras/add", post(add_camera))
|
||||
.route("/admin/cameras/:uuid/edit", get(edit_camera_page))
|
||||
.route("/admin/cameras/:uuid/edit", post(update_camera))
|
||||
.route("/admin/cameras/delete", post(delete_camera))
|
||||
}
|
||||
|
Reference in New Issue
Block a user