feat: support delete operation
This commit is contained in:
parent
78e7daf7cc
commit
dd888f1ae8
4 changed files with 74 additions and 21 deletions
|
@ -34,6 +34,7 @@ fn app() -> clap::Command<'static> {
|
|||
.long("readonly")
|
||||
.help("Only serve static files, no operations like upload and delete");
|
||||
|
||||
|
||||
let arg_auth = Arg::new("auth")
|
||||
.short('a')
|
||||
.long("auth")
|
||||
|
|
|
@ -59,14 +59,26 @@ html {
|
|||
}
|
||||
|
||||
.main .cell-name {
|
||||
max-width: 300px;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.main .cell-mtime {
|
||||
width: 150px;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.main .cell-size {
|
||||
text-align: right;
|
||||
width: 80px;
|
||||
width: 100px;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.main .cell-actions {
|
||||
width: 100px;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
|
||||
.path svg {
|
||||
height: 100%;
|
||||
fill: rgba(3,47,98,0.5);
|
||||
|
@ -98,5 +110,5 @@ html {
|
|||
}
|
||||
|
||||
.uploader {
|
||||
padding-right: 2em;
|
||||
}
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
|
|
@ -103,17 +103,41 @@
|
|||
}
|
||||
}
|
||||
|
||||
function addFile(file) {
|
||||
function addPath(file, index) {
|
||||
const actionTd = file.name === ".." || readonly ? "" : `
|
||||
<td class="cell-actions">
|
||||
<div onclick="deletePath(${index})" class="delete-btn" id="deleteBtn${index}" title="Delete ${file.name}">
|
||||
<svg width="18" height="18" viewBox="0 0 40 40"><path d="M28,40H11.8c-3.3,0-5.9-2.7-5.9-5.9V16c0-0.6,0.4-1,1-1s1,0.4,1,1v18.1c0,2.2,1.8,3.9,3.9,3.9H28c2.2,0,3.9-1.8,3.9-3.9V16 c0-0.6,0.4-1,1-1s1,0.4,1,1v18.1C33.9,37.3,31.2,40,28,40z"/></g><g><path d="M33.3,4.9h-7.6C25.2,2.1,22.8,0,19.9,0s-5.3,2.1-5.8,4.9H6.5c-2.3,0-4.1,1.8-4.1,4.1S4.2,13,6.5,13h26.9 c2.3,0,4.1-1.8,4.1-4.1S35.6,4.9,33.3,4.9z M19.9,2c1.8,0,3.3,1.2,3.7,2.9h-7.5C16.6,3.2,18.1,2,19.9,2z M33.3,11H6.5 c-1.1,0-2.1-0.9-2.1-2.1c0-1.1,0.9-2.1,2.1-2.1h26.9c1.1,0,2.1,0.9,2.1,2.1C35.4,10.1,34.5,11,33.3,11z"/></g><g><path d="M12.9,35.1c-0.6,0-1-0.4-1-1V17.4c0-0.6,0.4-1,1-1s1,0.4,1,1v16.7C13.9,34.6,13.4,35.1,12.9,35.1z"/></g><g><path d="M26.9,35.1c-0.6,0-1-0.4-1-1V17.4c0-0.6,0.4-1,1-1s1,0.4,1,1v16.7C27.9,34.6,27.4,35.1,26.9,35.1z"/></g><g><path d="M19.9,35.1c-0.6,0-1-0.4-1-1V17.4c0-0.6,0.4-1,1-1s1,0.4,1,1v16.7C20.9,34.6,20.4,35.1,19.9,35.1z"/></svg>
|
||||
</div>
|
||||
</td>`
|
||||
|
||||
$tbody.insertAdjacentHTML("beforeend", `
|
||||
<tr>
|
||||
<tr id="addPath${index}">
|
||||
<td class="path cell-name">
|
||||
<div>${getSvg(file.path_type)}</div>
|
||||
<a href="${encodeURI(file.path)}" title="${file.name}">${file.name}</a>
|
||||
</td>
|
||||
<td class="cell-mtime">${formatMtime(file.mtime)}</td>
|
||||
<td class="cell-size">${formatSize(file.size)}</td>
|
||||
</tr>
|
||||
`)
|
||||
${actionTd}
|
||||
</tr>`)
|
||||
}
|
||||
|
||||
async function deletePath(index) {
|
||||
const file = paths[index];
|
||||
if (!file) return;
|
||||
try {
|
||||
const res = await fetch(encodeURI(file.path), {
|
||||
method: "DELETE",
|
||||
});
|
||||
if (res.status !== 200) {
|
||||
const text = await res.text();
|
||||
throw new Error(text);
|
||||
}
|
||||
document.getElementById(`addPath${index}`).remove();
|
||||
} catch (err) {
|
||||
alert(`Failed to delete ${file.name}, ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function addUploadControl() {
|
||||
|
@ -164,11 +188,13 @@
|
|||
}
|
||||
|
||||
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
addBreadcrumb(breadcrumb);
|
||||
paths.forEach(file => addFile(file));
|
||||
if (readonly) {
|
||||
paths.forEach((file, index) => addPath(file, index));
|
||||
if (!readonly) {
|
||||
addUploadControl();
|
||||
document.querySelector(".main thead tr").insertAdjacentHTML("beforeend", `<th class="cell-actions">Actions</th>`);
|
||||
document.getElementById("file").addEventListener("change", e => {
|
||||
const files = e.target.files;
|
||||
for (const file of files) {
|
||||
|
|
|
@ -60,6 +60,12 @@ impl InnerService {
|
|||
}
|
||||
|
||||
pub async fn handle(self: Arc<Self>, req: Request) -> Result<Response, hyper::Error> {
|
||||
if !self.auth_guard(&req).unwrap_or_default() {
|
||||
let mut res = status_code!(StatusCode::UNAUTHORIZED);
|
||||
res.headers_mut().insert("WWW-Authenticate" , HeaderValue::from_static("Basic"));
|
||||
return Ok(res)
|
||||
}
|
||||
|
||||
let res = if req.method() == Method::GET {
|
||||
self.handle_static(req).await
|
||||
} else if req.method() == Method::PUT {
|
||||
|
@ -67,6 +73,8 @@ impl InnerService {
|
|||
return Ok(status_code!(StatusCode::FORBIDDEN));
|
||||
}
|
||||
self.handle_upload(req).await
|
||||
} else if req.method() == Method::DELETE {
|
||||
self.handle_delete(req).await
|
||||
} else {
|
||||
return Ok(status_code!(StatusCode::NOT_FOUND));
|
||||
};
|
||||
|
@ -74,11 +82,6 @@ impl InnerService {
|
|||
}
|
||||
|
||||
async fn handle_static(&self, req: Request) -> BoxResult<Response> {
|
||||
if !self.auth_guard(&req).unwrap_or_default() {
|
||||
let mut res = status_code!(StatusCode::UNAUTHORIZED);
|
||||
res.headers_mut().insert("WWW-Authenticate" , HeaderValue::from_static("Basic"));
|
||||
return Ok(res)
|
||||
}
|
||||
let path = match self.get_file_path(req.uri().path())? {
|
||||
Some(path) => path,
|
||||
None => return Ok(status_code!(StatusCode::FORBIDDEN)),
|
||||
|
@ -96,11 +99,6 @@ impl InnerService {
|
|||
}
|
||||
|
||||
async fn handle_upload(&self, mut req: Request) -> BoxResult<Response> {
|
||||
if !self.auth_guard(&req).unwrap_or_default() {
|
||||
let mut res = status_code!(StatusCode::UNAUTHORIZED);
|
||||
res.headers_mut().insert("WWW-Authenticate" , HeaderValue::from_static("Basic"));
|
||||
return Ok(res)
|
||||
}
|
||||
let path = match self.get_file_path(req.uri().path())? {
|
||||
Some(path) => path,
|
||||
None => return Ok(status_code!(StatusCode::FORBIDDEN)),
|
||||
|
@ -125,6 +123,22 @@ impl InnerService {
|
|||
return Ok(status_code!(StatusCode::OK));
|
||||
}
|
||||
|
||||
async fn handle_delete(&self, req: Request) -> BoxResult<Response> {
|
||||
let path = match self.get_file_path(req.uri().path())? {
|
||||
Some(path) => path,
|
||||
None => return Ok(status_code!(StatusCode::FORBIDDEN)),
|
||||
};
|
||||
|
||||
let meta = fs::metadata(&path).await?;
|
||||
if meta.is_file() {
|
||||
fs::remove_file(path).await?;
|
||||
} else {
|
||||
fs::remove_dir_all(path).await?;
|
||||
}
|
||||
Ok(status_code!(StatusCode::OK))
|
||||
|
||||
}
|
||||
|
||||
async fn handle_send_dir(&self, path: &Path) -> BoxResult<Response> {
|
||||
let base_path = &self.args.path;
|
||||
let mut rd = fs::read_dir(path).await?;
|
||||
|
@ -179,7 +193,7 @@ impl InnerService {
|
|||
|
||||
paths.sort_unstable();
|
||||
let breadcrumb = self.get_breadcrumb(path);
|
||||
let data = SendDirData { breadcrumb, paths, readonly: !self.args.readonly };
|
||||
let data = SendDirData { breadcrumb, paths, readonly: self.args.readonly };
|
||||
let data = serde_json::to_string(&data).unwrap();
|
||||
|
||||
let mut output =
|
||||
|
|
Loading…
Reference in a new issue