mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-06 18:36:55 +08:00
Compare commits
2 Commits
d5c9e663da
...
277497420c
| Author | SHA1 | Date | |
|---|---|---|---|
| 277497420c | |||
| eb306b66b0 |
@@ -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'" />
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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}`]]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
/* 可选:添加背景色 */
|
/* 可选:添加背景色 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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}`
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|||||||
13
src/main.ts
13
src/main.ts
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user