diff --git a/backend/app.js b/backend/app.js
index 263a965..f2a4939 100644
--- a/backend/app.js
+++ b/backend/app.js
@@ -12,6 +12,7 @@ const authRoutes = require("./routes/auth");
const networkRoutes = require("./routes/network");
const memberRoutes = require("./routes/member");
const controllerRoutes = require("./routes/controller");
+const downFileRoutes = require("./routes/downfile");
const app = express();
@@ -64,6 +65,7 @@ routerController.use("", controllerRoutes);
app.use("/auth", authRoutes);
app.use("/api", routerAPI); // offical SaaS API compatible
app.use("/controller", routerController); // other controller-specific routes
+app.use("/downfile", downFileRoutes); // file download routes
// error handlers
app.get("*", async function (req, res) {
diff --git a/backend/routes/downfile.js b/backend/routes/downfile.js
new file mode 100644
index 0000000..a82e1cb
--- /dev/null
+++ b/backend/routes/downfile.js
@@ -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;
diff --git a/backend/services/auth.js b/backend/services/auth.js
index 9833857..35c3c5f 100644
--- a/backend/services/auth.js
+++ b/backend/services/auth.js
@@ -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;
+}
diff --git a/frontend/down_folder/.keep b/frontend/down_folder/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/frontend/src/components/HomeLoggedIn/HomeLoggedIn.jsx b/frontend/src/components/HomeLoggedIn/HomeLoggedIn.jsx
index ff45f76..8d79365 100644
--- a/frontend/src/components/HomeLoggedIn/HomeLoggedIn.jsx
+++ b/frontend/src/components/HomeLoggedIn/HomeLoggedIn.jsx
@@ -5,6 +5,7 @@ import { Divider, Button, Grid, Typography, Box } from "@material-ui/core";
import useStyles from "./HomeLoggedIn.styles";
import NetworkButton from "./components/NetworkButton";
+import DownloadFile from "./components/DownloadFile";
import API from "utils/API";
import { generateNetworkConfig } from "utils/NetworkConfig";
@@ -40,6 +41,8 @@ function HomeLoggedIn() {
>
Create A Network
+
+
diff --git a/frontend/src/components/HomeLoggedIn/components/DownloadFile/DownloadFile.jsx b/frontend/src/components/HomeLoggedIn/components/DownloadFile/DownloadFile.jsx
new file mode 100644
index 0000000..83831b2
--- /dev/null
+++ b/frontend/src/components/HomeLoggedIn/components/DownloadFile/DownloadFile.jsx
@@ -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 (
+ <>
+ {
+ setFilename(e.target.value);
+ }}
+ margin="dense"
+ label="File name to download"
+ type="text"
+ />
+
+
+ >
+ );
+}
+
+export default DownloadFile;
diff --git a/frontend/src/components/HomeLoggedIn/components/DownloadFile/index.jsx b/frontend/src/components/HomeLoggedIn/components/DownloadFile/index.jsx
new file mode 100644
index 0000000..e720e16
--- /dev/null
+++ b/frontend/src/components/HomeLoggedIn/components/DownloadFile/index.jsx
@@ -0,0 +1 @@
+export { default } from "./DownloadFile";