forked from MrBrax/LiveStreamDVR
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
297 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
<template> | ||
<table class="table file-manager is-fullwidth is-striped" v-if="!error"> | ||
<tr class="file-manager-item" v-for="(item, index) in files"> | ||
<td class="file-manager-item-name">{{ item.name }}</td> | ||
<td class="file-manager-item-size">{{ formatBytes(item.size) }}</td> | ||
<td class="file-manager-item-date">{{ item.date }}</td> | ||
<td class="file-manager-item-actions"> | ||
<a class="button is-small is-confirm" :href="downloadLink(item)" target="_blank" download><fa icon="download"></fa></a> | ||
<button class="button is-small is-danger" @click="deleteFile(item)"><fa icon="trash"></fa></button> | ||
</td> | ||
</tr> | ||
</table> | ||
<div class="notification is-danger error" v-if="error"> | ||
{{ error }} | ||
</div> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import { useStore } from "@/store"; | ||
import { AxiosError } from "axios"; | ||
import { defineComponent } from "vue"; | ||
// import { library } from "@fortawesome/fontawesome-svg-core"; | ||
// import { faSkull, faTrash } from "@fortawesome/free-solid-svg-icons"; | ||
// import { useStore } from "@/store"; | ||
// import { JobStatus } from "@common/Defs"; | ||
// library.add(faSkull, faTrash); | ||
interface ApiFile { | ||
name: string; | ||
size: number; | ||
date: string; | ||
is_dir: boolean; | ||
} | ||
export default defineComponent({ | ||
name: "FileManager", | ||
props: { | ||
path: { | ||
type: String, | ||
required: true, | ||
}, | ||
web: { | ||
type: String, | ||
required: true, | ||
}, | ||
}, | ||
setup() { | ||
const store = useStore(); | ||
return { store }; | ||
}, | ||
data(): { | ||
files: ApiFile[]; | ||
error: string; | ||
} { | ||
return { | ||
files: [], | ||
error: "", | ||
}; | ||
}, | ||
created() { | ||
}, | ||
mounted() { | ||
this.fetchFileList(); | ||
}, | ||
methods: { | ||
fetchFileList() { | ||
console.debug("Fetching file list..."); | ||
this.$http.get(`/api/v0/files?path=${this.path}`).then((response) => { | ||
this.files = response.data.data.files; | ||
}).catch((error: AxiosError | Error) => { | ||
if ("response" in error && error.response?.data.message) { | ||
// alert(error.response.data.message); | ||
this.error = error.response.data.message; | ||
} | ||
}); | ||
}, | ||
deleteFile(file: ApiFile) { | ||
this.$http.delete(`/api/v0/files?path=${this.path}&name=${file.name}`).then((response) => { | ||
this.fetchFileList(); | ||
}); | ||
}, | ||
downloadLink(file: ApiFile) { | ||
const base = import.meta.env.BASE_URL || "/"; | ||
const url = `${base}${this.web}/${file.name}`; | ||
return url; | ||
}, | ||
}, | ||
components: { | ||
}, | ||
}); | ||
</script> | ||
|
||
<style lang="scss"> | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
|
||
import { DataRoot } from "Core/BaseConfig"; | ||
import express from "express"; | ||
import path from "path"; | ||
import fs from "fs"; | ||
import sanitize from "sanitize-filename"; | ||
import chalk from "chalk"; | ||
|
||
const allowedDataPaths = [ | ||
"storage", | ||
"logs", | ||
]; | ||
|
||
const validatePath = (nastyPath: string) => { | ||
|
||
// sanitize user path | ||
nastyPath = nastyPath.normalize(); | ||
|
||
// null and other control characters | ||
// eslint-disable-next-line no-control-regex | ||
if (nastyPath.match(/[\x00-\x1f\x80-\x9f]/g)) { | ||
return "Path contains invalid characters"; | ||
} | ||
|
||
// don't traverse up the tree | ||
if (!nastyPath.startsWith(DataRoot)) { | ||
return "Path is not valid"; | ||
} | ||
|
||
// only allow paths that are in the allowed paths | ||
if (!allowedDataPaths.some(allowedPath => nastyPath.startsWith(path.join(DataRoot, allowedPath)))) { | ||
return "Access denied"; | ||
} | ||
|
||
// check if path exists | ||
if (!fs.existsSync(nastyPath)) { | ||
return "Path does not exist"; | ||
} | ||
|
||
// check if user path is a directory | ||
if (!fs.lstatSync(nastyPath).isDirectory()) { | ||
return "Path is not a directory"; | ||
} | ||
|
||
return true; | ||
|
||
}; | ||
|
||
// console.debug("C:\\", validatePath("C:\\")); | ||
// console.debug("C:\\storage", validatePath("C:\\storage")); | ||
// console.debug("/", validatePath("/")); | ||
// console.debug("/storage", validatePath("/storage")); | ||
// console.debug(path.join(DataRoot, ".."), validatePath(path.join(DataRoot, ".."))); | ||
// console.debug(path.join(DataRoot, "\u0000"), validatePath(path.join(DataRoot, "\u0000"))); | ||
// console.debug(path.join(DataRoot, "CON1"), validatePath(path.join(DataRoot, "CON1"))); | ||
// console.debug(path.join(DataRoot, "storage", "saved_vods"), validatePath(path.join(DataRoot, "storage", "saved_vods"))); | ||
// console.debug(path.join(DataRoot, "cache"), validatePath(path.join(DataRoot, "cache"))); | ||
// console.debug(path.join(DataRoot, "logs"), validatePath(path.join(DataRoot, "logs"))); | ||
|
||
export function ListFiles(req: express.Request, res: express.Response): void { | ||
|
||
const user_path = req.query.path as string; | ||
|
||
if (user_path == undefined) { | ||
res.status(400).send({ | ||
status: "ERROR", | ||
message: "Path is not defined" | ||
}); | ||
return; | ||
} | ||
|
||
const full_path = path.join(DataRoot, user_path); | ||
|
||
const validation = validatePath(full_path); | ||
if (validation !== true) { | ||
res.status(400).send({ | ||
status: "ERROR", | ||
message: validation, | ||
}); | ||
return; | ||
} | ||
|
||
const raw_files = fs.readdirSync(full_path); | ||
|
||
const files = raw_files.map((file) => { | ||
return { | ||
name: file, | ||
size: fs.statSync(path.join(full_path, file)).size, | ||
date: fs.statSync(path.join(full_path, file)).mtime, | ||
is_dir: fs.lstatSync(path.join(full_path, file)).isDirectory(), | ||
}; | ||
}); | ||
|
||
res.send({ | ||
status: "OK", | ||
data: { | ||
files: files, | ||
}, | ||
}); | ||
|
||
} | ||
|
||
export function DeleteFile(req: express.Request, res: express.Response): void { | ||
|
||
const user_path = req.query.path as string; | ||
const file_name = req.query.name as string; | ||
|
||
if (user_path == undefined) { | ||
res.status(400).send({ | ||
status: "ERROR", | ||
message: "Path is not defined", | ||
}); | ||
return; | ||
} | ||
|
||
const full_path = path.join(DataRoot, user_path); | ||
|
||
const validation = validatePath(full_path); | ||
if (validation !== true) { | ||
res.status(400).send({ | ||
status: "ERROR", | ||
message: validation, | ||
}); | ||
return; | ||
} | ||
|
||
const sanitized_file_name = sanitize(file_name); | ||
|
||
const full_file_path = path.join(full_path, sanitized_file_name); | ||
|
||
if (!fs.existsSync(full_file_path)) { | ||
res.status(400).send({ | ||
status: "ERROR", | ||
message: "File does not exist", | ||
}); | ||
return; | ||
} | ||
|
||
fs.unlinkSync(full_file_path); | ||
console.log(chalk.bgRedBright.whiteBright(`Deleting file: ${full_file_path}`)); | ||
|
||
res.send({ | ||
status: "OK", | ||
message: "File deleted", | ||
}); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.