Added sanitization of request input for assets_download function. Changed assets download UI for button with little animation while downloading.
This commit is contained in:
parent
0afcf5a12b
commit
a5f66bda63
|
@ -56,7 +56,9 @@ function downloadAssetsList(url) {
|
||||||
for (const i in availableAssets[assetType]) {
|
for (const i in availableAssets[assetType]) {
|
||||||
const asset = availableAssets[assetType][i];
|
const asset = availableAssets[assetType][i];
|
||||||
const elemId = `assets_install_${assetType}_${i}`;
|
const elemId = `assets_install_${assetType}_${i}`;
|
||||||
let element = $('<input />', { type: 'checkbox', id: elemId})
|
let element = $('<button />', { id:elemId, type:"button", class:"asset-download-button menu_button"})
|
||||||
|
const label = $("<i class=\"fa-solid fa-download fa-xl\"></i>");
|
||||||
|
element.append(label);
|
||||||
|
|
||||||
//if (DEBUG_TONY_SAMA_FORK_MODE)
|
//if (DEBUG_TONY_SAMA_FORK_MODE)
|
||||||
// assetUrl = assetUrl.replace("https://github.com/SillyTavern/","https://github.com/Tony-sama/"); // DBG
|
// assetUrl = assetUrl.replace("https://github.com/SillyTavern/","https://github.com/Tony-sama/"); // DBG
|
||||||
|
@ -64,17 +66,20 @@ function downloadAssetsList(url) {
|
||||||
console.debug(DEBUG_PREFIX,"Checking asset",asset["id"], asset["url"]);
|
console.debug(DEBUG_PREFIX,"Checking asset",asset["id"], asset["url"]);
|
||||||
|
|
||||||
if (isAssetInstalled(assetType, asset["id"])) {
|
if (isAssetInstalled(assetType, asset["id"])) {
|
||||||
console.debug(DEBUG_PREFIX,"installed, checked")
|
console.debug(DEBUG_PREFIX,"installed, checked");
|
||||||
element.prop("disabled",true);
|
label.toggleClass("fa-download");
|
||||||
element.prop("checked",true);
|
label.toggleClass("fa-check");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.debug(DEBUG_PREFIX,"not installed, unchecked")
|
console.debug(DEBUG_PREFIX,"not installed, unchecked")
|
||||||
element.prop("checked",false);
|
element.prop("checked",false);
|
||||||
element.on("click", function(){
|
element.on("click", async function(){
|
||||||
installAsset(asset["url"], assetType, asset["id"]);
|
|
||||||
element.prop("disabled",true);
|
|
||||||
element.off("click");
|
element.off("click");
|
||||||
|
label.toggleClass("fa-download");
|
||||||
|
this.classList.toggle('asset-download-button-loading');
|
||||||
|
await installAsset(asset["url"], assetType, asset["id"]);
|
||||||
|
label.toggleClass("fa-check");
|
||||||
|
this.classList.toggle('asset-download-button-loading');
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +87,7 @@ function downloadAssetsList(url) {
|
||||||
|
|
||||||
$(`<i></i>`)
|
$(`<i></i>`)
|
||||||
.append(element)
|
.append(element)
|
||||||
.append(`<p>${asset["id"]}</p>`)
|
.append(`<span>${asset["id"]}</span>`)
|
||||||
.appendTo(assetTypeMenu);
|
.appendTo(assetTypeMenu);
|
||||||
}
|
}
|
||||||
assetTypeMenu.appendTo("#assets_menu");
|
assetTypeMenu.appendTo("#assets_menu");
|
||||||
|
@ -109,14 +114,15 @@ function isAssetInstalled(assetType,filename) {
|
||||||
|
|
||||||
async function installAsset(url, assetType, filename) {
|
async function installAsset(url, assetType, filename) {
|
||||||
console.debug(DEBUG_PREFIX,"Downloading ",url);
|
console.debug(DEBUG_PREFIX,"Downloading ",url);
|
||||||
const save_path = "public/assets/"+assetType+"/"+filename;
|
const category = assetType;
|
||||||
try {
|
try {
|
||||||
const result = await fetch(`/asset_download?url=${url}&save_path=${save_path}`, {
|
const result = await fetch(`/asset_download?url=${url}&category=${category}&filename=${filename}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: getRequestHeaders(),
|
headers: getRequestHeaders(),
|
||||||
});
|
});
|
||||||
let assets = result.ok ? (await result.json()) : [];
|
if(result.ok) {
|
||||||
return assets;
|
console.debug(DEBUG_PREFIX,"Download success.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
|
|
@ -6,4 +6,64 @@
|
||||||
.assets-list-div i {
|
.assets-list-div i {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: left;
|
||||||
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.assets-list-div i span{
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-download-button {
|
||||||
|
position: relative;
|
||||||
|
width: 50px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
border-radius: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-download-button:active {
|
||||||
|
background: #007a63;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-download-button-text {
|
||||||
|
font: bold 20px "Quicksand", san-serif;
|
||||||
|
color: #ffffff;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-download-button-loading .asset-download-button-text {
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-download-button-loading::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto;
|
||||||
|
border: 4px solid transparent;
|
||||||
|
border-top-color: #ffffff;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: asset-download-button-loading-spinner 1s ease infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes asset-download-button-loading-spinner {
|
||||||
|
from {
|
||||||
|
transform: rotate(0turn);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
transform: rotate(1turn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
38
server.js
38
server.js
|
@ -5056,11 +5056,37 @@ app.post('/asset_download', jsonParser, async (request, response) => {
|
||||||
const { finished } = require('stream/promises');
|
const { finished } = require('stream/promises');
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const url = request.query.url;
|
const url = request.query.url;
|
||||||
const file_path = request.query.save_path;
|
const inputCategory = request.query.category;
|
||||||
|
const inputFilename = request.query.filename;
|
||||||
|
const validCategories = ["bgm","ambient"]
|
||||||
|
|
||||||
|
// Check category
|
||||||
|
let category = null
|
||||||
|
for(i of validCategories)
|
||||||
|
if (i == inputCategory)
|
||||||
|
category = i
|
||||||
|
|
||||||
|
if (category === null) {
|
||||||
|
console.debug("Bad request: unsuported asset category.");
|
||||||
|
return response.sendStatus(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanitize filename
|
||||||
|
if (inputFilename.indexOf('\0') !== -1) {
|
||||||
|
console.debug("Bad request: poisong null bytes in filename.");
|
||||||
|
return response.sendStatus(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^[a-zA-Z0-9_\-\.]+$/.test(inputFilename)) {
|
||||||
|
console.debug("Bad request: illegal character in filename, only alphanumeric, '_', '-' are accepted.");
|
||||||
|
return response.sendStatus(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const safe_input = path.normalize(inputFilename).replace(/^(\.\.(\/|\\|$))+/, '');
|
||||||
|
const file_path = path.join(directories.assets, category, safe_input)
|
||||||
console.debug("Request received to download", url,"to",file_path);
|
console.debug("Request received to download", url,"to",file_path);
|
||||||
|
|
||||||
|
try {
|
||||||
const downloadFile = (async (url, file_path) => {
|
const downloadFile = (async (url, file_path) => {
|
||||||
const res = await fetch(url);
|
const res = await fetch(url);
|
||||||
const destination = path.resolve(file_path);
|
const destination = path.resolve(file_path);
|
||||||
|
@ -5069,7 +5095,13 @@ app.post('/asset_download', jsonParser, async (request, response) => {
|
||||||
console.debug("Download finished, file saved to",file_path);
|
console.debug("Download finished, file saved to",file_path);
|
||||||
});
|
});
|
||||||
|
|
||||||
downloadFile(url, file_path)
|
await downloadFile(url, file_path);
|
||||||
|
response.sendStatus(200);
|
||||||
|
}
|
||||||
|
catch(error) {
|
||||||
|
console.log(error);
|
||||||
|
response.sendStatus(500);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue