mirror of
https://github.com/VueTorrent/VueTorrent.git
synced 2025-03-14 12:10:18 +03:00
switched to using routes + add logout + truncate torrent names
This commit is contained in:
parent
51c494a5bd
commit
3456fa83bf
9 changed files with 288 additions and 252 deletions
15
.github/workflows/prod-deploy copy.yml
vendored
15
.github/workflows/prod-deploy copy.yml
vendored
|
@ -8,16 +8,21 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions/setup-node@master
|
||||
- name: Install & build
|
||||
uses: actions/setup-node@master
|
||||
- run: npm install
|
||||
- run: npm run build
|
||||
- uses: montudor/action-zip@v0.1.0
|
||||
- name: Zip it
|
||||
uses: montudor/action-zip@v0.1.0
|
||||
with:
|
||||
args: zip -qq -r ./release.zip ./vuetorrent
|
||||
- uses: "marvinpinto/action-automatic-releases@latest"
|
||||
- name: Get Version
|
||||
run: echo ::set-env name=VERSION::$(jq -r .version package.json)
|
||||
- name: Push release
|
||||
uses: "marvinpinto/action-automatic-releases@latest"
|
||||
with:
|
||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
automatic_release_tag: "prod-latest"
|
||||
automatic_release_tag: "prod-v${{ env.VERSION }}"
|
||||
prerelease: true
|
||||
title: "Stable Build"
|
||||
title: "Stable Build v${{ env.VERSION }}"
|
||||
files: release.zip
|
||||
|
|
36
src/App.vue
36
src/App.vue
|
@ -1,40 +1,21 @@
|
|||
<template>
|
||||
<v-app class="background">
|
||||
<AddModal />
|
||||
<div v-if="authenticated" class="background">
|
||||
<keep-alive><Navbar /></keep-alive>
|
||||
<v-content class="mx-4 mb-4">
|
||||
<v-app :style="{ backgroundColor : background }" >
|
||||
<AddModal />
|
||||
<Navbar v-if="authenticated" />
|
||||
<v-container fill-height fill-width>
|
||||
<v-content>
|
||||
<router-view></router-view>
|
||||
</v-content>
|
||||
</div>
|
||||
<v-container v-else fill-height>
|
||||
<v-layout
|
||||
row
|
||||
wrap
|
||||
align-center
|
||||
class="justify-center"
|
||||
justify-center
|
||||
>
|
||||
<div style="margin: 0 auto;">
|
||||
<Login />
|
||||
</div>
|
||||
</v-layout>
|
||||
</v-container>
|
||||
<div class="background">
|
||||
<p class="grey--text caption text-center">
|
||||
Made by Daan Wijns
|
||||
</p>
|
||||
</div>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import Navbar from './components/Navbar.vue'
|
||||
import Login from './components/Login.vue'
|
||||
import Navbar from '@/components/Navbar.vue'
|
||||
|
||||
export default {
|
||||
components: { Navbar, Login },
|
||||
components: { Navbar },
|
||||
name: 'App',
|
||||
data() {
|
||||
return {}
|
||||
|
@ -44,6 +25,9 @@ export default {
|
|||
...mapGetters(['getTheme']),
|
||||
theme() {
|
||||
return this.getTheme() ? 'dark' : 'light'
|
||||
},
|
||||
background(){
|
||||
return this.$vuetify.theme.themes[this.theme].background
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
<template>
|
||||
<v-container class="grey lighten-4">
|
||||
<v-card max-width="400" flat>
|
||||
<v-container :class="`pa-3 project done`">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>Login</h2>
|
||||
</v-card-title>
|
||||
<div class="mr-5 ml-5"></div>
|
||||
<v-card-text>
|
||||
<v-form class="px-3" ref="form">
|
||||
<v-text-field
|
||||
flat
|
||||
solo
|
||||
background-color="grey lighten-4"
|
||||
label="username"
|
||||
prepend-icon="person"
|
||||
v-model="username"
|
||||
:rules="inputRules"
|
||||
@keyup.enter.native="Login"
|
||||
autocomplete="current email"
|
||||
></v-text-field>
|
||||
<v-text-field
|
||||
flat
|
||||
solo
|
||||
background-color="grey lighten-4"
|
||||
type="password"
|
||||
label="password"
|
||||
prepend-icon="lock"
|
||||
v-model="password"
|
||||
:rules="inputRules"
|
||||
@keyup.enter.native="Login"
|
||||
autocomplete="current password"
|
||||
></v-text-field>
|
||||
<v-spacer></v-spacer>
|
||||
<v-card-actions class="justify-center">
|
||||
<v-btn
|
||||
:loading="loading"
|
||||
text
|
||||
@click="Login"
|
||||
class="blue_accent white--text mx-0 mt-3"
|
||||
>Login</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
username: '',
|
||||
password: '',
|
||||
inputRules: [v => v.length >= 1 || 'At least 1 character']
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
Login() {
|
||||
this.$store.state.loading = true
|
||||
this.$store.dispatch('LOGIN', {
|
||||
username: this.username,
|
||||
password: this.password
|
||||
})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['loading'])
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -145,26 +145,31 @@
|
|||
</v-layout>
|
||||
</v-card>
|
||||
</v-flex>
|
||||
<v-flex style="position:fixed; bottom: 15px; right: 15px;" >
|
||||
<v-list>
|
||||
<v-container>
|
||||
<v-row justify="space-between" style="position:fixed; bottom: 0px; right: 15px;">
|
||||
<v-col>
|
||||
<v-list-item @click="logout" link>
|
||||
<v-icon class="pr-2 white--text">exit_to_app</v-icon>
|
||||
<v-list-item-title class="white--text" style="font-size:14px">Log out</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-list-item @click="toggleTheme" link>
|
||||
<v-icon v-if="theme === 'Light'" class="pr-2 white--text"
|
||||
>brightness_7</v-icon
|
||||
>
|
||||
<v-icon v-if="theme === 'Light'" class="pr-2 white--text">brightness_7</v-icon>
|
||||
<v-icon v-else class="pr-2 white--text">brightness_2</v-icon>
|
||||
<v-list-item-title class="white--text" style="font-size:15px">{{
|
||||
theme
|
||||
}}</v-list-item-title>
|
||||
<v-list-item-title class="white--text" style="font-size:14px">
|
||||
{{theme}}
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-flex>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-navigation-drawer>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapMutations, mapState, mapGetters } from 'vuex'
|
||||
import { setInterval } from 'timers'
|
||||
import VueApexCharts from 'vue-apexcharts'
|
||||
import qbit from '@/services/qbit'
|
||||
|
||||
|
@ -241,6 +246,10 @@ export default {
|
|||
this.$store.commit('TOGGLE_THEME')
|
||||
this.$vuetify.theme.dark = !this.$vuetify.theme.dark;
|
||||
},
|
||||
logout(){
|
||||
this.$store.commit('LOGOUT')
|
||||
this.$router.push('/login')
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['stats', 'selected_torrents']),
|
||||
|
|
|
@ -1,123 +1,120 @@
|
|||
<template>
|
||||
<v-card ripple
|
||||
flat class="pointer torrent"
|
||||
:class="
|
||||
containsTorrent(torrent.hash) ? 'torrent_selected' : ''
|
||||
"
|
||||
@click.native="selectTorrent(torrent.hash)">
|
||||
<v-card ripple flat class="pointer torrent"
|
||||
:class="containsTorrent(torrent.hash) ? 'torrent_selected' : ''"
|
||||
@click.native="selectTorrent(torrent.hash)">
|
||||
<v-layout row wrap :class="`pa-4 ml-0 project ${torrent.state}`">
|
||||
<v-flex xs12 sm2 md3>
|
||||
<div class="caption grey--text">Torrent title</div>
|
||||
<div>{{ torrent.name }}</div>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Size</div>
|
||||
<div>
|
||||
{{
|
||||
torrent.size.substring(
|
||||
0,
|
||||
torrent.size.indexOf(' ')
|
||||
)
|
||||
}}
|
||||
<span class="caption grey--text">{{
|
||||
torrent.size.substring(
|
||||
torrent.size.indexOf(' ')
|
||||
)
|
||||
}}</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs5 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Done</div>
|
||||
<div>
|
||||
{{
|
||||
torrent.dloaded.substring(
|
||||
0,
|
||||
torrent.dloaded.indexOf(' ')
|
||||
)
|
||||
}}
|
||||
<span class="caption grey--text">{{
|
||||
torrent.dloaded.substring(
|
||||
torrent.dloaded.indexOf(' ')
|
||||
)
|
||||
}}</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Download</div>
|
||||
<div>
|
||||
{{
|
||||
torrent.dlspeed.substring(
|
||||
0,
|
||||
torrent.dlspeed.indexOf(' ')
|
||||
)
|
||||
}}
|
||||
<span class="caption grey--text">{{
|
||||
torrent.dlspeed.substring(
|
||||
torrent.dlspeed.indexOf(' ')
|
||||
)
|
||||
}}</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs5 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Upload</div>
|
||||
<div>
|
||||
{{
|
||||
torrent.upspeed.substring(
|
||||
0,
|
||||
torrent.upspeed.indexOf(' ')
|
||||
)
|
||||
}}
|
||||
<span class="caption grey--text">{{
|
||||
torrent.upspeed.substring(
|
||||
torrent.upspeed.indexOf(' ')
|
||||
)
|
||||
}}</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">ETA</div>
|
||||
<div>{{ torrent.eta }}</div>
|
||||
</v-flex>
|
||||
<v-flex xs5 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Peers</div>
|
||||
<div>
|
||||
{{ torrent.num_leechs }}
|
||||
<span class="grey--text caption"
|
||||
>/{{ torrent.available_peers }}</span
|
||||
>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs5 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Seeds</div>
|
||||
<div>
|
||||
{{ torrent.num_seeds }}
|
||||
<span class="grey--text caption"
|
||||
>/{{ torrent.available_seeds }}</span
|
||||
>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs4 sm12 md1>
|
||||
<div class="right">
|
||||
<v-chip
|
||||
small
|
||||
:class="
|
||||
`${torrent.state} white--text my-2 caption`
|
||||
"
|
||||
>{{ torrent.state }}</v-chip
|
||||
>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs12 sm12 md12>
|
||||
<v-progress-linear
|
||||
height="3"
|
||||
color="cyan darken-1"
|
||||
background-color="cyan lighten-3"
|
||||
:value="(torrent.dloaded / torrent.size) * 100"
|
||||
></v-progress-linear>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<v-divider></v-divider>
|
||||
</v-card>
|
||||
<v-flex xs12 sm2 md3>
|
||||
<div class="caption grey--text">Torrent title</div>
|
||||
<div class="truncate">{{ torrent.name }}</div>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Size</div>
|
||||
<div>
|
||||
{{
|
||||
torrent.size.substring(
|
||||
0,
|
||||
torrent.size.indexOf(' ')
|
||||
)
|
||||
}}
|
||||
<span class="caption grey--text">{{
|
||||
torrent.size.substring(
|
||||
torrent.size.indexOf(' ')
|
||||
)
|
||||
}}</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs5 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Done</div>
|
||||
<div>
|
||||
{{
|
||||
torrent.dloaded.substring(
|
||||
0,
|
||||
torrent.dloaded.indexOf(' ')
|
||||
)
|
||||
}}
|
||||
<span class="caption grey--text">{{
|
||||
torrent.dloaded.substring(
|
||||
torrent.dloaded.indexOf(' ')
|
||||
)
|
||||
}}</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Download</div>
|
||||
<div>
|
||||
{{
|
||||
torrent.dlspeed.substring(
|
||||
0,
|
||||
torrent.dlspeed.indexOf(' ')
|
||||
)
|
||||
}}
|
||||
<span class="caption grey--text">{{
|
||||
torrent.dlspeed.substring(
|
||||
torrent.dlspeed.indexOf(' ')
|
||||
)
|
||||
}}</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs5 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Upload</div>
|
||||
<div>
|
||||
{{
|
||||
torrent.upspeed.substring(
|
||||
0,
|
||||
torrent.upspeed.indexOf(' ')
|
||||
)
|
||||
}}
|
||||
<span class="caption grey--text">{{
|
||||
torrent.upspeed.substring(
|
||||
torrent.upspeed.indexOf(' ')
|
||||
)
|
||||
}}</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">ETA</div>
|
||||
<div>{{ torrent.eta }}</div>
|
||||
</v-flex>
|
||||
<v-flex xs5 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Peers</div>
|
||||
<div>
|
||||
{{ torrent.num_leechs }}
|
||||
<span class="grey--text caption"
|
||||
>/{{ torrent.available_peers }}</span
|
||||
>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs5 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Seeds</div>
|
||||
<div>
|
||||
{{ torrent.num_seeds }}
|
||||
<span class="grey--text caption"
|
||||
>/{{ torrent.available_seeds }}</span
|
||||
>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs4 sm12 md1>
|
||||
<div class="right">
|
||||
<v-chip
|
||||
small
|
||||
:class="
|
||||
`${torrent.state} white--text my-2 caption`
|
||||
"
|
||||
>{{ torrent.state }}</v-chip
|
||||
>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs12 sm12 md12>
|
||||
<v-progress-linear
|
||||
height="3"
|
||||
color="cyan darken-1"
|
||||
background-color="cyan lighten-3"
|
||||
:value="(torrent.dloaded / torrent.size) * 100"
|
||||
></v-progress-linear>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<v-divider></v-divider>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -164,8 +161,12 @@ export default {
|
|||
.v-chip.paused {
|
||||
background: #cfd8dc !important;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
.truncate {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
import Dashboard from './views/Dashboard.vue'
|
||||
import Dashboard from '@/views/Dashboard.vue'
|
||||
import Login from '@/views/Login.vue'
|
||||
import store from '@/store'
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
export default new Router({
|
||||
const router = new Router({
|
||||
mode: 'history',
|
||||
base: process.env.BASE_URL,
|
||||
routes: [
|
||||
|
@ -12,6 +14,38 @@ export default new Router({
|
|||
path: '/',
|
||||
name: 'dashboard',
|
||||
component: Dashboard
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: Login,
|
||||
meta: {
|
||||
public: true, // Allow access to even if not logged in
|
||||
onlyWhenLoggedOut: true
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
const isPublic = to.matched.some(record => record.meta.public)
|
||||
const onlyWhenLoggedOut = to.matched.some(record => record.meta.onlyWhenLoggedOut)
|
||||
const loggedIn = store.state.authenticated
|
||||
|
||||
if (!isPublic && !loggedIn) {
|
||||
return next({
|
||||
path:'/login',
|
||||
query: {redirect: to.fullPath} // Store the full path to redirect the user to after login
|
||||
});
|
||||
}
|
||||
|
||||
// Do not allow user to visit login page or register page if they are logged in
|
||||
if (loggedIn && onlyWhenLoggedOut) {
|
||||
return next('/')
|
||||
}
|
||||
|
||||
next();
|
||||
})
|
||||
|
||||
export default router;
|
|
@ -68,19 +68,11 @@ export default new Vuex.Store({
|
|||
TOGGLE_THEME(state) {
|
||||
state.darkTheme = !state.darkTheme
|
||||
},
|
||||
REMOVE_TORRENTS: async state => {
|
||||
if (state.selected_torrents.length !== 0) {
|
||||
qbit.remove_torrents(state.selected_torrents)
|
||||
}
|
||||
LOGOUT: state => {
|
||||
state.authenticated = false
|
||||
},
|
||||
LOGIN: async (state, payload) => {
|
||||
const res = await qbit.login(payload)
|
||||
console.log(res)
|
||||
if (res === 'Ok.') {
|
||||
Vue.$toast.success('Successfully logged in!')
|
||||
state.authenticated = true
|
||||
}
|
||||
state.loading = false
|
||||
state.authenticated = payload
|
||||
},
|
||||
updateMainData: async state => {
|
||||
const rid = state.rid ? state.rid : undefined
|
||||
|
@ -135,8 +127,16 @@ export default new Vuex.Store({
|
|||
}, 2000)
|
||||
},
|
||||
LOGIN: async (context, payload) => {
|
||||
context.commit('LOGIN', payload)
|
||||
context.commit('updateMainData')
|
||||
const res = await qbit.login(payload)
|
||||
console.log(res)
|
||||
if (res === 'Ok.') {
|
||||
Vue.$toast.success('Successfully logged in!')
|
||||
context.commit('LOGIN', true)
|
||||
context.commit('updateMainData')
|
||||
return true;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
import { mapState, mapMutations} from 'vuex'
|
||||
import Torrent from '@/components/Torrent'
|
||||
|
||||
|
||||
export default {
|
||||
name:'Dashboard',
|
||||
components: {Torrent},
|
||||
|
|
77
src/views/Login.vue
Normal file
77
src/views/Login.vue
Normal file
|
@ -0,0 +1,77 @@
|
|||
<template>
|
||||
<v-layout row wrap align-center class="justify-center" >
|
||||
<div style="margin: 0 auto;">
|
||||
<v-container class="grey lighten-4 pa-0">
|
||||
<v-card max-width="400" flat>
|
||||
<v-container :class="`pa-3 project done`">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>Login</h2>
|
||||
</v-card-title>
|
||||
<div class="mr-5 ml-5"></div>
|
||||
<v-card-text>
|
||||
<v-form class="px-3" ref="form">
|
||||
<v-text-field
|
||||
flat
|
||||
solo
|
||||
background-color="grey lighten-4"
|
||||
label="username"
|
||||
prepend-icon="person"
|
||||
v-model="username"
|
||||
:rules="inputRules"
|
||||
@keyup.enter.native="Login"
|
||||
autocomplete="current email"
|
||||
></v-text-field>
|
||||
<v-text-field
|
||||
flat
|
||||
solo
|
||||
background-color="grey lighten-4"
|
||||
type="password"
|
||||
label="password"
|
||||
prepend-icon="lock"
|
||||
v-model="password"
|
||||
:rules="inputRules"
|
||||
@keyup.enter.native="Login"
|
||||
autocomplete="current password"
|
||||
></v-text-field>
|
||||
<v-spacer></v-spacer>
|
||||
<v-card-actions class="justify-center">
|
||||
<v-btn
|
||||
text
|
||||
@click="Login"
|
||||
class="blue_accent white--text mx-0 mt-3"
|
||||
>Login</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</div>
|
||||
</v-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Login',
|
||||
data() {
|
||||
return {
|
||||
username: '',
|
||||
password: '',
|
||||
inputRules: [v => v.length >= 1 || 'At least 1 character']
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async Login() {
|
||||
const authenticated = await this.$store.dispatch('LOGIN', {
|
||||
username: this.username,
|
||||
password: this.password
|
||||
})
|
||||
|
||||
if(authenticated){
|
||||
this.$router.push('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
Loading…
Add table
Reference in a new issue