diff --git a/api/index.js b/api/index.js index bb67126fd14181974f5e3f476fdc13ac6f209c5a..c3228c59d9cf0aed941095ef5e1b311fe3d383ee 100644 --- a/api/index.js +++ b/api/index.js @@ -1,4 +1,6 @@ const express = require('express') +const fileUpload = require('express-fileupload'); +const cors = require('cors'); var bodyParser = require('body-parser') // Create express instance const app = express() @@ -13,6 +15,10 @@ const kolloquiums = require('./routes/kolloquiums') app.use(bodyParser.urlencoded({ extended: false })) // parse application/json app.use(bodyParser.json()) +app.use(cors()) +app.use(fileUpload({ + createParentPath: true + })); app.use(users) app.use(kolloquiums) diff --git a/api/routes/kolloquiums.js b/api/routes/kolloquiums.js index d12858ae114112ad2e679375e4694cf397e34d78..a754ae0c560c8421ea643ff325154954046457e6 100644 --- a/api/routes/kolloquiums.js +++ b/api/routes/kolloquiums.js @@ -1,7 +1,9 @@ const { Router } = require('express') + const router = Router() var fs = require("fs") +const kolloquiumDirectory = 'Kolloquiums' function getDirectories(path) { return fs.readdirSync(path).filter(function (file) { @@ -9,13 +11,14 @@ function getDirectories(path) { }); } -// function removeDangerousSymbols(title) { -// return title -// } +function isEmpty(checkString) { + return (!checkString || checkString.trim().length == 0) +} function removeDangerousSymbols(fname) { // https://stackoverflow.com/a/31976060 // https://gist.github.com/doctaphred/d01d05291546186941e1b7ddc02034d3 + // removed check for empty: if is empty, nothing should be done with the string by the other api functions const fname_original = fname; @@ -26,10 +29,7 @@ function removeDangerousSymbols(fname) { fname = fname.replaceAll(/[<>.:"\/\\\|?*\x00-\x1F]/g, ''); // advanced trim fname = fname.replace(/\.$/, ""); - // empty filename - if (!fname.length) { - fname = '_'; - } + // forbidden filenames if (fname.match(/^(CON|PRN|AUX|NUL|COM1|COM2|COM3|COM4|COM5|COM6|COM7|COM8|COM9|LPT1|LPT2|LPT3|LPT4|LPT5|LPT6|LPT7|LPT8|LPT9)(\..+)?$/)) { fname = `_${fname}`; @@ -38,11 +38,11 @@ function removeDangerousSymbols(fname) { return fname; } -fs.mkdir('Kolloquiums',function(err) { +fs.mkdir(kolloquiumDirectory,function(err) { if (err && err.code === "EEXIST") { console.log('Kolloquiums Directory already existed') } - else if (err && err.code != "EEXIST") { + else if (err) { return console.error(err); } else { @@ -52,72 +52,99 @@ fs.mkdir('Kolloquiums',function(err) { // Get Kolloquiums router.use('/getKolloquiums', (req, res) => { - var directories = getDirectories('Kolloquiums') + console.log('/getKolloquiums') + var directories = getDirectories(kolloquiumDirectory) return res.json({ kolloquiums: directories }); }) + +// Get Abgaben +router.use('/getAbgaben', (req, res) => { + console.log('/getAbgaben') + let { kolloquium } = req.body + let safeKolloquium = removeDangerousSymbols(kolloquium) + + console.log('from Kolloquium: ' + safeKolloquium) + if(isEmpty(safeKolloquium)) { + console.warn('Folder has no name') + return res.json({ + success: false, + message: 'Folder has no name' + }) + } + + var directories = getDirectories(kolloquiumDirectory + '/' + safeKolloquium) + return res.json({ + success: true, + abgaben: directories + }); +}) + + // Delete Kolloquium router.use('/deleteKolloquium', (req, res) => { - console.log('deleting Kolloquium') + console.log('/deleteKolloquium') let { title } = req.body - if(!title || title.trim().length == 0) { + let safeTitle = removeDangerousSymbols(title) + + if(isEmpty(safeTitle)) { console.error('Folder has no name') return res.json({ - status: 'error', + success: false, message: 'Folder has no name' }) } - let safeTitle = removeDangerousSymbols(title) - fs.rmdir("Kolloquiums/" + safeTitle, function(err) { + fs.rmdir(kolloquiumDirectory + '/' + safeTitle, function(err) { if (err) { console.error(err); return res.json({ - status: 'error', + success: false, message: err }) } }); return res.json({ - status: 'success', + success: true, message: 'removed Kolloquium ' + safeTitle }) }) // Create Kolloquium router.use('/createKolloquium', (req, res) => { - console.log('creating Kolloquium') + console.log('createKolloquium') console.log(req.body) let { title } = req.body - if( !title || title.trim().length == 0) { + let safeTitle = removeDangerousSymbols(title) + + if(isEmpty(safeTitle)) { console.error('Title was empty') return res.json({ - status: 'error', + success: false, message: 'Title was empty' }) } - let safeTitle = removeDangerousSymbols(title) - fs.mkdir('Kolloquiums/' + safeTitle, function(err) { + fs.mkdir(kolloquiumDirectory + '/' + safeTitle, function(err) { if (err && err.code === "EEXIST") { console.warn('Directory "' + safeTitle + '" already existed') return res.json({ - status: 'warning', + success: true, message: 'Directory "' + safeTitle + '" already existed' }) } - else if (err && err.code != "EEXIST") { + else if (err) { console.error(err); return res.json({ - status: 'error', + success: false, message: err }) } else { console.log('Directory "' + safeTitle + '" created successfully!'); return res.json({ - status: 'success', + success: true, message: 'Directory "' + safeTitle + '" created successfully!' }) } @@ -126,23 +153,24 @@ router.use('/createKolloquium', (req, res) => { // Rename Kolloquium router.use('/renameKolloquium', (req, res) => { - console.log('renaming Kolloquium') + console.log('/renameKolloquium') console.log(req.body); let { oldTitle, newTitle } = req.body - if( !oldTitle || oldTitle.trim().length == 0 || !newTitle || newTitle.trim().length == 0) { + let safeOldTitle = removeDangerousSymbols(oldTitle) + let safeNewTitle = removeDangerousSymbols(newTitle) + + if(isEmpty(safeOldTitle) || isEmpty(safeNewTitle)) { console.error('at least one of the names was empty') return res.json({ - status: 'error', + success: false, message: 'at least one of the names was empty' }) } - let oldSafeTitle = removeDangerousSymbols(oldTitle) - let newSafeTitle = removeDangerousSymbols(newTitle) - fs.rename('Kolloquiums/' + oldSafeTitle, 'Kolloquiums/' + newSafeTitle, (err) => { + fs.rename(kolloquiumDirectory + '/' + safeOldTitle, kolloquiumDirectory + '/' + safeNewTitle, (err) => { if(err) { console.error(err); return res.json({ - status: 'error', + success: false, message: err }) } @@ -151,4 +179,67 @@ router.use('/renameKolloquium', (req, res) => { }); }) + +// Submit Abgabe +router.use('/submitAbgabe', (req, res) => { + console.log('/submitAbgabe') + let { kolloquium, name, matrikelnummer, file, filename } = req.body + console.log('filename: ' + filename) + // let file = file.file + let safeMatrikelnummer = removeDangerousSymbols(matrikelnummer) + let safeName = removeDangerousSymbols(name) + let safeKolloquium = removeDangerousSymbols(kolloquium) + + if(isEmpty(safeKolloquium) || isEmpty(safeMatrikelnummer || isEmpty(safeName))) { + console.error('Matrikelnummer, Name or Kolloquium was empty') + return res.json({ + success: false, + message: 'Matrikelnummer, Name or Kolloquium' + }) + } + let directory = kolloquiumDirectory + '/' + safeKolloquium + '/' + safeMatrikelnummer + '_' + safeName; + console.log('directory: ' + directory) + fs.mkdir(directory, function(err) { + if (err && err.code === "EEXIST") { + console.warn('Directory "' + directory + '" already existed') + // return res.json({ + // success: true, + // message: 'Directory "' + directory + '" already existed' + // }) + } + else if (err) { + console.error(err); + return res.json({ + success: false, + message: err + }) + } + else { + console.log('Directory "' + directory + '" created successfully!'); + // return res.json({ + // success: true, + // message: 'Directory "' + directory + '" created successfully!' + // }) + } + // fs.write(directory + '/' + filename, file, function(err) { + // if (err) { + // console.error(err); + // return res.json({ + // success: false, + // message: err + // }) + // } + // console.log("file '" + filename + "' was created!"); + // return res.json({ + // success: true, + // message: "file '" + filename + "' was created" + // }) + // }) + console.log('saving file ' + filename) + file.mv(directory + '/' + filename); + }) +}) + + + module.exports = router diff --git a/layouts/error.vue b/layouts/error.vue index 8bf58550bcf5e689cec2f72d610684ce5a3c8c63..b31177ad9ff0e4781edc9f5e0de95e4da3ae92cc 100644 --- a/layouts/error.vue +++ b/layouts/error.vue @@ -1,7 +1,6 @@ <template> <section class="container"> <div> - <Logo /> <h1 class="title"> {{ error.statusCode }} </h1> diff --git a/package-lock.json b/package-lock.json index f41e8c6edfb8c67a90060e84caa45412567c58e2..8ed6618e9c95345948cc3ac0cbc57f594f951a63 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,9 @@ "@nuxt/http": "latest", "@nuxtjs/axios": "^5.13.6", "body-parser": "^1.19.1", + "cors": "^2.8.5", "express": "latest", + "express-fileupload": "^1.2.1", "nuxt": "latest" }, "devDependencies": { @@ -4734,6 +4736,17 @@ "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" }, + "node_modules/busboy": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.3.1.tgz", + "integrity": "sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==", + "dependencies": { + "dicer": "0.3.0" + }, + "engines": { + "node": ">=4.5.0" + } + }, "node_modules/bytes": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", @@ -5503,6 +5516,18 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/cosmiconfig": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", @@ -6370,6 +6395,17 @@ "resolved": "https://registry.npmjs.org/devalue/-/devalue-2.0.1.tgz", "integrity": "sha512-I2TiqT5iWBEyB8GRfTDP0hiLZ0YeDJZ+upDxjBfOC2lebO5LezQMv7QvIUTzdb64jQyAKLf1AHADtGN+jw6v8Q==" }, + "node_modules/dicer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz", + "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==", + "dependencies": { + "streamsearch": "0.1.2" + }, + "engines": { + "node": ">=4.5.0" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -7207,6 +7243,17 @@ "node": ">= 0.10.0" } }, + "node_modules/express-fileupload": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.2.1.tgz", + "integrity": "sha512-fWPNAkBj+Azt9Itmcz/Reqdg3LeBfaXptDEev2JM8bCC0yDptglCnlizhf0YZauyU5X/g6v7v4Xxqhg8tmEfEA==", + "dependencies": { + "busboy": "^0.3.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -15568,6 +15615,14 @@ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" }, + "node_modules/streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/strict-uri-encode": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", @@ -21871,6 +21926,14 @@ "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" }, + "busboy": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.3.1.tgz", + "integrity": "sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==", + "requires": { + "dicer": "0.3.0" + } + }, "bytes": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", @@ -22475,6 +22538,15 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, "cosmiconfig": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", @@ -23128,6 +23200,14 @@ "resolved": "https://registry.npmjs.org/devalue/-/devalue-2.0.1.tgz", "integrity": "sha512-I2TiqT5iWBEyB8GRfTDP0hiLZ0YeDJZ+upDxjBfOC2lebO5LezQMv7QvIUTzdb64jQyAKLf1AHADtGN+jw6v8Q==" }, + "dicer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz", + "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==", + "requires": { + "streamsearch": "0.1.2" + } + }, "didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -23815,6 +23895,14 @@ } } }, + "express-fileupload": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.2.1.tgz", + "integrity": "sha512-fWPNAkBj+Azt9Itmcz/Reqdg3LeBfaXptDEev2JM8bCC0yDptglCnlizhf0YZauyU5X/g6v7v4Xxqhg8tmEfEA==", + "requires": { + "busboy": "^0.3.1" + } + }, "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", @@ -30175,6 +30263,11 @@ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" + }, "strict-uri-encode": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", diff --git a/package.json b/package.json index b450bcec37973e729a33e97bea908ffcf8408b0b..754185f6bc759a8c4cd528ee265765bcb278e028 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,9 @@ "@nuxt/http": "latest", "@nuxtjs/axios": "^5.13.6", "body-parser": "^1.19.1", + "cors": "^2.8.5", "express": "latest", + "express-fileupload": "^1.2.1", "nuxt": "latest" }, "devDependencies": { diff --git a/pages/abgabe/_kolloquium.vue b/pages/abgabe/_kolloquium.vue new file mode 100644 index 0000000000000000000000000000000000000000..964b61fe1823e241b48d9936f64fc313ef905d3e --- /dev/null +++ b/pages/abgabe/_kolloquium.vue @@ -0,0 +1,88 @@ +<template> + <div class="relative flex flex-col items-center justify-top min-h-screen bg-gray-100 sm:items-top sm:pt-0"> + <box> + <template slot="title"> + Abgabe für <span class="font-semibold">{{ this.$route.params.kolloquium }}</span> hochladen + </template> + <template slot="content"> + Auf dieser Seite kann man seine CAD/Datasmith-Datei hochladen. + </template> + </box> + <box> + <template slot="title"> + Details + </template> + <template slot="content"> + <form + accept-charset="UTF-8" + v-on:submit.prevent="onSubmit()" + method="POST" + > + <div class="flex flex-row items-center mb-1"> + Name: + <input class="ml-1 w-full rounded border p-1" v-model="name"> + </div> + <div class="flex flex-row items-center mb-1"> + Matrikelnummer: + <input class="ml-1 w-full rounded border p-1" v-model="matrikelnummer"> + </div> + <div class="flex flex-row items-center mb-1"> + Datei: + <input + type=file + ref="file" + @change="fileSelected" + class="ml-1 w-full rounded border p-1" + > + </div> + <button type="submit" class="border rounded mt-4 p-2 font-semibold text-white bg-green-500 hover:bg-green-600 focus:bg-green-700"> + Hochladen + </button> + </form> + </template> + </box> + </div> +</template> + +<script> +import Box from '~/components/Box' +import ListItem from '~/components/ListItem' + +export default { + components: { + Box, + ListItem + }, + data() { + return { + name: '', + matrikelnummer: '', + filename: '', + } + }, + methods: { + fileSelected(e) { + // this.$emit('input', e.target.files[0]) + this.file = this.$refs.file.files[0] + this.filename = this.file.name + }, + onSubmit() { + let formData = new FormData() + formData.append('file', this.file) + formData.append('filename', this.filename) + formData.append('kolloquium', this.$route.params.kolloquium) + formData.append('name', this.name) + formData.append('matrikelnummer', this.matrikelnummer) + this.$axios.post( + 'api/submitAbgabe', + formData, + { + headers: { + 'content-type': 'multipart/form-data' + } + } + ) + } + } +} +</script> diff --git a/pages/index.vue b/pages/index.vue index b5ce5998056ad03095407d1da19eb4b50403ef8d..4087f5ce625f0e17eaaecddafd2a4f8ae76bb2c2 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -49,7 +49,7 @@ <button class="border rounded mt-4 p-2 font-semibold text-white bg-green-500 hover:bg-green-600 focus:bg-green-700"> Aktivieren </button> - <n-link to="/abgabe-hochladen"> + <n-link :to="'/abgabe/' + selectedKolloquium"> <button class="border rounded mt-4 p-2 font-semibold text-white bg-blue-500 hover:bg-blue-600 focus:bg-blue-700"> Link Teilen </button> @@ -77,17 +77,15 @@ export default { return { selectedKolloquium: "", selectedAbgabe: "", - abgaben: [ - "Hier ist eine Liste von Abgaben für das ausgewählte Kolloquium.", - "Man kann einzelne Abgaben löschen.", - "Wenn man auf Aktivieren klickt, werden die Abgaben vom Kolloquium in das VR-Programm geladen" - ] } }, methods: { - selectKolloquium(kolloquium) { + async selectKolloquium(kolloquium) { this.selectedKolloquium = kolloquium.title this.selectedAbgabe = '' + this.abgaben = [] + const data = await this.$axios.$post('/api/getAbgaben/', { kolloquium: kolloquium.title }) + this.abgaben = data.abgaben }, selectAbgabe(abgabe) { this.selectedAbgabe = abgabe @@ -134,16 +132,17 @@ export default { } }, }, - async asyncData ({ $http }) { - const data = await $http.$get('/api/getKolloquiums/') + async asyncData ({ $axios }) { + const dataKolloquiums = await $axios.$get('/api/getKolloquiums/') let kolloquiumList = [] - data.kolloquiums.forEach(title => { + dataKolloquiums.kolloquiums.forEach(title => { kolloquiumList.push({ title: title, inEdit: false }) }) - return { kolloquiums: kolloquiumList } + const dataAbgaben = await $axios.$post('/api/getAbgaben/', { kolloquium: kolloquiumList[0].title }) + return { kolloquiums: kolloquiumList, abgaben: dataAbgaben.abgaben, selectedKolloquium: kolloquiumList[0].title } }, } </script>