Initial commit
This commit is contained in:
commit
50f83b47d1
|
@ -0,0 +1,6 @@
|
|||
node_modules
|
||||
data
|
||||
custom/*.js
|
||||
public/dist
|
||||
config.js
|
||||
*.sublime-workspace
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"folders":
|
||||
[
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
port: 3000,
|
||||
|
||||
nodeModulesPath: path.join(__dirname, 'node_modules'),
|
||||
userDatabasePath: path.join(__dirname, 'data', 'users'),
|
||||
fileUploadPath: path.join(__dirname, 'data', 'files'),
|
||||
fileUploadPublicPath: '/files',
|
||||
|
||||
jwtSecret: 'change me to a random generated string'
|
||||
};
|
|
@ -0,0 +1,165 @@
|
|||
'use strict'
|
||||
|
||||
const config = require('./config');
|
||||
const JsonUserDatabase = require('./lib/JsonUserDatabase');
|
||||
|
||||
const _ = require('lodash');
|
||||
const express = require('express');
|
||||
const bodyParser = require('body-parser');
|
||||
const tus = require('tus-node-server');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const path = require('path');
|
||||
|
||||
const webpack = require('webpack');
|
||||
const webpackDevMiddleware = require('webpack-dev-middleware');
|
||||
const webpackHotMiddleware = require('webpack-hot-middleware');
|
||||
const webpackConfig = require('./webpack.config.js');
|
||||
|
||||
/*
|
||||
function metadataToObject(stringValue)
|
||||
{
|
||||
const keyValuePairList = stringValue.split(',');
|
||||
|
||||
return _.reduce(keyValuePairList , (metadata, keyValuePair) => {
|
||||
let [key, base64Value] = keyValuePair.split(' ');
|
||||
metadata[key] = new Buffer(base64Value, "base64").toString("ascii");
|
||||
|
||||
return metadata;
|
||||
}, {});
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
function checkAuthorization(req, res, onVerified)
|
||||
{
|
||||
if (!req.headers.authorization || req.headers.authorization.split(' ')[0] !== 'Bearer')
|
||||
{
|
||||
res.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
||||
var token = req.headers.authorization.split(' ')[1];
|
||||
jwt.verify(token, config.jwtSecret, (err, decoded) =>
|
||||
{
|
||||
if (err)
|
||||
{
|
||||
res.sendStatus(403);
|
||||
return;
|
||||
}
|
||||
|
||||
onVerified(decoded);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
(async function()
|
||||
{
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production';
|
||||
|
||||
const userDatabase = new JsonUserDatabase(config.userDatabasePath);
|
||||
await userDatabase.load();
|
||||
|
||||
const tusServer = new tus.Server();
|
||||
tusServer.datastore = new tus.FileStore({
|
||||
path: config.fileUploadPublicPath,
|
||||
directory: config.fileUploadPath
|
||||
});
|
||||
|
||||
/*
|
||||
tusServer.on(tus.EVENTS.EVENT_UPLOAD_COMPLETE, (event) =>
|
||||
{
|
||||
console.log(event);
|
||||
|
||||
const metadata = metadataToObject(event.file.upload_metadata);
|
||||
jwt.verify(metadata.token, config.jwtSecret, (err, decoded) =>
|
||||
{
|
||||
if (err)
|
||||
return;
|
||||
|
||||
const filePath = path.join(config.fileUploadPath, event.file.id);
|
||||
console.log(filePath);
|
||||
|
||||
// TODO save metadata for file and notify people
|
||||
});
|
||||
});
|
||||
*/
|
||||
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
app.use(bodyParser.json());
|
||||
|
||||
|
||||
// Token API
|
||||
app.post('/token/upload', async (req, res) =>
|
||||
{
|
||||
if (!req.body.code)
|
||||
{
|
||||
res.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
||||
if (await userDatabase.isValidCode(req.body.code))
|
||||
{
|
||||
jwt.sign({
|
||||
code: req.body.code
|
||||
}, config.jwtSecret, (err, token) =>
|
||||
{
|
||||
if (err)
|
||||
res.sendStatus(500);
|
||||
else
|
||||
res.send(token);
|
||||
});
|
||||
}
|
||||
else
|
||||
res.sendStatus(403);
|
||||
});
|
||||
|
||||
|
||||
// Upload API
|
||||
app.post('/complete', (req, res) =>
|
||||
{
|
||||
if (!req.body.files)
|
||||
{
|
||||
res.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
||||
checkAuthorization(req, res, (decoded) =>
|
||||
{
|
||||
console.log(req.body.files);
|
||||
// TODO save set
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Tus upload
|
||||
const uploadApp = express();
|
||||
uploadApp.all('*', (req, res) =>
|
||||
{
|
||||
checkAuthorization(req, res, (decoded) =>
|
||||
{
|
||||
tusServer.handle(req, res);
|
||||
});
|
||||
});
|
||||
|
||||
app.use('/upload', uploadApp);
|
||||
|
||||
|
||||
// Frontend
|
||||
if (isDevelopment)
|
||||
{
|
||||
const compiler = webpack(webpackConfig);
|
||||
|
||||
app.use(webpackDevMiddleware(compiler, {
|
||||
publicPath: webpackConfig.output.publicPath
|
||||
}));
|
||||
|
||||
app.use(webpackHotMiddleware(compiler));
|
||||
}
|
||||
|
||||
app.use(express.static(path.join(__dirname, 'public', 'dist')));
|
||||
|
||||
var server = app.listen(config.port, () => console.log('Recv running on port ' + server.address().port));
|
||||
})();
|
|
@ -0,0 +1,91 @@
|
|||
const debug = require('debug')('recv:JsonUserDatabase');
|
||||
const uuidv4 = require('uuid/v4');
|
||||
const fs = require('mz/fs');
|
||||
const mkdir = require('mkdir-promise');
|
||||
const path = require('path');
|
||||
|
||||
|
||||
class JsonUserDatabase
|
||||
{
|
||||
constructor(path)
|
||||
{
|
||||
this.path = path;
|
||||
|
||||
this.codes = {};
|
||||
this.users = {};
|
||||
}
|
||||
|
||||
|
||||
async load()
|
||||
{
|
||||
debug('loading database from ' + this.path);
|
||||
|
||||
try
|
||||
{
|
||||
var files = await fs.readdir(this.path);
|
||||
}
|
||||
catch(err)
|
||||
{
|
||||
if (err.code == 'ENOENT')
|
||||
// Path does not exist, no worries
|
||||
files = null;
|
||||
else
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (!files)
|
||||
return;
|
||||
|
||||
for (var i = 0; i < files.length; i++)
|
||||
{
|
||||
var fullPath = path.join(this.path, files[i]);
|
||||
var stats = await fs.lstat(fullPath);
|
||||
|
||||
if (stats.isFile())
|
||||
{
|
||||
debug('loading ' + fullPath);
|
||||
try
|
||||
{
|
||||
var userInfo = JSON.parse(await fs.readFile(fullPath));
|
||||
if (userInfo.type !== 'user')
|
||||
throw new Error('unsupported file type: ' + userInfo.type);
|
||||
|
||||
this.users[userInfo.uid] = userInfo;
|
||||
}
|
||||
catch (err)
|
||||
{
|
||||
console.error('error while loading file ' + fullPath + ', skipped:');
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug(Object.keys(this.users).length + ' user(s) loaded');
|
||||
}
|
||||
|
||||
|
||||
addUser(info)
|
||||
{
|
||||
var userId = uuidv4();
|
||||
|
||||
// TODO add user
|
||||
// TODO save file
|
||||
|
||||
return userId;
|
||||
}
|
||||
|
||||
|
||||
isValidCode(code)
|
||||
{
|
||||
debug('validating code: ' + code);
|
||||
|
||||
// TODO check code
|
||||
var valid = true;
|
||||
|
||||
debug('valid = ' + valid);
|
||||
return valid;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = JsonUserDatabase;
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"name": "recv",
|
||||
"version": "1.0.0",
|
||||
"description": "Recv - self-hosted web file transfer",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev": "node index.js",
|
||||
"build": "webpack --mode production",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://www.github.com/MvRens/Recv"
|
||||
},
|
||||
"author": "Mark van Renswoude <mark@x2software.net>",
|
||||
"license": "Unlicense",
|
||||
"dependencies": {
|
||||
"body-parser": "^1.18.2",
|
||||
"debug": "^3.1.0",
|
||||
"express": "^4.16.2",
|
||||
"jsonwebtoken": "^8.1.1",
|
||||
"mkdir-promise": "^1.0.0",
|
||||
"mz": "^2.7.0",
|
||||
"npm": "^5.7.1",
|
||||
"tus-node-server": "^0.2.10",
|
||||
"uuid": "^3.2.1",
|
||||
"lodash": "^4.17.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"axios": "^0.18.0",
|
||||
"babel-loader": "^7.1.4",
|
||||
"babel-plugin-syntax-dynamic-import": "^6.18.0",
|
||||
"css-loader": "^0.28.10",
|
||||
"html-webpack-plugin": "^3.0.6",
|
||||
"node-sass": "^4.7.2",
|
||||
"purecss": "^1.0.0",
|
||||
"sass-loader": "^6.0.7",
|
||||
"style-loader": "^0.20.2",
|
||||
"tus-js-client": "^1.4.5",
|
||||
"uppy": "^0.23.2",
|
||||
"vue": "^2.5.13",
|
||||
"vue-i18n": "^7.4.2",
|
||||
"vue-loader": "^14.2.1",
|
||||
"vue-router": "^3.0.1",
|
||||
"vue-style-loader": "^4.0.2",
|
||||
"vue-template-compiler": "^2.5.13",
|
||||
"webpack": "^4.1.0",
|
||||
"webpack-cli": "^2.0.10",
|
||||
"webpack-dev-middleware": "^3.0.1",
|
||||
"webpack-hot-middleware": "^2.21.2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
<template>
|
||||
<div id="app">
|
||||
<div v-cloak>
|
||||
<div class="notificationContainer">
|
||||
<div class="notification" :class="{ error: notification != null && notification.error }" v-if="notification != null" @click.prevent="hideNotification">
|
||||
<span class="message">{{ notification.message }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<div class="logo">
|
||||
<img src="/images/logo.png">
|
||||
</div>
|
||||
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
|
||||
<div class="disclaimer">
|
||||
{{ $t('disclaimer') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash';
|
||||
import shared from './shared';
|
||||
|
||||
|
||||
if (typeof customMessages !== 'undefined')
|
||||
{
|
||||
_.merge(messages, customMessages);
|
||||
|
||||
if (customMessages.hasOwnProperty('allLocales'))
|
||||
{
|
||||
for (var key in messages)
|
||||
{
|
||||
if (key !== 'allLocales' && messages.hasOwnProperty(key))
|
||||
_.merge(messages[key], customMessages.allLocales);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
data () {
|
||||
return {
|
||||
notification: null
|
||||
}
|
||||
},
|
||||
|
||||
created()
|
||||
{
|
||||
var self = this;
|
||||
document.title = self.$i18n.t('title');
|
||||
|
||||
self.notificationTimer = null;
|
||||
|
||||
shared.$on('showNotification', (message, error) =>
|
||||
{
|
||||
var self = this;
|
||||
self.notification = {
|
||||
message: message,
|
||||
error: error
|
||||
};
|
||||
|
||||
if (self.notificationTimer != null)
|
||||
clearTimeout(self.notificationTimer);
|
||||
|
||||
self.notificationTimer = setTimeout(() =>
|
||||
{
|
||||
self.notification = null;
|
||||
self.notificationTimer = null;
|
||||
}, 5000);
|
||||
});
|
||||
|
||||
shared.$on('hideNotification', () =>
|
||||
{
|
||||
var self = this;
|
||||
self.notification = null;
|
||||
|
||||
if (self.notificationTimer != null)
|
||||
{
|
||||
clearTimeout(self.notificationTimer);
|
||||
self.notificationTimer = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../../node_modules/purecss/build/base.css';
|
||||
@import '../../node_modules/purecss/build/buttons.css';
|
||||
@import '../../node_modules/purecss/build/forms.css';
|
||||
@import '../../node_modules/purecss/build/grids.css';
|
||||
|
||||
/* open-sans-regular - latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('/fonts/open-sans-v15-latin-regular.eot'); /* IE9 Compat Modes */
|
||||
src: local('Open Sans Regular'), local('OpenSans-Regular'),
|
||||
url('/fonts/open-sans-v15-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
||||
url('/fonts/open-sans-v15-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
|
||||
url('/fonts/open-sans-v15-latin-regular.woff') format('woff'), /* Modern Browsers */
|
||||
url('/fonts/open-sans-v15-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */
|
||||
url('/fonts/open-sans-v15-latin-regular.svg#OpenSans') format('svg'); /* Legacy iOS */
|
||||
}
|
||||
|
||||
|
||||
body
|
||||
{
|
||||
background-color: #f0f0f0;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
#app
|
||||
{
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 768px;
|
||||
}
|
||||
|
||||
.content
|
||||
{
|
||||
background-color: white;
|
||||
margin: 2rem;
|
||||
padding: 2rem;
|
||||
box-shadow: 0 0 15px #c0c0c0;
|
||||
}
|
||||
|
||||
|
||||
.notificationContainer
|
||||
{
|
||||
position: fixed;
|
||||
top: 1.5rem;
|
||||
z-index: 666;
|
||||
width: 512px;
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
|
||||
.notification
|
||||
{
|
||||
background: #d5e8f6;
|
||||
border: solid 1px rgba(0, 0, 0, 0.25);
|
||||
box-shadow: 0 0 10px #c0c0c0;
|
||||
color: black;
|
||||
cursor: pointer;
|
||||
padding: .5em;
|
||||
margin-bottom: 2rem;
|
||||
|
||||
position: relative;
|
||||
left: -50%;
|
||||
}
|
||||
|
||||
.notification .message
|
||||
{
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.notification.error
|
||||
{
|
||||
background: #973a38;
|
||||
}
|
||||
|
||||
|
||||
.logo
|
||||
{
|
||||
margin-bottom: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.disclaimer
|
||||
{
|
||||
font-size: 8pt;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,33 @@
|
|||
import Vue from 'vue';
|
||||
import VueI18n from 'vue-i18n';
|
||||
import VueRouter from 'vue-router';
|
||||
import App from './App.vue';
|
||||
import messages from './lang';
|
||||
|
||||
Vue.use(VueI18n);
|
||||
Vue.use(VueRouter);
|
||||
|
||||
const i18n = new VueI18n({
|
||||
locale: navigator.language.split('-')[0],
|
||||
fallbackLocale: 'en',
|
||||
messages: messages
|
||||
});
|
||||
|
||||
|
||||
const Landing = () => import('./route/Landing.vue');
|
||||
const Upload = () => import('./route/Upload.vue');
|
||||
|
||||
const router = new VueRouter({
|
||||
routes: [
|
||||
{ path: '/', component: Landing },
|
||||
{ path: '/c/:codeParam', component: Landing, props: true },
|
||||
{ path: '/u/:codeParam', component: Upload, props: true }
|
||||
]
|
||||
});
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
i18n,
|
||||
router,
|
||||
render: h => h(App)
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Recv</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,47 @@
|
|||
export default {
|
||||
en: {
|
||||
title: 'File upload - Recv',
|
||||
disclaimer: '',
|
||||
|
||||
landing: {
|
||||
invitePlaceholder: 'Code',
|
||||
inviteButton: 'Check code',
|
||||
inviteButtonChecking: 'Checking...',
|
||||
|
||||
loginButton: 'Log in'
|
||||
},
|
||||
|
||||
notification: {
|
||||
invalidCode: 'The specified code is invalid or has expired'
|
||||
},
|
||||
|
||||
uppyDashboard: {
|
||||
done: 'Done',
|
||||
dropPaste: 'Drop files here, paste or',
|
||||
browse: 'browse'
|
||||
}
|
||||
},
|
||||
|
||||
nl: {
|
||||
title: 'Bestandsoverdracht - Recv',
|
||||
disclaimer: '',
|
||||
|
||||
landing: {
|
||||
invitePlaceholder: 'Code',
|
||||
inviteButton: 'Controleer code',
|
||||
inviteButtonChecking: 'Controleren...',
|
||||
|
||||
loginButton: 'Inloggen'
|
||||
},
|
||||
|
||||
notification: {
|
||||
invalidCode: 'De ingevoerde code is ongeldig of verlopen'
|
||||
},
|
||||
|
||||
uppyDashboard: {
|
||||
done: 'Gereed',
|
||||
dropPaste: 'Sleep bestanden, plak of ',
|
||||
browse: 'selecteer'
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
<template>
|
||||
<div id="landing">
|
||||
<form class="pure-form pure-form-stacked" @submit.prevent="checkCode">
|
||||
<fieldset class="pure-group">
|
||||
<input type="text" class="pure-input-1-2" v-model="code.value" :placeholder="$t('landing.invitePlaceholder')">
|
||||
</fieldset>
|
||||
|
||||
<button type="submit" class="pure-button pure-button-primary" :disabled="code.value.trim() == '' || code.checking">{{ $t(code.checking ? 'landing.inviteButtonChecking' : 'landing.inviteButton') }} <span v-if="code.checking"><i class="fas fa-spinner fa-pulse"></i></span></button>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import shared from '../shared';
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
|
||||
props: [
|
||||
'codeParam'
|
||||
],
|
||||
|
||||
data () {
|
||||
return {
|
||||
code: {
|
||||
value: '',
|
||||
checking: false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
created()
|
||||
{
|
||||
var self = this;
|
||||
if (self.codeParam)
|
||||
{
|
||||
self.code.value = self.codeParam;
|
||||
self.checkCode();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
checkCode()
|
||||
{
|
||||
var self = this;
|
||||
if (self.code.checking)
|
||||
return;
|
||||
|
||||
self.code.checking = true;
|
||||
|
||||
axios.post('/token/upload', {
|
||||
code: self.code
|
||||
})
|
||||
.then((response) =>
|
||||
{
|
||||
shared.token = response.data;
|
||||
self.$router.push({ path: '/u/' + self.code.value });
|
||||
})
|
||||
.catch((error) =>
|
||||
{
|
||||
if (error.response && error.response.status == 403)
|
||||
shared.$emit('showNotification', self.$i18n.t('notification.invalidCode'), false);
|
||||
else
|
||||
shared.$emit('showNotification', error.message);
|
||||
})
|
||||
.then(() =>
|
||||
{
|
||||
self.code.checking = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../../../node_modules/uppy/src/scss/uppy.scss';
|
||||
|
||||
|
||||
#landing
|
||||
{
|
||||
text-align: center;
|
||||
|
||||
input
|
||||
{
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.login
|
||||
{
|
||||
margin-top: 5em;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,107 @@
|
|||
<template>
|
||||
<div id="upload">
|
||||
<router-link to="/">Terug</router-link>
|
||||
<div class="uploadTarget"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash';
|
||||
import shared from '../shared';
|
||||
import Uppy from 'uppy/lib/core';
|
||||
import Dashboard from 'uppy/lib/plugins/Dashboard';
|
||||
import Tus from 'uppy/lib/plugins/Tus';
|
||||
import axios from 'axios';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'codeParam',
|
||||
'i18n'
|
||||
],
|
||||
|
||||
data () {
|
||||
return {
|
||||
token: shared.token
|
||||
}
|
||||
},
|
||||
|
||||
created()
|
||||
{
|
||||
var self = this;
|
||||
|
||||
if (!self.token)
|
||||
{
|
||||
if (self.codeParam)
|
||||
self.$router.push('/c/' + self.codeParam);
|
||||
else
|
||||
self.$router.push('/c/');
|
||||
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
mounted()
|
||||
{
|
||||
var self = this;
|
||||
|
||||
self.uppy = Uppy({
|
||||
id: 'userUpload',
|
||||
autoProceed: false,
|
||||
debug: true
|
||||
})
|
||||
.use(Dashboard, {
|
||||
inline: true,
|
||||
target: '.uploadTarget',
|
||||
replaceTargetContent: true,
|
||||
locale: {
|
||||
strings: {
|
||||
done: self.$i18n.t('uppyDashboard.done'),
|
||||
dropPaste: self.$i18n.t('uppyDashboard.dropPaste'),
|
||||
browse: self.$i18n.t('uppyDashboard.browse')
|
||||
}
|
||||
}
|
||||
})
|
||||
.use(Tus, {
|
||||
endpoint: '/upload/',
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + self.token
|
||||
}
|
||||
})
|
||||
.run();
|
||||
|
||||
self.uppy.on('complete', (result) =>
|
||||
{
|
||||
axios.post('/complete', {
|
||||
files: _.map(result.successful, (file) =>
|
||||
{
|
||||
return {
|
||||
id: file.tus.uploadUrl.substr(file.tus.uploadUrl.lastIndexOf('/') + 1),
|
||||
name: file.name
|
||||
};
|
||||
})
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + self.token
|
||||
}
|
||||
})
|
||||
.then((response) =>
|
||||
{
|
||||
// TODO anything?
|
||||
})
|
||||
.catch((error) =>
|
||||
{
|
||||
// TODO can we convince Uppy that the files actually failed?
|
||||
});
|
||||
});
|
||||
|
||||
self.uppy.on('upload-success', (file, resp, uploadURL) => {
|
||||
// Clear the uploadURL so the dashboard will not display it as a link.
|
||||
// The file can't be viewed by the user anyways since JWT headers are required.
|
||||
self.uppy.setFileState(file.id, {
|
||||
uploadURL: false
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,9 @@
|
|||
import Vue from 'vue';
|
||||
|
||||
export default new Vue({
|
||||
data () {
|
||||
return {
|
||||
token: null
|
||||
}
|
||||
},
|
||||
});
|
|
@ -0,0 +1,117 @@
|
|||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
entry: './public/src/app.js',
|
||||
|
||||
output: {
|
||||
path: path.resolve(__dirname, './public/dist'),
|
||||
publicPath: '/',
|
||||
filename: 'build.js'
|
||||
},
|
||||
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
'vue-style-loader',
|
||||
'css-loader'
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
use: [
|
||||
'vue-style-loader',
|
||||
'css-loader',
|
||||
'sass-loader'
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: 'vue-loader',
|
||||
options: {
|
||||
loaders: {
|
||||
'scss': [
|
||||
'vue-style-loader',
|
||||
'css-loader',
|
||||
'sass-loader'
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
exclude: /node_modules/
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpg|gif|svg)$/,
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: '[name].[ext]?[hash]'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({
|
||||
template: path.resolve(__dirname, './public/src/index.html')
|
||||
})
|
||||
],
|
||||
|
||||
resolve: {
|
||||
alias: {
|
||||
'vue$': 'vue/dist/vue.esm.js'
|
||||
},
|
||||
extensions: ['*', '.js', '.vue', '.json']
|
||||
},
|
||||
|
||||
devServer: {
|
||||
historyApiFallback: true,
|
||||
noInfo: true,
|
||||
overlay: true
|
||||
},
|
||||
|
||||
performance: {
|
||||
hints: false
|
||||
},
|
||||
|
||||
devtool: '#eval-source-map'
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'production')
|
||||
{
|
||||
module.exports.devtool = '#source-map'
|
||||
module.exports.plugins = (module.exports.plugins || []).concat([
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
NODE_ENV: '"production"'
|
||||
}
|
||||
}),
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
sourceMap: true,
|
||||
compress: {
|
||||
warnings: false
|
||||
}
|
||||
}),
|
||||
new webpack.LoaderOptionsPlugin({
|
||||
minimize: true
|
||||
})
|
||||
]);
|
||||
}
|
||||
else
|
||||
{
|
||||
module.exports.entry = [
|
||||
module.exports.entry,
|
||||
'webpack-hot-middleware/client'
|
||||
];
|
||||
|
||||
module.exports.plugins = (module.exports.plugins || []).concat([
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
new webpack.NoEmitOnErrorsPlugin()
|
||||
]);
|
||||
}
|
Loading…
Reference in New Issue