🐛 修复 #264

 支持 db.postAttachment 和 db.getAttachment
♻️ 重构版本更新机制
This commit is contained in:
muwoo 2023-10-18 11:02:14 +08:00
parent a7a85a7c62
commit bde5377282
12 changed files with 195 additions and 120 deletions

View File

@ -22,11 +22,11 @@
<template #renderItem="{ item }"> <template #renderItem="{ item }">
<a-list-item> <a-list-item>
<template #actions> <template #actions>
<a v-if="!item.plugin.isdownload && !item.plugin.isloading" key="list-loadmore-edit" <a v-if="!item.plugin?.isdownload && !item.plugin?.isloading" key="list-loadmore-edit"
@click="() => downloadPlugin(item.plugin)"> @click="() => downloadPlugin(item.plugin)">
<CloudDownloadOutlined style="font-size: 18px;"/> <CloudDownloadOutlined style="font-size: 18px;"/>
</a> </a>
<a v-if="item.plugin.isloading" key="list-loadmore-edit"> <a v-if="item.plugin?.isloading" key="list-loadmore-edit">
<LoadingOutlined style="font-size: 18px;"/> <LoadingOutlined style="font-size: 18px;"/>
</a> </a>
<a key="list-loadmore-edit" @click="() => showKeys(item)"> <a key="list-loadmore-edit" @click="() => showKeys(item)">
@ -36,11 +36,11 @@
<a-list-item-meta :description="`${item.keys.length} 份文档`"> <a-list-item-meta :description="`${item.keys.length} 份文档`">
<template #title> <template #title>
<div> <div>
<span>{{ item.plugin.pluginName }}</span> <span>{{ item.plugin?.pluginName }}</span>
</div> </div>
</template> </template>
<template #avatar> <template #avatar>
<a-avatar shape="square" :src="item.plugin.logo"/> <a-avatar shape="square" :src="item.plugin?.logo"/>
</template> </template>
</a-list-item-meta> </a-list-item-meta>
</a-list-item> </a-list-item>

View File

@ -1,6 +1,6 @@
{ {
"name": "rubick", "name": "rubick",
"version": "4.0.4", "version": "4.0.5",
"author": "muwoo <2424880409@qq.com>", "author": "muwoo <2424880409@qq.com>",
"private": true, "private": true,
"scripts": { "scripts": {
@ -69,7 +69,7 @@
"less-loader": "^5.0.0", "less-loader": "^5.0.0",
"prettier": "^2.8.4", "prettier": "^2.8.4",
"typescript": "~4.1.5", "typescript": "~4.1.5",
"vue-cli-plugin-electron-builder": "~2.1.1", "vue-cli-plugin-electron-builder": "3.0.0-alpha.4",
"worker-plugin": "^5.0.1" "worker-plugin": "^5.0.1"
}, },
"resolutions": { "resolutions": {

View File

@ -85,6 +85,10 @@ window.rubick = {
remove: (doc) => ipcSendSync('dbRemove', { doc }), remove: (doc) => ipcSendSync('dbRemove', { doc }),
bulkDocs: (docs) => ipcSendSync('dbBulkDocs', { docs }), bulkDocs: (docs) => ipcSendSync('dbBulkDocs', { docs }),
allDocs: (key) => ipcSendSync('dbAllDocs', { key }), allDocs: (key) => ipcSendSync('dbAllDocs', { key }),
postAttachment: (docId, attachment, type) =>
ipcSendSync('dbPostAttachment', { docId, attachment, type }),
getAttachment: (docId) => ipcSendSync('dbGetAttachment', { docId }),
getAttachmentType: (docId) => ipcSendSync('dbGetAttachmentType', { docId }),
}, },
dbStorage: { dbStorage: {
setItem: (key, value) => { setItem: (key, value) => {

View File

@ -204,4 +204,38 @@ export default class DB {
this.pouchDB = syncDb.pouchDB; this.pouchDB = syncDb.pouchDB;
await webdavClient.createReadStream(this.pouchDB); await webdavClient.createReadStream(this.pouchDB);
} }
public async postAttachment(
name: string,
docId: string,
attachment: Buffer | Uint8Array,
type: string
) {
const buffer = Buffer.from(attachment);
if (buffer.byteLength > this.docAttachmentMaxByteLength)
return this.errorInfo(
'exception',
'attachment data up to ' +
this.docAttachmentMaxByteLength / 1024 / 1024 +
'M'
);
try {
const result = await this.pouchDB.put({
_id: this.getDocId(name, docId),
_attachments: { 0: { data: buffer, content_type: type } },
});
result.id = this.replaceDocId(name, result.id);
return result;
} catch (e) {
return this.errorInfo(e.name, e.message);
}
}
async getAttachment(name: string, docId: string, len = '0') {
try {
return await this.pouchDB.getAttachment(this.getDocId(name, docId), len);
} catch (e) {
return null;
}
}
} }

View File

@ -11,8 +11,8 @@ type WebDavOptions = {
}; };
type DBInstance = { type DBInstance = {
loadIt: (stream: unknown) => void; loadIt: (stream: unknown, options?: any) => void;
dump: (stream: unknown) => void; dump: (stream: unknown, options?: any) => void;
}; };
export default class WebDav { export default class WebDav {
@ -54,7 +54,12 @@ export default class WebDav {
}).show(); }).show();
} }
const ws = new MemoryStream(); const ws = new MemoryStream();
dbInstance.dump(ws); dbInstance.dump(ws, {
filter: (doc) => {
// attachment 文档导出有问题,
return !doc._attachments;
},
});
ws.pipe( ws.pipe(
this.client.createWriteStream(this.cloudPath, {}, () => { this.client.createWriteStream(this.cloudPath, {}, () => {
new Notification({ new Notification({

View File

@ -157,13 +157,17 @@ class AdapterHandler {
*/ */
private async execCommand(cmd: string, modules: string[]): Promise<string> { private async execCommand(cmd: string, modules: string[]): Promise<string> {
return new Promise((resolve: any, reject: any) => { return new Promise((resolve: any, reject: any) => {
const args: string[] = [cmd] let args: string[] = [cmd].concat(
.concat( cmd !== 'uninstall' && cmd !== 'link'
cmd !== 'uninstall' ? modules.map((m) => `${m}@latest`) : modules ? modules.map((m) => `${m}@latest`)
) : modules
.concat('--color=always') );
.concat('--save') if (cmd !== 'link') {
.concat(`--registry=${this.registry}`); args = args
.concat('--color=always')
.concat('--save')
.concat(`--registry=${this.registry}`);
}
const npm = spawn('npm', args, { const npm = spawn('npm', args, {
cwd: this.baseDir, cwd: this.baseDir,

View File

@ -73,23 +73,31 @@ export default () => {
const init = (plugin, window: BrowserWindow) => { const init = (plugin, window: BrowserWindow) => {
if (view === null || view === undefined) { if (view === null || view === undefined) {
if (viewInstance.getView(plugin.name) && !commonConst.dev()) { createView(plugin, window);
view = viewInstance.getView(plugin.name).view; // if (viewInstance.getView(plugin.name) && !commonConst.dev()) {
window.setBrowserView(view); // view = viewInstance.getView(plugin.name).view;
view.inited = true; // window.setBrowserView(view);
viewReadyFn(window, plugin); // view.inited = true;
} else { // viewReadyFn(window, plugin);
createView(plugin, window); // } else {
viewInstance.addView(plugin.name, view); // createView(plugin, window);
} // viewInstance.addView(plugin.name, view);
// }
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
require('@electron/remote/main').enable(view.webContents); require('@electron/remote/main').enable(view.webContents);
} }
}; };
const createView = (plugin, window: BrowserWindow) => { const createView = (plugin, window: BrowserWindow) => {
const { tplPath, indexPath, development, name, main, pluginSetting, ext } = const {
plugin; tplPath,
indexPath,
development,
name,
main = 'index.html',
pluginSetting,
ext,
} = plugin;
let pluginIndexPath = tplPath || indexPath; let pluginIndexPath = tplPath || indexPath;
let preloadPath; let preloadPath;
let darkMode; let darkMode;
@ -160,7 +168,7 @@ export default () => {
window.removeBrowserView(view); window.removeBrowserView(view);
window.setSize(800, 60); window.setSize(800, 60);
executeHooks('PluginOut', null); executeHooks('PluginOut', null);
window.webContents.executeJavaScript(`window.initRubick()`); window.webContents?.executeJavaScript(`window.initRubick()`);
view = undefined; view = undefined;
}; };
@ -174,7 +182,7 @@ export default () => {
} catch(e) {} } catch(e) {}
} }
`; `;
view.webContents.executeJavaScript(evalJs); view.webContents?.executeJavaScript(evalJs);
}; };
return { return {

View File

@ -14,13 +14,18 @@ import { screenCapture } from '@/core';
import plist from 'plist'; import plist from 'plist';
import ks from 'node-key-sender'; import ks from 'node-key-sender';
import { DECODE_KEY } from '@/common/constans/main'; import {
DECODE_KEY,
PLUGIN_INSTALL_DIR as baseDir,
} from '@/common/constans/main';
import getCopyFiles from '@/common/utils/getCopyFiles'; import getCopyFiles from '@/common/utils/getCopyFiles';
import common from '@/common/utils/commonConst';
import mainInstance from '../index'; import mainInstance from '../index';
import { runner, detach } from '../browsers'; import { runner, detach } from '../browsers';
import DBInstance from './db'; import DBInstance from './db';
import getWinPosition from './getWinPosition'; import getWinPosition from './getWinPosition';
import path from 'path';
const runnerInstance = runner(); const runnerInstance = runner();
const detachInstance = detach(); const detachInstance = detach();
@ -77,8 +82,29 @@ class API extends DBInstance {
} }
public openPlugin({ data: plugin }, window) { public openPlugin({ data: plugin }, window) {
if (plugin.platform && !plugin.platform.includes(process.platform)) {
return new Notification({
title: `插件不支持当前 ${process.platform} 系统`,
body: `插件仅支持 ${plugin.platform.join(',')}`,
icon: plugin.logo,
}).show();
}
window.setSize(window.getSize()[0], 60); window.setSize(window.getSize()[0], 60);
this.removePlugin(null, window); this.removePlugin(null, window);
// 模板文件
if (!plugin.main) {
plugin.tplPath = common.dev()
? 'http://localhost:8083/#/'
: `file://${__static}/tpl/index.html`;
}
if (!plugin.indexPath) {
const pluginPath = path.resolve(baseDir, 'node_modules', plugin.name);
plugin.indexPath = `file://${path.join(
pluginPath,
'./',
plugin.main || ''
)}`;
}
runnerInstance.init(plugin, window); runnerInstance.init(plugin, window);
this.currentPlugin = plugin; this.currentPlugin = plugin;
window.webContents.executeJavaScript( window.webContents.executeJavaScript(

View File

@ -54,4 +54,20 @@ export default class DBInstance {
public dbImport({ data }) { public dbImport({ data }) {
return dbInstance.importDb(data.target); return dbInstance.importDb(data.target);
} }
public dbPostAttachment({ data }) {
const { docId, attachment, type } = data;
return dbInstance.postAttachment(this.DBKEY, docId, attachment, type);
}
public dbGetAttachment({ data }) {
return dbInstance.getAttachment(this.DBKEY, data.docId);
}
public async dbGetAttachmentType({ data }) {
const res: any = await this.dbGet(data.docId);
if (!res || !res._attachments) return null;
const result = res._attachments[0];
return result ? result.content_type : null;
}
} }

View File

@ -0,0 +1,27 @@
// for referer policy, we can't use it in renderer
import axios from 'axios';
const RELEASE_URL = 'https://api.github.com/repos/rubickCenter/rubick/releases';
export const getLatestVersion = async (isCheckBetaUpdate = false) => {
let res = '';
try {
res = await axios
.get(RELEASE_URL, {
headers: {
Referer: 'https://github.com',
},
})
.then((r) => {
const list = r.data;
if (isCheckBetaUpdate) {
const betaList = list.filter((item) => item.name.includes('beta'));
return betaList[0].name;
}
const normalList = list.filter((item) => !item.name.includes('beta'));
return normalList[0].name;
});
} catch (err) {
console.log(err);
}
return res;
};

View File

@ -1,96 +1,45 @@
import { dialog } from 'electron'; import { dialog, shell } from 'electron';
import { autoUpdater } from 'electron-updater';
import pkg from '../../../package.json'; import pkg from '../../../package.json';
import { main } from '../browsers'; import { lt } from 'semver';
import { getLatestVersion } from './getLatestVersion';
const version = pkg.version;
const downloadUrl = 'https://github.com/rubickCenter/rubick/releases/latest';
class VersionHandler { const checkVersion = async () => {
private lastestVersion: string; const res: string = await getLatestVersion();
private currentVersion: string; if (res !== '') {
private releaseNotes: string; const latest = res;
private isUpdate: boolean; const result = compareVersion2Update(version, latest);
if (result) {
constructor() { dialog
this.lastestVersion = ''; .showMessageBox({
this.currentVersion = pkg.version; type: 'info',
this.releaseNotes = ''; title: 'Rubick 更新提示',
this.isUpdate = false; buttons: ['Yes', 'No'],
autoUpdater.autoDownload = false; message: `发现新版本 v${latest},是否更新?`,
autoUpdater.autoInstallOnAppQuit = false; })
.then((res) => {
if (res.response === 0) {
// if selected yes
shell.openExternal(downloadUrl);
}
});
}
} else {
return false;
} }
};
checkForMacAndWindows() { // if true -> update else return false
let sendUpdateMsg = false; const compareVersion2Update = (current: string, latest: string) => {
autoUpdater.removeAllListeners(); try {
// update-available 会触发多次,限制只通知一次 if (latest.includes('beta')) {
autoUpdater.checkForUpdates(); return false;
}
autoUpdater.on('download-progress', ({ percent }) => { return lt(current, latest);
console.log('下载进度', percent); } catch (e) {
// if (percent < 50) { return false;
// }
this.isUpdate = true;
});
autoUpdater.on('update-available', (info) => {
if (sendUpdateMsg) return;
const { version, releaseName = 'normal', releaseNotes } = info;
this.lastestVersion = version;
sendUpdateMsg = true;
autoUpdater.on('update-downloaded', () => {
console.log('下载完成');
this.isUpdate = false;
if (releaseName === 'major') {
autoUpdater.quitAndInstall(true, true);
}
const mainWindow = main().getWindow();
dialog
.showMessageBox(mainWindow, {
title: '版本更新',
message: `发现新版本${this.lastestVersion},是否更新\n\n${releaseNotes}`,
type: 'info',
buttons: ['稍后提示', '立即更新'],
})
.then(({ response }) => {
console.log(response);
if (response === 1) {
this.update();
}
});
});
// 自动下载安装包
if (!this.isUpdate) {
autoUpdater.downloadUpdate();
console.log('download');
}
});
autoUpdater.on('update-not-available', (info) => {
if (sendUpdateMsg) return;
sendUpdateMsg = true;
});
autoUpdater.on('error', () => {
this.isUpdate = false;
});
} }
};
checkUpdate(): void { export default checkVersion;
this.checkForMacAndWindows();
}
update() {
let sendUpdateMsg = false;
this.checkUpdate();
autoUpdater.on('update-downloaded', () => {
if (sendUpdateMsg) return;
sendUpdateMsg = true;
this.isUpdate = false;
autoUpdater.quitAndInstall(true, true);
// App.quit();
});
}
}
export default new VersionHandler();

View File

@ -22,6 +22,7 @@ import {
import '../common/utils/localPlugin'; import '../common/utils/localPlugin';
import registerySystemPlugin from './common/registerySystemPlugin'; import registerySystemPlugin from './common/registerySystemPlugin';
import checkVersion from './common/versionHandler';
class App { class App {
public windowCreator: { init: () => void; getWindow: () => BrowserWindow }; public windowCreator: { init: () => void; getWindow: () => BrowserWindow };
@ -62,6 +63,7 @@ class App {
} }
onReady() { onReady() {
const readyFunction = async () => { const readyFunction = async () => {
checkVersion();
await localConfig.init(); await localConfig.init();
const config = await localConfig.getConfig(); const config = await localConfig.getConfig();
if (!config.perf.common.guide) { if (!config.perf.common.guide) {