mirror of
https://github.com/ZeroCatDev/Classworks.git
synced 2026-02-04 16:03:10 +00:00
Compare commits
No commits in common. "7ce2deb61cd29a8463caa18a59f9247e5bcf6311" and "b905c4390c2672d7e025c5cdbba9e813c164988b" have entirely different histories.
7ce2deb61c
...
b905c4390c
@ -90,7 +90,6 @@
|
|||||||
import SettingsCard from "@/components/SettingsCard.vue";
|
import SettingsCard from "@/components/SettingsCard.vue";
|
||||||
import {getSetting} from "@/utils/settings";
|
import {getSetting} from "@/utils/settings";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import {tryWithRotation, isRotationEnabled} from "@/utils/serverRotation";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "DataProviderSettingsCard",
|
name: "DataProviderSettingsCard",
|
||||||
@ -133,9 +132,8 @@ export default {
|
|||||||
async checkServerConnection() {
|
async checkServerConnection() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.serverchecktime = new Date();
|
this.serverchecktime = new Date();
|
||||||
const triedServers = [];
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const domain = getSetting("server.domain");
|
||||||
const siteKey = getSetting("server.siteKey");
|
const siteKey = getSetting("server.siteKey");
|
||||||
|
|
||||||
// Prepare headers including site key if available
|
// Prepare headers including site key if available
|
||||||
@ -144,45 +142,6 @@ export default {
|
|||||||
headers["x-site-key"] = siteKey;
|
headers["x-site-key"] = siteKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use rotation for classworkscloud provider
|
|
||||||
if (isRotationEnabled()) {
|
|
||||||
const response = await tryWithRotation(
|
|
||||||
async (serverUrl) => {
|
|
||||||
const res = await axios.get(`${serverUrl}/check`, {
|
|
||||||
method: "GET",
|
|
||||||
headers,
|
|
||||||
});
|
|
||||||
if (res.data.status !== "success") {
|
|
||||||
throw new Error("服务器响应异常");
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
{
|
|
||||||
onServerTried: ({url, status, tried}) => {
|
|
||||||
triedServers.length = 0;
|
|
||||||
triedServers.push(...tried);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Build success message with tried servers info
|
|
||||||
const latency = new Date() - this.serverchecktime;
|
|
||||||
const successServer = triedServers.find(s => s.status === "success");
|
|
||||||
let message = `服务器连接正常 延迟${latency}ms`;
|
|
||||||
|
|
||||||
if (triedServers.length > 1) {
|
|
||||||
const serverList = triedServers.map((s, i) =>
|
|
||||||
`${i + 1}. ${s.url} (${s.status === "success" ? "成功" : "失败"})`
|
|
||||||
).join("\n");
|
|
||||||
message += `\n\n依次尝试的服务器:\n${serverList}`;
|
|
||||||
} else if (successServer) {
|
|
||||||
message += `\n服务器: ${successServer.url}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$message.success("连接成功", message);
|
|
||||||
} else {
|
|
||||||
// Standard single-server check for other providers
|
|
||||||
const domain = getSetting("server.domain");
|
|
||||||
const response = await axios.get(`${domain}/check`, {
|
const response = await axios.get(`${domain}/check`, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers,
|
headers,
|
||||||
@ -196,19 +155,8 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
throw new Error("服务器响应异常");
|
throw new Error("服务器响应异常");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Build error message with tried servers info
|
this.$message.error("连接失败", error.message || "无法连接到服务器");
|
||||||
let errorMessage = error.message || "无法连接到服务器";
|
|
||||||
|
|
||||||
if (triedServers.length > 0) {
|
|
||||||
const serverList = triedServers.map((s, i) =>
|
|
||||||
`${i + 1}. ${s.url} (失败${s.error ? `: ${s.error}` : ""})`
|
|
||||||
).join("\n");
|
|
||||||
errorMessage += `\n\n依次尝试的服务器:\n${serverList}\n\n所有服务器均连接失败`;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$message.error("连接失败", errorMessage);
|
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import axios from "@/axios/axios";
|
import axios from "@/axios/axios";
|
||||||
import {getSetting} from "@/utils/settings";
|
import {getSetting} from "@/utils/settings";
|
||||||
import {tryWithRotation, isRotationEnabled} from "@/utils/serverRotation";
|
|
||||||
|
|
||||||
// Helper function to check if provider is valid for API calls
|
// Helper function to check if provider is valid for API calls
|
||||||
const isValidProvider = () => {
|
const isValidProvider = () => {
|
||||||
@ -34,18 +33,9 @@ export const getNamespaceInfo = async () => {
|
|||||||
throw new Error("当前数据提供者不支持此操作");
|
throw new Error("当前数据提供者不支持此操作");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
// Use rotation for classworkscloud provider
|
|
||||||
if (isRotationEnabled()) {
|
|
||||||
const response = await tryWithRotation(async (serverUrl) => {
|
|
||||||
return await axios.get(`${serverUrl}/kv/_info`, {
|
|
||||||
headers: getHeaders(),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return response.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
const serverUrl = getSetting("server.domain");
|
const serverUrl = getSetting("server.domain");
|
||||||
|
|
||||||
|
try {
|
||||||
const response = await axios.get(`${serverUrl}/kv/_info`, {
|
const response = await axios.get(`${serverUrl}/kv/_info`, {
|
||||||
headers: getHeaders(),
|
headers: getHeaders(),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import {kvLocalProvider} from "./providers/kvLocalProvider";
|
import {kvLocalProvider} from "./providers/kvLocalProvider";
|
||||||
import {kvServerProvider} from "./providers/kvServerProvider";
|
import {kvServerProvider} from "./providers/kvServerProvider";
|
||||||
import {getSetting, setSetting} from "./settings";
|
import {getSetting, setSetting} from "./settings";
|
||||||
import {getEffectiveServerUrl} from "./serverRotation";
|
|
||||||
|
|
||||||
export const formatResponse = (data) => data;
|
export const formatResponse = (data) => data;
|
||||||
|
|
||||||
@ -174,16 +173,7 @@ export default {
|
|||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const provider = getSetting("server.provider");
|
let serverUrl = getSetting("server.domain");
|
||||||
let serverUrl;
|
|
||||||
|
|
||||||
// Use effective server URL for classworkscloud provider
|
|
||||||
if (provider === "classworkscloud") {
|
|
||||||
serverUrl = getEffectiveServerUrl();
|
|
||||||
} else {
|
|
||||||
serverUrl = getSetting("server.domain");
|
|
||||||
}
|
|
||||||
|
|
||||||
let siteKey = getSetting("server.siteKey");
|
let siteKey = getSetting("server.siteKey");
|
||||||
const machineId = getSetting("device.uuid");
|
const machineId = getSetting("device.uuid");
|
||||||
let configured = false;
|
let configured = false;
|
||||||
@ -210,8 +200,6 @@ export default {
|
|||||||
|
|
||||||
// 设置provider为classworkscloud
|
// 设置provider为classworkscloud
|
||||||
setSetting("server.provider", "classworkscloud");
|
setSetting("server.provider", "classworkscloud");
|
||||||
// Get effective URL after setting provider
|
|
||||||
serverUrl = getEffectiveServerUrl();
|
|
||||||
} else {
|
} else {
|
||||||
return formatError("云端配置无效,请检查服务器域名和设备UUID", "CONFIG_ERROR");
|
return formatError("云端配置无效,请检查服务器域名和设备UUID", "CONFIG_ERROR");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import axios from "@/axios/axios";
|
import axios from "@/axios/axios";
|
||||||
import {formatResponse, formatError} from "../dataProvider";
|
import {formatResponse, formatError} from "../dataProvider";
|
||||||
import {getSetting} from "../settings";
|
import {getSetting} from "../settings";
|
||||||
import {tryWithRotation, isRotationEnabled} from "../serverRotation";
|
|
||||||
|
|
||||||
// Helper function to get request headers with kvtoken
|
// Helper function to get request headers with kvtoken
|
||||||
const getHeaders = () => {
|
const getHeaders = () => {
|
||||||
@ -23,18 +22,9 @@ const getHeaders = () => {
|
|||||||
export const kvServerProvider = {
|
export const kvServerProvider = {
|
||||||
async loadNamespaceInfo() {
|
async loadNamespaceInfo() {
|
||||||
try {
|
try {
|
||||||
// Use rotation for classworkscloud provider
|
// 使用 Classworks Cloud 或者用户配置的服务器域名
|
||||||
if (isRotationEnabled()) {
|
|
||||||
return await tryWithRotation(async (serverUrl) => {
|
|
||||||
const res = await axios.get(`${serverUrl}/kv/_info`, {
|
|
||||||
headers: getHeaders(),
|
|
||||||
});
|
|
||||||
return formatResponse(res.data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Standard single-server mode
|
|
||||||
const serverUrl = getSetting("server.domain");
|
const serverUrl = getSetting("server.domain");
|
||||||
|
|
||||||
const res = await axios.get(`${serverUrl}/kv/_info`, {
|
const res = await axios.get(`${serverUrl}/kv/_info`, {
|
||||||
headers: getHeaders(),
|
headers: getHeaders(),
|
||||||
});
|
});
|
||||||
@ -52,17 +42,8 @@ export const kvServerProvider = {
|
|||||||
|
|
||||||
async updateNamespaceInfo(data) {
|
async updateNamespaceInfo(data) {
|
||||||
try {
|
try {
|
||||||
// Use rotation for classworkscloud provider
|
|
||||||
if (isRotationEnabled()) {
|
|
||||||
return await tryWithRotation(async (serverUrl) => {
|
|
||||||
const res = await axios.put(`${serverUrl}/kv/_info`, data, {
|
|
||||||
headers: getHeaders(),
|
|
||||||
});
|
|
||||||
return res;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const serverUrl = getSetting("server.domain");
|
const serverUrl = getSetting("server.domain");
|
||||||
|
|
||||||
const res = await axios.put(`${serverUrl}/kv/_info`, data, {
|
const res = await axios.put(`${serverUrl}/kv/_info`, data, {
|
||||||
headers: getHeaders(),
|
headers: getHeaders(),
|
||||||
});
|
});
|
||||||
@ -78,17 +59,8 @@ export const kvServerProvider = {
|
|||||||
|
|
||||||
async loadData(key) {
|
async loadData(key) {
|
||||||
try {
|
try {
|
||||||
// Use rotation for classworkscloud provider
|
|
||||||
if (isRotationEnabled()) {
|
|
||||||
return await tryWithRotation(async (serverUrl) => {
|
|
||||||
const res = await axios.get(`${serverUrl}/kv/${key}`, {
|
|
||||||
headers: getHeaders(),
|
|
||||||
});
|
|
||||||
return formatResponse(res.data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const serverUrl = getSetting("server.domain");
|
const serverUrl = getSetting("server.domain");
|
||||||
|
|
||||||
const res = await axios.get(`${serverUrl}/kv/${key}`, {
|
const res = await axios.get(`${serverUrl}/kv/${key}`, {
|
||||||
headers: getHeaders(),
|
headers: getHeaders(),
|
||||||
});
|
});
|
||||||
@ -108,16 +80,6 @@ export const kvServerProvider = {
|
|||||||
|
|
||||||
async saveData(key, data) {
|
async saveData(key, data) {
|
||||||
try {
|
try {
|
||||||
// Use rotation for classworkscloud provider
|
|
||||||
if (isRotationEnabled()) {
|
|
||||||
return await tryWithRotation(async (serverUrl) => {
|
|
||||||
await axios.post(`${serverUrl}/kv/${key}`, data, {
|
|
||||||
headers: getHeaders(),
|
|
||||||
});
|
|
||||||
return formatResponse(true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const serverUrl = getSetting("server.domain");
|
const serverUrl = getSetting("server.domain");
|
||||||
await axios.post(`${serverUrl}/kv/${key}`, data, {
|
await axios.post(`${serverUrl}/kv/${key}`, data, {
|
||||||
headers: getHeaders(),
|
headers: getHeaders(),
|
||||||
@ -155,6 +117,8 @@ export const kvServerProvider = {
|
|||||||
*/
|
*/
|
||||||
async loadKeys(options = {}) {
|
async loadKeys(options = {}) {
|
||||||
try {
|
try {
|
||||||
|
const serverUrl = getSetting("server.domain");
|
||||||
|
|
||||||
// 设置默认参数
|
// 设置默认参数
|
||||||
const {
|
const {
|
||||||
sortBy = "key",
|
sortBy = "key",
|
||||||
@ -171,17 +135,6 @@ export const kvServerProvider = {
|
|||||||
skip: skip.toString()
|
skip: skip.toString()
|
||||||
});
|
});
|
||||||
|
|
||||||
// Use rotation for classworkscloud provider
|
|
||||||
if (isRotationEnabled()) {
|
|
||||||
return await tryWithRotation(async (serverUrl) => {
|
|
||||||
const res = await axios.get(`${serverUrl}/kv/_keys?${params}`, {
|
|
||||||
headers: getHeaders(),
|
|
||||||
});
|
|
||||||
return formatResponse(res.data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const serverUrl = getSetting("server.domain");
|
|
||||||
const res = await axios.get(`${serverUrl}/kv/_keys?${params}`, {
|
const res = await axios.get(`${serverUrl}/kv/_keys?${params}`, {
|
||||||
headers: getHeaders(),
|
headers: getHeaders(),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,106 +0,0 @@
|
|||||||
/**
|
|
||||||
* Server rotation utility for Classworks Cloud provider
|
|
||||||
* Provides fallback mechanism across multiple server endpoints
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { getSetting } from "./settings";
|
|
||||||
|
|
||||||
// Server list for classworkscloud provider (in priority order)
|
|
||||||
const CLASSWORKS_CLOUD_SERVERS = [
|
|
||||||
"https://kv-service.houlang.cloud",
|
|
||||||
"https://kv-service.wuyuan.dev",
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the list of servers to try for the given provider
|
|
||||||
* @param {string} provider - The provider type
|
|
||||||
* @returns {string[]} Array of server URLs to try
|
|
||||||
*/
|
|
||||||
export function getServerList(provider) {
|
|
||||||
if (provider === "classworkscloud") {
|
|
||||||
return [...CLASSWORKS_CLOUD_SERVERS];
|
|
||||||
}
|
|
||||||
|
|
||||||
// For other providers, use the configured domain
|
|
||||||
const domain = getSetting("server.domain");
|
|
||||||
return domain ? [domain] : [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Try an operation with server rotation fallback
|
|
||||||
* @param {Function} operation - Async function that takes a serverUrl and returns a promise
|
|
||||||
* @param {Object} options - Options
|
|
||||||
* @param {string} options.provider - Provider type (optional, defaults to current setting)
|
|
||||||
* @param {Function} options.onServerTried - Callback called when a server is tried (optional)
|
|
||||||
* Receives: { url, status, tried } where tried is a snapshot of attempts
|
|
||||||
* @returns {Promise} Result from the first successful server, or throws the last error
|
|
||||||
*/
|
|
||||||
export async function tryWithRotation(operation, options = {}) {
|
|
||||||
const provider = options.provider || getSetting("server.provider");
|
|
||||||
const onServerTried = options.onServerTried;
|
|
||||||
const hasCallback = typeof onServerTried === 'function';
|
|
||||||
|
|
||||||
const servers = getServerList(provider);
|
|
||||||
const triedServers = [];
|
|
||||||
let lastError = null;
|
|
||||||
|
|
||||||
for (const serverUrl of servers) {
|
|
||||||
try {
|
|
||||||
triedServers.push({ url: serverUrl, status: "trying" });
|
|
||||||
if (hasCallback) {
|
|
||||||
// Provide a snapshot to prevent callback from mutating internal state
|
|
||||||
onServerTried({ url: serverUrl, status: "trying", tried: [...triedServers] });
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await operation(serverUrl);
|
|
||||||
|
|
||||||
triedServers[triedServers.length - 1].status = "success";
|
|
||||||
if (hasCallback) {
|
|
||||||
onServerTried({ url: serverUrl, status: "success", tried: [...triedServers] });
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
} catch (error) {
|
|
||||||
lastError = error;
|
|
||||||
triedServers[triedServers.length - 1].status = "failed";
|
|
||||||
triedServers[triedServers.length - 1].error = error.message || String(error);
|
|
||||||
if (hasCallback) {
|
|
||||||
onServerTried({ url: serverUrl, status: "failed", error, tried: [...triedServers] });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Continue to next server
|
|
||||||
console.warn(`Server ${serverUrl} failed:`, error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// All servers failed
|
|
||||||
console.error("All servers failed. Tried:", triedServers);
|
|
||||||
const error = lastError || new Error("All servers failed");
|
|
||||||
error.triedServers = triedServers;
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the effective server URL for the current provider
|
|
||||||
* For classworkscloud, returns the first server in the list
|
|
||||||
* For other providers, returns the configured domain
|
|
||||||
* @returns {string} Server URL
|
|
||||||
*/
|
|
||||||
export function getEffectiveServerUrl() {
|
|
||||||
const provider = getSetting("server.provider");
|
|
||||||
|
|
||||||
if (provider === "classworkscloud") {
|
|
||||||
return CLASSWORKS_CLOUD_SERVERS[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return getSetting("server.domain") || "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if rotation is enabled for the current provider
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
export function isRotationEnabled() {
|
|
||||||
const provider = getSetting("server.provider");
|
|
||||||
return provider === "classworkscloud";
|
|
||||||
}
|
|
||||||
@ -4,18 +4,12 @@
|
|||||||
|
|
||||||
import {io} from 'socket.io-client';
|
import {io} from 'socket.io-client';
|
||||||
import {getSetting} from '@/utils/settings';
|
import {getSetting} from '@/utils/settings';
|
||||||
import {getEffectiveServerUrl, isRotationEnabled, tryWithRotation} from '@/utils/serverRotation';
|
|
||||||
|
|
||||||
let socket = null;
|
let socket = null;
|
||||||
let connectedDomain = null;
|
let connectedDomain = null;
|
||||||
const listeners = new Set();
|
const listeners = new Set();
|
||||||
|
|
||||||
export function getServerUrl() {
|
export function getServerUrl() {
|
||||||
// For classworkscloud provider, use the effective server URL from rotation
|
|
||||||
if (isRotationEnabled()) {
|
|
||||||
return getEffectiveServerUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer configured server domain; fallback to env; then current origin
|
// Prefer configured server domain; fallback to env; then current origin
|
||||||
const cfg = getSetting('server.domain');
|
const cfg = getSetting('server.domain');
|
||||||
const envUrl = import.meta?.env?.VITE_SERVER_URL;
|
const envUrl = import.meta?.env?.VITE_SERVER_URL;
|
||||||
@ -34,11 +28,6 @@ export function getSocket() {
|
|||||||
socket = null;
|
socket = null;
|
||||||
}
|
}
|
||||||
connectedDomain = serverUrl;
|
connectedDomain = serverUrl;
|
||||||
|
|
||||||
// For classworkscloud, create socket with the first server in rotation
|
|
||||||
// Note: Socket.IO's built-in reconnection will retry the same server URL.
|
|
||||||
// Server rotation is handled at the HTTP request level, not Socket.IO level.
|
|
||||||
// If the Socket.IO server goes down, the connection will fail until the server recovers.
|
|
||||||
socket = io(serverUrl, {transports: ["polling","websocket"]});
|
socket = io(serverUrl, {transports: ["polling","websocket"]});
|
||||||
|
|
||||||
// Re-attach previously registered event handlers on new socket instance
|
// Re-attach previously registered event handlers on new socket instance
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user