feat(pages): Deploy mocked demo version (#1690)
Some checks are pending
Build project and release / Run Release Please action (push) Waiting to run
Build project and release / Build VueTorrent (vuetorrent-build, ./vuetorrent, build) (push) Waiting to run
Build project and release / Build VueTorrent (vuetorrent-demo, ./vuetorrent-demo, build-demo) (push) Waiting to run
Build project and release / Push to nightly branch (push) Blocked by required conditions
Build project and release / Push to demo repo (push) Blocked by required conditions
Build project and release / Upload release to GitHub (push) Blocked by required conditions
Build project and release / Push to latest branch (push) Blocked by required conditions
CodeQL / Analyze (javascript-typescript) (push) Waiting to run

This commit is contained in:
Rémi Marseault 2024-05-18 10:26:02 +02:00 committed by GitHub
parent e121cebf53
commit f8695bf9a6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 98 additions and 70 deletions

View file

@ -14,24 +14,28 @@ jobs:
permissions: permissions:
contents: write contents: write
pull-requests: write pull-requests: write
actions: write
steps: steps:
- name: Run release please - name: Run release please
uses: google-github-actions/release-please-action@v4 uses: googleapis/release-please-action@v4
id: release id: release
with: with:
token: ${{ secrets.GITHUB_TOKEN }}
config-file: release-please-config.json config-file: release-please-config.json
manifest-file: .release-please-manifest.json manifest-file: .release-please-manifest.json
build-release: build-release:
name: Build VueTorrent name: Build VueTorrent
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: strategy:
contents: write matrix:
pull-requests: write npm-step: [ 'build', 'build-demo' ]
actions: write include:
- npm-step: build
artifact-name: vuetorrent-build
artifact-path: ./vuetorrent
- npm-step: build-demo
artifact-name: vuetorrent-demo
artifact-path: ./vuetorrent-demo
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
@ -45,7 +49,7 @@ jobs:
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: ~/.npm path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} key: "${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}"
restore-keys: ${{ runner.os }}-node- restore-keys: ${{ runner.os }}-node-
- name: Install dependencies - name: Install dependencies
@ -63,13 +67,13 @@ jobs:
tolgee_secret: ${{ secrets.TOLGEE_TOKEN }} tolgee_secret: ${{ secrets.TOLGEE_TOKEN }}
- name: Build VueTorrent - name: Build VueTorrent
run: npm run build run: npm run ${{ matrix.npm-step }}
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: vuetorrent-build name: ${{ matrix.artifact-name }}
path: ./vuetorrent path: ${{ matrix.artifact-path }}
retention-days: 1 retention-days: 1
push-nightly: push-nightly:
@ -94,9 +98,29 @@ jobs:
branch: nightly-release branch: nightly-release
folder: ./vuetorrent folder: ./vuetorrent
push-demo:
name: Push to demo repo
runs-on: ubuntu-latest
needs: build-release
steps:
- name: Checkout repository
uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: vuetorrent-demo
path: ./vuetorrent-demo
- name: Push to demo repo
uses: JamesIves/github-pages-deploy-action@v4.6.0
with:
folder: './vuetorrent-demo'
repository-name: 'VueTorrent/demo'
token: ${{ secrets.GH_PAT }}
upload-release: upload-release:
name: Upload release to GitHub name: Upload release to GitHub
needs: [setup-release, build-release] needs: [ setup-release, build-release ]
if: needs.setup-release.outputs.release_created if: needs.setup-release.outputs.release_created
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -120,7 +144,7 @@ jobs:
push-release: push-release:
name: Push to latest branch name: Push to latest branch
needs: [setup-release, build-release] needs: [ setup-release, build-release ]
if: needs.setup-release.outputs.release_created if: needs.setup-release.outputs.release_created
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -135,6 +159,5 @@ jobs:
- name: Push to latest-release - name: Push to latest-release
uses: JamesIves/github-pages-deploy-action@v4.6.0 uses: JamesIves/github-pages-deploy-action@v4.6.0
with: with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} branch: latest-release
BRANCH: latest-release folder: ./vuetorrent
FOLDER: ./vuetorrent

View file

@ -25,8 +25,8 @@ jobs:
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: ~/.npm path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} key: "${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}"
restore-keys: ${{ runner.os }}-node- restore-keys: "${{ runner.os }}-node-"
- name: Install dependencies - name: Install dependencies
run: npm ci run: npm ci

View file

@ -26,8 +26,8 @@ jobs:
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: ~/.npm path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} key: "${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}"
restore-keys: ${{ runner.os }}-node restore-keys: "${{ runner.os }}-node"
- name: Install dependencies - name: Install dependencies
run: npm ci run: npm ci

1
.gitignore vendored
View file

@ -27,6 +27,7 @@ coverage
# Custom # Custom
vuetorrent/** vuetorrent/**
vuetorrent-demo/**
docker/** docker/**
coverage/ coverage/
config/ config/

View file

@ -37,6 +37,15 @@ The sleekest looking WebUI for qBittorrent made with Vue.js!
<img src="readme_assets/screenshot-mobile-navbar-dark-mode.png" width="400" alt="Screenshot Mobile Dashboard (Light Mode)" /> <img src="readme_assets/screenshot-mobile-navbar-dark-mode.png" width="400" alt="Screenshot Mobile Dashboard (Light Mode)" />
</p> </p>
## Demo
A live demo **with mocked data** is available here: https://vuetorrent.github.io/demo
> [!NOTE]
> This version isn't connected to a qBittorrent instance.
>
> Don't try to download torrents or change preferences, it won't work 😉
## Installation ## Installation
Checkout the [wiki](https://github.com/VueTorrent/VueTorrent/wiki/Installation)! Checkout the [wiki](https://github.com/VueTorrent/VueTorrent/wiki/Installation)!
@ -50,9 +59,9 @@ Checkout the [wiki](https://github.com/VueTorrent/VueTorrent/wiki/Installation)!
- `docker-compose up -d` (starts a qbittorrent docker, optional) - `docker-compose up -d` (starts a qbittorrent docker, optional)
- Open the WebUI on localhost with the default credentials - Open the WebUI on localhost with the default credentials
- Default username is always `admin` - Default username is always `admin`
- Default password is `adminadmin` **on 4.6.0 and below**, and is generated on 4.6.1 and above - Default password is `adminadmin` **on 4.6.0 and below**, and is generated in logs on 4.6.1 and above
- Make sure "CSRF protection" and "Host header verification" is disabled on the target server! - Make sure "CSRF protection" and "Host header verification" is disabled in the qBittorrent preferences
- Edit `env.development` to tweak your dev environment (e.g. fake torrents) - Edit `env.development` to tweak your dev environment (e.g. mocked data)
## Features ## Features
@ -89,7 +98,7 @@ Checkout the [wiki](https://github.com/VueTorrent/VueTorrent/wiki/Installation)!
VueTorrent is a **WebUI** (think of it as a "visual skin") that uses qBittorrent's API, enabling compatibility with automation solutions like the Servarr stack. VueTorrent is a **WebUI** (think of it as a "visual skin") that uses qBittorrent's API, enabling compatibility with automation solutions like the Servarr stack.
Everything that is compatible with qBittorrent will work regardless of the WebUI you chose to use. Everything that is compatible with the classic qBittorrent WebUI will work regardless of the WebUI you chose to use, whether its VueTorrent or another one.
## Contributing ## Contributing
@ -101,16 +110,13 @@ See the [Contributing Guidelines](https://github.com/VueTorrent/VueTorrent/blob/
## Support ## Support
[![Discord](https://img.shields.io/discord/1170618192956243998?logo=discord)](https://discord.gg/KDQP7fR467) - [![Discord](https://img.shields.io/discord/1170618192956243998?logo=discord)](https://discord.gg/KDQP7fR467)
[![Wiki](https://img.shields.io/badge/Wiki-blue)](https://github.com/VueTorrent/VueTorrent/wiki) - [![Wiki](https://img.shields.io/badge/Wiki-blue)](https://github.com/VueTorrent/VueTorrent/wiki)
[![FAQ](https://img.shields.io/badge/FAQ-orange)](https://github.com/VueTorrent/VueTorrent/wiki/FAQ) - [![FAQ](https://img.shields.io/badge/FAQ-orange)](https://github.com/VueTorrent/VueTorrent/wiki/FAQ)
Open up an issue 😛 If any of the above didn't help, feel free to open an issue!
but before you do that: See the [Contributing Guidelines](https://github.com/VueTorrent/VueTorrent/blob/master/.github/CONTRIBUTING.md) for more information.
- confirm you're on the latest version of VueTorrent
- confirm there is no other issue mentioning the same problem
## Funding ## Funding

View file

@ -8,8 +8,10 @@
"dev": "vite", "dev": "vite",
"build": "vue-tsc && vite build", "build": "vue-tsc && vite build",
"postbuild": "node write-version.cjs", "postbuild": "node write-version.cjs",
"build-demo": "vue-tsc && vite build -m demo",
"check-build": "vue-tsc", "check-build": "vue-tsc",
"preview": "vite preview", "preview": "vite preview",
"preview-demo": "vite preview -m demo",
"lint": "eslint --fix && prettier . -w -u", "lint": "eslint --fix && prettier . -w -u",
"test": "vitest run", "test": "vitest run",
"coverage": "vitest run --coverage" "coverage": "vitest run --coverage"

View file

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { DiskIOMode, DiskIOType, ResumeDataStorageType, UploadChokingAlgorithm, UploadSlotsBehavior, UtpTcpMixedMode } from '@/constants/qbit/AppPreferences' import { DiskIOMode, DiskIOType, ResumeDataStorageType, UploadChokingAlgorithm, UploadSlotsBehavior, UtpTcpMixedMode } from '@/constants/qbit/AppPreferences'
import { qbit } from '@/services' import qbit from '@/services/qbit'
import { usePreferenceStore } from '@/stores' import { usePreferenceStore } from '@/stores'
import { computed, onBeforeMount, ref } from 'vue' import { computed, onBeforeMount, ref } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'

View file

@ -1,20 +0,0 @@
import IProvider from './IProvider'
import QBitProvider from './QbitProvider'
async function getProvider(): Promise<IProvider> {
if (import.meta.env.DEV && import.meta.env.VITE_USE_MOCK_PROVIDER === 'true') {
try {
const { default: MockProvider } = await import('./MockProvider')
return MockProvider.getInstance()
} catch (error) {
console.error('Failed to load MockProvider:', error)
return new QBitProvider()
}
} else {
return new Promise(resolve => resolve(new QBitProvider()))
}
}
const qbit = await getProvider()
export { qbit }

View file

@ -24,7 +24,7 @@ import IProvider from './IProvider'
export default class MockProvider implements IProvider { export default class MockProvider implements IProvider {
private static instance: MockProvider private static instance: MockProvider
private static hashes: string[] = Array(parseInt(import.meta.env.VITE_FAKE_TORRENTS_COUNT)) private static hashes: string[] = Array(parseInt(import.meta.env.VITE_FAKE_TORRENTS_COUNT || 15))
.fill('') .fill('')
.map((_, i) => (i + 1).toString(16).padStart(40, '0')) .map((_, i) => (i + 1).toString(16).padStart(40, '0'))
@ -666,7 +666,7 @@ export default class MockProvider implements IProvider {
async getTorrents(_?: GetTorrentPayload): Promise<Torrent[]> { async getTorrents(_?: GetTorrentPayload): Promise<Torrent[]> {
const result = MockProvider.hashes.map(hash => { const result = MockProvider.hashes.map(hash => {
const added_on = faker.date.past().getTime() const added_on = faker.date.past().getTime() / 1000
const name = faker.system.fileName() const name = faker.system.fileName()
const num_complete = faker.number.int({ min: 0, max: 250 }) const num_complete = faker.number.int({ min: 0, max: 250 })
const num_incomplete = faker.number.int({ min: 0, max: 250 }) const num_incomplete = faker.number.int({ min: 0, max: 250 })
@ -682,7 +682,7 @@ export default class MockProvider implements IProvider {
availability: faker.number.float({ min: 0, max: 100, multipleOf: 0.01 }), availability: faker.number.float({ min: 0, max: 100, multipleOf: 0.01 }),
category: faker.helpers.arrayElement(['ISO', 'Other', 'Movie', 'Music', 'TV']), category: faker.helpers.arrayElement(['ISO', 'Other', 'Movie', 'Music', 'TV']),
completed, completed,
completion_on: faker.date.between({ from: added_on, to: Date.now() }).getTime(), completion_on: faker.date.between({ from: added_on, to: Date.now() }).getTime() / 1000,
content_path: faker.system.filePath(), content_path: faker.system.filePath(),
dl_limit: faker.number.float({ min: 0, max: 1, multipleOf: 0.01 }), dl_limit: faker.number.float({ min: 0, max: 1, multipleOf: 0.01 }),
dlspeed: faker.number.int({ min: 0, max: 5_000_000 }), // [0; 5 Mo/s] dlspeed: faker.number.int({ min: 0, max: 5_000_000 }), // [0; 5 Mo/s]

View file

@ -25,9 +25,10 @@ import type IProvider from './IProvider'
type Parameters = Record<string, any> type Parameters = Record<string, any>
export default class QBitProvider implements IProvider { export default class QBitProvider implements IProvider {
private static _instance: QBitProvider
private axios: AxiosInstance private axios: AxiosInstance
constructor() { private constructor() {
this.axios = axios.create({ this.axios = axios.create({
baseURL: 'api/v2' baseURL: 'api/v2'
}) })
@ -35,6 +36,13 @@ export default class QBitProvider implements IProvider {
this.axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded' this.axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
} }
public static getInstance() {
if (!this._instance) {
this._instance = new QBitProvider()
}
return this._instance
}
/// Misc /// /// Misc ///
/** /**

View file

@ -0,0 +1,8 @@
import IProvider from './IProvider'
import MockProvider from './MockProvider'
import QBitProvider from './QbitProvider'
const qbit: IProvider =
import.meta.env.MODE === 'demo' || (import.meta.env.DEV && import.meta.env.VITE_USE_MOCK_PROVIDER === 'true') ? MockProvider.getInstance() : QBitProvider.getInstance()
export default qbit

View file

@ -1,4 +1,4 @@
import { qbit } from '@/services' import qbit from '@/services/qbit'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { ref } from 'vue' import { ref } from 'vue'

View file

@ -1,6 +1,6 @@
import { ref } from 'vue' import { ref } from 'vue'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { qbit } from '@/services' import qbit from '@/services/qbit'
export const useAuthStore = defineStore('auth', () => { export const useAuthStore = defineStore('auth', () => {
const isAuthenticated = ref(false) const isAuthenticated = ref(false)

View file

@ -1,6 +1,6 @@
import { useSearchQuery, useTreeBuilder } from '@/composables' import { useSearchQuery, useTreeBuilder } from '@/composables'
import { FilePriority } from '@/constants/qbit' import { FilePriority } from '@/constants/qbit'
import { qbit } from '@/services' import qbit from '@/services/qbit'
import { useDialogStore } from '@/stores/dialog' import { useDialogStore } from '@/stores/dialog'
import { useMaindataStore } from '@/stores/maindata' import { useMaindataStore } from '@/stores/maindata'
import { useVueTorrentStore } from '@/stores/vuetorrent' import { useVueTorrentStore } from '@/stores/vuetorrent'

View file

@ -1,4 +1,4 @@
import { qbit } from '@/services' import qbit from '@/services/qbit'
import { Log } from '@/types/qbit/models' import { Log } from '@/types/qbit/models'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { ref, watch } from 'vue' import { ref, watch } from 'vue'

View file

@ -1,7 +1,7 @@
import { useTorrentBuilder } from '@/composables' import { useTorrentBuilder } from '@/composables'
import { SortOptions } from '@/constants/qbit' import { SortOptions } from '@/constants/qbit'
import { extractHostname } from '@/helpers' import { extractHostname } from '@/helpers'
import { qbit } from '@/services' import qbit from '@/services/qbit'
import { Category, ServerState } from '@/types/qbit/models' import { Category, ServerState } from '@/types/qbit/models'
import { defineStore, storeToRefs } from 'pinia' import { defineStore, storeToRefs } from 'pinia'
import { MaybeRefOrGetter, ref, toValue } from 'vue' import { MaybeRefOrGetter, ref, toValue } from 'vue'

View file

@ -1,7 +1,7 @@
import { ref } from 'vue' import { ref } from 'vue'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import AppPreferences from '@/types/qbit/models/AppPreferences' import AppPreferences from '@/types/qbit/models/AppPreferences'
import { qbit } from '@/services' import qbit from '@/services/qbit'
export const usePreferenceStore = defineStore( export const usePreferenceStore = defineStore(
'preferences', 'preferences',

View file

@ -1,4 +1,4 @@
import { qbit } from '@/services' import qbit from '@/services/qbit'
import { Feed, FeedRule } from '@/types/qbit/models' import { Feed, FeedRule } from '@/types/qbit/models'
import { RssArticle } from '@/types/vuetorrent' import { RssArticle } from '@/types/vuetorrent'
import { AxiosError } from 'axios' import { AxiosError } from 'axios'

View file

@ -1,4 +1,4 @@
import { qbit } from '@/services' import qbit from '@/services/qbit'
import { SearchPlugin } from '@/types/qbit/models' import { SearchPlugin } from '@/types/qbit/models'
import { SearchData } from '@/types/vuetorrent' import { SearchData } from '@/types/vuetorrent'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'

View file

@ -1,4 +1,4 @@
import { qbit } from '@/services' import qbit from '@/services/qbit'
import { TorrentProperties } from '@/types/qbit/models' import { TorrentProperties } from '@/types/qbit/models'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { ref } from 'vue' import { ref } from 'vue'

View file

@ -1,7 +1,7 @@
import { useSearchQuery } from '@/composables' import { useSearchQuery } from '@/composables'
import { SortOptions, TorrentState } from '@/constants/qbit' import { SortOptions, TorrentState } from '@/constants/qbit'
import { extractHostname } from '@/helpers' import { extractHostname } from '@/helpers'
import { qbit } from '@/services' import qbit from '@/services/qbit'
import { AddTorrentPayload, GetTorrentPayload } from '@/types/qbit/payloads' import { AddTorrentPayload, GetTorrentPayload } from '@/types/qbit/payloads'
import { Torrent } from '@/types/vuetorrent' import { Torrent } from '@/types/vuetorrent'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'

View file

@ -1,4 +1,4 @@
import { ContentLayout } from '@/constants/qbit/AppPreferences.ts' import { ContentLayout } from '@/constants/qbit/AppPreferences'
import { AddTorrentParams } from '@/types/qbit/models' import { AddTorrentParams } from '@/types/qbit/models'
export interface LegacyFeedRule { export interface LegacyFeedRule {

View file

@ -16,7 +16,7 @@ export default defineConfig(({ mode }) => {
base: './', base: './',
build: { build: {
target: 'esnext', target: 'esnext',
outDir: './vuetorrent/public', outDir: mode === 'demo' ? './vuetorrent-demo' : './vuetorrent/public',
rollupOptions: { rollupOptions: {
output: { output: {
manualChunks: { manualChunks: {