diff --git a/README.md b/README.md index 9252875..6bb389c 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,8 @@ Languages: English, 中文 [TODO](https://github.com/CzBiX/qb-web/projects/2) -## Download - -[Releases](https://github.com/CzBiX/qb-web/releases/latest) +## How to use +see: [Wiki](https://github.com/CzBiX/qb-web/wiki/How-to-use) ## Install diff --git a/screenshot/CORS-settings.png b/screenshot/CORS-settings.png new file mode 100644 index 0000000..70218de Binary files /dev/null and b/screenshot/CORS-settings.png differ diff --git a/src/Api.ts b/src/Api.ts index 8aa8103..7fb68ce 100644 --- a/src/Api.ts +++ b/src/Api.ts @@ -2,18 +2,36 @@ import Axios, { AxiosInstance, AxiosResponse } from 'axios'; import { RssNode, RssRule, SearchPlugin, ApiCategory, SearchTaskResponse } from '@/types'; -class Api { +const apiEndpoint = 'api/v2'; +class Api { private axios: AxiosInstance; constructor() { this.axios = Axios.create({ - baseURL: 'api/v2', + baseURL: apiEndpoint, + withCredentials: true, }); this.axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'; } + private normalizeBaseUrl(baseUrl?: string) { + if (!baseUrl) { + return apiEndpoint; + } + + if (!baseUrl.endsWith('/')) { + baseUrl += '/'; + } + + return baseUrl + apiEndpoint; + } + + public changeBaseUrl(baseUrl: string) { + this.axios.defaults.baseURL = this.normalizeBaseUrl(baseUrl); + } + public getAppVersion() { return this.axios.get('/app/version'); } @@ -22,12 +40,13 @@ class Api { return this.axios.get('/app/webapiVersion'); } - public login(params: any) { + public login(params: any, baseUrl?: string) { const data = new URLSearchParams(params); return this.axios.post('/auth/login', data, { validateStatus(status) { return status === 200 || status === 403; }, + baseURL: this.normalizeBaseUrl(baseUrl), }).then(Api.handleResponse); } @@ -330,4 +349,5 @@ class Api { } } -export default new Api(); +const api = new Api(); +export default api; diff --git a/src/App.vue b/src/App.vue index 09a664e..bafa6ea 100644 --- a/src/App.vue +++ b/src/App.vue @@ -75,6 +75,8 @@ import api from './Api'; import Component from 'vue-class-component'; import { Watch } from 'vue-property-decorator'; import { MainData } from './types'; +import { Config } from './store/config'; +import Api from './Api'; let appWrapEl: HTMLElement; @@ -98,6 +100,7 @@ let appWrapEl: HTMLElement; 'mainData', 'rid', 'preferences', + 'needAuth', ]), ...mapGetters(['config']), }, @@ -106,11 +109,11 @@ let appWrapEl: HTMLElement; 'updateMainData', 'updatePreferences', 'setPasteUrl', + 'updateNeedAuth', ]), } }) export default class App extends Vue { - needAuth = false drawer = true drawerOptions = { showLogs: false, @@ -122,11 +125,13 @@ export default class App extends Vue { mainData!: MainData rid!: number preferences!: any - config!: any + config!: Config + needAuth!: boolean updateMainData!: (_: any) => void updatePreferences!: (_: any) => void setPasteUrl!: (_: any) => void + updateNeedAuth!: (_: boolean) => void get phoneLayout() { return this.$vuetify.breakpoint.xsOnly; @@ -159,13 +164,22 @@ export default class App extends Vue { } async getInitData() { + const href = location.href; + if (!this.config.baseUrl) { + if (href.includes("czbix.github.io") || href.includes("localhost")) { + this.updateNeedAuth(true); + return; + } else { + Api.changeBaseUrl(href); + } + } else { + Api.changeBaseUrl(this.config.baseUrl); + } + try { await this.getMainData(); } catch (e) { - if (e.response.status === 403) { - this.needAuth = true; - } - + this.updateNeedAuth(true); return; } diff --git a/src/components/LoginForm.vue b/src/components/LoginForm.vue index e5e7aac..3a1315d 100644 --- a/src/components/LoginForm.vue +++ b/src/components/LoginForm.vue @@ -21,11 +21,17 @@ @keyup.enter.capture="submit" v-bind="{ [`grid-list-${$vuetify.breakpoint.name}`]: true }" > + @@ -36,7 +42,6 @@ @click:append="showPassword = !showPassword" :label="$t('password')" :type="showPassword ? 'text' : 'password'" - :rules="[v => !!v || $t('msg.item_is_required', { item: $t('password') })]" required /> @@ -63,56 +68,64 @@ diff --git a/src/locale/en.ts b/src/locale/en.ts index 984f97c..dfaff16 100644 --- a/src/locale/en.ts +++ b/src/locale/en.ts @@ -99,6 +99,7 @@ export default { reannounced: 'Reannounced', rechecking: 'Rechecking…', dht_nodes: '%{smart_count} node |||| %{smart_count} nodes', + base_url: 'Base URL', }, msg: { diff --git a/src/locale/index.ts b/src/locale/index.ts index 9b85e81..70453a8 100644 --- a/src/locale/index.ts +++ b/src/locale/index.ts @@ -30,7 +30,7 @@ function matchLocale() { export const defaultLocale = matchLocale() function updateLocale() { - let locale: LocaleKey | undefined = loadConfig()['locale']; + let locale = loadConfig()['locale'] as LocaleKey; if (!locale) { locale = defaultLocale; diff --git a/src/store/config.ts b/src/store/config.ts index 9ef6e9c..d4be7b7 100644 --- a/src/store/config.ts +++ b/src/store/config.ts @@ -5,7 +5,22 @@ import { ConfigState, ConfigPayload } from './types'; const configKey = 'qb-config'; +export interface Config { + baseUrl: string | null; + updateInterval: number; + pageOptions: any; + filter: { + state: string | null; + category: string | null; + site: string | null; + query: string | null; + }; + locale: string | null; + darkMode: string | null; +} + const defaultConfig = { + baseUrl: null, updateInterval: 2000, pageOptions: { itemsPerPage: 50, @@ -20,8 +35,6 @@ const defaultConfig = { darkMode: null, }; -export type Config = typeof defaultConfig - function saveConfig(obj: any) { localStorage.setItem(configKey, JSON.stringify(obj)); } diff --git a/src/store/index.ts b/src/store/index.ts index 9e47e5c..b9e4bcc 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -27,6 +27,7 @@ const store = new Vuex.Store({ mainData: undefined, preferences: null, pasteUrl: null, + needAuth: false, }, mutations: { /* eslint-disable no-param-reassign */ @@ -60,6 +61,9 @@ const store = new Vuex.Store({ const { url } = payload; state.pasteUrl = url; }, + updateNeedAuth(state, payload) { + state.needAuth = payload; + }, /* eslint-enable no-param-reassign */ }, getters: { diff --git a/src/store/types.ts b/src/store/types.ts index 262f2db..1f6fbc9 100644 --- a/src/store/types.ts +++ b/src/store/types.ts @@ -6,6 +6,7 @@ export interface RootState { mainData?: MainData; preferences: any; pasteUrl: string | null; + needAuth: boolean; } export interface SearchEnginePage { diff --git a/tests/unit/store/index.spec.ts b/tests/unit/store/index.spec.ts index 71f2241..90bb4d0 100644 --- a/tests/unit/store/index.spec.ts +++ b/tests/unit/store/index.spec.ts @@ -13,6 +13,7 @@ const emtpyState: RootState = { mainData: undefined, preferences: null, pasteUrl: null, + needAuth: false, }; const mockState = mock(emtpyState);