Compare commits

..

2 Commits

15 changed files with 180 additions and 169 deletions

View File

@@ -93,7 +93,7 @@
<Suspense> <Suspense>
<TempComponent> <TempComponent>
<NLayoutContent> <NLayoutContent>
<NElement> <NElement style="height: 100vh;">
<ViewerLayout v-if="layout == 'viewer'" /> <ViewerLayout v-if="layout == 'viewer'" />
<ManageLayout v-else-if="layout == 'manage'" /> <ManageLayout v-else-if="layout == 'manage'" />
<OpenLiveLayout v-else-if="layout == 'open-live'" /> <OpenLiveLayout v-else-if="layout == 'open-live'" />

View File

@@ -1,80 +1,84 @@
import { QueryGetAPI, QueryPostAPI, QueryPostAPIWithParams } from '@/api/query' import { QueryGetAPI, QueryPostAPI, QueryPostAPIWithParams } from '@/api/query';
import { ACCOUNT_API_URL, VTSURU_API_URL } from '@/data/constants' import { ACCOUNT_API_URL, VTSURU_API_URL } from '@/data/constants';
import { useLocalStorage } from '@vueuse/core' import { isSameDay } from 'date-fns';
import { isSameDay } from 'date-fns' import { createDiscreteApi } from 'naive-ui';
import { createDiscreteApi } from 'naive-ui' import { ref } from 'vue';
import { ref } from 'vue' import { APIRoot, AccountInfo, FunctionTypes } from './api-models';
import { APIRoot, AccountInfo, FunctionTypes } from './api-models' import { StorageSerializers } from '@vueuse/core';
export const ACCOUNT = ref<AccountInfo>({} as AccountInfo) export const ACCOUNT = ref<AccountInfo>({} as AccountInfo);
export const isLoadingAccount = ref(true) export const isLoadingAccount = ref(true);
export const isLoggedIn = computed<boolean>(() => { export const isLoggedIn = computed<boolean>(() => {
return ACCOUNT.value.id > 0 return ACCOUNT.value.id > 0;
}) });
const { message } = createDiscreteApi(['message']) const { message } = createDiscreteApi(['message']);
const cookie = useLocalStorage('JWT_Token', '') export const cookie = useLocalStorage<{ cookie: string; refreshDate: number }>('Cookie', {cookie: '', refreshDate: 0}, { serializer: StorageSerializers.object });
const cookieRefreshDate = useLocalStorage('JWT_Token_Last_Refresh', 0)
export async function GetSelfAccount(token?: string) { export async function GetSelfAccount(token?: string) {
if (cookie.value || token) { if (cookie.value.cookie || token) {
const result = await Self(token) const result = await Self(token);
if (result.code == 200) { if (result.code == 200) {
if (!ACCOUNT.value.id) { if (!ACCOUNT.value.id) {
ACCOUNT.value = result.data ACCOUNT.value = result.data;
} else { } else {
result.data.settings = ACCOUNT.value.settings result.data.settings = ACCOUNT.value.settings;
ACCOUNT.value = result.data ACCOUNT.value = result.data;
} }
isLoadingAccount.value = false isLoadingAccount.value = false;
//console.log('[vtsuru] 已获取账户信息') //console.log('[vtsuru] 已获取账户信息')
if (!isSameDay(new Date(), cookieRefreshDate.value)) { if (!cookie.value.cookie || !isSameDay(new Date(), cookie.value!.refreshDate)) {
refreshCookie(token) refreshCookie(token);
} }
return result.data return result.data;
} else if (result.code == 401) { } else if (result.code == 401) {
localStorage.removeItem('JWT_Token') localStorage.removeItem('JWT_Token');
console.warn('[vtsuru] Cookie 已失效, 需要重新登陆') if (!token) {
message.error('Cookie 已失效, 需要重新登陆') cookie.value = undefined;
console.warn('[vtsuru] Cookie 已失效, 需要重新登陆');
message.error('Cookie 已失效, 需要重新登陆');
setTimeout(() => { setTimeout(() => {
location.reload() location.reload();
}, 1500) }, 1500);
}
} else { } else {
console.warn('[vtsuru] ' + result.message) console.warn('[vtsuru] ' + result.message);
message.error(result.message) message.error(result.message);
} }
} }
isLoadingAccount.value = false isLoadingAccount.value = false;
} }
export function UpdateAccountLoop() { export function UpdateAccountLoop() {
setInterval(() => { setInterval(() => {
if (ACCOUNT.value && window.$route?.name != 'question-display') { if (ACCOUNT.value && window.$route?.name != 'question-display') {
// 防止在问题详情页刷新 // 防止在问题详情页刷新
GetSelfAccount() GetSelfAccount();
} }
}, 60 * 1000) }, 60 * 1000);
} }
function refreshCookie(token?: string) { function refreshCookie(token?: string) {
QueryPostAPIWithParams<string>(`${ACCOUNT_API_URL}refresh-token`, { token }).then((data) => { QueryPostAPIWithParams<string>(`${ACCOUNT_API_URL}refresh-token`, { token }).then((data) => {
if (data.code == 200) { if (data.code == 200) {
cookie.value = data.data cookie.value = {
cookieRefreshDate.value = Date.now() cookie: data.data,
console.log('[vtsuru] 已刷新Cookie') refreshDate: new Date().getTime()
};
console.log('[vtsuru] 已刷新Cookie');
} }
}) });
} }
export async function SaveAccountSettings() { export async function SaveAccountSettings() {
return await QueryPostAPI( return await QueryPostAPI(
ACCOUNT_API_URL + 'update-setting', ACCOUNT_API_URL + 'update-setting',
ACCOUNT.value?.settings ACCOUNT.value?.settings
) );
} }
export async function SaveEnableFunctions(functions: FunctionTypes[]) { export async function SaveEnableFunctions(functions: FunctionTypes[]) {
return await QueryPostAPI( return await QueryPostAPI(
ACCOUNT_API_URL + 'update-enable-functions', ACCOUNT_API_URL + 'update-enable-functions',
functions functions
) );
} }
export async function SaveSetting( export async function SaveSetting(
name: name:
@@ -94,44 +98,44 @@ export async function SaveSetting(
name name
}, },
setting setting
) );
return result.message return result.message;
} }
export async function UpdateFunctionEnable(func: FunctionTypes) { export async function UpdateFunctionEnable(func: FunctionTypes) {
if (ACCOUNT.value) { if (ACCOUNT.value) {
const oldValue = JSON.parse( const oldValue = JSON.parse(
JSON.stringify(ACCOUNT.value.settings.enableFunctions) JSON.stringify(ACCOUNT.value.settings.enableFunctions)
) );
if (ACCOUNT.value?.settings.enableFunctions.includes(func)) { if (ACCOUNT.value?.settings.enableFunctions.includes(func)) {
ACCOUNT.value.settings.enableFunctions = ACCOUNT.value.settings.enableFunctions =
ACCOUNT.value.settings.enableFunctions.filter((f) => f != func) ACCOUNT.value.settings.enableFunctions.filter((f) => f != func);
} else { } else {
ACCOUNT.value.settings.enableFunctions.push(func) ACCOUNT.value.settings.enableFunctions.push(func);
} }
await SaveEnableFunctions(ACCOUNT.value?.settings.enableFunctions) await SaveEnableFunctions(ACCOUNT.value?.settings.enableFunctions)
.then((data) => { .then((data) => {
if (data.code == 200) { if (data.code == 200) {
message.success( message.success(
`${ACCOUNT.value?.settings.enableFunctions.includes(func) ? '启用' : '禁用'}` `${ACCOUNT.value?.settings.enableFunctions.includes(func) ? '启用' : '禁用'}`
) );
} else { } else {
if (ACCOUNT.value) { if (ACCOUNT.value) {
ACCOUNT.value.settings.enableFunctions = oldValue ACCOUNT.value.settings.enableFunctions = oldValue;
} }
message.error( message.error(
`${ACCOUNT.value?.settings.enableFunctions.includes(func) ? '启用' : '禁用'}失败: ${data.message}` `${ACCOUNT.value?.settings.enableFunctions.includes(func) ? '启用' : '禁用'}失败: ${data.message}`
) );
} }
}) })
.catch((err) => { .catch((err) => {
message.error( message.error(
`${ACCOUNT.value?.settings.enableFunctions.includes(func) ? '启用' : '禁用'}失败: ${err}` `${ACCOUNT.value?.settings.enableFunctions.includes(func) ? '启用' : '禁用'}失败: ${err}`
) );
}) });
} }
} }
export function useAccount() { export function useAccount() {
return ACCOUNT return ACCOUNT;
} }
export async function Register( export async function Register(
@@ -145,7 +149,7 @@ export async function Register(
email, email,
password, password,
token token
}) });
} }
export async function Login( export async function Login(
@@ -155,10 +159,10 @@ export async function Login(
return QueryPostAPI<string>(`${ACCOUNT_API_URL}login`, { return QueryPostAPI<string>(`${ACCOUNT_API_URL}login`, {
nameOrEmail, nameOrEmail,
password password
}) });
} }
export async function Self(token?: string): Promise<APIRoot<AccountInfo>> { export async function Self(token?: string): Promise<APIRoot<AccountInfo>> {
return QueryPostAPIWithParams<AccountInfo>(`${ACCOUNT_API_URL}self`, token ? { token } : undefined) return QueryPostAPIWithParams<AccountInfo>(`${ACCOUNT_API_URL}self`, token ? { token } : undefined);
} }
export async function AddBiliBlackList( export async function AddBiliBlackList(
id: number, id: number,
@@ -167,70 +171,70 @@ export async function AddBiliBlackList(
return QueryGetAPI<AccountInfo>(`${ACCOUNT_API_URL}black-list/add-bili`, { return QueryGetAPI<AccountInfo>(`${ACCOUNT_API_URL}black-list/add-bili`, {
id: id, id: id,
name: name name: name
}) });
} }
export async function DelBiliBlackList(id: number): Promise<APIRoot<unknown>> { export async function DelBiliBlackList(id: number): Promise<APIRoot<unknown>> {
return QueryGetAPI<AccountInfo>(`${ACCOUNT_API_URL}black-list/del-bili`, { return QueryGetAPI<AccountInfo>(`${ACCOUNT_API_URL}black-list/del-bili`, {
id: id id: id
}) });
} }
export async function DelBlackList(id: number): Promise<APIRoot<unknown>> { export async function DelBlackList(id: number): Promise<APIRoot<unknown>> {
return QueryGetAPI<AccountInfo>(`${ACCOUNT_API_URL}black-list/del`, { return QueryGetAPI<AccountInfo>(`${ACCOUNT_API_URL}black-list/del`, {
id: id id: id
}) });
} }
export function downloadConfigDirect(name: string) { export function downloadConfigDirect(name: string) {
return QueryGetAPI<string>(VTSURU_API_URL + 'get-config', { return QueryGetAPI<string>(VTSURU_API_URL + 'get-config', {
name: name name: name
}) });
} }
export type ConfigStatus = 'success' | 'error' | 'notfound' export type ConfigStatus = 'success' | 'error' | 'notfound';
export async function DownloadConfig<T>(name: string, id?: number): Promise< export async function DownloadConfig<T>(name: string, id?: number): Promise<
| { | {
msg: undefined msg: undefined;
status: ConfigStatus status: ConfigStatus;
data: T data: T;
} }
| { | {
msg: string msg: string;
status: ConfigStatus status: ConfigStatus;
data: undefined data: undefined;
} }
> { > {
try { try {
const resp = await QueryGetAPI<string>(VTSURU_API_URL + (id ? 'get-user-config' : 'get-config'), { const resp = await QueryGetAPI<string>(VTSURU_API_URL + (id ? 'get-user-config' : 'get-config'), {
name: name, name: name,
id: id id: id
}) });
if (resp.code == 200) { if (resp.code == 200) {
console.log('已获取配置文件: ' + name) console.log('已获取配置文件: ' + name);
return { return {
msg: undefined, msg: undefined,
status: 'success', status: 'success',
data: JSON.parse(resp.data) as T data: JSON.parse(resp.data) as T
} };
} else if (resp.code == 404) { } else if (resp.code == 404) {
console.error(`未找到名为 ${name} 的配置文件`) console.error(`未找到名为 ${name} 的配置文件`);
return { return {
msg: `未找到名为 ${name} 的配置文件, 需要先上传`, msg: `未找到名为 ${name} 的配置文件, 需要先上传`,
status: 'notfound', status: 'notfound',
data: undefined data: undefined
} };
} else { } else {
console.error(`无法获取配置文件 [${name}]: ` + resp.message) console.error(`无法获取配置文件 [${name}]: ` + resp.message);
return { return {
msg: `无法获取配置文件 [${name}]: ` + resp.message, msg: `无法获取配置文件 [${name}]: ` + resp.message,
status: 'error', status: 'error',
data: undefined data: undefined
} };
} }
} catch (err) { } catch (err) {
console.error(`无法获取配置文件 [${name}]: ` + err) console.error(`无法获取配置文件 [${name}]: ` + err);
return { return {
msg: `无法获取配置文件 [${name}]: ` + err, msg: `无法获取配置文件 [${name}]: ` + err,
status: 'error', status: 'error',
data: undefined data: undefined
} };
} }
} }
export async function UploadConfig(name: string, data: unknown) { export async function UploadConfig(name: string, data: unknown) {
@@ -238,70 +242,70 @@ export async function UploadConfig(name: string, data: unknown) {
const resp = await QueryPostAPI(VTSURU_API_URL + 'set-config', { const resp = await QueryPostAPI(VTSURU_API_URL + 'set-config', {
name: name, name: name,
json: JSON.stringify(data) json: JSON.stringify(data)
}) });
if (resp.code == 200) { if (resp.code == 200) {
console.log('已保存配置文件至服务器:' + name) console.log('已保存配置文件至服务器:' + name);
return true return true;
} else { } else {
console.error('保存失败: ' + resp.message) console.error('保存失败: ' + resp.message);
} }
} catch (err) { } catch (err) {
console.error(`保存配置文件失败: ` + err) console.error(`保存配置文件失败: ` + err);
} }
return false return false;
} }
export async function EnableFunction(func: FunctionTypes) { export async function EnableFunction(func: FunctionTypes) {
if (ACCOUNT.value) { if (ACCOUNT.value) {
if (ACCOUNT.value.settings.enableFunctions.includes(func)) { if (ACCOUNT.value.settings.enableFunctions.includes(func)) {
return true return true;
} else { } else {
ACCOUNT.value.settings.enableFunctions.push(func) ACCOUNT.value.settings.enableFunctions.push(func);
if (await updateFunctionEnable()) { if (await updateFunctionEnable()) {
return true return true;
} else { } else {
ACCOUNT.value.settings.enableFunctions.splice( ACCOUNT.value.settings.enableFunctions.splice(
ACCOUNT.value.settings.enableFunctions.indexOf(func), ACCOUNT.value.settings.enableFunctions.indexOf(func),
1 1
) );
return false return false;
} }
} }
} }
return false return false;
} }
export async function DisableFunction(func: FunctionTypes) { export async function DisableFunction(func: FunctionTypes) {
if (ACCOUNT.value) { if (ACCOUNT.value) {
if (!ACCOUNT.value.settings.enableFunctions.includes(func)) { if (!ACCOUNT.value.settings.enableFunctions.includes(func)) {
return true return true;
} else { } else {
ACCOUNT.value.settings.enableFunctions.splice( ACCOUNT.value.settings.enableFunctions.splice(
ACCOUNT.value.settings.enableFunctions.indexOf(func), ACCOUNT.value.settings.enableFunctions.indexOf(func),
1 1
) );
if (await updateFunctionEnable()) { if (await updateFunctionEnable()) {
return true return true;
} else { } else {
ACCOUNT.value.settings.enableFunctions.push(func) ACCOUNT.value.settings.enableFunctions.push(func);
return false return false;
} }
} }
} }
return false return false;
} }
async function updateFunctionEnable() { async function updateFunctionEnable() {
if (ACCOUNT.value) { if (ACCOUNT.value) {
try { try {
const data = await SaveEnableFunctions( const data = await SaveEnableFunctions(
ACCOUNT.value.settings.enableFunctions ACCOUNT.value.settings.enableFunctions
) );
if (data.code == 200) { if (data.code == 200) {
return true return true;
} else { } else {
return false return false;
} }
} catch (err) { } catch (err) {
console.log(err) console.log(err);
return false return false;
} }
} }
} }

View File

@@ -2,8 +2,7 @@
import { apiFail } from '@/data/constants' import { apiFail } from '@/data/constants'
import { useLocalStorage } from '@vueuse/core' import { useLocalStorage } from '@vueuse/core'
import { APIRoot, PaginationResponse } from './api-models' import { APIRoot, PaginationResponse } from './api-models'
import { cookie } from './account';
const cookie = useLocalStorage('JWT_Token', '')
export async function QueryPostAPI<T>( export async function QueryPostAPI<T>(
urlString: string, urlString: string,
@@ -58,7 +57,7 @@ async function QueryPostAPIWithParamsInternal<T>(
headers.forEach((header) => { headers.forEach((header) => {
h[header[0]] = header[1] h[header[0]] = header[1]
}) })
if (cookie.value) h['Authorization'] = `Bearer ${cookie.value}` if (cookie.value.cookie) h['Authorization'] = `Bearer ${cookie.value.cookie}`
h['Content-Type'] = contentType h['Content-Type'] = contentType
return await QueryAPIInternal<T>(url, { return await QueryAPIInternal<T>(url, {
@@ -112,8 +111,8 @@ async function QueryGetAPIInternal<T>(
headers.forEach((header) => { headers.forEach((header) => {
h[header[0]] = header[1] h[header[0]] = header[1]
}) })
if (cookie.value) { if (cookie.value.cookie) {
h['Authorization'] = `Bearer ${cookie.value}` h['Authorization'] = `Bearer ${cookie.value.cookie}`
} }
return await QueryAPIInternal<T>(url, { method: 'get', headers: h }) return await QueryAPIInternal<T>(url, { method: 'get', headers: h })
} catch (err) { } catch (err) {
@@ -163,5 +162,5 @@ export async function QueryGetPaginationAPI<T>(
return await QueryGetAPIInternal<PaginationResponse<T>>(urlString, params) return await QueryGetAPIInternal<PaginationResponse<T>>(urlString, params)
} }
export function GetHeaders(): [string, string][] { export function GetHeaders(): [string, string][] {
return [['Authorization', `Bearer ${cookie.value}`]] return [['Authorization', `Bearer ${cookie.value?.cookie}`]]
} }

View File

@@ -219,7 +219,7 @@
data: [{ value: eventsPerSecond.value, name: '事件/秒' }] data: [{ value: eventsPerSecond.value, name: '事件/秒' }]
}] }]
}; };
gaugeOption.value = option; // 保留原始option用于初始化 gaugeOption.value ??= option; // 保留原始option用于初始化
if (gaugeChart.value) { if (gaugeChart.value) {
gaugeChart.value.setOption(option, false); gaugeChart.value.setOption(option, false);
} }
@@ -239,7 +239,7 @@
markLine: { data: [{ type: 'average', name: '平均值' }] } markLine: { data: [{ type: 'average', name: '平均值' }] }
}] }]
}; };
//historyOption.value = option; // 保留原始option用于初始化 historyOption.value ??= option; // 保留原始option用于初始化
if (historyChart.value) { if (historyChart.value) {
historyChart.value.setOption(option, false); historyChart.value.setOption(option, false);
} }
@@ -259,7 +259,7 @@
labelLine: { show: false }, data: typeData labelLine: { show: false }, data: typeData
}] }]
}; };
typeDistributionOption.value = option; // 保留原始option用于初始化 typeDistributionOption.value ??= option; // 保留原始option用于初始化
if (typeDistributionChart.value) { if (typeDistributionChart.value) {
typeDistributionChart.value.setOption(option, false); typeDistributionChart.value.setOption(option, false);
} }

View File

@@ -1,12 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { useAccount } from '@/api/account'; import { cookie, useAccount } from '@/api/account';
import { useWebFetcher } from '@/store/useWebFetcher'; import { useWebFetcher } from '@/store/useWebFetcher';
import { openUrl } from '@tauri-apps/plugin-opener'; import { openUrl } from '@tauri-apps/plugin-opener';
import { useElementSize } from '@vueuse/core'; import { useElementSize } from '@vueuse/core';
import { roomInfo, streamingInfo } from './data/info'; import { roomInfo, streamingInfo } from './data/info';
const accountInfo = useAccount(); const accountInfo = useAccount();
const cookie = useLocalStorage('JWT_Token', '')
const webfetcher = useWebFetcher(); const webfetcher = useWebFetcher();
@@ -91,9 +90,7 @@ import { roomInfo, streamingInfo } from './data/info';
<template #header> <template #header>
<NSpace align="center"> <NSpace align="center">
直播状态 直播状态
<NTag <NTag :type="!accountInfo.streamerInfo?.isStreaming ? 'error' : 'success'">
:type="!accountInfo.streamerInfo?.isStreaming ? 'error' : 'success'"
>
{{ !accountInfo.streamerInfo?.isStreaming ? '未直播' : '直播中' }} {{ !accountInfo.streamerInfo?.isStreaming ? '未直播' : '直播中' }}
</NTag> </NTag>
</NSpace> </NSpace>
@@ -108,15 +105,13 @@ import { roomInfo, streamingInfo } from './data/info';
v-if="roomInfo?.user_cover" v-if="roomInfo?.user_cover"
style="position: relative" style="position: relative"
> >
<div <div style="position: relative; width: 100%; max-width: 500px;">
style="position: relative; width: 100%; max-width: 500px;"
>
<NImage <NImage
ref="coverRef" ref="coverRef"
:src="roomInfo?.user_cover" :src="roomInfo?.user_cover"
style="width: 100%; opacity: 0.5; border-radius: 8px;" style="width: 100%; opacity: 0.5; border-radius: 8px;"
referrerpolicy="no-referrer" referrerpolicy="no-referrer"
:img-props="{ referrerpolicy: 'no-referrer', style: { width: '100%'} }" :img-props="{ referrerpolicy: 'no-referrer', style: { width: '100%' } }"
/> />
</div> </div>
<div <div

View File

@@ -244,6 +244,9 @@ import { CloudArchive24Filled, Settings24Filled } from '@vicons/fluent';
<NLayoutContent <NLayoutContent
class="main-layout-content" class="main-layout-content"
:native-scrollbar="false" :native-scrollbar="false"
:scrollbar-props="{
trigger: 'none'
}"
> >
<div style="padding: 12px; padding-right: 15px;"> <div style="padding: 12px; padding-right: 15px;">
<RouterView v-slot="{ Component }"> <RouterView v-slot="{ Component }">
@@ -278,7 +281,7 @@ import { CloudArchive24Filled, Settings24Filled } from '@vicons/fluent';
justify-content: center; justify-content: center;
/* 计算高度,减去 WindowBar 的高度 (假设为 30px) */ /* 计算高度,减去 WindowBar 的高度 (假设为 30px) */
height: calc(100vh - 30px); height: calc(100vh - 30px);
background-color: #f8f8fa; background-color: var(--n-color);
/* 可选:添加背景色 */ /* 可选:添加背景色 */
} }

View File

@@ -29,6 +29,7 @@ export async function initAll() {
return; return;
} }
let permissionGranted = await isPermissionGranted(); let permissionGranted = await isPermissionGranted();
checkUpdate();
// If not we need to request it // If not we need to request it
if (!permissionGranted) { if (!permissionGranted) {
@@ -117,6 +118,7 @@ export function OnClientUnmounted() {
async function checkUpdate() { async function checkUpdate() {
const update = await check(); const update = await check();
console.log(update);
if (update) { if (update) {
console.log( console.log(
`found update ${update.version} from ${update.date} with notes ${update.body}` `found update ${update.version} from ${update.date} with notes ${update.body}`

View File

@@ -1,4 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { cookie } from '@/api/account';
import { AccountInfo } from '@/api/api-models' import { AccountInfo } from '@/api/api-models'
import { QueryGetAPI, QueryPostAPI } from '@/api/query' import { QueryGetAPI, QueryPostAPI } from '@/api/query'
import { ACCOUNT_API_URL, TURNSTILE_KEY } from '@/data/constants' import { ACCOUNT_API_URL, TURNSTILE_KEY } from '@/data/constants'
@@ -43,7 +44,6 @@ const registerModel = ref<RegisterModel>({} as RegisterModel)
const loginModel = ref<LoginModel>({} as LoginModel) const loginModel = ref<LoginModel>({} as LoginModel)
const token = ref('') const token = ref('')
const turnstile = ref() const turnstile = ref()
const cookie = useLocalStorage('JWT_Token', '')
const selectedTab = ref('login') const selectedTab = ref('login')
const inputForgetPasswordValue = ref('') const inputForgetPasswordValue = ref('')
@@ -133,7 +133,10 @@ function onRegisterButtonClick() {
.then((data) => { .then((data) => {
if (data.code == 200) { if (data.code == 200) {
message.success(`注册成功`) message.success(`注册成功`)
cookie.value = data.data cookie.value = {
cookie: data.data,
refreshDate: Date.now()
}
setTimeout(() => { setTimeout(() => {
location.reload() location.reload()
}, 1000) }, 1000)
@@ -159,7 +162,10 @@ function onLoginButtonClick() {
}) })
.then(async (data) => { .then(async (data) => {
if (data.code == 200) { if (data.code == 200) {
localStorage.setItem('JWT_Token', data.data.token) cookie.value = {
cookie: data.data.token,
refreshDate: Date.now()
}
message.success(`成功登陆为 ${data?.data.account.name}`) message.success(`成功登陆为 ${data?.data.account.name}`)
setTimeout(() => { setTimeout(() => {
location.reload() location.reload()
@@ -212,7 +218,7 @@ onUnmounted(() => {
<template #header-extra> <template #header-extra>
<slot name="header-extra" /> <slot name="header-extra" />
</template> </template>
<template v-if="cookie"> <template v-if="cookie.cookie">
<NAlert type="warning"> <NAlert type="warning">
你已经登录 你已经登录
</NAlert> </NAlert>

View File

@@ -5,8 +5,8 @@ import { useStorage } from '@vueuse/core';
import { NSpin, useLoadingBar, useMessage, useModal } from 'naive-ui' import { NSpin, useLoadingBar, useMessage, useModal } from 'naive-ui'
import { onMounted } from 'vue' import { onMounted } from 'vue'
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { cookie } from '@/api/account';
const cookie = useStorage('JWT_Token', '')
const accountInfo = useAccount() const accountInfo = useAccount()
// Setup code // Setup code

View File

@@ -10,6 +10,8 @@ const failoverAPI = `https://failover-api.vtsuru.suki.club/`;
export const isBackendUsable = ref(true); export const isBackendUsable = ref(true);
export const isDev = import.meta.env.MODE === 'development'; export const isDev = import.meta.env.MODE === 'development';
// @ts-ignore
export const isTauri = window.__TAURI__ !== undefined || window.__TAURI_INTERNAL__ !== undefined;
export const AVATAR_URL = 'https://workers.vrp.moe/api/bilibili/avatar/'; export const AVATAR_URL = 'https://workers.vrp.moe/api/bilibili/avatar/';
export const FILE_BASE_URL = 'https://files.vtsuru.live'; export const FILE_BASE_URL = 'https://files.vtsuru.live';

View File

@@ -1,5 +1,5 @@
import { QueryGetAPI } from '@/api/query' import { QueryGetAPI } from '@/api/query'
import { apiFail, BASE_API_URL } from '@/data/constants' import { apiFail, BASE_API_URL, isTauri } from '@/data/constants'
import HyperDX from '@hyperdx/browser' import HyperDX from '@hyperdx/browser'
import EasySpeech from 'easy-speech' import EasySpeech from 'easy-speech'
import { createDiscreteApi, NButton, NFlex, NText } from 'naive-ui' import { createDiscreteApi, NButton, NFlex, NText } from 'naive-ui'
@@ -16,9 +16,6 @@ import { useNotificationStore } from './store/useNotificationStore'
const pinia = createPinia() const pinia = createPinia()
export const getPinia = () => pinia export const getPinia = () => pinia
const app = createApp(App)
app.use(router).use(pinia).mount('#app')
QueryGetAPI<string>(`${BASE_API_URL}vtsuru/version`) QueryGetAPI<string>(`${BASE_API_URL}vtsuru/version`)
.then((version) => { .then((version) => {
if (version.code == 200) { if (version.code == 200) {
@@ -58,6 +55,10 @@ QueryGetAPI<string>(`${BASE_API_URL}vtsuru/version`)
const path = url.pathname const path = url.pathname
if (!path.startsWith('/obs')) { if (!path.startsWith('/obs')) {
if (isTauri) {
location.reload();
}
else {
const n = notification.info({ const n = notification.info({
title: '发现新的版本更新', title: '发现新的版本更新',
content: '是否现在刷新?', content: '是否现在刷新?',
@@ -87,6 +88,7 @@ QueryGetAPI<string>(`${BASE_API_URL}vtsuru/version`)
}) })
} }
} }
}
}, },
) )
}, 60 * 1000) }, 60 * 1000)
@@ -126,6 +128,9 @@ QueryGetAPI<string>(`${BASE_API_URL}vtsuru/version`)
UpdateAccountLoop() UpdateAccountLoop()
}) })
const app = createApp(App)
app.use(router).use(pinia).mount('#app')
let currentVersion: string let currentVersion: string
let isHaveNewVersion = false let isHaveNewVersion = false

View File

@@ -1,4 +1,4 @@
import { useAccount } from '@/api/account' import { cookie, useAccount } from '@/api/account'
import { import {
BaseRTCClient, BaseRTCClient,
MasterRTCClient, MasterRTCClient,
@@ -25,7 +25,6 @@ export const useWebRTC = defineStore('WebRTC', () => {
function send(event: string, data: any) { function send(event: string, data: any) {
client.value?.send(event, data) client.value?.send(event, data)
} }
const cookie = useStorage('JWT_Token', '')
const route = useRoute() const route = useRoute()
async function Init(type: 'master' | 'slave') { async function Init(type: 'master' | 'slave') {
if (isInitializing) { if (isInitializing) {
@@ -38,7 +37,7 @@ export const useWebRTC = defineStore('WebRTC', () => {
{ ifAvailable: true }, { ifAvailable: true },
async (lock) => { async (lock) => {
if (lock) { if (lock) {
if (!cookie.value && !route.query.token) { if (!cookie.value.cookie && !route.query.token) {
console.log('[RTC] 未登录, 跳过RTC初始化') console.log('[RTC] 未登录, 跳过RTC初始化')
return return
} }

View File

@@ -6,7 +6,7 @@ import { compress } from 'brotli-compress';
import { format } from 'date-fns'; import { format } from 'date-fns';
import * as signalR from '@microsoft/signalr'; import * as signalR from '@microsoft/signalr';
import * as msgpack from '@microsoft/signalr-protocol-msgpack'; import * as msgpack from '@microsoft/signalr-protocol-msgpack';
import { useAccount } from '@/api/account'; // 假设账户信息路径 import { cookie, useAccount } from '@/api/account'; // 假设账户信息路径
import { BASE_HUB_URL, isDev } from '@/data/constants'; // 假设常量路径 import { BASE_HUB_URL, isDev } from '@/data/constants'; // 假设常量路径
import BaseDanmakuClient from '@/data/DanmakuClients/BaseDanmakuClient'; // 假设弹幕客户端基类路径 import BaseDanmakuClient from '@/data/DanmakuClients/BaseDanmakuClient'; // 假设弹幕客户端基类路径
import DirectClient, { DirectClientAuthInfo } from '@/data/DanmakuClients/DirectClient'; // 假设直连客户端路径 import DirectClient, { DirectClientAuthInfo } from '@/data/DanmakuClients/DirectClient'; // 假设直连客户端路径
@@ -16,7 +16,6 @@ import { getEventType, recordEvent, streamingInfo } from '@/client/data/info';
import { useWebRTC } from './useRTC'; import { useWebRTC } from './useRTC';
export const useWebFetcher = defineStore('WebFetcher', () => { export const useWebFetcher = defineStore('WebFetcher', () => {
const cookie = useLocalStorage('JWT_Token', '');
const route = useRoute(); const route = useRoute();
const account = useAccount(); const account = useAccount();
const rtc = useWebRTC(); const rtc = useWebRTC();
@@ -220,7 +219,7 @@ export const useWebFetcher = defineStore('WebFetcher', () => {
logInfo(prefix.value + '正在连接到 vtsuru 服务器...'); logInfo(prefix.value + '正在连接到 vtsuru 服务器...');
const connection = new signalR.HubConnectionBuilder() const connection = new signalR.HubConnectionBuilder()
.withUrl(BASE_HUB_URL + 'web-fetcher?token=' + (route.query.token ?? account.value.token), { // 使用 account.token .withUrl(BASE_HUB_URL + 'web-fetcher?token=' + (route.query.token ?? account.value.token), { // 使用 account.token
headers: { Authorization: `Bearer ${cookie.value}` }, headers: { Authorization: `Bearer ${cookie.value?.cookie}` },
skipNegotiation: true, skipNegotiation: true,
transport: signalR.HttpTransportType.WebSockets transport: signalR.HttpTransportType.WebSockets
}) })

View File

@@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { NavigateToNewTab, isDarkMode } from '@/Utils' import { NavigateToNewTab, isDarkMode } from '@/Utils'
import { isLoadingAccount, useAccount } from '@/api/account' import { cookie, isLoadingAccount, useAccount } from '@/api/account'
import { ThemeType } from '@/api/api-models' import { ThemeType } from '@/api/api-models'
import { QueryGetAPI } from '@/api/query' import { QueryGetAPI } from '@/api/query'
import RegisterAndLogin from '@/components/RegisterAndLogin.vue' import RegisterAndLogin from '@/components/RegisterAndLogin.vue'
@@ -69,7 +69,6 @@ const type = computed(() => {
} }
return '' return ''
}) })
const cookie = useStorage('JWT_Token', '')
const musicRquestStore = useMusicRequestProvider() const musicRquestStore = useMusicRequestProvider()
const canResendEmail = ref(false) const canResendEmail = ref(false)

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { useAccount } from '@/api/account' import { cookie, useAccount } from '@/api/account'
import { BiliAuthCodeStatusType, BiliAuthModel } from '@/api/api-models' import { BiliAuthCodeStatusType, BiliAuthModel } from '@/api/api-models'
import { QueryGetAPI, QueryPostAPI } from '@/api/query' import { QueryGetAPI, QueryPostAPI } from '@/api/query'
import EventFetcherStatusCard from '@/components/EventFetcherStatusCard.vue' import EventFetcherStatusCard from '@/components/EventFetcherStatusCard.vue'
@@ -40,8 +40,6 @@ const token = ref('')
const turnstile = ref() const turnstile = ref()
const accountInfo = useAccount() const accountInfo = useAccount()
const useAuth = useAuthStore()
const cookie = useLocalStorage('JWT_Token', '')
const message = useMessage() const message = useMessage()
const resetEmailModalVisiable = ref(false) const resetEmailModalVisiable = ref(false)