release version update

This commit is contained in:
duanfuxiang
2025-08-20 22:08:04 +08:00
parent 80e72394a9
commit 9a848489e3
15 changed files with 1404 additions and 148 deletions

294
src/hooks/use-infio.ts Normal file
View File

@@ -0,0 +1,294 @@
/* eslint-disable no-console, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call */
import JSZip from "jszip";
import { App, Notice, Plugin, requestUrl } from "obsidian";
import { INFIO_BASE_URL } from "../constants";
// 扩展App类型以包含plugins属性
type AppWithPlugins = App & {
plugins: {
reloadPlugin: (id: string) => void;
};
};
// 类型保护函数
function hasPluginsProperty(app: App): app is AppWithPlugins {
return 'plugins' in app &&
app.plugins !== undefined &&
typeof app.plugins === 'object' &&
'reloadPlugin' in app.plugins &&
typeof app.plugins.reloadPlugin === 'function';
}
// API响应类型定义
export type UserPlanResponse = {
plan: string;
status: string;
dl_zip?: string;
[key: string]: unknown;
};
export type UpgradeResult = {
success: boolean;
message: string;
};
export const fetchUserPlan = async (apiKey: string): Promise<UserPlanResponse> => {
const response = await requestUrl({
url: `${INFIO_BASE_URL}/subscription/status`,
headers: {
Authorization: `Bearer ${apiKey}`,
},
});
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return response.json;
}
/**
* 检查用户是否为Pro用户
*/
export const checkIsProUser = async (apiKey: string): Promise<boolean> => {
try {
if (!apiKey) {
return false;
}
const userPlan = await fetchUserPlan(apiKey);
return userPlan.plan?.toLowerCase().startsWith('pro') || false;
} catch (error) {
// eslint-disable-next-line no-console
console.error('检查Pro用户状态失败:', error);
return false;
}
}
/**
* 清理临时目录
*/
const cleanupTempDirectory = async (adapter: Plugin['app']['vault']['adapter'], tempDir: string): Promise<void> => {
try {
// 检查目录是否存在
if (await adapter.exists(tempDir)) {
console.log(`清理临时目录: ${tempDir}`);
// 删除临时目录及其所有内容
await adapter.rmdir(tempDir, true);
console.log(`临时目录清理完成: ${tempDir}`);
}
} catch (error) {
console.log("清理临时目录失败:", error);
// 不抛出错误,因为这不是关键操作
}
};
/**
* 下载并解压ZIP文件到临时目录
*/
const downloadAndExtractToTemp = async (
adapter: Plugin['app']['vault']['adapter'],
tempDir: string,
downloadUrl: string
): Promise<void> => {
console.log(`开始下载文件: ${downloadUrl}`);
// 下载ZIP文件
let zipResponse;
try {
zipResponse = await requestUrl({
url: downloadUrl,
method: "GET",
});
console.log("文件下载完成,状态:", zipResponse.status);
} catch (error) {
console.log("下载失败:", error);
throw new Error("网络连接失败无法下载Pro版本文件");
}
if (!zipResponse.arrayBuffer) {
console.log("响应格式无效缺少arrayBuffer");
throw new Error("下载的文件格式无效");
}
console.log("正在解压文件到临时目录...");
console.log(`开始解压文件到临时目录: ${tempDir}`);
// 解压ZIP文件
let zipData: JSZip;
try {
const zip = new JSZip();
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
zipData = await zip.loadAsync(zipResponse.arrayBuffer);
console.log("ZIP文件解析成功");
} catch (error) {
console.log("ZIP文件解析失败:", error);
throw new Error("文件解压失败,可能文件已损坏");
}
// 创建临时目录
try {
if (!(await adapter.exists(tempDir))) {
await adapter.mkdir(tempDir);
console.log(`临时目录创建成功: ${tempDir}`);
} else {
console.log(`临时目录已存在: ${tempDir}`);
}
} catch (error) {
console.log("创建临时目录失败:", error);
throw new Error("无法创建临时目录");
}
// 解压所有文件到临时目录
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const files = Object.keys(zipData.files);
console.log(files);
console.log(`ZIP文件中包含 ${files.length} 个条目`);
let extractedCount = 0;
for (const filename of files) {
const file = zipData.files[filename];
// 跳过目录
if (file?.dir) {
console.log(`跳过目录: ${filename}`);
continue;
}
console.log(`正在解压文件: ${filename}`);
// 获取文件内容
const content = await file?.async("text");
if (!content) {
console.log(`跳过空文件: ${filename}`);
continue;
}
// 提取文件名(去掉路径前缀)
const pathParts = filename.split('/');
const actualFileName = pathParts[pathParts.length - 1];
// 直接写入到临时目录根目录,不创建子目录
const tempFilePath = `${tempDir}/${actualFileName}`;
// 写入文件到临时目录
await adapter.write(tempFilePath, content);
extractedCount++;
console.log(`文件解压完成: ${actualFileName} (${extractedCount}/${files.filter(f => !zipData.files[f].dir).length})`);
}
console.log(`所有文件解压完成,共解压 ${extractedCount} 个文件`);
} catch (error) {
console.log("文件解压过程中出错:", error);
throw new Error("文件解压过程中出现错误");
}
};
/**
* 从临时目录复制文件到插件目录
*/
const copyFilesFromTemp = async (
adapter: Plugin['app']['vault']['adapter'],
tempDir: string,
pluginDir: string
): Promise<void> => {
console.log("正在更新插件文件...");
console.log(`开始从临时目录复制文件到插件目录: ${tempDir} -> ${pluginDir}`);
// 需要复制的关键文件
const filesToCopy = ['main.js', 'styles.css', 'manifest.json'];
// 检查必需文件是否存在
const mainJsPath = `${tempDir}/main.js`;
if (!(await adapter.exists(mainJsPath))) {
console.log("关键文件缺失: main.js");
throw new Error("升级文件不完整,缺少关键组件");
}
// 复制文件
let copiedCount = 0;
for (const filename of filesToCopy) {
const tempFilePath = `${tempDir}/${filename}`;
const pluginFilePath = `${pluginDir}/${filename}`;
try {
if (await adapter.exists(tempFilePath)) {
const content = await adapter.read(tempFilePath);
await adapter.write(pluginFilePath, content);
copiedCount++;
} else if (filename !== 'main.js') {
console.log(`可选文件不存在,跳过: ${filename}`);
}
} catch (error) {
throw new Error(`文件更新失败: ${filename}`);
}
}
console.log(`文件复制完成,共复制 ${copiedCount} 个文件`);
};
/**
* 下载并安装Pro版本
*/
export const upgradeToProVersion = async (
plugin: Plugin,
dl_zip: string
): Promise<UpgradeResult> => {
const tempDir = '.infio_download_cache';
const adapter = plugin.app.vault.adapter;
try {
// 获取插件目录
const pluginDir = plugin.manifest.dir;
if (!pluginDir) {
console.log("插件目录未找到");
throw new Error("无法找到插件目录");
}
new Notice("正在加载...");
await cleanupTempDirectory(adapter, tempDir);
await downloadAndExtractToTemp(
adapter,
tempDir,
dl_zip
);
await copyFilesFromTemp(adapter, tempDir, pluginDir);
new Notice("加载完成成功升级为Pro");
await cleanupTempDirectory(adapter, tempDir);
setTimeout(() => {
console.log(`重载插件: ${plugin.manifest.id}`);
if (hasPluginsProperty(plugin.app)) {
plugin.app.plugins.reloadPlugin(plugin.manifest.id);
}
}, 1000);
return {
success: true,
message: "加载完成成功升级为Pro"
};
} catch (error) {
console.log("错误详情:", error);
// 发生错误时也要清理临时目录
await cleanupTempDirectory(adapter, tempDir);
const errorMessage = error instanceof Error ? error.message : "升级过程中出现未知错误";
console.log(`最终错误信息: ${errorMessage}`);
new Notice(`加载失败: ${errorMessage}`);
return {
success: false,
message: errorMessage
};
}
}