适配ClassworksKV

This commit is contained in:
SunWuyuan
2025-10-06 14:30:32 +08:00
parent 6cf67d17c6
commit 68d6582ae0
13 changed files with 835 additions and 1207 deletions
+8 -40
View File
@@ -7,18 +7,19 @@ const isValidProvider = () => {
return provider === "kv-server" || provider === "classworkscloud";
};
// Helper function to get request headers with site key and namespace password
// Helper function to get request headers with kvtoken
const getHeaders = () => {
const headers = { Accept: "application/json" };
const kvToken = getSetting("server.kvToken");
const siteKey = getSetting("server.siteKey");
const namespacePassword = getSetting("namespace.password");
if (siteKey) {
// 优先使用新的kvToken
if (kvToken) {
headers["x-app-token"] = kvToken;
} else if (siteKey) {
// 向后兼容旧的siteKey
headers["x-site-key"] = siteKey;
}
if (namespacePassword) {
headers["x-namespace-password"] = namespacePassword;
}
return headers;
};
@@ -33,10 +34,9 @@ export const getNamespaceInfo = async () => {
}
const serverUrl = getSetting("server.domain");
const machineId = getSetting("device.uuid");
try {
const response = await axios.get(`${serverUrl}/${machineId}/_info`, {
const response = await axios.get(`${serverUrl}/kv/_info`, {
headers: getHeaders(),
});
@@ -44,36 +44,4 @@ export const getNamespaceInfo = async () => {
} catch (error) {
throw new Error(error.response?.data?.message || "获取命名空间信息失败");
}
};
/**
* Update namespace password
* @param {string} oldPassword - Current password (if exists)
* @param {string} newPassword - New password to set
* @returns {Promise<Object>} Response data
*/
export const updateNamespacePassword = async (oldPassword, newPassword) => {
if (!isValidProvider()) {
throw new Error("当前数据提供者不支持此操作");
}
const serverUrl = getSetting("server.domain");
const machineId = getSetting("device.uuid");
try {
const response = await axios.put(
`${serverUrl}/${machineId}/_infopassword`,
{
oldPassword,
newPassword,
},
{
headers: getHeaders(),
}
);
return response.data;
} catch (error) {
throw new Error(error.response?.data?.message || "更新命名空间密码失败");
}
};
+24 -88
View File
@@ -1,19 +1,20 @@
import axios from "@/axios/axios";
import { formatResponse, formatError } from "../dataProvider";
import { getSetting, setSetting } from "../settings";
import { getSetting } from "../settings";
// Helper function to get request headers with site key and password if available
// Helper function to get request headers with kvtoken
const getHeaders = () => {
const headers = { Accept: "application/json" };
const kvToken = getSetting("server.kvToken");
const siteKey = getSetting("server.siteKey");
const password = getSetting("namespace.password");
if (siteKey) {
// 优先使用新的kvToken
if (kvToken) {
headers["x-app-token"] = kvToken;
} else if (siteKey) {
// 向后兼容旧的siteKey
headers["x-site-key"] = siteKey;
}
if (password) {
headers["x-namespace-password"] = password;
}
return headers;
};
@@ -21,30 +22,18 @@ const getHeaders = () => {
export const kvServerProvider = {
async loadNamespaceInfo() {
try {
// 使用 Classworks Cloud 或者用户配置的服务器域名
const provider = getSetting("server.provider");
const serverUrl = getSetting("server.domain");
const machineId = getSetting("device.uuid");
const res = await axios.get(`${serverUrl}/${machineId}/_info`, {
const res = await axios.get(`${serverUrl}/kv/_info`, {
headers: getHeaders(),
});
const { name, accessType } = res.data;
// 如果name为null,使用班级号作为名称并更新
if (name === null) {
const classNumber = getSetting("server.classNumber");
await this.updateNamespaceInfo({ name: classNumber });
// 重新加载命名空间信息
return await this.loadNamespaceInfo();
}
// 更新本地访问权限设置
if (accessType) {
setSetting("namespace.accessType", accessType);
}
return formatResponse(res);
// 直接返回新格式 API 数据,包含 device 和 account 信息
return formatResponse(res.data);
} catch (error) {
console.error("获取命名空间信息失败:", error);
return formatError(
error.response?.data?.message || "获取命名空间信息失败",
"NAMESPACE_ERROR"
@@ -55,9 +44,8 @@ export const kvServerProvider = {
async updateNamespaceInfo(data) {
try {
const serverUrl = getSetting("server.domain");
const machineId = getSetting("device.uuid");
const res = await axios.put(`${serverUrl}/${machineId}/_info`, data, {
const res = await axios.put(`${serverUrl}/kv/_info`, data, {
headers: getHeaders(),
});
@@ -70,61 +58,11 @@ export const kvServerProvider = {
}
},
async updatePassword(newPassword, oldPassword, passwordHint = null) {
try {
const serverUrl = getSetting("server.domain");
const machineId = getSetting("device.uuid");
const res = await axios.post(
`${serverUrl}/${machineId}/_password`,
{
password: newPassword,
oldPassword,
passwordHint,
},
{
headers: getHeaders(),
}
);
if (res.status === 200) {
// 更新本地存储的密码
setSetting("namespace.password", newPassword || "");
}
return res;
} catch (error) {
return formatError(
error.response?.data?.message || "更新密码失败",
"PASSWORD_ERROR"
);
}
},
async deletePassword() {
try {
const serverUrl = getSetting("server.domain");
const machineId = getSetting("device.uuid");
const res = await axios.delete(`${serverUrl}/${machineId}/_password`, {
headers: getHeaders(),
});
setSetting("namespace.password", "");
return res;
} catch (error) {
return formatError(
error.response?.data?.message || "删除密码失败",
"PASSWORD_ERROR"
);
}
},
async loadData(key) {
try {
const serverUrl = getSetting("server.domain");
const machineId = getSetting("device.uuid");
const res = await axios.get(`${serverUrl}/${machineId}/${key}`, {
const res = await axios.get(`${serverUrl}/kv/${key}`, {
headers: getHeaders(),
});
@@ -144,8 +82,7 @@ export const kvServerProvider = {
async saveData(key, data) {
try {
const serverUrl = getSetting("server.domain");
const machineId = getSetting("device.uuid");
await axios.post(`${serverUrl}/${machineId}/${key}`, data, {
await axios.post(`${serverUrl}/kv/${key}`, data, {
headers: getHeaders(),
});
return formatResponse(true);
@@ -166,7 +103,7 @@ export const kvServerProvider = {
* @param {number} options.limit - 每页返回的记录数,默认为 100
* @param {number} options.skip - 跳过的记录数,默认为 0
* @returns {Promise<Object>} 包含键名列表和分页信息的响应对象
*
*
* 返回值示例:
* {
* keys: ["key1", "key2", "key3"],
@@ -182,8 +119,7 @@ export const kvServerProvider = {
async loadKeys(options = {}) {
try {
const serverUrl = getSetting("server.domain");
const machineId = getSetting("device.uuid");
// 设置默认参数
const {
sortBy = "key",
@@ -191,7 +127,7 @@ export const kvServerProvider = {
limit = 100,
skip = 0
} = options;
// 构建查询参数
const params = new URLSearchParams({
sortBy,
@@ -199,11 +135,11 @@ export const kvServerProvider = {
limit: limit.toString(),
skip: skip.toString()
});
const res = await axios.get(`${serverUrl}/${machineId}/_keys?${params}`, {
const res = await axios.get(`${serverUrl}/kv/_keys?${params}`, {
headers: getHeaders(),
});
return formatResponse(res.data);
} catch (error) {
if (error.response?.status === 404) {
@@ -222,4 +158,4 @@ export const kvServerProvider = {
);
}
},
};
};
+29 -25
View File
@@ -1,4 +1,3 @@
import { v4 as uuidv4 } from "uuid";
// 请求通知权限
async function requestNotificationPermission() {
if (Notification && Notification.requestPermission) {
@@ -66,17 +65,11 @@ if (typeof window !== "undefined") {
// 存储所有设置的localStorage键名
const SETTINGS_STORAGE_KEY = "Classworks_settings";
/**
* 生成UUID v4
* @returns {string} 生成的UUID字符串
*/
function generateUUID() {
return uuidv4();
}
// 新增: Classworks云端存储的默认设置
const classworksCloudDefaults = {
"server.domain": "https://kv.wuyuan.dev",
//"server.domain": "http://localhost:3030",
"server.siteKey": "",
};
@@ -88,26 +81,11 @@ const settingsDefinitions = {
// 设备标识
"device.uuid": {
type: "string",
default: generateUUID(),
default: '00000000-0000-4000-8000-000000000000',
description: "设备唯一标识符",
icon: "mdi-identifier",
},
// 命名空间设置
"namespace.password": {
type: "string",
default: "",
description: "命名空间访问密码",
icon: "mdi-key",
},
"namespace.accessType": {
type: "string",
default: "readwrite",
description: "访问权限类型",
icon: "mdi-shield-lock",
validate: (value) => ["readonly", "readwrite"].includes(value),
},
// 存储设置
"storage.persistOnLoad": {
type: "boolean",
@@ -213,9 +191,35 @@ const settingsDefinitions = {
icon: "mdi-key-chain",
// 用于后端验证请求的令牌,将作为请求头 x-site-key 发送
},
"server.kvToken": {
type: "string",
default: "",
description: "KV授权令牌",
icon: "mdi-shield-key",
// 用于KV服务器认证的令牌,将作为请求头 x-app-token 发送
},
"server.authDomain": {
type: "string",
default: "https://kv.houlang.cloud",
description: "授权服务器域名",
icon: "mdi-shield-account",
validate: (value) => {
// 如果值为空,直接通过
if (!value) return true;
// 验证URL格式
try {
new URL(value);
return true;
} catch (e) {
console.error("授权域名格式无效:", e);
return false;
}
},
// 用于CSKV授权跳转的服务器域名
},
"server.provider": {
type: "string",
default: "kv-local",
default: "classworkscloud",
validate: (value) =>
["kv-local", "kv-server", "classworkscloud"].includes(value),
description: "数据提供者",