qbit-api-v2 begin

This commit is contained in:
Daan Wijns 2020-03-22 10:59:38 +01:00
parent f74d378895
commit 7a6ea22b91
16 changed files with 993 additions and 669 deletions

View file

@ -15,14 +15,15 @@ module.exports = {
plugins: ['vue'], plugins: ['vue'],
rules: { rules: {
semi: ['warn', 'never'], semi: ['warn', 'never'],
'no-console' : 0, 'no-console': 0,
camelcase: 0, camelcase: 0,
'no-restricted-syntax': 0, 'no-restricted-syntax': 0,
'no-shadow': 0, 'no-shadow': 0,
'class-methods-use-this': 0, 'class-methods-use-this': 0,
'prefer-promise-reject-errors': 0, 'prefer-promise-reject-errors': 0,
'no-underscore-dangle': 0, 'no-underscore-dangle': 0,
'no-param-reassign' : 0, 'no-param-reassign': 0,
'no-unused-vars': 0 'no-unused-vars': 0,
'indent': 4
}, },
} }

View file

@ -1,14 +1,16 @@
FROM node:10-slim # build stage
FROM node:10-slim as build-stage
# Create app directory # Create app directory
WORKDIR /usr/src/app WORKDIR /usr/src/app
COPY package*.json ./ COPY package*.json ./
RUN npm install RUN npm install
COPY . . COPY . .
RUN npm run build
# production stage
FROM node:10-slim as production-stage
COPY --from=build-stage /usr/src/app /usr/src/app
#serve
EXPOSE 3001 EXPOSE 3001
CMD ["node", "server/server.js"]
CMD ["node", "server/server.js"]

View file

@ -1,3 +1,3 @@
module.exports = { module.exports = {
presets: ['@vue/app'] presets: ['@vue/app'],
}; }

1010
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -9,42 +9,42 @@
"start": "nodemon server/server.js" "start": "nodemon server/server.js"
}, },
"dependencies": { "dependencies": {
"apexcharts": "^3.10.1", "apexcharts": "^3.17.0",
"axios": "^0.18.1", "axios": "^0.18.1",
"cors": "^2.8.5", "cors": "^2.8.5",
"date-fns": "^1.30.1", "date-fns": "^1.30.1",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"express": "^4.17.1", "express": "^4.17.1",
"filepond": "^4.8.0", "filepond": "^4.13.0",
"filepond-plugin-file-validate-size": "^2.2.0", "filepond-plugin-file-validate-size": "^2.2.0",
"filepond-plugin-file-validate-type": "^1.2.4", "filepond-plugin-file-validate-type": "^1.2.4",
"filepond-plugin-image-preview": "^4.5.0", "filepond-plugin-image-preview": "^4.6.1",
"multer": "^1.4.2", "multer": "^1.4.2",
"qbittorrent-api": "^1.0.0", "qbittorrent-api-v2": "^1.2.0",
"register-service-worker": "^1.6.2", "register-service-worker": "^1.7.1",
"vue": "^2.6.10", "vue": "^2.6.11",
"vue-apexcharts": "^1.5.1", "vue-apexcharts": "^1.5.2",
"vue-filepond": "^5.1.3", "vue-filepond": "^5.1.3",
"vue-router": "^3.1.3", "vue-router": "^3.1.6",
"vuetify": "^1.5.21", "vuetify": "^1.5.24",
"vuex": "^3.1.2" "vuex": "^3.1.3"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-babel": "^3.12.1", "@vue/cli-plugin-babel": "^3.12.1",
"@vue/cli-plugin-eslint": "^3.12.1", "@vue/cli-plugin-eslint": "^3.12.1",
"@vue/cli-plugin-pwa": "^3.12.1", "@vue/cli-plugin-pwa": "^3.12.1",
"@vue/cli-service": "^3.12.1", "@vue/cli-service": "^3.12.1",
"eslint": "^6.7.2", "eslint": "^6.8.0",
"eslint-config-airbnb-base": "^14.0.0", "eslint-config-airbnb-base": "^14.1.0",
"eslint-plugin-import": "^2.19.1", "eslint-plugin-import": "^2.20.1",
"eslint-plugin-vue": "^5.2.3", "eslint-plugin-vue": "^5.2.3",
"stylus": "^0.54.7", "stylus": "^0.54.7",
"stylus-loader": "^3.0.1", "stylus-loader": "^3.0.1",
"vue-cli-plugin-vuetify": "^0.4.6", "vue-cli-plugin-vuetify": "^0.4.6",
"vue-template-compiler": "^2.6.10", "vue-template-compiler": "^2.6.11",
"vuetify-loader": "^1.4.3", "vuetify-loader": "^1.4.3",
"webpack": "^4.41.2", "webpack": "^4.42.0",
"webpack-cli": "^3.3.10" "webpack-cli": "^3.3.11"
}, },
"eslintConfig": { "eslintConfig": {
"root": true, "root": true,

View file

@ -1,89 +1,101 @@
const api = require('qbittorrent-api') const api = require('qbittorrent-api-v2')
const dotenv = require('dotenv') const dotenv = require('dotenv')
const Torrent = require('./models/torrent.class.js') const Torrent = require('./models/torrent.class.js')
const Stat = require('./models/stat.class.js') const Stat = require('./models/stat.class.js')
dotenv.config({ path: '../.env' }) dotenv.config()
const connection= api.connect(process.env.QBIT_HOST, process.env.QBIT_USER, process.env.QBIT_PASS)
// server
const qbt = api.connect(process.env.QBIT_HOST, process.env.QBIT_USER, process.env.QBIT_PASS)
class Qbit { class Qbit {
async get_all(prop) { async get_all(prop) {
return new Promise((resolve, reject) => { try {
qbt.all({ sort: prop.name, reverse: prop.reverse }, (err, res) => { const res = await (await connection).torrents('all', null, prop.name, String(prop.reverse), '20', null, null)
const torrents = [] const torrents = []
res.forEach((el) => { res.forEach((el) => {
// console.log(el);
const t = new Torrent(el) const t = new Torrent(el)
torrents.push(t) torrents.push(t)
}) })
// console.log(torrents[0]); return torrents
resolve(torrents) }
reject(err) catch(err){
}) return `something went wrong:${err}`
}) }
} }
async get_session_stats() { async get_session_stats() {
return new Promise((resolve, reject) => { try {
qbt.transferInfo((err, res) => { const res = await (await connection).transferInfo()
const stat = new Stat(res) return new Stat(res)
// console.log(stat); }
resolve(stat) catch(err){
reject(`something went wrong:${err}`) return `something went wrong:${err}`
}) }
})
} }
async pause_torrents(torrents) { async pause_torrents(torrents) {
return new Promise((resolve, reject) => { let _torrents = ''
qbt.pause(torrents, (err, res) => { torrents.forEach(el=> {
resolve(res) _torrents += el + '|'
reject(`something went wrong:${err}`)
})
}) })
try {
return await (await connection).pauseTorrents(_torrents)
}
catch(err){
return `something went wrong:${err}`
}
} }
async pause_all() { async pause_all() {
return new Promise((resolve, reject) => { try {
qbt.pauseAll((err, res) => { return await (await connection).pauseTorrents('all')
resolve(res) }
reject(`something went wrong:${err}`) catch(err){
}) return `something went wrong:${err}`
}) }
} }
async resume_torrents(torrents) { async resume_torrents(torrents) {
return new Promise((resolve, reject) => { let _torrents = ''
qbt.resume(torrents, (err, res) => { torrents.forEach(el=> {
resolve(res) _torrents += el + '|'
reject(`something went wrong:${err}`)
})
}) })
console.log(_torrents);
try {
return await (await connection).resumeTorrents(_torrents)
}
catch(err){
return `something went wrong:${err}`
}
} }
async resume_all() { async resume_all() {
return new Promise((resolve, reject) => { try {
qbt.resumeAll((err, res) => { return await (await connection).resumeTorrents('all')
resolve(res) }
reject(`something went wrong:${err}`) catch(err){
}) return `something went wrong:${err}`
}) }
} }
async remove_torrents(torrents) { async remove_torrents(torrents) {
return new Promise((resolve, reject) => { let _torrents = ''
qbt.delete(torrents, (err, res) => { torrents.forEach(el=> {
resolve(res) _torrents += el + '|'
reject(`something went wrong:${err}`)
})
}) })
console.log(_torrents);
try {
return await (await connection).deleteTorrents(_torrents, 'true')
}
catch(err){
return `something went wrong:${err}`
}
} }
async add(torrent) { async add(torrent) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
qbt.add(torrent.path, null, null, (err, res) => { connection.add(torrent.path, null, null, (err, res) => {
resolve(res) resolve(res)
reject(err) reject(err)
}) })
@ -91,6 +103,6 @@ class Qbit {
} }
} }
const qbit = new Qbit() const qbit = new Qbit();
module.exports = qbit module.exports = qbit

View file

@ -5,8 +5,7 @@ const path = require('path')
const dotenv = require('dotenv') const dotenv = require('dotenv')
const qbit = require('./qbit') const qbit = require('./qbit')
dotenv.config()
dotenv.config({ path: '../.env' })
const PORT = process.env.PORT || 3000 const PORT = process.env.PORT || 3000

View file

@ -32,32 +32,32 @@
</template> </template>
<script> <script>
import Navbar from "./components/Navbar"; import { mapState } from 'vuex'
import Login from "./components/Login"; import Navbar from './components/Navbar'
import { mapState } from "vuex"; import Login from './components/Login'
export default { export default {
components: { Navbar, Login }, components: { Navbar, Login },
name: "App", name: 'App',
data() { data() {
return {}; return {}
}, },
computed: { computed: {
...mapState([ ...mapState([
"authenticated", 'authenticated',
"snackbar_error", 'snackbar_error',
"error_msg", 'error_msg',
"snackbar", 'snackbar',
"succes_msg" 'succes_msg',
]) ]),
}, },
methods: { methods: {
snackbar_errorClose() { snackbar_errorClose() {
this.$store.state.snackbar_error = false; this.$store.state.snackbar_error = false
}, },
snackbarClose() { snackbarClose() {
this.$store.state.snackbar = false; this.$store.state.snackbar = false
} },
} },
}; }
</script> </script>

View file

@ -47,71 +47,70 @@
<script> <script>
// Import Vue FilePond // Import Vue FilePond
import vueFilePond from "vue-filepond"; import vueFilePond from 'vue-filepond'
// Import FilePond styles // Import FilePond styles
import "filepond/dist/filepond.min.css"; import 'filepond/dist/filepond.min.css'
// Import image preview plugin styles // Import image preview plugin styles
import "filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.css"; import 'filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.css'
// Import image preview and file type validation plugins // Import image preview and file type validation plugins
import FilePondPluginFileValidateType from "filepond-plugin-file-validate-type"; import FilePondPluginFileValidateType from 'filepond-plugin-file-validate-type'
import FilePondPluginImagePreview from "filepond-plugin-image-preview"; import FilePondPluginImagePreview from 'filepond-plugin-image-preview'
import FilePondPluginFileValidateSize from "filepond-plugin-file-validate-size"; import FilePondPluginFileValidateSize from 'filepond-plugin-file-validate-size'
// Create component // Create component
const FilePond = vueFilePond( const FilePond = vueFilePond(
FilePondPluginFileValidateType, FilePondPluginFileValidateType,
FilePondPluginImagePreview, FilePondPluginImagePreview,
FilePondPluginFileValidateSize FilePondPluginFileValidateSize,
); )
export default { export default {
data() { data() {
return { return {
filename: "", filename: '',
directory: "", directory: '',
inputRules: [ inputRules: [
v => (v) => v.indexOf('magnet') > -1
v.indexOf("magnet") > -1 || || v.indexOf('http') > -1
v.indexOf("http") > -1 || || this.validFile
this.validFile || || 'Not a valid magnet link',
"Not a valid magnet link"
], ],
loading: false, loading: false,
dialog: false, dialog: false,
Files: [] Files: [],
}; }
}, },
methods: { methods: {
submit() { submit() {
if (this.$refs.form.validate()) { if (this.$refs.form.validate()) {
this.loading = true; this.loading = true
this.$store.dispatch("ADD_TORRENT", { this.$store.dispatch('ADD_TORRENT', {
name: this.filename, name: this.filename,
dir: this.directory dir: this.directory,
}); })
//reset input // reset input
this.$refs.form.reset(); this.$refs.form.reset()
this.filename = ""; this.filename = ''
this.directory = ""; this.directory = ''
this.$refs.pond.removeFiles(); this.$refs.pond.removeFiles()
this.dialog = false; this.dialog = false
this.loading = false; this.loading = false
} }
} },
}, },
computed: { computed: {
validFile() { validFile() {
return this.Files.length > 0; return this.Files.length > 0
} },
}, },
components: { components: {
vueFilePond vueFilePond,
} },
}; }
</script> </script>

View file

@ -46,26 +46,27 @@
</template> </template>
<script> <script>
import { mapState } from "vuex"; import { mapState } from 'vuex'
export default { export default {
data() { data() {
return { return {
username: "", username: '',
password: "", password: '',
inputRules: [v => v.length >= 1 || "At least 1 character"] inputRules: [(v) => v.length >= 1 || 'At least 1 character'],
}; }
}, },
methods: { methods: {
Login() { Login() {
this.$store.state.loading = true; this.$store.state.loading = true
this.$store.dispatch("LOGIN", { this.$store.dispatch('LOGIN', {
username: this.username, username: this.username,
password: this.password password: this.password,
}); })
} },
}, },
computed: { computed: {
...mapState(["loading"]) ...mapState(['loading']),
} },
}; }
</script> </script>

View file

@ -106,10 +106,11 @@
</template> </template>
<script> <script>
import AddTorrent from "./AddTorrent"; import { mapMutations, mapGetters, mapState } from 'vuex'
import Settings from "./Settings"; import { setInterval } from 'timers'
import { mapMutations, mapGetters, mapState } from "vuex"; import AddTorrent from './AddTorrent'
import { setInterval } from "timers"; import Settings from './Settings'
export default { export default {
components: { AddTorrent, Settings }, components: { AddTorrent, Settings },
data() { data() {
@ -117,89 +118,89 @@ export default {
drawer: false, drawer: false,
paused: false, paused: false,
links: [ links: [
{ icon: "dashboard", text: "Dashboard", route: "/" }, { icon: 'dashboard', text: 'Dashboard', route: '/' },
{ icon: "settings", text: "Settings", route: "/settings" } { icon: 'settings', text: 'Settings', route: '/settings' },
], ],
chartOptions: { chartOptions: {
chart: { chart: {
sparkline: { sparkline: {
enabled: true enabled: true,
}, },
animations: { animations: {
enabled: false, enabled: false,
dynamicAnimation: { dynamicAnimation: {
speed: 2000 speed: 2000,
} },
} },
}, },
colors: ["#00b3fa", "#64CEAA"], colors: ['#00b3fa', '#64CEAA'],
stroke: { stroke: {
show: true, show: true,
curve: "smooth", curve: 'smooth',
lineCap: "round", lineCap: 'round',
width: 4 width: 4,
}, },
fill: { fill: {
type: "gradient", type: 'gradient',
gradient: { gradient: {
shade: "dark", shade: 'dark',
type: "vertical", type: 'vertical',
shadeIntensity: 0.5, shadeIntensity: 0.5,
opacityFrom: 0.6, opacityFrom: 0.6,
opacityTo: 0.5, opacityTo: 0.5,
stops: [0, 50, 100] stops: [0, 50, 100],
} },
} },
}, },
series: [ series: [
{ {
name: "upload", name: 'upload',
type: "area", type: 'area',
data: this.$store.state.upload_data data: this.$store.state.upload_data,
}, },
{ {
name: "download", name: 'download',
type: "area", type: 'area',
data: this.$store.state.download_data data: this.$store.state.download_data,
} },
] ],
};
},
methods: {
...mapMutations(["REFRESH_TORRENTS", "CLEAR_INTERVALS"]),
clearInterval() {
this.$store.commit("CLEAR_INTERVALS");
this.$data.paused = !this.$data.paused;
},
startInterval() {
this.$store.dispatch("REFRESH_TORRENTS");
this.$store.dispatch("REFRESH_SESSION_STATS");
this.$data.paused = !this.$data.paused;
},
pauseTorrents() {
this.$store.dispatch("PAUSE_TORRENTS");
},
resumeTorrents() {
this.$store.dispatch("RESUME_TORRENTS");
},
removeTorrents() {
this.$store.dispatch("REMOVE_TORRENTS");
},
refreshTorrents() {
this.$store.state.init_torrents = false;
this.$store.dispatch("REFRESH_TORRENTS");
},
closeSnackbar() {
this.$store.state.snackbar = false;
} }
}, },
created: function() { methods: {
this.$store.dispatch("REFRESH_SESSION_STATS"); ...mapMutations(['REFRESH_TORRENTS', 'CLEAR_INTERVALS']),
clearInterval() {
this.$store.commit('CLEAR_INTERVALS')
this.$data.paused = !this.$data.paused
},
startInterval() {
this.$store.dispatch('REFRESH_TORRENTS')
this.$store.dispatch('REFRESH_SESSION_STATS')
this.$data.paused = !this.$data.paused
},
pauseTorrents() {
this.$store.dispatch('PAUSE_TORRENTS')
},
resumeTorrents() {
this.$store.dispatch('RESUME_TORRENTS')
},
removeTorrents() {
this.$store.dispatch('REMOVE_TORRENTS')
},
refreshTorrents() {
this.$store.state.init_torrents = false
this.$store.dispatch('REFRESH_TORRENTS')
},
closeSnackbar() {
this.$store.state.snackbar = false
},
},
created() {
this.$store.dispatch('REFRESH_SESSION_STATS')
}, },
computed: { computed: {
...mapState(["stats", "snackbar_error", "error_msg", "snackbar"]) ...mapState(['stats', 'snackbar_error', 'error_msg', 'snackbar']),
} },
}; }
</script> </script>
<style> <style>
.project.nav_upload { .project.nav_upload {

View file

@ -1,18 +1,18 @@
import Vue from 'vue'; import Vue from 'vue'
import Vuetify from 'vuetify/lib'; import Vuetify from 'vuetify/lib'
import 'vuetify/src/stylus/app.styl'; import 'vuetify/src/stylus/app.styl'
Vue.use(Vuetify, { Vue.use(Vuetify, {
iconfont: 'md', iconfont: 'md',
theme: { theme: {
primary: '#35495e', primary: '#35495e',
secondary: '#3e556d', secondary: '#3e556d',
secondary_lighter: "#56718c", secondary_lighter: '#56718c',
blue_accent: '#3cd1c2', blue_accent: '#3cd1c2',
info: '#ffaa2c', info: '#ffaa2c',
error: '#f83e70', error: '#f83e70',
green_accent: '#3cd1c2', green_accent: '#3cd1c2',
download : '#64CEAA', download: '#64CEAA',
upload: '#00b3fa' upload: '#00b3fa',
} },
}); })

View file

@ -4,29 +4,29 @@ import { register } from 'register-service-worker'
if (process.env.NODE_ENV === 'production') { if (process.env.NODE_ENV === 'production') {
register(`${process.env.BASE_URL}service-worker.js`, { register(`${process.env.BASE_URL}service-worker.js`, {
ready () { ready() {
console.log( console.log(
'App is being served from cache by a service worker.\n' + 'App is being served from cache by a service worker.\n'
'For more details, visit https://goo.gl/AFskqB' + 'For more details, visit https://goo.gl/AFskqB',
) )
}, },
registered () { registered() {
console.log('Service worker has been registered.') console.log('Service worker has been registered.')
}, },
cached () { cached() {
console.log('Content has been cached for offline use.') console.log('Content has been cached for offline use.')
}, },
updatefound () { updatefound() {
console.log('New content is downloading.') console.log('New content is downloading.')
}, },
updated () { updated() {
console.log('New content is available; please refresh.') console.log('New content is available; please refresh.')
}, },
offline () { offline() {
console.log('No internet connection found. App is running in offline mode.') console.log('No internet connection found. App is running in offline mode.')
}, },
error (error) { error(error) {
console.error('Error during service worker registration:', error) console.error('Error during service worker registration:', error)
} },
}) })
} }

View file

@ -11,7 +11,7 @@ export default new Router({
{ {
path: '/', path: '/',
name: 'dashboard', name: 'dashboard',
component: Dashboard component: Dashboard,
} },
] ],
}) })

View file

@ -120,124 +120,125 @@
</template> </template>
<script> <script>
import { mapState, mapMutations, mapGetters } from "vuex"; import { mapState, mapMutations, mapGetters } from 'vuex'
export default { export default {
data() { data() {
return { return {
sort_input: "" sort_input: '',
}; }
}, },
computed: { computed: {
...mapState(["torrents", "init_torrents"]) ...mapState(['torrents', 'init_torrents']),
}, },
methods: { methods: {
...mapMutations(["SORT_TORRENTS"]), ...mapMutations(['SORT_TORRENTS']),
sortBy() { sortBy() {
let name, reverse; let name; let
//search if order was presented reverse
const index = this.sort_input.indexOf(" "); // search if order was presented
const index = this.sort_input.indexOf(' ')
if (index > -1) { if (index > -1) {
name = this.sort_input.substring(0, index); name = this.sort_input.substring(0, index)
let temp = this.sort_input.substring(index); const temp = this.sort_input.substring(index)
if (temp.indexOf("asc") > -1) { if (temp.indexOf('asc') > -1) {
reverse = false; reverse = false
} else if (temp.indexOf("desc") > -1) { } else if (temp.indexOf('desc') > -1) {
reverse = true; reverse = true
} }
} else { } else {
//no order so we assume input is propname // no order so we assume input is propname
name = this.sort_input; name = this.sort_input
reverse = false; reverse = false
} }
//prop names // prop names
switch (name) { switch (name) {
case "title": case 'title':
case "name": case 'name':
case "Name": case 'Name':
case "Title": case 'Title':
name = "name"; name = 'name'
break; break
case "size": case 'size':
case "Size": case 'Size':
name = "size"; name = 'size'
break; break
case "dlspeed": case 'dlspeed':
case "Dlspeed": case 'Dlspeed':
case "Download": case 'Download':
case "download": case 'download':
case "downloadspeed": case 'downloadspeed':
name = "dlspeed"; name = 'dlspeed'
break; break
case "upspeed": case 'upspeed':
case "upload": case 'upload':
case "Upload": case 'Upload':
case "Upspeed": case 'Upspeed':
case "uploadspeed": case 'uploadspeed':
name = "upspeed"; name = 'upspeed'
break; break
case "leechs": case 'leechs':
case "leechers": case 'leechers':
case "leech": case 'leech':
case "peers": case 'peers':
case "Leechs": case 'Leechs':
case "Leechers": case 'Leechers':
case "Leech": case 'Leech':
case "Peers": case 'Peers':
name = "num_leechs"; name = 'num_leechs'
break; break
case "seeds": case 'seeds':
case "seeders": case 'seeders':
case "Seeds": case 'Seeds':
case "Seeders": case 'Seeders':
name = "num_seeds"; name = 'num_seeds'
break; break
case "remaining": case 'remaining':
case "time": case 'time':
case "Time": case 'Time':
case "ETA": case 'ETA':
case "eta": case 'eta':
name = "eta"; name = 'eta'
break; break
case "done": case 'done':
case "downloaded": case 'downloaded':
case "dloaded": case 'dloaded':
case "Done": case 'Done':
case "Downloaded": case 'Downloaded':
case "Dloaded": case 'Dloaded':
name = "downloaded"; name = 'downloaded'
break; break
case "state": case 'state':
case "status": case 'status':
case "State": case 'State':
case "Status": case 'Status':
name = "state"; name = 'state'
break; break
default: default:
name = "name"; name = 'name'
break; break
} }
this.$store.state.sort_options = { name, reverse }; this.$store.state.sort_options = { name, reverse }
}, },
selectTorrent(hash) { selectTorrent(hash) {
if (this.containsTorrent(hash)) { if (this.containsTorrent(hash)) {
this.$store.dispatch("REMOVE_SELECTED", hash); this.$store.dispatch('REMOVE_SELECTED', hash)
} else { } else {
this.$store.dispatch("ADD_SELECTED", hash); this.$store.dispatch('ADD_SELECTED', hash)
} }
}, },
containsTorrent(hash) { containsTorrent(hash) {
return this.$store.getters["CONTAINS_TORRENT"](hash); return this.$store.getters.CONTAINS_TORRENT(hash)
}, },
resetSelected() { resetSelected() {
this.$store.dispatch("RESET_SELECTED"); this.$store.dispatch('RESET_SELECTED')
} },
}, },
created: function() { created() {
this.$store.dispatch("REFRESH_TORRENTS"); this.$store.dispatch('REFRESH_TORRENTS')
} },
}; }
</script> </script>
<style> <style>

View file

@ -1,5 +1,5 @@
module.exports = { module.exports = {
devServer: { devServer: {
proxy: 'http://localhost:3000/', proxy: 'http://localhost:3001/',
}, },
} }