mirror of
https://github.com/dec0dOS/zero-ui.git
synced 2025-08-19 13:01:30 -07:00
feat: add file download support
feat: backend: new api to download file such as planet /PROJECT/frontend/down_folder/ http://locahost:4000/downfile/:downfilename feat: frontend: add file download component for ease use Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
This commit is contained in:
parent
96fd0b70aa
commit
17bf258ea1
7 changed files with 147 additions and 0 deletions
|
@ -12,6 +12,7 @@ const authRoutes = require("./routes/auth");
|
||||||
const networkRoutes = require("./routes/network");
|
const networkRoutes = require("./routes/network");
|
||||||
const memberRoutes = require("./routes/member");
|
const memberRoutes = require("./routes/member");
|
||||||
const controllerRoutes = require("./routes/controller");
|
const controllerRoutes = require("./routes/controller");
|
||||||
|
const downFileRoutes = require("./routes/downfile");
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
@ -64,6 +65,7 @@ routerController.use("", controllerRoutes);
|
||||||
app.use("/auth", authRoutes);
|
app.use("/auth", authRoutes);
|
||||||
app.use("/api", routerAPI); // offical SaaS API compatible
|
app.use("/api", routerAPI); // offical SaaS API compatible
|
||||||
app.use("/controller", routerController); // other controller-specific routes
|
app.use("/controller", routerController); // other controller-specific routes
|
||||||
|
app.use("/downfile", downFileRoutes); // file download routes
|
||||||
|
|
||||||
// error handlers
|
// error handlers
|
||||||
app.get("*", async function (req, res) {
|
app.get("*", async function (req, res) {
|
||||||
|
|
21
backend/routes/downfile.js
Normal file
21
backend/routes/downfile.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
const express = require("express");
|
||||||
|
const path = require("path");
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
const auth = require("../services/auth");
|
||||||
|
|
||||||
|
router.get("/:downfilename", async function (req, res) {
|
||||||
|
const ret = await auth.isUserLoggedIn(req);
|
||||||
|
if (ret) {
|
||||||
|
const filename = req.params.downfilename;
|
||||||
|
res.sendFile(
|
||||||
|
path.join(__dirname, "..", "..", "frontend", "down_folder", filename)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
res
|
||||||
|
.status(401)
|
||||||
|
.json({ error: "401 Not authorized, must Login to download" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
|
@ -35,3 +35,16 @@ async function isAuthorized(req, res, next) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.isUserLoggedIn = isUserLoggedIn;
|
||||||
|
async function isUserLoggedIn(req) {
|
||||||
|
if (process.env.ZU_DISABLE_AUTH === "true") {
|
||||||
|
// assuming logged in
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!req.token) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const user = await db.get("users").find({ token: req.token }).value();
|
||||||
|
return !!user ? true : false;
|
||||||
|
}
|
||||||
|
|
0
frontend/down_folder/.keep
Normal file
0
frontend/down_folder/.keep
Normal file
|
@ -5,6 +5,7 @@ import { Divider, Button, Grid, Typography, Box } from "@material-ui/core";
|
||||||
import useStyles from "./HomeLoggedIn.styles";
|
import useStyles from "./HomeLoggedIn.styles";
|
||||||
|
|
||||||
import NetworkButton from "./components/NetworkButton";
|
import NetworkButton from "./components/NetworkButton";
|
||||||
|
import DownloadFile from "./components/DownloadFile";
|
||||||
|
|
||||||
import API from "utils/API";
|
import API from "utils/API";
|
||||||
import { generateNetworkConfig } from "utils/NetworkConfig";
|
import { generateNetworkConfig } from "utils/NetworkConfig";
|
||||||
|
@ -40,6 +41,8 @@ function HomeLoggedIn() {
|
||||||
>
|
>
|
||||||
Create A Network
|
Create A Network
|
||||||
</Button>
|
</Button>
|
||||||
|
<Divider orientation="vertical" />
|
||||||
|
<DownloadFile />
|
||||||
<Divider />
|
<Divider />
|
||||||
<Grid container spacing={3} className={classes.container}>
|
<Grid container spacing={3} className={classes.container}>
|
||||||
<Grid item xs={6}>
|
<Grid item xs={6}>
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useLocalStorage } from "react-use";
|
||||||
|
import { TextField, Button, Snackbar } from "@material-ui/core";
|
||||||
|
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
function DownloadFile() {
|
||||||
|
const [snackbarOpen, setSnackbarOpen] = useState(false);
|
||||||
|
|
||||||
|
const [filename, setFilename] = useState("planet");
|
||||||
|
const [errormsg, setErrormsg] = useState("");
|
||||||
|
|
||||||
|
const [token] = useLocalStorage("token", null);
|
||||||
|
|
||||||
|
const downloadFile = async (fileName) => {
|
||||||
|
if (!!!token) {
|
||||||
|
await handleDownFileErr(`Invalid token`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof filename === "undefined" || filename.length === 0) {
|
||||||
|
await handleDownFileErr(`Invalid file name [${filename}]`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let ret;
|
||||||
|
ret = await axios
|
||||||
|
.create({
|
||||||
|
baseURL: `/downfile/`,
|
||||||
|
responseType: "arraybuffer",
|
||||||
|
withCredentials: "true",
|
||||||
|
headers:
|
||||||
|
localStorage.getItem("disableAuth") === "true"
|
||||||
|
? {}
|
||||||
|
: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.get(`${fileName}`)
|
||||||
|
.then((resp) => {
|
||||||
|
const blobUrl = window.URL.createObjectURL(
|
||||||
|
new Blob([resp.data], { type: "application/octet-stream" })
|
||||||
|
);
|
||||||
|
const tmpLink = document.createElement("a");
|
||||||
|
tmpLink.style.display = "none";
|
||||||
|
tmpLink.href = blobUrl;
|
||||||
|
tmpLink.setAttribute("download", `${fileName}`);
|
||||||
|
if (typeof tmpLink.download === "undefined") {
|
||||||
|
tmpLink.setAttribute("target", "_blank");
|
||||||
|
}
|
||||||
|
document.body.appendChild(tmpLink);
|
||||||
|
tmpLink.click();
|
||||||
|
document.body.removeChild(tmpLink);
|
||||||
|
window.URL.revokeObjectURL(blobUrl);
|
||||||
|
})
|
||||||
|
.catch(async (e) => {
|
||||||
|
let errmsg = `downfile(${fileName}): ${e}: ${ret}`;
|
||||||
|
await handleDownFileErr(errmsg);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDownFileBtnClick = async () => {
|
||||||
|
await downloadFile(filename);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDownFileErr = async (errormsg) => {
|
||||||
|
setErrormsg(errormsg);
|
||||||
|
setSnackbarOpen(true);
|
||||||
|
let sleep = function (ms) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, ms);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
await sleep(3 * 1000);
|
||||||
|
setSnackbarOpen(false);
|
||||||
|
setErrormsg("");
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TextField
|
||||||
|
value={filename}
|
||||||
|
onChange={(e) => {
|
||||||
|
setFilename(e.target.value);
|
||||||
|
}}
|
||||||
|
margin="dense"
|
||||||
|
label="File name to download"
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={handleDownFileBtnClick}
|
||||||
|
>
|
||||||
|
Download file
|
||||||
|
</Button>
|
||||||
|
<Snackbar
|
||||||
|
open={snackbarOpen}
|
||||||
|
anchorOrigin={{
|
||||||
|
vertical: "top",
|
||||||
|
horizontal: "center",
|
||||||
|
}}
|
||||||
|
message={errormsg}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DownloadFile;
|
|
@ -0,0 +1 @@
|
||||||
|
export { default } from "./DownloadFile";
|
Loading…
Add table
Add a link
Reference in a new issue